Part 2 Shape the software around its problem

The next chapters turn intended behavior into concepts, ownership, and boundaries. The running example acquires more precision only where a distinction changes what the system must do.

6 Model the domain before choosing abstractions

A domain model earns its place when it helps explain a behavior that would otherwise remain ambiguous. It is not a list of nouns waiting to become classes.

In the acquisition example, a person asks for one resource, a transfer is attempted, candidate bytes appear locally, checks run, and a consumer eventually receives something usable. Calling all of these “the download” is convenient until one stage succeeds and another fails. Then the system needs distinctions that its vocabulary has concealed.

Start with examples. The same resource can be requested twice. One attempt can fail while another succeeds. Two artifacts may represent different versions. A candidate can exist without being ready. A published version can remain in use while a replacement is prepared. These statements reveal identity and lifecycle questions before any architecture is selected.

Distinguish things that change independently

A resource identity answers which thing is wanted. An attempt answers which effort to obtain it is being discussed. A candidate answers what is locally present but not yet accepted. A published resource answers what a consumer may currently use.

Those concepts need not become four services or four classes. A small implementation may represent them with a few records and functions. The important point is that the system can express their different states without contradictory meanings.

Consider an attempt identifier used as a permanent resource identifier. It seems adequate while every request succeeds once. When an attempt is retried, the application may create a second identity for the same resource. Alternatively, if every retry reuses one mutable record, historical events may become impossible to distinguish. The correct choice depends on what callers must be able to refer to and what records must preserve.

A vocabulary table can expose the problem:

Concept Remains the same when Changes when
Requested resource A transfer is retried for the same selected identity The selected identity or version changes
Acquisition attempt One attempt moves through its lifecycle A new attempt is admitted
Candidate artifact Checks examine the same candidate bytes The candidate is replaced or reconstructed
Published reference Existing readers retain the agreed version Publication deliberately changes the reference

The table states an illustrative contract. It is not a claim that all products should use these exact identities.

Name the rule before the object

An invariant is a condition the system must preserve across relevant transitions. “An unverified candidate is never advertised as ready” is one. “Cancelling one consumer does not cancel another consumer's continuing demand” might be another if acquisition is shared.

An invariant tells you something about a boundary. If several fields must change together to preserve it, they may need one consistency owner. If unrelated changes do not affect it, they may not belong in that owner's responsibility.

Evans's domain-driven design reference connects shared language, identity, bounded contexts, and consistency rules. These ideas are useful for asking where a meaning is valid and who enforces a rule. They do not require a microservice per bounded context or a class for every concept. Domain Driven Design Reference.

A framework can then implement the model. It should not silently supply the model's meaning. A database row, asynchronous task, or HTTP request may be a convenient representation, but its technical lifetime need not match the domain object's lifetime. Much later complexity comes from assuming that it does.

Test the vocabulary against disagreement

Ask two people to describe what happens when transfer completes but publication fails. If one says the resource is complete and the other says it is unavailable, they may be using “complete” for different events. Resolve the distinction before making the status field more elaborate.

The same method applies to agents. A task may be assigned, admitted, running, awaiting input, or finished at a harness level. None automatically means its requested outcome has been accepted. If a single state called done serves all those meanings, downstream code will invent its own interpretation.

Domain modeling is therefore also interface work. The words in a tool response teach the caller how to act. A misleading word can create a bug even when the underlying data is accurate.

Resist the urge to complete the ontology

There is always another distinction you could represent. The useful stopping point is the set needed for current behavior, meaningful variation, and known failure boundaries. If a proposed concept cannot explain a scenario, enforce a rule, or support an actual decision, it may not belong yet.

For an exercise, take three status names from a system you maintain. For each, write what is true, what the caller may do, and one situation it must exclude. Then identify a pair whose meanings overlap or conflict. Repair the vocabulary before adding more states. The result may be a new concept, a simpler name, or the removal of an unnecessary distinction.

Search the book

Type to find a passage.