
For sales, a customer is an account in negotiation. For billing, it is the party assuming an obligation to pay. One Customer class serves both departments, accumulating optional fields, contradictory states and validations that apply only on certain screens. Renaming the class cannot resolve the difference in meaning.
Eric Evans' Domain-Driven Design helps investigate that difference before software makes it permanent. I recommend the book when business rules and vocabulary create more difficulty than infrastructure. Its payoff is building models that let you discuss concrete decisions with people who understand the domain.
The reference is the first edition of Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley/Pearson's listing records publication on August 20, 2003, and a 2004 copyright. The book covers using the model, building blocks, refactoring toward deeper insight and strategic design.
That combination matters. You can learn names for entities while remaining unable to decide where a rule belongs. You can draw contexts while code reproduces a contradictory global model. I suggest checking how each language decision changes an operation, test or responsibility.
In Designing Software by Crunching Knowledge, Evans describes developers and experts learning together, with scenarios and an implementation feeding further discussion. Modeling participates in development rather than ending when someone delivers a diagram.
For our review example, asking which fields a customer contains yields an unhelpful list. Asking whether a proposal can be accepted before a validated payer exists reveals a rule and potentially a departmental difference. The answer needs examples, including exceptions and rejected operations.
Useful language appears in conversations and software decisions. If sales says "proposal accepted" while code says status = 3, meaning relies on external memory. Making the transition explicit allows discussion of when it happens and what it authorizes. The objective extends beyond translating database table names into commercial vocabulary.
Evans' 2015 DDD Reference collects the concepts used here. A bounded context sets a model's scope; ubiquitous language supports communication. Entities retain identity, value objects represent values, and aggregates group elements under consistency rules.
Apply those distinctions to decisions rather than treating them as a glossary. Sales may track an account without complete tax registration. Billing must identify a party eligible to assume an obligation. The same company can appear in both models, but sharing an identifier does not make their rules equivalent.
These meanings can be separated into modules within one process. A microservice would introduce decisions about deployment, communication and operations. A semantic conflict gives a reason to separate models; distributing execution requires additional justification. A context can also contain several operations and modules without becoming one enormous class.
Consider this hypothetical exercise, containing no production data or measured results. Sales records proposals and their items. Billing issues charges. Our fictional policy permits proposal acceptance before financial validation but forbids issuing a charge without identifying the debtor. This asymmetry is the reason to try a boundary.
The records below represent the same transaction at different stages. Amounts use integer minor units and identifiers are invented. The billing column describes an obligation created after validation, not an object already present during negotiation.
| Decision | Sales | Billing |
|---|---|---|
| Relevant identity | Proposal P-81, account C-14 | Obligation OB-22, debtor D-9 |
| State | Accepted proposal, revision 2 | Recognized obligation |
| Amount | Items A: 2 × 3000, B: 1 × 4000 | Principal: 10000 minor units, currency BRL |
| Information absent from sales | Validated debtor | Requires explicit mapping C-14 → D-9 |
Sales may care about proposal validity, negotiated prices and acceptance. Billing cares about the amount owed, due dates and settlement. A shared accepted state would conceal the gap between commercial acceptance and a billable obligation.
The total is 2 × 3000 + 1 × 4000 = 10000. This exercise excludes tax, discounts and installments. In a real project, decide who calculates those components and which representation authorizes charging. Copying a total from a screen without establishing its meaning merely transports the ambiguity.
Free and paid products to turn ideas into a foundation you can actually ship.
13 products availableContinue exploring similar topics

Use Vaughn Vernon's approach to define consistency boundaries around rules that must remain true under concurrent work.

Apply Michael Nygard's approach to timeouts, isolation and recovery through a controlled failure exercise.

Turn passive study into a learning project with direct practice, feedback and spaced retrieval.
A 47-point checklist to find bugs, security risks, and performance issues before launch.
Production-tested templates trusted by developers. Save weeks of setup on your next project.
An accepted proposal produces an instruction containing instructionId=I-81-2, proposalId=P-81, revision=2, accountRef=C-14, amountMinor=10000 and currency=BRL. The instruction records the commercial commitment used in this integration. It does not assert that the debtor has already been validated.
Billing's translation resolves accountRef to a debtor and checks eligibility. Without a valid mapping, it marks the instruction as pending and issues no charge. Whoever handles pending instructions must see the cause and complete registration; silent failure would disguise a process problem as an integration detail.
Establish verifiable invariants. The instructionId stays stable across retries and must not create two obligations. Reusing it with different commercial content, such as another proposal, revision, account, currency or amount, creates a conflict for investigation; it does not authorize overwriting the obligation. Instruction currency must match the currency permitted for that obligation. Later commercial cancellation needs an explicit operation; it cannot automatically erase a financial record. In an implementation, recording deduplication and creating the obligation must be atomic; this conceptual example does not demonstrate that property.
A proposal change creates another revision, but whether it replaces or supplements an obligation requires an agreed policy. A revision number cannot decide that. These contracts make the boundary open to review: you can now request duplicate, amendment and cancellation examples before choosing events, endpoints or tables.
In our sales model, P-81 retains its identity when a contact address changes. Treating it as an entity lets us ask which transitions belong to the same proposal. Commercial account and financial debtor have separate identities; maintaining their association requires work rather than assuming equal numbers mean equal concepts.
A monetary value can hold amount and currency together. Two values of 10,000 BRL minor units may be equivalent for calculation; values in different currencies are not interchangeable. Immutability helps prevent a shared object's modification from accidentally altering an accepted proposal.
For proposal and items, try an aggregate boundary preventing acceptance of mixed-currency items or price changes to an accepted revision. These are invariants of our fictional policy. Implementation must prevent direct writes from bypassing the operations maintaining them, including maintenance code.
Do not group every account, proposal and charge into one aggregate simply because relationships exist. That would increase the work required to change any part and might broaden transactional locking. A financial obligation can maintain its own consistency. Across boundaries, the exercise requires translation and pending-work handling, with explicit costs.
Draw a simple map: sales supplies instructions; billing translates them and decides about issuance. Identify who approves the contract and communicates changes. If billing cannot influence the format, adaptation becomes its burden. Showing that dependency matters more than drawing symmetrical boxes with no owners.
Keep tests using the exercise records. Check acceptance without a debtor, a pending instruction without a mapping and a single obligation after two identical deliveries. Add the conflicting amount and later revision. Cases should exercise the actual implementation of whichever contract you choose.
Separation costs translation code, tracing and reconciliation. Calls and transactions inside a monolith may simplify some work. If communication later becomes asynchronous, failures and duplication require additional operational guarantees. A conceptual design cannot demonstrate working delivery or deduplication.
Its strength is making seemingly natural models open to challenge. A Customer class looks harmless until it carries incompatible rules. Following scenarios can reveal whether a concept or boundary is missing, or whether a rule was misunderstood. That creates a technical discussion with commercial consequences.
The reading requires effort and adaptation. Object-oriented examples and Java code reflect the edition's context. Principles can inform other languages, but mechanically reproducing hierarchies and repositories cannot guarantee effective modeling. A functional application may express the same distinctions through other mechanisms.
Reducing DDD to a folder structure is especially risky. Without access to experts or examples of decisions, names may merely organize assumptions. One expert may also represent only part of the process. Seek disagreement between operations, support and finance before declaring the language settled.
I recommend it to developers who have experienced changing business rules and can discuss tests, persistence and transactions. Object-oriented knowledge helps with the edition, but the main investment is reasoning about meaning and responsibility. A newer developer benefits from connecting each concept to a concrete flow.
For an administrative application with few rules or disagreements, clear CRUD may suffice. Complex aggregates around every table can consume time without improving decisions. When a business is still finding its vocabulary, keep models as small, revisable hypotheses. Avoid freezing the entire organization into a supposedly final schema.
Vaughn Vernon's Implementing Domain-Driven Design complements implementation discussions. Clean Architecture helps examine dependency direction. Separating infrastructure cannot itself determine whether commercial customer and financial debtor mean the same thing. Investigating that question is the reason to read Evans.
Start with communication and language. Take an ambiguous word to two departments and record conflicting scenarios. Study context and mapping to test a boundary, then return to tactical patterns when a rule needs explicit identity or consistency. This is my suggested study route, not a required order from the book.
Implement a small handoff, such as the exercise instruction, and ask both departments to explain its records and rejected cases. If people must reinterpret everything in a spreadsheet, investigate what the model omitted. Revise vocabulary as new situations appear.
I strongly recommend the book for domains where misunderstandings become code and rework. The desired outcome is explaining which rule belongs to each model, which commitment crosses the boundary and who answers for it. That criterion matters more than counting introduced entities, services or aggregates.