Patterns & Constructs as Knowledge-Gap Indicators
Almost every design pattern/principle can be used as a completeness-check on domain knowledge.
When you’re reaching for a pattern, ask “does the domain knowledge mandate the coupling this pattern would add or remove?” If you can’t answer, you’ve found a knowledge gap. If you can, the knowledge prescribes the structure directly — the pattern adds nothing the domain hadn’t already decided.
Each family of patterns sharpens that question in its own way. Where a family frames it differently, its framing opens above that family’s posts.
Pick a post on the left. It opens on the right.
Most design patterns and principles can be used as a completeness-check on domain knowledge.
Almost every design pattern/principle can be used as a completeness-check on domain knowledge. For FP typeclasses and algebraic structures, the mathematics (laws, coherence, composability) is the instrument; domain knowledge is why we reach for it. The domain-knowledge lens asks whether the construct encodes domain structure.
Almost every design pattern/principle can be used as a completeness-check on domain knowledge. For DDD’s tactical patterns, the justification is explicitly domain-driven — which makes the gap check sharper: is the pattern the knowledge itself, or ceremony wearing its clothes?
Almost every design pattern/principle can be used as a completeness-check on domain knowledge. Analysis patterns are the purest case: they ARE recurring domain structures — so the gap question is simply “is our situation genuinely one of these?”
Process models are different from design patterns: the artifact claims to describe the domain, so the gap question is not “should we use this?” but “does this model tell the truth?” Between the process (a knowledge claim) and the notation (modelling vocabulary) sits every silent misrepresentation.
Case study: Abstract Factory Pattern
GoF Intent: “Provide an interface for creating families of related or dependent objects without specifying their concrete classes.” [1]
The domain knowledge gap it signals
The set of concrete types that must vary together (the “product family”) is domain knowledge. If your domain model already crystallizes that “when you’re in context X, you use implementations A₁, B₁, C₁; in context Y, you use A₂, B₂, C₂” — and that these groupings are cohesive units in the domain (e.g., a regulatory regime, a hardware platform, a deployment tier) — then an Abstract Factory is just embodying that knowledge. The factory interface is the domain’s own abstraction; the concrete factories are its real partitions.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Strategy Pattern
GoF Intent: “Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.” [1]
The domain knowledge gap it signals
The existence of multiple algorithms for the same operation is domain knowledge. If your domain model already crystallizes that “this operation admits variants A, B, C, and there is a domain rule that selects among them,” then Strategy is just embodying that knowledge. The strategy interface is the domain’s own abstraction (the what); the concrete strategies are its real alternatives (the how).
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Observer Pattern
GoF Intent: “Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.” [1]
The domain knowledge gap it signals
The fact that “a change in A constitutes a domain event that B, C, and D must know about” is domain knowledge. If your domain model already crystallizes that certain state transitions are meaningful events to other entities — that the event is a first-class domain concept — then Observer is just embodying that knowledge. The subject interface is the domain’s own event source; the observer interface is the domain’s own event consumer.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Dispose Pattern
The domain knowledge gap it signals
The fact that a resource has a bounded lifetime — that acquiring it and releasing it are two ends of a single domain transaction — is domain knowledge. If your domain model already crystallizes that “this resource must be acquired before use and released after, and the interval between acquisition and release is a meaningful domain scope (a session, a connection, a lock window),” then Dispose is just embodying that knowledge. The disposal interface is the domain’s own “bounded lifetime” contract; the using block is the domain’s own scoping rule made mechanical.
Case study: Adapter Pattern
GoF Intent: “Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.” [1]
The domain knowledge gap it signals
The fact that two entities must talk to each other but speak incompatible languages is domain knowledge. If your domain model already crystallizes that “system A understands protocol α, system B understands protocol β, and there is a domain rule requiring A and B to interoperate,” then Adapter is just embodying that knowledge. The adapter is the domain’s own translation rule; the adapted interface is the domain’s own target vocabulary.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Builder Pattern
GoF Intent: “Separate the construction of a complex object from its representation so that the same construction process can create different representations.” [1]
The domain knowledge gap it signals
The fact that constructing an object is a multi-step process whose steps are domain-significant — not just parameter-passing — is domain knowledge. If your domain model already crystallizes that “creating an X requires collecting parts A, B, C from different sources, in an order that matters, with validation between steps,” then Builder is just embodying that knowledge. The builder is the domain’s own construction protocol; each step is a domain operation.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Command Pattern
GoF Intent: “Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.” [1, p. 233]
The pattern decouples invoker from receiver — the invoker must NOT know which concrete receiver handles the request. This is structural: the invoker dispatches to a Command interface and stays receiver-agnostic. (Queuing, logging, and undo follow from the encapsulation.)
The knowledge gap it signals
Command needs the invoker to be receiver-agnostic. The question is not “would the domain break if the invoker knew?” — the pattern exists because the invoker must NOT know. The gap: does your architecture need receiver-agnostic dispatch?
CQS/CQRS “Command” is a different concept — a message that mutates state (contrast: Query) — and is not covered here.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Composite Pattern
GoF Intent: “Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.” [1]
The GoF Composite pattern composes objects into tree structures to represent part-whole hierarchies. It lets clients treat individual objects and compositions of objects uniformly through a shared Component interface.
The domain knowledge gap it signals
The domain recognizes a part-whole hierarchy where the whole and its parts answer the same questions. A department and its sub-departments both have budgets. A file and a directory both have sizes. When you find yourself reaching for Composite, the gap is: does the domain actually treat wholes and parts uniformly, or are you imposing a tree structure on something the domain views as flat or differently?
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Decorator Pattern
GoF Intent: “Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.” [1]
The domain knowledge gap it signals
A decorator adds behavior without changing the interface or what the object is. Domain intuition: a paper invoice through administration. One office stamps “PAID” — still an invoice, now endorsed. Another stamps “CONFIDENTIAL” — still an invoice, now classified. Each endorsement decorates without changing identity; for clients it is still the same object. The gap: what is intrinsic to the entity vs. an optional endorsement. An unpaid invoice is still an invoice — “paid” is an endorsement, not part of what makes it an invoice. Baking payment status into the invoice class reveals the gap.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: State Pattern
GoF Intent: “Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.” [1]
The domain knowledge gap it signals
That an entity’s behavior depends on which state it’s in — and that the set of states and the transitions between them are domain rules — is domain knowledge. If your domain model already crystallizes a state machine — “an order starts as Pending, can transition to Confirmed or Cancelled, and in each state different operations are allowed” — then State is just embodying that knowledge: each state is a domain state; each transition is a domain rule.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Template Method Pattern
GoF Intent: “Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.” [1]
The domain knowledge gap it signals
The fact that operations share an invariant skeleton with variant steps can be domain knowledge — but only when the skeleton itself is a domain rule. If your domain model already crystallizes that “the regulatory review process has the same framework (notify, review, approve) regardless of jurisdiction; only the steps within each phase differ,” then Template Method is just embodying that knowledge. The base class is the domain’s own procedural framework; the hook methods are the domain’s own variation points. In most applications, however, the “skeleton” is code similarity mistaken for domain structure — the domain expert sees one coherent algorithm, not a template with extension points.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Facade Pattern
GoF Intent: “Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.” [1]
The domain knowledge gap it signals
The fact that a subsystem presents a single entry point — that “to interact with this part of the system, you go through here” — is domain knowledge if the boundary is a domain boundary. If your domain model already crystallizes that “ordering involves inventory, payment, and shipping, but from the customer’s perspective it’s one operation,” then Facade is just embodying that knowledge. The facade is the domain’s own simplified interface; the subsystem behind it is the domain’s own internal complexity.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Singleton Pattern
GoF Intent: “Ensure a class only has one instance, and provide a global point of access to it.” [1]
The domain knowledge gap it signals
The fact that there is exactly one of something — and that “exactly one” is a domain rule, not a coincidence — is domain knowledge. If your domain model already crystallizes that “there is one and only one tax authority, one central bank, one system clock,” then Singleton is just embodying that knowledge. The singleton instance is the domain’s own uniqueness constraint; the global access point is the domain’s own “ask the one X.”
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Bridge Pattern
GoF Intent: “Decouple an abstraction from its implementation so that the two can vary independently.” [1]
The domain knowledge gap it signals
The fact that an abstraction and its implementation vary independently is domain knowledge — but the two axes need not both be domain-recognized. The GoF’s own Window/WindowImp example separates domain abstraction from platform-specific implementation; one axis is technical. If your domain model crystallizes that “a Payment can be Credit, Debit, or BankTransfer, and it can be processed via Stripe, PayPal, or DirectBank — and these two axes are independent concerns,” then Bridge is just embodying that knowledge. The abstraction hierarchy is the domain’s own “what” axis; the implementation hierarchy may be domain (payment processors) or technical (rendering engines, database backends). Both are valid; the true test is independent variation, not domain recognition of both axes.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Proxy Pattern
GoF Intent: “Provide a surrogate or placeholder for another object to control access to it.” [1]
The domain knowledge gap it signals
The fact that access to an entity must be mediated — that “you don’t talk to X directly; you go through a surrogate that controls, delays, or logs the interaction” — is domain knowledge when the mediation is a domain rule. If your domain model already crystallizes that “access to the ledger requires authorization checks and an audit trail,” then Proxy is just embodying that knowledge. The proxy is the domain’s own access-control rule; the real subject is the domain’s own protected resource.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Iterator Pattern
GoF Intent: “Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.” [1]
The domain knowledge gap it signals
The fact that a collection should be traversed without exposing its structure — and that the traversal itself is a domain-meaningful act — is domain knowledge. If your domain model already crystallizes that “you can walk through the orders, one by one, in submission order, and the fact that they’re stored in a B-tree is irrelevant to the walker,” then Iterator is just embodying that knowledge. The iterator is the domain’s own traversal protocol; the aggregate is the domain’s own collection.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Factory Method Pattern
GoF Intent: “Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.” [1]
The domain knowledge gap it signals
The fact that the choice of which concrete type to create is a domain decision — and that the decision belongs to a specific domain context — is domain knowledge. If your domain model already crystallizes that “when processing a domestic order, create a DomesticTaxCalculator; when processing an international order, create an InternationalTaxCalculator — and the order type determines the calculator type,” then Factory Method is just embodying that knowledge. The factory method is the domain’s own “decide which implementation” rule.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Prototype Pattern
GoF Intent: “Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.” [1]
The domain knowledge gap it signals
The fact that creating an object by copying an existing one is the domain-correct way to instantiate it — not just a performance optimization — is domain knowledge. If your domain model already crystallizes that “a new insurance policy starts as a copy of the template policy; a new document inherits the template’s clauses and only overrides what differs,” then Prototype is just embodying that knowledge. The prototype instance is the domain’s own exemplar; the clone operation is the domain’s own “create from template” rule.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Flyweight Pattern
GoF Intent: “Use sharing to support large numbers of fine-grained objects efficiently.” [1]
The domain knowledge gap it signals
The fact that many objects share the same intrinsic state — and that this shared state is domain-significant, not an accident of design — is domain knowledge. If your domain model already crystallizes that “every character rendered in this document shares a font, size, and style; the only thing that varies per character is its position,” then Flyweight is just embodying that knowledge. The flyweight object is the domain’s own “shared properties” bundle; the extrinsic state is the domain’s own “per-instance” data.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Chain of Responsibility Pattern
GoF Intent: “Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.” [1]
The domain knowledge gap it signals
The fact that a request should be passed along a chain of handlers until one handles it — and that the order of handlers is a domain rule — is domain knowledge. If your domain model already crystallizes that “an expense report goes to the manager; if within their limit, approved; otherwise, passes to the director; if within their limit, approved; otherwise, passes to the VP,” then Chain of Responsibility is just embodying that knowledge. Each handler is a domain role; the chain order is the domain’s own escalation path.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Mediator Pattern
GoF Intent: “Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.” [1]
The domain knowledge gap it signals
The fact that a set of entities should communicate only through a central coordinator — and that the coordination logic is itself a domain concept — is domain knowledge. If your domain model already crystallizes that “when a flight lands, the gate must be updated, baggage handling must be notified, and the arrival board must refresh — and no one talks to anyone else directly; the control tower coordinates everything,” then Mediator is just embodying that knowledge. The mediator is the domain’s own coordination hub; the colleagues are the domain’s own independent actors.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Memento Pattern
GoF Intent: “Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.” [1]
The domain knowledge gap it signals
The fact that an entity’s state can be captured and restored — and that this is a domain operation, not a debugging aid — is domain knowledge. If your domain model already crystallizes that “a document has versions; an order can be rolled back to a previous state; a workflow checkpoint can be saved and resumed,” then Memento is just embodying that knowledge. The memento is the domain’s own snapshot; the originator is the domain’s own versioned entity; the caretaker is the version keeper.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Visitor Pattern
GoF Intent: “Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.” [1]
The domain knowledge gap it signals
The fact that an operation must be defined over a fixed set of element types — and that new operations are added more often than new element types — is domain knowledge. If your domain model already crystallizes that “a tax rule must be applied to every kind of financial instrument (stock, bond, derivative), and new tax rules appear more frequently than new instrument types,” then Visitor is just embodying that knowledge. The element hierarchy is the domain’s own stable taxonomy; each visitor is a domain operation that must handle every case.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Interpreter Pattern
GoF Intent: “Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.” [1]
The domain knowledge gap it signals
The fact that the domain has its own language — a grammar, a syntax, and rules for evaluating sentences in that language — is domain knowledge. If your domain model already crystallizes that “a trading system has a domain-specific language for defining trading strategies; a rule engine evaluates expressions like ‘if risk > threshold and exposure > limit, then reject’,” then Interpreter is just embodying that knowledge. The grammar is the domain’s own syntax; each expression type is a domain concept.
References
[1] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design patterns: Elements of reusable object-oriented software. Reading, MA: Addison-Wesley, 1994.
Case study: Content-Based Router Pattern
EIP Intent: “How do we handle a situation where the implementation of a single logical function (e.g., inventory check) is spread across multiple physical systems?” [2]
The domain knowledge gap it signals
The fact that messages carry content that determines their destination is domain knowledge. If your domain model crystallizes that “an order from region R goes to warehouse W, a payment above threshold T goes to fraud-check,” then Content-Based Router embodies that knowledge. The routing table is the domain’s own dispatch logic; each destination is a domain actor that receives specific kinds of work.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Publish-Subscribe Channel Pattern
EIP Intent: “How can the sender broadcast an event to all interested receivers?” [2]
The domain knowledge gap it signals
The fact that multiple parties independently care about the same occurrence is domain knowledge. If your domain model already crystallizes that “when an order is fulfilled, the warehouse needs to restock, the customer needs a notification, the loyalty program needs to update points, and the finance system needs to recognize revenue,” then Publish-Subscribe is just embodying that knowledge. The topic is the domain event; the subscribers are domain actors who, by the rules of the domain, must react when that event occurs.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Dead Letter Channel Pattern
EIP Intent: “What will the messaging system do with a message it cannot deliver?” [2]
The domain knowledge gap it signals
The fact that some messages cannot be delivered and that the domain has rules for what happens then is domain knowledge. If your domain model already crystallizes that “an insurance claim missing a policy number is undeliverable and goes to manual triage; a payment with a mismatched currency code goes to the reconciliation desk; and anything else unrouteable gets queued for the operations team,” then Dead Letter Channel is just embodying that knowledge. The dead-letter queue is the domain’s own undeliverable-work queue; the dead-letter consumer is the domain’s own exception-handling actor.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Message Filter Pattern
EIP Intent: “How can a component avoid receiving uninteresting messages?” [2]
The domain knowledge gap it signals
The fact that a component should only receive a subset of the messages flowing through the system is domain knowledge. If your domain model crystallizes that “the premium-customer system cares only about orders from customers at loyalty tier 3 or above; the standard-order pipeline should never see test-mode orders,” then Message Filter embodies that knowledge. The filter predicate is the domain’s own membership condition.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Recipient List Pattern
EIP Intent: “How do we route a message to a list of dynamically specified recipients?” [2]
The domain knowledge gap it signals
The fact that a message has a domain-determined list of recipients — where the list itself is a domain artifact, not a configuration file — is domain knowledge. If your domain model already crystallizes that “a customs declaration must go to the importing country’s customs authority, the exporting country’s customs authority, the carrier, and the importer of record; a trade confirmation goes to both counterparties’ settlement desks,” then Recipient List is just embodying that knowledge. The list of recipients is the domain’s own distribution rule.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Splitter Pattern
EIP Intent: “How can we process a message if it contains multiple elements, each of which may have to be processed in a different way?” [2]
The domain knowledge gap it signals
The fact that a composite message is domain-recognized as containing independently processable parts, and that the decomposition rule is a domain rule, is domain knowledge. If your domain model already crystallizes that “an insurance claim package contains a claim form, a police report, medical records, and repair estimates — each processed by a different team,” then Splitter is just embodying that knowledge. The decomposition rule is the domain’s own partitioning; each sub-message is a domain-recognized unit of work.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Aggregator Pattern
EIP Intent: “How do we combine the results of individual, but related messages so that they can be processed as a whole?” [2]
The domain knowledge gap it signals
The fact that multiple independent messages together form a domain-recognized whole — and that the domain specifies when the whole is complete — is domain knowledge. If your domain model already crystallizes that “a trade is complete only when we have both the execution report from the exchange and the allocation from the trader; a purchase order is fully processed when all its line items have been fulfilled from their respective warehouses,” then Aggregator is just embodying that knowledge. The completion condition is the domain’s own completeness rule; the aggregated result is the domain’s own composite state.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Scatter-Gather Pattern
EIP Intent: “How do you maintain the overall message flow when a message needs to be sent to multiple recipients, each of which may send a reply?” [2]
The domain knowledge gap it signals
The fact that a single request requires answers from multiple domain sources, and that the best answer is a domain-defined composition of those answers, is domain knowledge. If your domain model already crystallizes that “to price a shipment, you need quotes from carrier A, carrier B, and carrier C, then you select the lowest; to approve a loan, you need a credit-score check, an income-verification check, and a fraud check — all must pass,” then Scatter-Gather is just embodying that knowledge. The recipients are the domain’s own information sources; the aggregation rule (lowest, all-pass, majority) is the domain’s own decision procedure.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Routing Slip Pattern
EIP Intent: “How do we route a message consecutively through a series of processing steps when the sequence of steps is not known at design-time and may vary for each message?” [2]
The domain knowledge gap it signals
The fact that a message must visit a sequence of processors, and that the sequence itself is a domain process that changes based on domain rules, is domain knowledge. If your domain model already crystallizes that “a purchase order above $10,000 goes: budget check → compliance review → director approval → procurement; a purchase order below $10,000 goes: compliance review → procurement — and for international orders, add export-control review after compliance,” then Routing Slip is just embodying that knowledge. The slip is the domain’s own process definition; each step is a domain actor in a domain sequence.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Process Manager Pattern
EIP Intent: “How do we route a message through multiple processing steps when the required steps may not be known at design-time and may not be sequential?” [2]
The domain knowledge gap it signals
The fact that a long-running multi-step process has a domain-defined state machine — where states, transitions, and compensating actions are all domain concepts — is domain knowledge. If your domain model crystallizes that “a trade lifecycle is: initiated → allocated → affirmed → confirmed → settled; an insurance claim lifecycle is: filed → assessed → approved/rejected → paid; and each state transition has domain preconditions and domain rollback procedures,” then Process Manager embodies that knowledge. The state machine is the domain’s own lifecycle; each transition is a domain event; compensating actions are the domain’s own reversal procedures.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Message Broker Pattern
EIP Intent: “How can you decouple the destination of a message from the sender and maintain central control over the flow of messages?” [2]
The domain knowledge gap it signals
The fact that the systems in a domain are decoupled — that system A should not know about system B, that the topology is a domain property not a deployment detail — is domain knowledge. If your domain model crystallizes that “the order-entry system produces orders; the warehouse system consumes orders; the revenue-recognition system consumes fulfilled orders — and none of them should know about each other because they’re separate bounded contexts with different ownership and release cadences,” then Message Broker embodies that knowledge. The broker is the domain’s decentralized hub; the channels are the domain’s communication paths.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Content Enricher Pattern
EIP Intent: “How do we communicate with another system if the message originator does not have all the required data items available?” [2]
The domain knowledge gap it signals
The fact that a message arrives with incomplete information and that the missing information has a domain-designated source is domain knowledge. If your domain model already crystallizes that “an order message carries a customer ID, but the warehouse needs the shipping address — which is owned by the customer profile service,” then Content Enricher is just embodying that knowledge. The missing field is domain information that belongs to a different domain authority; the enricher is the domain’s own information-completion step.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Content Filter Pattern
EIP Intent: “How do you simplify dealing with a large message, when you are interested only in a few data items?” [2]
The domain knowledge gap it signals
The fact that a downstream component should receive only a subset of the fields in a message — and that the subset is defined by the component’s domain responsibility, not by technical parsimony — is domain knowledge. If your domain model crystallizes that “the compliance-audit system only needs the trade ID, counterparty, and amount — not the full trade message with settlement instructions, broker details, and execution venue; the customer notification system only needs the order number, status, and tracking link — not the internal warehouse-routing fields,” then Content Filter embodies that knowledge. The field subset is the consumer’s domain interface; the dropped fields are information the consumer has no domain authority to see.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Normalizer Pattern
EIP Intent: “How do you process messages that are semantically equivalent, but arrive in a different format?” [2]
The domain knowledge gap it signals
Multiple systems expressing the same domain concept in different formats, with a canonical representation as the domain’s own definition, is domain knowledge. If your domain model crystallizes that “system A represents currency as USD, system B as 840, system C as $ — but the domain recognizes ISO 4217 alpha codes as authoritative,” then Normalizer embodies that knowledge. The canonical format is the domain’s own type; the conversion rules are the domain’s own translation from local dialects to the canonical form.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Canonical Data Model Pattern
EIP Intent: “How can you minimize dependencies when integrating applications that use different data formats?” [2]
The domain knowledge gap it signals
The fact that an enterprise has a shared vocabulary for a concept that transcends any single system — and that this vocabulary is maintained as a domain artifact by domain governance, not by individual development teams — is domain knowledge. If your domain model crystallizes that “a Customer has a stable definition across order-entry, billing, support, and marketing — though each system uses a different subset of Customer attributes,” then Canonical Data Model embodies that knowledge. The CDM is the domain’s own enterprise-wide type system; message transformations are mappings from local vocabularies to the shared vocabulary.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Claim Check Pattern
EIP Intent: “How can we reduce the data volume of message sent across the system without sacrificing information content?” [2]
The domain knowledge gap it signals
The fact that a message carries a reference to bulk data rather than the data itself — and that the reference system is the domain’s own data store, not a workaround for messaging infrastructure — is domain knowledge. If your domain model crystallizes that “the order message carries an order ID, and the full order detail (line items, pricing, tax calculation) is retrievable from the order-management system; the claim message carries a claim number, and the supporting documents (police report, photos, estimates) live in the document-management system,” then Claim Check embodies that knowledge. The claim-check key is the domain’s own identifier; the data store is the domain’s own system of record; the retrieval step is the domain’s own information-access pattern.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Command Message Pattern
EIP Intent: “How can messaging be used to invoke a procedure in another application?” [2]
The domain knowledge gap it signals
The fact that a message represents an instruction — “do this” — is a message-semantics choice with domain consequences. If your domain model already crystallizes that “do X” and “X happened” are distinct domain actions — the domain recognizes that commands can be rejected and events record what already occurred — then Command Message is just embodying that knowledge. The command is the domain’s own imperative; the command’s receiver is the domain’s own designated executor; the command’s validation rules are the domain’s own preconditions.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Document Message Pattern
EIP Intent: “How can messaging be used to transfer data between applications?” [2]
The domain knowledge gap it signals
The fact that a message carries a self-contained unit of data that the receiver interprets — where the meaning is in the data, not in the sending — is a message-intent choice with domain consequences. If your domain model crystallizes that “a purchase order is a document — it carries the order details, and the receiving system decides what to do with it,” then Document Message embodies that knowledge. The document is the domain’s own information unit; the receiver’s autonomy in interpreting it is the domain’s own separation of concerns between sender and receiver.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Event Message Pattern
EIP Intent: “How can messaging be used to transmit events from one application to another?” [2]
The domain knowledge gap it signals
The fact that something happened — and that the system boundary determines whether a message is a fact or an instruction — is domain knowledge when the domain itself distinguishes the two. If your domain model crystallizes that “‘OrderPlaced’ is an event — it records that an order was created; no receiver can reject an event, because it already happened,” then Event Message embodies that knowledge. The event is the domain’s own fact; the event’s immutability is the domain’s own recognition that the past cannot be changed.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Request-Reply Pattern
EIP Intent: “When an application sends a message, how can it get a response from the receiver?” [2]
The domain knowledge gap it signals
The fact that a message requires a response — and that the domain specifies what the response must contain and when it must arrive — is domain knowledge. If your domain model already crystallizes that “when a payment gateway is asked to authorize a transaction, it must respond with an approval code or a decline reason within 30 seconds; when a pricing engine is queried for a quote, it must respond with a price or an error before the caller times out,” then Request-Reply is just embodying that knowledge. The request is the domain’s own query; the reply is the domain’s own answer; the timeout is the domain’s own SLA.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Correlation Identifier Pattern
EIP Intent: “How does a requestor that has received a reply know which request this is the reply for?” [2]
The domain knowledge gap it signals
The fact that independent messages belong to the same domain entity — and that the domain itself defines the entity by which messages are grouped — is domain knowledge. If your domain model crystallizes that “an OrderCreated, an OrderShipped, and an OrderDelivered all belong to the same order, identified by the order ID,” then Correlation Identifier embodies that knowledge. The correlation ID is the domain’s own entity identifier; the messages that share it are the domain’s own lifecycle events for that entity.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Competing Consumers Pattern
EIP Intent: “How can a messaging client process multiple messages concurrently?” [2]
The domain knowledge gap it signals
Work that is domain-interchangeable across workers, where assignment carries no domain significance, is domain knowledge. If your domain crystallizes that “any order processor can handle any order; any worker can resize any image,” then Competing Consumers embodies that knowledge. The pool of consumers is the domain’s own pool of interchangeable workers; the competing-consumers mechanism is the domain’s own work-distribution pattern.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Idempotent Receiver Pattern
EIP Intent: “How can a message receiver deal with duplicate messages?” [2]
The domain knowledge gap it signals
The fact that duplicate messages are a domain reality — and that the domain itself defines what “the same message” means — is domain knowledge. If your domain model crystallizes that “a trade-execution message is uniquely identified by its trade reference; receiving the same trade reference twice means ‘you already executed this trade — do nothing,’” then Idempotent Receiver embodies that knowledge. The deduplication key is the domain’s own identity concept; the idempotent behavior is the domain’s own rule for handling repetitions.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Wire Tap Pattern
EIP Intent: “How do you inspect messages that travel on a point-to-point channel?” [2]
The domain knowledge gap it signals
The fact that observing messages is a domain requirement — for audit, compliance, or monitoring purposes that the domain itself specifies — is domain knowledge. If your domain model crystallizes that “every trade message must be copied to the audit system for regulatory record-keeping; every payment message must be copied to the fraud-detection system for real-time screening; every customer-interaction message must be copied to the analytics system for business intelligence,” then Wire Tap embodies that knowledge. The tap point is the domain’s own observation point; the tap destination is the domain’s own monitoring actor; the tap’s read-only semantics reflect the domain requirement that observation must not alter the observed flow.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Message History Pattern
EIP Intent: “How can we effectively analyze and debug the flow of messages in a loosely coupled system?” [2]
The domain knowledge gap it signals
The fact that the path a message took through the system is domain-relevant — for audit, debugging, or traceability purposes that the domain itself specifies — is domain knowledge. If your domain model crystallizes that “a trade message must record every system it passed through, because regulators require a complete audit trail; a claim message must record every processing step, because the claims department needs to reconstruct the adjudication path for disputed claims,” then Message History embodies that knowledge. The history is the domain’s own audit trail; the recorded steps are the domain’s own processing stations; the history’s integrity is the domain’s own requirement for tamper-proof provenance.
References
[2] G. Hohpe and B. Woolf, Enterprise integration patterns: Designing, building, and deploying messaging solutions. Boston, MA: Addison-Wesley, 2003.
Case study: Monad Pattern
Yorgey (Typeclassopedia): A monad sequences computations in a context, binding each result into the next. [3]
The domain knowledge gap it signals
Some operations have structured non-value outcomes a domain expert would reason about: “can return nothing,” “can fail with a domain error,” “depends on flowing context,” “must run in an order the world fixes.” In FP this knowledge lives in the underlying type — Maybe, Either, Reader, IO/Task — and the Monad pattern composes these outcomes so downstream computations respect them. (The monadic interface — bind/flatMap — is the generic machinery; the domain carrier is the underlying type.) Effect sequencing is knowledge too: write-before-acknowledge is an audit obligation; acquire-then-release is a resource fact.
Case study: Functor
Yorgey (Typeclassopedia): A functor is a type constructor whose values can be mapped over. [3]
The domain knowledge gap it signals
The fact that a domain transformation must be lifted into a context — that the operation happens inside a container or effect, and the container’s structure must be preserved — is domain knowledge. If your domain model already crystallizes that “every field in this validated form must be transformed to canonical form without discarding validation failures,” then Functor is just embodying that knowledge. The map operation is the domain’s own “transform the contents, preserve the context” rule.
Case study: Applicative
Yorgey (Typeclassopedia): An applicative functor applies a function inside a context to values in the same context. [3]
The domain knowledge gap it signals
The fact that multiple independent computations must combine — and that the combination preserves each computation’s failure independently while gathering all results — is domain knowledge. If your domain model already crystallizes that “validating a form means checking name, email, and address independently; all failures must be collected, not just the first one; a valid form requires all three to succeed,” then Applicative is just embodying that knowledge. The apply/zip operation is the domain’s own independent-validation-then-combine rule.
Case study: Fold / Reduce
Yorgey (Typeclassopedia): A fold reduces a structure to a single value by combining its elements. [3]
The domain knowledge gap it signals
The fact that a collection must be reduced to a single value — and that the reduction rule is a domain operation — is domain knowledge. If your domain model crystallizes that “total invoice value is the sum of line-item totals; the consolidated risk score aggregates individual exposure assessments; the merged document incorporates all section contributions,” then Fold embodies that knowledge. The accumulator function is the domain’s own combination rule; the initial value is the domain’s own neutral element.
Case study: Lens
The domain knowledge gap it signals
The fact that an entity’s internal structure must be navigated and updated through a defined path — not through getters and setters — is domain knowledge. If your domain model already crystallizes that “a shipment’s destination city is accessed via order → fulfillment → address → city, and updating the city means replacing the address without touching the rest of the order,” then Lens is just embodying that knowledge. The lens is the domain’s own access path; the get/set/modify operations are the domain’s own nested-update rule.
Case study: Currying
Hughes: Currying turns a function of several arguments into a function that takes them one at a time. [4]
The domain knowledge gap it signals
The domain-knowledge lens for currying asks: when does staged parameter supply map onto a domain concept? If your domain model crystallizes that “a tax calculation needs the tax year, the jurisdiction, and the income amount — and these arrive from different domain events,” then Currying captures that knowledge. The partially-applied function represents “everything we know so far, waiting for the last piece.” Unlike structural patterns where the code IS the domain structure, currying is an encoding choice: a Builder expresses the same behavior, though it loses the compositional benefits.
Case study: Immutability
Hughes: Immutable values are never changed in place; a transformation yields a new value. [4]
The domain knowledge gap it signals
Immutability is unique: it is an FP baseline, not a pattern chosen from alternatives. Its primary justifications (referential transparency, equational reasoning, thread safety) are computational guarantees, not domain-specific rules. The domain-knowledge question is supplementary: when the domain ALSO mandates immutability, the construct gains additional justification. If your domain model crystallizes that “a ledger entry, once recorded, is never modified; corrections produce a new entry referencing the original,” then Immutability encodes domain knowledge on top of its computational benefits.
Case study: Higher-Order Function
Hughes: A higher-order function takes another function as an argument or returns one as a result. [4]
The domain knowledge gap it signals
An operation parameterized by another operation — a rule taking a rule as input — is knowledge. If your domain model crystallizes that “the discount policy varies by customer tier, and the order total applies whichever policy is in effect,” then Higher-Order Functions embody that knowledge; the function-accepting-function is the domain’s “supply the policy at call time” rule. The lens does not stop at the business layer: retry windows, audit logging, error recovery, and resource limits are knowledge too — failure modes, obligations, the operating environment. The question is never “domain or infrastructure.” It is whether named knowledge stands behind the parameterization.
Case study: Semigroup
Yorgey (Typeclassopedia): A semigroup is a type with an associative binary operation that combines two values. [3]
The domain knowledge gap it signals
The fact that two values of the same type can be combined — and that the combination is associative and produces another value of that type — is domain knowledge. If your domain model crystallizes that “two risk assessments combine into a composite risk; two budgets sum into a consolidated budget; two permission sets merge into a unified set,” then Semigroup is one encoding of that knowledge. The combine operation captures the domain’s merging rule; associativity means domain order doesn’t change the result. Unlike structural patterns where the code IS the domain structure, the typeclass is an encoding choice: a plain combine function gives the same runtime behavior, but the typeclass adds compile-time guarantees.
Case study: Monoid
Yorgey (Typeclassopedia): A monoid is a semigroup with an identity element. [3]
The domain knowledge gap it signals
A type with a neutral element and closed associative combination is domain knowledge. If your domain model crystallizes that “zero is the neutral element for monetary amounts; an empty permission set is the starting point before merging,” then Monoid is one encoding of that knowledge. empty captures the domain’s identity; combine captures its associative merge. Unlike structural patterns where the code IS the domain structure, the typeclass is an encoding choice: a constant ZERO and a plain combine give the same runtime behavior, but the typeclass adds law-based reasoning and generic derivation.
Case study: Foldable
Yorgey (Typeclassopedia): A foldable structure is one whose elements can be combined into a single result. [3]
What Foldable actually is
Foldable answers one question: can I combine the elements of this thing into one value? The primitive is foldMap :: Monoid m => (a -> m) -> t a -> m. Two facts follow.
First, the fold is a reduction; iteration is only the derived case (toList is itself a foldMap). The result is one value of the monoid you choose — and m = [a] rebuilds the list, so a fold need not lose anything.
Second, the container varies while the element type is fixed. Maybe a and Either e a (Result) are Foldable too: foldMap f Nothing = mempty, foldMap f (Just x) = f x; foldMap f (Left e) = mempty, foldMap f (Right x) = f x. Foldable thus covers 0-or-1 elements too.
The domain knowledge gap it signals
The domain knowledge is the combining rule: which monoid, and which fold. If your domain says “line items total in order of entry,” Foldable just embodies it. The class fixes no order; direction belongs to the chosen fold and monoid, not to Foldable.
Case study: Traversable
Yorgey (Typeclassopedia): A traversable structure lets an effectful action be applied to each element and the shape rebuilt. [3]
What Traversable actually is
Traversable asks: can I apply an effectful step to every element and rebuild the shape? The primitive is traverse :: Applicative f => (a -> f b) -> t a -> f (t b), mirrored by sequenceA = traverse id.
The effect must be an Applicative. Identity gives back fmap; Maybe is all-or-nothing; Either stops at the first error; Validation accumulates errors. One traverse serves them all.
The shape is preserved while the element type changes: t a becomes t b at the same positions, but one function and one effect must serve every position.
The domain knowledge gap it signals
The domain knowledge is the per-element step and the shape that must survive it. If your domain says “validate every line item and rebuild the order,” Traversable embodies it. Two limits matter: the same step runs at every position, so per-position rules need another tool; and on failure with Either you get the error, not the structure.
Case study: Bifunctor
Yorgey (Typeclassopedia): A bifunctor is a type with two parameters, both of which can be mapped over. [3]
The domain knowledge gap it signals
A type with two independent parameters, each varying for its own reasons, is domain knowledge. If your domain model crystallizes that “a Result type holds either a domain error or a success value, and both sides transform independently: errors map to user-facing messages, values map to domain objects,” then Bifunctor is one encoding of that knowledge. The bimap operation captures the “transform each side independently” rule. Unlike structural patterns where the code IS the domain structure, the typeclass is an encoding choice: mapError and mapValue give the same runtime behavior, but the typeclass adds law-based reasoning and a unified interface.
Case study: Contravariant
Yorgey (Typeclassopedia): A contravariant functor maps its input in the opposite direction. [3]
A contravariant type is a consumer: an Ordering[A] (a comparator) compares values instead of producing them. Contravariant maps consumers in reverse: given a way to turn a B into an A, an Ordering[A] becomes an Ordering[B]. On its own that is pure plumbing — any function adapts any consumer, so it says nothing about your domain. The domain knowledge is the coupling: which mapping the consumer uses.
The knowledge gap the pattern signals
Your domain says orders are ranked by their customer. That is a fact about what an Order is: it has a customer, and the ranking uses it. Given Ordering[Customer] and the mapping Order => Customer, contramap yields Ordering[Order] — the domain rule encoded once. If you can’t say why Order => Customer is the right mapping, you have found incomplete domain knowledge, not a missing pattern. Unlike a structural pattern, where the code IS the domain structure, this is an encoding choice — it adds reusable laws and a unified interface, not new domain knowledge.
Case study: Profunctor
Yorgey (Typeclassopedia): A profunctor is a functor over two arguments, contravariant in the first and covariant in the second. [3]
A profunctor is a function-shaped type with two independently transformable sides: it consumes an input (contravariant) and produces an output (covariant). dimap maps both sides at once: pre-process the input, post-process the output. On its own that is pure plumbing — any function can be adapted, so it says nothing about your domain. The domain knowledge is the coupling: which input and output mappings the pipeline uses.
The knowledge gap the pattern signals
Your domain says validation takes a RawOrderRequest and produces a ValidationResult. That is a fact about what validation is: the input it accepts and the output it returns are domain concepts. When rules change, the input narrows (lmap) and the output enriches (rmap) on the same pipeline. Given those mappings, dimap yields one validation function — the rule encoded once. If you can’t say why those mappings are right, you have found incomplete domain knowledge, not a missing pattern. Unlike a structural pattern, where the code IS the domain structure, this is an encoding choice — it adds laws and coherence, not new domain knowledge.
Case study: Alternative
Yorgey (Typeclassopedia): Alternative is an applicative functor with an empty value and a choice operation. [3]
Alternative is the FP typeclass behind the <|> operator, read as “try this, else that.”
The gap it signals
That a computation can fail, and that there is a domain-recognized fallback (not just an error), is domain knowledge. If your model says “try the primary payment processor; if it fails, use the secondary; if a config key is missing, use the default,” then Alternative is one encoding of that rule. <|> captures the “try this, else that” part. Unlike structural patterns, where the code is the domain structure, here the operator is a choice: if/else gives the same runtime behavior, but the operator adds law-based reasoning.
Case study: Algebraic Data Types
Lipovača: An algebraic data type is built by combining other types as sums (one of) and products (all of). [5]
The domain knowledge gap it signals
An algebraic data type (ADT) builds a type from other types — a sum (one of these) or a product (all of these together). Whether a domain concept is a sum or a product is domain knowledge. If your domain model crystallizes that “a Payment is either CreditCard, BankTransfer, or Cash — and a CreditCard payment always has a card number, expiry, and CVV together,” then ADTs embody that knowledge. The sum type is the domain’s own enumeration of cases; the product type is the domain’s own bundled fields. ADTs make illegal states unrepresentable — which is precisely what domain knowledge demands.
Case study: Pattern Matching
Lipovača: Pattern matching selects a branch by the shape of a value and binds its parts. [5]
The domain knowledge gap it signals
Pattern matching selects a branch by the shape (variant) of a value, and the compiler checks that every case is handled. The fact that an operation behaves differently for each variant — and that the set is closed — is domain knowledge. If your domain model already crystallizes that “a Payment is either CreditCard, BankTransfer, or Cash, and each type has its own processing rules; every payment MUST be processed through one of these paths,” then Pattern Matching is just embodying that knowledge. Each match arm is a domain rule; exhaustiveness checking is the domain’s own “every case must be handled” constraint.
Case study: Lazy Evaluation
Hughes: Lazy evaluation defers a computation until its result is needed. [4]
The domain knowledge gap it signals
The fact that a value may never be demanded — and that deferring it is a domain decision, not only a performance optimization — is domain knowledge. If your domain model already crystallizes that “most requests never open the full report detail; only a few accounts need the risk breakdown; the customer may never submit the form and so never need a premium quote,” then Lazy Evaluation embodies that knowledge. The thunk is the domain’s own “derive only if demanded” contract: the value is computed only when something asks for it.
Case study: Trampoline
The domain knowledge gap it signals
A trampoline is a loop that runs recursive steps as plain returns, so deep recursion does not overflow the stack. Stack safety is one axis. The domain question is the other: when the computation has meaningful discrete phases the domain recognizes, the trampoline’s steps are those phases. If your domain model crystallizes that “processing a batch of claims requires progression through validation, assessment, approval, and payout — each step is a domain phase,” then the trampoline’s structure is the domain’s own.
Case study: Memoization
Michie: Memoization stores a function’s result under its arguments so a repeated call reuses it. [6]
The domain knowledge gap it signals
Memoization stores a computation’s result under its inputs, so a repeated call reuses it. That a result is determined solely by its inputs — and that caching it is a domain guarantee, not just a performance optimization — is domain knowledge. If your domain model already crystallizes that “a tax table lookup for a given bracket always returns the same rate; a risk score for a given exposure profile is immutable for the scoring period,” then Memoization is just embodying that knowledge. The cache is the domain’s own “this value, once computed, is stable” guarantee.
Case study: Pure Function
Hughes: A pure function has no side effects: its result depends only on its arguments. [4]
The domain knowledge gap it signals
The fact that a domain demands deterministic, side-effect-free computation is domain knowledge. Purity is also the FP default, but the domain’s demand for it is still a choice. The question is whether the domain demands referential transparency. If your domain model crystallizes that “computing a tax liability from income and bracket must yield the same result every time for the same inputs — and must not depend on or modify anything outside those inputs,” then the Pure Function matches the domain’s own determinism constraint. Purity is double-counted: computational correctness AND domain fidelity. Domain-essential effects are handled with effect types, not by violating purity.
Case study: Recursion Scheme
Meijer et al.: A recursion scheme is a reusable function that captures a standard traversal of a recursive data type. [7]
The domain knowledge gap it signals
A recursion scheme is a reusable function that captures a standard traversal of a recursive data type, such as a fold. When a domain operation is defined over such a type and the recursion pattern is domain-meaningful — not just algorithmic convenience — that is domain knowledge. If your domain model crystallizes that “an org chart folds to its root to compute headcount,” then a Recursion Scheme encodes that knowledge. The fold captures bottom-up reduction; the unfold captures top-down generation. Unlike structural patterns, where the code is the domain structure, the recursion scheme is an encoding choice. A recursive function or Visitor gives the same behavior, but loses the clean separation of traversal from per-node logic.
Case study: Continuation
Reynolds: A continuation is a function that represents the rest of a computation. [8]
The domain knowledge gap it signals
A continuation is a function that represents “the rest of the computation,” passed in so the caller decides what happens next. That a computation’s next step is a domain decision passed at runtime — that “what happens after” is not fixed — is knowledge. If your domain model crystallizes that “after payment processing, send a confirmation email on success or queue a fraud review on failure,” then Continuations encode that knowledge. CPS captures the domain’s “here’s what to do next” parameterization. Unlike structural patterns where the code is the domain structure, CPS is an encoding choice: a Strategy, a callback, or an event emitter gives the same behavior, but loses CPS’s composable control flow.
Case study: Actor Model
Hewitt et al.: The actor model represents concurrency as independent actors that communicate only by asynchronous messages. [9]
An actor is an isolated unit that owns its state and processes arriving messages one at a time.
The domain knowledge gap it signals
The fact that a domain entity owns its state and reacts to the work that arrives to it is domain knowledge. If your domain model crystallises that “this entity processes its work one unit at a time, in isolation, and the only way to influence it is to hand it a new unit of work,” then the Actor Model embodies that knowledge. The heart of an actor is the behaviour: the entity’s own rule for each unit of work. However the code is dressed, this entity owns its state and changes it only in response to what it received.
Case study: Semaphore / bounded resource
Dijkstra: A semaphore is an integer with atomic wait and signal operations used to coordinate access to a shared resource. [10]
A semaphore is a counter that limits how many tasks may use a shared resource at once.
The domain knowledge gap it signals
The fact that only a fixed number of these things may be in use at once is domain knowledge. If your domain model already crystallises that “there are exactly eight loading docks,” “we hold at most fifty concurrent licences,” or “the lot has two hundred spaces,” then the bound is a fact about the world. A semaphore is that fact made executable. The count is the domain’s own “how many can exist at once” rule; the acquire/release discipline is the domain’s own “hold one only while the world lends it” rule.
Case study: Read-Write Lock
Courtois et al.: A readers-writer lock permits concurrent readers or a single writer. [11]
The domain knowledge gap it signals
The fact that many parties may look at the same thing at once, but only one may change it, is domain knowledge. If your domain model crystallises that “a timetable may be read by every dispatcher while a single edit is in flight,” or “a catalogue may be browsed while one price is corrected,” then the read-write asymmetry is a fact about the world, not a performance guess. A read-write lock is that fact made executable: readers share, writers exclude.
Case study: Producer–Consumer
Dijkstra: A producer–consumer system decouples production from consumption through a bounded buffer. [10]
The domain knowledge gap it signals
Producer–Consumer is a pattern where one side adds work to a queue and the other takes it off. The fact that work arrives on one rhythm and is handled on another is domain knowledge. If your domain model crystallises that “orders arrive whenever customers click, but the kitchen cooks one ticket at a time,” then the queue is not an optimisation — it is the domain’s own buffer between two activities that run at different rates, with the bounded queue as the domain’s own “we can hold only so much waiting work” rule.
Case study: Barrier (phase synchronization)
Goetz et al.: A barrier makes a set of threads wait until all have reached a common point. [12]
A barrier makes every participant wait until all have arrived, then releases them together.
The domain knowledge gap it signals
The fact that a stage cannot begin until all participants have finished the prior stage is domain knowledge. If your domain model crystallises that “the meeting cannot start until every attendee has arrived,” or “the tally is only valid once every ward has reported,” then the barrier is the domain’s own rendezvous rule made executable.
Case study: Double-Checked Locking / lazy initialization
Goetz et al.: Double-checked locking tests a flag, then locks and tests again before creating an object, to avoid locking on the common path. [12]
Double-checked locking tests a flag first, then locks and tests again before it creates the object.
The domain knowledge gap it signals
The fact that this thing should not come into existence until it is first needed is domain knowledge. If your domain model crystallises that “a ledger is opened only when the first entry is posted,” or “the archive is materialised only when someone asks for it,” then laziness is the domain’s own “existence begins on demand” rule. Double-checked locking makes that rule executable safely. When the domain has not named when existence begins, that is unwritten knowledge.
Case study: Immutable Snapshot / copy-on-write
Goetz et al.: An immutable snapshot exposes a consistent instance while writers replace it copy-on-write. [12]
The domain knowledge gap it signals
The fact that an observation must reflect a single consistent instant is domain knowledge. If your domain model crystallises that “a quote is the price at the moment it was issued,” “a bank statement is the balance as of the closing date,” or “a submitted tax return is frozen,” then a snapshot is the domain’s own “this reading is a point in time” rule made executable. Copy-on-write is one encoding: the reader sees a stable version while a writer prepares a new one.
Case study: Saga / compensating action
Garcia-Molina and Salem: A saga splits a long-lived transaction into steps, each with a compensating action to undo it. [13]
A saga runs local steps in sequence and, on failure, compensates each completed one.
The domain knowledge gap it signals
The fact that the domain knows how to reverse or offset each step is domain knowledge. If your domain model crystallises that “a booking can be cancelled,” “a shipment can be returned and restocked,” and “a charge can be refunded,” then a saga is the domain’s own set of reverse actions made executable. Each compensating step is a domain rule — the reverse of a domain act — not a technical rollback invented by the transaction machinery.
Case study: Circuit Breaker
Nygard: A circuit breaker trips after repeated failures and short-circuits calls until the dependency recovers. [14]
The domain knowledge gap it signals
The fact that the domain names a dependency as unavailable is domain knowledge. If your domain model crystallises that “when the card processor is down, we take the order on account,” or “when the address service is unavailable, we route to manual review,” then the domain has named the failure state: this dependency is down. A circuit breaker is that state made executable — after repeated failure it trips open and stops the calls. The trip threshold is a chosen bound; the down state it acts on is the domain’s rule.
Case study: Scheduler / time-triggered job
A scheduler runs a job at a set time or on a recurring timetable.
The domain knowledge gap it signals
The fact that something happens at a particular time is domain knowledge. If your domain model crystallises that “the books close at end of business on the last working day,” “interest posts on the first of the month,” or “the report runs every Friday before the stand-up,” then the trigger is a domain rule and the cron expression is merely its encoding. The schedule is the domain’s own calendar made executable; the job is the domain’s own “this must happen then” rule.
Case study: Thread Confinement / thread-local state
Goetz et al.: Thread confinement restricts an object to a single thread, removing the need for synchronization. [12]
The domain knowledge gap it signals
The fact that one worker handles one case from start to finish is domain knowledge. If your domain model crystallises that “the assigned caseworker owns the case until it closes,” or “one clerk processes one application throughout,” then confinement of the working state to that worker is the domain’s own “this caretaker owns this case” rule made executable. Thread-local state is one encoding: the state belongs to the worker, not to the shared code.
Case study: Value Object
Evans: “An object that describes some characteristic or attribute but carries no concept of identity.” [15]
A Value Object is a small immutable type that code compares by field values, not by identity.
The domain knowledge gap it signals
The fact that this thing is defined by its attributes and has no identity is domain knowledge. If your domain model crystallises that “a €10 note is any €10 note,” “an address is whatever its fields say,” or “two measurements of 5 kg are the same measurement,” then equality-by-value is a domain rule, not a convenience. A Value Object is that rule made executable: immutable, interchangeable, replaceable wholesale.
Case study: Entity
Evans: “An object that is not fundamentally defined by its attributes, but rather by a thread of continuity and identity.” [15]
An Entity is a type whose instances code tracks by an identity separate from their fields.
The domain knowledge gap it signals
The fact that this thing remains itself while everything about it changes is domain knowledge. If your domain model crystallises that “the customer who moves house and changes her name is still the same customer,” then identity over time is a domain rule, not a database convenience. An Entity is that rule made executable: identity that persists through state changes, and a lifecycle the domain tracks.
Case study: Aggregate
Evans: “A cluster of associated objects that we treat as a unit for the purpose of data changes.” [15]
An Aggregate is a group of objects that code loads, changes, and saves as one unit through a single root.
The domain knowledge gap it signals
The fact that these things change together, and that this rule must hold at every moment, is domain knowledge. If your domain model crystallises that “an order’s total can never disagree with its lines,” or “a reservation may not exceed the room’s capacity,” then the invariant is a business rule and the boundary around it is the domain’s own consistency claim. An Aggregate is that claim made executable: one root, one transaction, invariants enforced inside.
Case study: Repository
Evans: “A mechanism for encapsulating storage, retrieval, and search behavior which emulates a collection of objects.” [15]
A Repository is an interface that gives code collection-like access to stored domain objects.
The domain knowledge gap it signals
The fact that the domain speaks of a collection of these things is domain knowledge. If your domain model crystallises that “all active orders are here; we add orders; we find open ones,” then the collection metaphor is the domain’s own vocabulary. A Repository is that vocabulary made executable: an in-memory-collection facade (add, find, remove) over persistence, named in domain terms.
Case study: Domain Service
Evans: “An operation offered as an interface that stands alone in the model, without encapsulating state, as entities and value objects do.” [15]
A Domain Service is a stateless operation that holds a domain rule belonging to no single entity.
The domain knowledge gap it signals
The fact that this operation belongs to the domain yet has no natural home on a single entity is domain knowledge. If your domain model crystallises that “transferring money involves two accounts and a rule neither owns alone,” then the Domain Service is that fact made executable: a stateless operation named in domain language, placed where the domain says it lives.
Case study: Domain Event
Fowler: “A Domain Event is a full-fledged part of the domain model, a representation of something that happened in the domain.” [16]
A Domain Event is a message that records something meaningful that happened in the domain.
The domain knowledge gap it signals
The fact that the business talks about this occurrence — that it has a name, a time, and consequences — is domain knowledge. If your domain model crystallises that “when an order is placed, fulfilment and invoicing both react,” then “OrderPlaced” is a domain fact, not a messaging artefact. A Domain Event is that fact made executable: something that happened, named in the experts’ words, with the past tense the experts use.
Case study: Factory
Evans: “Shift the responsibility for creating instances of complex objects and aggregates to a separate object, which may itself have no responsibility in the domain model.” [15]
A Factory is a construct that wraps the code that creates an object.
The domain knowledge gap it signals
The fact that creating this thing involves rules — what must be true for it to exist at all — is domain knowledge. If your domain model crystallises that “an order cannot exist without a customer and at least one line,” or “a policy is created through quotation, not directly,” then the creation rules are domain invariants. A Factory is that fact made executable: the domain’s own birth rules, stated once, at the birthplace.
Case study: Specification
Evans: “A predicate that determines if an object does or does not satisfy some criteria.” [15]
A Specification is a rule object that code can check and combine with other rules.
The domain knowledge gap it signals
The fact that this predicate is a named business rule — one the experts evaluate, discuss, and sometimes change — is domain knowledge. If your domain model crystallises that “eligibility for the discount” is a thing the business defines, audits, and redefines, then the rule is a domain concept in its own right. A Specification is that fact made executable: a first-class, nameable, combinable rule object.
Case study: Bounded Context
Evans: “A description of a boundary (typically a subsystem, or the work of a particular team) within which a particular model is defined and applicable.” [15]
A Bounded Context is a boundary inside which one model and one set of terms apply.
The domain knowledge gap it signals
The fact that the same word means different things to different parts of the business is domain knowledge. If your organisation crystallises that “a ‘product’ to sales is a SKU with margin; to logistics it is a box with weight; to support it is a warranty scope,” then one shared model is a falsehood, and the boundary between the meanings is the domain’s own division. A Bounded Context is that division made executable: inside this boundary, each term has exactly one meaning.
Case study: Ubiquitous Language
Evans: “A language structured around the domain model and used by all team members within a bounded context to connect all the activities of the team with the software.” [15]
A Ubiquitous Language is the shared vocabulary that both domain experts and code use.
The domain knowledge gap it signals
The fact that a term has an agreed meaning between experts and code is itself the completeness-check. Ubiquitous Language is not a structure but a claim: “the words in the code are the words the business uses, with the meanings the business gives them.” Wherever the claim breaks — a “record” that the business calls a “booking,” a “status” no expert recognises — you have found the gap directly.
Case study: Anti-Corruption Layer
Evans: “Create an isolating layer to provide clients with functionality in terms of their own domain model.” [15]
An Anti-Corruption Layer is a translation boundary between your model and a foreign one.
The domain knowledge gap it signals
The fact that the foreign system’s model is not our model — and must not become it — is domain knowledge. If your domain crystallises that “the legacy system’s ‘customer’ is a billing record, not our customer,” then the translation between the two models is a domain decision. An Anti-Corruption Layer is that decision made executable: one place where foreign concepts are converted into ours, and ours never leak out raw.
Case study: Anemic Domain Model (anti-pattern)
Fowler: “The fundamental horror of this anti-pattern is that it is so contrary to the basic idea of object-oriented design — to combine data and process together.” [17]
An Anemic Domain Model is a model of data classes that hold no behavior of their own.
The domain knowledge gap it signals
A model of data classes with no behaviour claims, implicitly, that the domain has facts but no rules — that orders merely have totals but nothing enforces validity. That claim is almost never true. The anemic model is therefore a standing question: where did the rules go? The answer is usually “into services no expert named,” and that displacement is the knowledge gap made visible.
Case study: Module (tactical packaging)
Evans: “Choose modules that tell the story of the system and contain a cohesive set of concepts.” [15]
A Module is a named package that groups related domain concepts.
The domain knowledge gap it signals
The fact that these concepts belong together in the experts’ minds is domain knowledge. If your domain model crystallises that “leasing, maintenance, and resale are different parts of the fleet business — each with its own vocabulary,” then the module divisions are the domain’s own chapters. A Module is that division made executable: a named, cohesive packaging of a piece of the domain, whose name an expert would recognise.
Case study: Policy / Process (tactical)
A Policy is a rule object that expresses “when this happens, do that.”
The domain knowledge gap it signals
The fact that the business responds to occurrences with prescribed actions — “when a claim is filed, assign an adjuster within two days” — is domain knowledge. A Policy is that fact made executable: a rule in the experts’ own if-then shape, attached to the occurrence it responds to. It is the domain’s own “when X, then Y” made first-class instead of buried in a sequence of ifs.
Value Objects were checked from the DDD side in #85: identity doesn’t matter, equality by content is the domain’s own notion of sameness. This post adds only the OOP-language half of that claim.
Case study: Immutable Value Objects
Evans: “Value objects should be immutable: rather than modifying one, you create a new one in its place.” [15]
A value object is an immutable type identified by its values; you replace it instead of changing it.
The overlap, and the one thing this post adds
#85 established the construct: a value object makes executable the domain rule “a €10 note is any €10 note.” Immutability is the other half of that same claim, and #85 named it only in passing. The OOP angle is narrower: immutability is a language-level commitment that the domain replaces this fact rather than revises it. An address correction is a new address, not a mutated one. If the business speaks that way, immutability is the code catching up. Where it goes wrong is on both sides. Immutability adopted without a domain warrant — chosen for thread safety, cacheability, or safe sharing on things the domain revises — forces contortions: builders and copy-methods emulating updates the domain actually performs. The choice is still a decision, but the domain never named a replacement rule, and that is the gap — not that immutability is “merely technical.” The inverse is a mutable value-shaped object, whose shared aliasing silently couples changes the domain never coupled.
Case study: Party
Fowler: Model people and organizations with a common supertype, Party. [18]
Party is the analysis pattern that lets a person and an organisation stand in for each other wherever a role applies.
The domain knowledge gap it signals
The fact that a person and an organisation can play the same role — both can be a customer, a supplier, an employee — is domain knowledge. If your domain crystallises that “whoever we invoice may be a person or a company, and downstream we don’t care which,” then Party is that fact made executable: a common abstraction above Person and Organisation, where the roles live.
Case study: Organization Structure
Fowler: Represent an organization structure as a hierarchy of Parties. [18]
Organization Structure is the analysis pattern that models an organisation as typed units joined by rule-carrying relationships.
The domain knowledge gap it signals
The fact that the organisation has formal relationships with rules — “regional offices report to headquarters,” “this division owns these cost centres” — is domain knowledge. Organization Structure models those relationships as typed, rule-carrying structures: a parent, a child, a type, and the constraints each type implies.
Case study: Organization Hierarchy
Fowler: An organization hierarchy in which each organization has at most one parent. [18]
Organization Hierarchy is the analysis pattern that treats every unit as having exactly one superior.
The domain knowledge gap it signals
The fact that the organisation is a strict tree — every unit has exactly one superior, authority flows one way — is a domain claim, and often an unexamined one. Organization Hierarchy is that claim made executable: single-parent structure with transitive rules (“the whole branch reports up through here”).
Case study: Accountability
Fowler: Record the accountability of one party to another. [18]
Accountability is the analysis pattern that records who answers to whom, for what, and for how long.
The domain knowledge gap it signals
The fact that one party is answerable to another for something — with a scope, a start, and sometimes an end — is domain knowledge. If your domain crystallises that “this doctor is responsible to this clinic for patient care until further notice,” then Accountability is that fact made executable: a responsibility relationship between parties, typed, scoped, and time-bounded.
Case study: Knowledge Level
Fowler: Separate the knowledge level (the rules) from the operational level it governs. [18]
Knowledge Level is the analysis pattern that separates the rules about kinds of things from the things themselves.
The domain knowledge gap it signals
The fact that the business distinguishes kinds of things from the things themselves — job positions vs. the people in them, account types vs. accounts, ward categories vs. wards — is domain knowledge. A Knowledge Level is that distinction made executable: one layer defines the types and their rules; the other holds the operational objects that must obey them.
Case study: Quantity
Fowler: A number together with a unit of measure. [18]
Quantity is the analysis pattern for a number that carries its unit of measure.
The domain knowledge gap it signals
The fact that a number in this domain is never just a number — it is an amount of something, in a unit, comparable only to its own kind — is domain knowledge. If your domain crystallises that “5 kilograms cannot be added to 3 metres,” then Quantity is that fact made executable: a value carrying its unit, with conversion and comparison rules that respect the physics the business lives by.
Case study: Money
Fowler: A quantity whose unit is a currency. [18]
Money is the analysis pattern for an amount that carries its currency.
The domain knowledge gap it signals
The fact that an amount in this domain always belongs to a currency — and that mixing currencies is a domain error, not a rounding detail — is domain knowledge. Money is that fact made executable: amount plus currency, with addition restricted to like currency and comparison routed through explicit conversion. It is Quantity specialised to the one domain every business shares.
Case study: Observation
Fowler: A value observed or measured about something. [18]
Observation is the analysis pattern that treats a recorded statement as a first-class thing, separate from the truth it records.
The domain knowledge gap it signals
The fact that a statement about a subject was made by someone, at a time, against a protocol is domain knowledge. If your domain crystallises that “a blood pressure reading is an observation — taken by a clinician, at a moment, interpretable against reference ranges,” then Observation is that fact made executable: the recording as a first-class thing, separate from the truth of what it records.
Case study: Measurement
Fowler: An observation of a value, together with its unit of measure. [18]
Measurement is the analysis pattern for a value that carries the procedure that produced it.
The domain knowledge gap it signals
The fact that a measured quantity comes from a procedure, against a defined scale, with a normal range — that how a value is obtained is part of its meaning — is domain knowledge. Measurement is Observation specialised for quantities: the value carries its protocol, because the domain says a number without its method is not interpretable.
Case study: Phenomenon / Category (typed observations)
Fowler: A controlled vocabulary of phenomena and categories for typed observations. [18]
Phenomenon and Category are the analysis patterns that model a controlled vocabulary of things that can be observed or classified.
The domain knowledge gap it signals
The fact that the business has a controlled vocabulary of things that can be observed or classified — diagnosis codes, defect types, product categories — and that free text is not acceptable, is domain knowledge. Phenomenon types and categories are that fact made executable: the vocabulary itself is modelled, with each observation pointing at a term the domain owns.
🧠 Why it matters here Whether the list of valid values is closed is one of the most consequential hidden gaps in data modeling: an open set sneaks in as varchar, and surfaces years later as a migration.
Case study: Inventory / Asset
Fowler: Separate things tracked individually (assets) from things tracked by quantity (inventory). [18]
Inventory and Asset are the analysis patterns that separate things tracked individually from things counted in bulk.
The domain knowledge gap it signals
The fact that the business distinguishes things it owns and tracks individually from stuff it counts — this van (serial number, service history) vs. 400 litres of diesel — is domain knowledge. The Inventory/Asset distinction is that fact made executable: discrete trackable assets vs. aggregate quantities, with different identity, valuation, and lifecycle rules.
Case study: Account / Entry
Fowler: An account whose balance is derived from an append-only list of entries. [18]
Account with Entries is the analysis pattern that derives a balance from an append-only list of entries.
The domain knowledge gap it signals
The fact that a balance in this domain is derived, never stored — the result of an append-only list of entries — is domain knowledge. If your domain crystallises that “every change must be traceable to a recorded event,” then Account with Entries is that fact made executable: entries are the facts, the balance is their consequence.
Case study: Posting Rule
Fowler: A rule that maps a business event to the accounting entries it books. [18]
A Posting Rule is the analysis pattern that maps a business event to the accounting entries it books.
The domain knowledge gap it signals
The fact that the business has named derivation rules — “when we bill, the service account is credited and the tax account is charged” — is domain knowledge. A Posting Rule is that fact made executable: the trigger-to-entries mapping as a first-class thing, owned by accounting logic rather than scattered through process code.
Case study: Action & Resource Allocation (planning)
Fowler: Link a planned action to the resources it consumes. [18]
Action & Resource Allocation is the analysis pattern that links planned work to committed resources.
The domain knowledge gap it signals
The fact that the business plans work against committed resources — this technician, this room, this window — and distinguishes a commitment from a request, is domain knowledge. Action & Resource Allocation is that fact made executable: actions that consume resource allocations, with request/commit/withdraw as real domain states.
Case study: Identification Scheme
Fowler: A named, owned rule that says how a thing is identified. [18]
An Identification Scheme is the analysis pattern for a named, owned rule that says how a thing is identified.
The domain knowledge gap it signals
The fact that the business has an agreed way to point at a thing — “by passport number,” “by our customer number,” “by the vehicle registration” — is domain knowledge. An Identification Scheme is that fact made executable: a named, owned rule for mapping identifiers to objects, so “which one is it?” has an official answer.
Case study: Object Merge
Fowler: Unite two records found to be the same thing while keeping their separate identities. [18]
Object Merge is the analysis pattern that unites two records found to be the same thing while keeping both histories.
The domain knowledge gap it signals
The fact that two records can turn out to be the same thing — and that the business has a way to reconcile them — is domain knowledge. Object Merge is that fact made executable: a merge act that preserves both histories, redirects references, and remains undoable, because the domain says “we may discover sameness, but we do not erase what we knew.”
Case study: Transaction
Fowler: Group several entries as one business act. [18]
Transaction is the analysis pattern that groups entries as one business act.
The domain knowledge gap it signals
The fact that these entries belong together — one act, several consequences, atomic or not at all — is domain knowledge. The Transaction pattern is that fact made executable: a grouping of entries that records “these happened as one business event,” so the ledger can answer “what did this act consist of?” as well as “what is the balance?”
Case study: Contract
Fowler: Model standing agreed terms between parties, separate from any single act. [18]
Contract is the analysis pattern that models standing agreed terms between parties, separate from any single order.
The domain knowledge gap it signals
The fact that two parties are bound by agreed terms — obligations that outlive any single order or payment — is domain knowledge. The Contract pattern is that fact made executable: parties, roles, and terms as a first-class thing that products and services hang off, so “what did we agree?” has an address in the model.
Case study: Quote
Fowler: A dated offer to sell at a price, with a lifetime. [18]
Quote is the analysis pattern for a dated offer to sell at a price, with a lifetime.
The domain knowledge gap it signals
The fact that a promise to sell at a price has a lifetime — it exists, it binds for a while, it expires — is domain knowledge. The Quote pattern is that fact made executable: a dated offer with conditions, distinct from both the inquiry that preceded it and the sale that may follow it.
Case study: Portfolio
Fowler: Treat a group of positions as one owned thing. [18]
Portfolio is the analysis pattern that treats a group of positions as one owned thing.
The domain knowledge gap it signals
The fact that the business treats a group of positions as one thing — this client’s holdings, this fund’s assets — is domain knowledge. The Portfolio pattern is that fact made executable: an owning collection with its own identity, valuation, and rules, where “the whole” answers questions the parts cannot.
Case study: Deferred Choice
van der Aalst et al.: A point where the choice among alternatives is deferred until an event enables one of them. [19]
The domain knowledge gap it signals
The fact that the world picks the branch, at the moment it happens, is domain knowledge. If the business says “either the customer pays or the order expires — whichever comes first,” the choice is not made by the process but by an external event that commits one path and withdraws the others. Deferred Choice is that fact made executable: a branch point where the first event to occur commits one path and withdraws the rest.
Case study: Milestone
van der Aalst et al.: A milestone enables a task only while a named state holds. [19]
The domain knowledge gap it signals
The fact that a step is permitted only while a named state still holds is domain knowledge. “An order may be changed only before it ships”; “a claim may be reopened only within thirty days.” A Milestone is that fact made executable: a condition on a state, not a sequence position — an activity enabled because a named situation currently holds.
Case study: Cancellation Region
van der Aalst et al.: An activity that cancels all activities running inside its region, including itself. [19]
The domain knowledge gap it signals
The fact that the business may abandon work already in flight — and that abandonment withdraws everything downstream, not just one step — is domain knowledge. “An order may be cancelled, and everything queued for it stops”; “a claim may be withdrawn, and all its pending checks end.” A Cancellation Region is that fact made executable: a scope that, once cancelled, retires every activity inside it.
Case study: Exclusive Choice vs. Multi-Choice
van der Aalst et al.: An exclusive choice (XOR-split) enables exactly one branch; a multi-choice (OR-split) may enable several. [19]
The domain knowledge gap it signals
How many branches may run at a split is a domain claim. Exclusive choice asserts “exactly one applies”; multi-choice asserts “any subset may apply, including several.” If the business says “every applicable discount is applied” or “each defect class present triggers its own inspection,” the multiplicity is knowledge — and the gateway choice records it.
Case study: Parallel Split & Synchronization
van der Aalst et al.: A parallel split (AND-split) enables all branches; a synchronization (AND-join) waits for all of them. [19]
The domain knowledge gap it signals
Two facts are claimed here: the sub-works run at the same time independently, and the next step is meaningful only once all of them have finished. “A loan is checked for credit, fraud, and compliance concurrently; the decision waits for all three.” Parallel Split asserts co-occurrence; Synchronization asserts a barrier. Both are domain claims — the same concurrency questions as the Barrier pattern, now stated as claims about the process.
Case study: Gateways & Token Semantics
OMG: BPMN gateways control how a token is consumed and produced to route a process. [20]
What the token is — and is not
A token looks like pure engine bookkeeping — “how far has this case advanced.” But the split and join rules built on tokens are still domain decisions, not engine detail: each join asserts “any one arrival is enough,” “all are required,” or “some are.” The engine’s default is not neutral; it picks one of those rules, and the domain must authorise it.
Case study: Swimlanes
OMG: Swimlanes partition a process by the participant, role, or system that performs each activity. [20]
The domain knowledge gap it signals
The fact that a step belongs to a particular actor is domain knowledge — and it is authority, not convenience. “Only the underwriter approves the policy”; “the warehouse, not sales, confirms dispatch.” Swimlanes are that fact made executable: the lane claims “this actor owns this step,” and crossing a lane claims a hand-off between parties.
Case study: Boundary Events
OMG: A boundary event attaches to an activity to model an interrupting or non-interrupting event. [20]
The domain knowledge gap it signals
The fact that the outside world can interrupt a step already running is domain knowledge. “The patient deteriorates during the procedure”; “the customer cancels while the order is being picked”; “the deadline passes while the claim waits.” A Boundary Event is that fact made executable: an interruption attached to an activity’s lifetime, available only while it runs.
Case study: Case Management / Ad-hoc Processes
OMG: Case management models knowledge-driven work whose steps are not known in advance. [21]
The domain knowledge gap it signals
The fact that the order of steps is not knowable in advance is domain knowledge. In investigations, clinical care, and legal matters, a knowledge worker decides the next act as the case unfolds — guided by goals and constraints, not by a fixed route. Case Management is that fact made executable: a case file with milestones and available actions, rather than a prescribed sequence.
Case study: Process Mining / Conformance Checking
van der Aalst: Process mining derives process models from event logs and checks observed behaviour against a model. [22]
The domain knowledge gap it signals
This one measures the gap directly. The believed process (the model) and the actual process (the event log) are two claims about the same domain, and conformance checking is the difference between them. Where the log deviates from the model, one of two things is true: the model is wrong, or the workarounds are wrong — and someone must decide which.
Case study: Choreography vs. Orchestration
Peltz: Orchestration centralizes control in a single coordinator; choreography distributes it across cooperating peers. [23]
The domain knowledge gap it signals
Who sees and who coordinates the process is organizational knowledge. Orchestration puts one party in charge of the whole flow; choreography describes each participant’s behaviour and lets no one own the whole. “We drive the process with our suppliers” versus “each party acts on the messages the others send” is a real, and often disputed, business distinction.
Case study: State Machines for Business Entities
Harel: Statecharts model the permitted states and transitions of a system. [24]
The domain knowledge gap it signals
The fact that a thing may change only in ways the business allows is domain knowledge. “An order cannot go from shipped back to draft”; “a claim cannot be approved before it is assessed.” A state machine is that fact made executable: named states and permitted transitions, so the entity’s allowed changes are stated once and enforced everywhere.
Case study: EPC — Event/Function Alternation
Scheer: An event-driven process chain alternates events and functions to describe a process. [25]
The domain knowledge gap it signals
EPC’s structural claim is that events and functions alternate: every action is triggered by a state, and produces one. That is a domain distinction — an event is a fact the business observes (“what happened”), a function is an act the business chose (“what we did”). These are different kinds, and EPC forces the difference where BPMN does not.
Case study: SLA / Escalation
Dumas et al.: A process commitment with a time bound and an escalation path taken when it is missed. [26]
The domain knowledge gap it signals
The fact that the business has promised a time in which something will happen — and a defined response when it does not — is domain knowledge. “Claims are acknowledged within two days”; “if the approval is not given by Friday, it goes to the manager.” Deadlines, escalation, and delegation are that fact made executable: the promise, the clock, and the authority to escalate.
Encapsulation is a language mechanism — access control on fields and methods. Whether a boundary earns its keep is not a language question; it is a question about what callers depend on.
Case study: Encapsulation
Parnas: Modules should hide a design decision behind an interface so that the decision can change without affecting clients. [27]
The domain knowledge gap it signals
An encapsulation boundary splits a type into what callers may depend on and what they never see. The real claim is not “something is hidden here” but “this interface is exactly what callers need.” That claim has content only if the team knows the callers’ needs — which parts the domain assigns to this thing, and which are one way of fulfilling them. The gap shows when the split is drawn by habit: the interface carries choices no caller asked about, or withholds something needed. Where the domain fixes a concrete detail, that detail belongs in the interface; an abstraction above it asserts a concept nobody decided.
An extends clause is a claim: “this IS-A that, permanently, in the domain.” Usually it is code reuse wearing a taxonomy hat.
Case study: Inheritance / Subtyping
Liskov and Wing: A subtype must be usable in place of its supertype without altering the correctness of the program. [28]
The domain knowledge gap it signals
Every extends freezes a classification into the code: the child is declared a kind of the parent, for all time. That claim has content only if the domain draws the distinction — if a domain expert would state the classification as a fact of the business, and if substitution holds under every change the domain can force. The gap shows when the hierarchy answers a question nobody asked the business: the taxonomy reflects how the code happens to be organised, not how the domain classifies. The square–rectangle trap is the textbook case; the everyday version is a parent class that exists only because two classes shared three methods.
Composition over inheritance is the flip side of yesterday’s post (#136). The domain question is: “is this a stable classification, or a role the thing plays?”
Case study: Composition over Inheritance
Gamma et al.: Favour object composition over class inheritance. [1]
The domain knowledge gap it signals
Choosing inheritance freezes a relationship as permanent taxonomy; choosing composition says the relationship is a role — attached, detachable, reassignment-capable. An employee is a person who plays the employee role; a discount policy is attached to an order, not what an order is. The gap shows when the structural choice was made for reuse or convenience without asking which reading the domain supports — the choice still encodes a claim about the business, and that claim is unwritten until the domain states it. Only one reading is true.
DRY says every piece of knowledge must have a single representation. It presupposes you know which duplications are the same domain rule — and which are coincidental text similarity.
Case study: DRY (Don’t Repeat Yourself)
Hunt and Thomas: Every piece of knowledge must have a single, unambiguous representation within a system. [29]
The domain knowledge gap it signals
DRY is about knowledge, not text — but the only instrument the programmer has operates on text. Two fragments look alike, someone extracts the common code, and the extraction itself is a claim: “these are one rule, and a change to it must reach both.” That claim has content only if the domain agrees. The gap shows when the team merged by similarity without asking whether one domain decision stands behind both fragments — the merge couples two change drivers, and when one rule moves and the other must not, the shared abstraction breaks in production.
The Law of Demeter says a method may call its immediate collaborators only, never reach through them. “Who may know whom” is a coupling claim. An a.b().c() chain asserts a navigation path the domain may not mandate.
Case study: Law of Demeter
Lieberherr and Holland: A method should talk only to its immediate collaborators, not to their collaborators. [30]
The domain knowledge gap it signals
Every reach-through chain asserts that the caller’s business genuinely depends on the far object along this route — that the path is a stable domain relationship, not an accident of object layout. Sometimes it is: an order’s customer’s billing address is a path the business itself reasons about. Sometimes it is digging: the caller exploits an implementation trail, following a route that exists only in the object graph. The gap shows when nobody can say which of the two a given chain is — the coupling exists, its legitimacy unexamined.
Speculative extension points are guessed change drivers. YAGNI is the discipline of refusing structure for a driver you cannot name — and it is not a license to ignore drivers you can.
Case study: YAGNI
Fowler: Build only what the requirements demand now; do not add speculative generality. [31]
The domain knowledge gap it signals
Every “we’ll need this to be flexible” asserts that the domain can force a variation here — a forcing condition exists, and the structure stands ready for it. The gap is anticipating variation no domain mechanism can force: a plugin point for providers the market does not contain, a config switch for a rule the business holds fixed, an abstraction layer against a change nobody can cause. The reverse gap is costlier: “you aren’t gonna need it” said about a variation the business has already announced — the seam was domain knowledge, and YAGNI was invoked to unsee it.
Information hiding is Parnas’s rule: each module hides one decision that may change. Information hiding audits the whole decomposition: is the module structure the map of the domain’s forcing conditions?
Case study: Information Hiding (Parnas)
Parnas: Decompose a system by hiding the design decisions most likely to change behind stable interfaces. [27]
The domain knowledge gap it signals
Parnas’s rule works at the system level: derive module structure from the inventory of decisions, grouped by what could force them to change. One module may hide several decisions — when the same conditions force them to change together — and one decision may not be split. The likelihood reading asks for a forecast that can never be checked when it is made. The recoverable reading lists decisions whose forcing conditions are demonstrable today — a regulator who has revised the rule. The gap shows as a break in the correspondence: co-changing decisions split across modules, or change-apart decisions hidden behind one wall.
Behavior placement encodes where the knowledge lives. “Tell, don’t ask” is a claim that the object holding the data holds the knowledge to act on it.
Case study: Tell, Don’t Ask
Hunt and Thomas: Tell an object what to do rather than asking it for its state and acting on it yourself. [29]
The domain knowledge gap it signals
if (account.balance > amount) account.withdraw(...) looks harmless — but the overdraft rule just lived in the caller. Every “ask then decide” asserts that the decision belongs outside the object whose state feeds it; every “tell” asserts it belongs inside. Both are placement claims about domain knowledge, and both can be wrong. The gap shows when the placement was never reasoned about: rules drift into whichever file was open, and the object that owns the state no longer owns the rule that governs it — so a change to the rule hunts through every caller.
An extension point is a pre-built seam where a new variation plugs in without editing existing code. It is legitimate only when the domain knowledge you have today selects the seam. Built for a hypothetical future variation, it is a bet nobody can check.
Case study: Extension Points
The domain knowledge gap it signals
Every extension point — a plugin interface, a strategy slot, a template method — claims “the domain varies here, and not there.” No slogan tells you which point to build or when; a module can be extensible somewhere, never everywhere, so every seam is a bet about the location of change. The gap is the bet placed without the knowledge that settles it. Two failure signatures follow: seams on axes the domain holds fixed, and hardcoding on the axis it varies — the pricing formula’s shape changes while the seam sat at the rates.
Immutability is a language-level commitment that the domain treats a thing as replaceable rather than updatable. The DDD side of this was covered in #85 — this post adds only the OOP-language angle.
Case study: Immutable Value Objects
Evans: Value objects should be immutable: rather than modifying one, you create a new one in its place. [15]
The domain knowledge gap it signals
Post #85 covered the identity question: a value object claims that identity doesn’t matter, that equality by content is the domain’s own notion of sameness. What remains for this folder is the mutation question. Making a class immutable asserts: “when this fact changes, the domain replaces it — the old one stops being true.” An address correction is a new address, not a mutated one; a price revision supersedes the old price. Where it goes wrong: immutability chosen for technical convenience — thread safety, cacheability — applied to facts the domain revises. Thread safety is a real requirement, but it does not decide whether a fact is replaced or mutated; the domain does. If the domain revises the fact, immutability asserts a replacement nobody decided — the rule is unwritten — and builders and copy-methods must emulate updates the business performs. And the inverse gap: mutable value-shaped objects whose shared aliasing silently couples changes that should be independent.
A static member claims more than a singleton (#13) does: not “exactly one instance” but “no instance at all — this knowledge belongs to the concept as such.”
Case study: Static Fields and Methods
The domain knowledge gap it signals
Every static is a placement claim: this fact or behavior exists independent of any particular thing — it holds for the type itself. That claim has content only if the domain speaks that way: a conversion constant the physics fixes, a rule that governs the concept rather than its instances, construction logic that is merely named creation (#91). The gap shows when static was chosen so the code could reach something without knowing which thing owns it — the knowledge lives nowhere, so it lives everywhere.
Patterns & Constructs as Knowledge-Gap Indicators
The faithfulness test is a 3-way verdict, not a binary: Applicable — if yes, the pattern encodes what the domain already dictates.
Inapplicable — if no, the pattern forces coupling the domain doesn’t have. Unclear — if you can’t answer, you’ve found incomplete domain knowledge. If you can’t answer, you haven’t found that the pattern is wrong — you’ve found that your domain knowledge is incomplete.
Case study: Command Pattern
GoF: Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. [1]
The domain knowledge gap it signals
The fact that an action is a first-class thing — not just a method call — is domain knowledge. If your domain model already crystallizes that “an operation must be recorded, queued, undone, or scheduled — the action itself is a domain entity with identity and lifecycle,” then Command is just embodying that knowledge.
Case study: Dependency Injection
Fowler: A form of Inversion of Control in which a component is given its dependencies rather than creating them itself. [32]
The domain knowledge gap it signals
With dependency injection, a service declares: I need these dependencies — but I do not need to know where to get them, or how to build them; give them to me. A faithful declaration carries domain knowledge: an order service needs persistence and messaging. Even which database, which queue, and how they are built is a domain decision: someone chose them. The interface states the need; the composition root supplies the answer. The gap it signals is whether the need is real and whether the supply can vary.
DI is not the composition root
DI is a technique: how a service declares its dependencies and receives them from outside.
The composition root is a place: where the application assembles its object graph, at the entry point. Only applications should have one — composition belongs to the application. A DI container belongs only there.
DI does not specify where the object graph is assembled; the composition root answers that.
Two failures hide when the names are conflated: - Scattered wiring: composition at each use site, with no root. A deployment change touches many files. - A frozen choice: a root that bakes a per-operation choice in at startup.
A composition root is the single place where the object graph is assembled, at the entry point. It looks like a purely technical construct, but what it assembles — and where it sits — follows from a modularization rule derived from domain knowledge.
Case study: Composition Root
Seemann: A single, logical location in an application where modules are composed together. [33]
The domain knowledge gap it signals
The assembly looks purely technical, but it is still a domain decision: the same modularization rule decides where the composition root goes, what each service receives, what belongs inside each service, and whether each dependency is fixed for the object’s lifetime. The root decides those choices once, for the whole graph. The rule is derived from domain knowledge: it follows from which choices the domain owns and which it leaves open. The service’s content is fixed by the rule, not by the wiring that supplies it. The gap is the rule: if you cannot say which choices the domain owns, you cannot say where the composition root goes or what the services contain.