ArchitectMind
  1. Home
  2. Tutorials
  3. Book 7 · Application Architecture & Distributed Systems
  4. Volume III
  5. Domain-Driven Design
Book 7 · Volume III — Domain-Driven Design & Business Boundaries · Topic 1.1

Domain-Driven Design

Domain, model and ubiquitous language

19 min readStage 3 · Systems8 interview questions
Chapter
Chapter 1 — DDD Fundamentals
Prerequisites
Volume II Chapter 7 (domain-centric architectures)
You will be able to
Define domain, model and ubiquitous language precisely, run knowledge-crunching sessions with domain experts, express a model in Java code that reads like the business, maintain and evolve the ubiquitous language across conversations, documents, tests and code, and recognise when DDD is (and isn't) worth the investment
Covers
Domain; Model; Ubiquitous language
On this page
  1. 1. Concept / Definition
  2. 2. Why It Exists
  3. 3. Core Concepts
  4. 4. How It Works Internally — Knowledge Crunching
  5. 4A. Deep Dive — When DDD Is Worth It
  6. 5. APIs / Features / Syntax
  7. 6. Practical Examples
  8. 7. Common Problems & Mistakes
  9. 8. Performance & Production Considerations
  10. 9. Architecture & Design Considerations
  11. 10. Interview Questions & Answers

1Concept / Definition

Domain-Driven Design (DDD) is an approach to building software for complex business problems by placing the domain and a model of it at the centre of design, developed collaboratively by domain experts and developers using a shared ubiquitous language.

Domain-driven design (TOC)
Figure 1 — Domain-driven design (TOC)
TOC conceptDefinition
DomainThe sphere of knowledge, activity and problems the software addresses — e.g., retail, insurance, logistics — including its rules, vocabulary and experts
ModelA selective, purposeful abstraction of the domain that captures the concepts, rules and behaviour needed to solve specific problems; expressed in conversation, diagrams and — crucially — code
Ubiquitous languageA rigorous shared vocabulary, derived from the model, used consistently by experts and developers in speech, documents, tests and code within a boundary
Table 1 — The three TOC concepts

2Why It Exists

Most software failures in complex businesses come not from technology but from misunderstanding: developers translate business requests into technical terms, rules get scattered and misread, and every change becomes risky. DDD reduces translation by making the business language and rules explicit in the code.

Data-centric vs domain-centric modelling
Figure 2 — Data-centric vs domain-centric modelling

3Core Concepts

3.1 Domain

Domain (TOC): ShopNow retail
Figure 3 — Domain (TOC): ShopNow retail
Domain aspectShopNow example
ActivitiesSelling products online, fulfilling orders, handling returns
RulesPromotions can't stack beyond 40%; back-orders only for certain categories
VocabularyAllocation, back-order, dispatch, price override
ExpertsMerchandisers, pricing analysts, warehouse leads
ProblemsOversell, margin leakage, slow dispatch
Table 2 — Describing a domain

3.2 Model

Model (TOC): from reality to code
Figure 4 — Model (TOC): from reality to code
JAVA
// The model expressed in code: behaviour and rules, business vocabulary
public final class Order {
    private final OrderId id;
    private final List<OrderLine> lines = new ArrayList<>();
    private OrderStatus status = OrderStatus.DRAFT;
 
    public void addLine(Sku sku, Quantity qty, Money unitPrice) {
        requireStatus(OrderStatus.DRAFT);
        lines.add(new OrderLine(sku, qty, unitPrice));
    }
 
    public OrderPlaced place(BackOrderPolicy backOrders, StockAvailability stock) {
        requireStatus(OrderStatus.DRAFT);
        for (OrderLine l : lines)
            if (!stock.covers(l) && !backOrders.allows(l.sku()))
                throw new InsufficientStockException(l.sku());
        status = OrderStatus.PLACED;
        return new OrderPlaced(id, total(), Instant.now());
    }
}
Good model propertyMeaning
PurposefulCaptures what matters for the problem, not everything
Behaviour-richRules live with the concepts they govern
ExpressiveCode reads like expert conversation
EvolvingRefined as understanding deepens
Table 3 — Qualities of a useful model

3.3 Ubiquitous language

Ubiquitous language (TOC)
Figure 5 — Ubiquitous language (TOC)
JAVA
// Language in tests: Given-When-Then with business terms
@Test
void backOrderIsAllowedForPreOrderCategory() {
    var order = draftOrderWith(line(PRE_ORDER_GAME, 1));
    var noStock = StockAvailability.none();
    var policy = BackOrderPolicy.forCategories(Category.PRE_ORDER);
 
    var placed = order.place(policy, noStock);
 
    assertThat(placed).isNotNull();
    assertThat(order.status()).isEqualTo(OrderStatus.PLACED);
}
Language disciplinePractice
One term, one meaning (per context)Glossary with definitions
Terms in codeClass/method names from the glossary
No technical synonymsNot "OrderDTOManager" for a business concept
Change language → change codeRename when experts refine terms
Table 4 — Keeping the language alive

4How It Works Internally — Knowledge Crunching

Knowledge crunching
Figure 6 — Knowledge crunching
Event storming board (excerpt)
Figure 7 — Event storming board (excerpt)
Collaborative techniqueOutput
Event Storming (big picture)Timeline of domain events, hot spots
Event Storming (design level)Commands, aggregates, policies
Domain StorytellingPictographic stories of work
Example MappingRules with concrete examples
Glossary sessionsAgreed definitions
Table 5 — Crunching knowledge

4ADeep Dive — When DDD Is Worth It

SituationDDD value
Complex, changing business rulesHigh
Many experts, ambiguous termsHigh
Differentiating capabilityHigh
Simple CRUD, stable formsLow — use simple layering
Pure technical problem (e.g., log shipping)Low
Table 6 — Where to apply DDD
JAVA
// jMolecules annotations make DDD roles explicit without framework coupling
import org.jmolecules.ddd.annotation.*;
 
@AggregateRoot
public final class Order {
    @Identity private final OrderId id;
    …
}
 
@ValueObject
public record Money(BigDecimal amount, Currency currency) { }

5APIs / Features / Syntax

NeedJava / tooling
Expressive value typesRecords (Money, Sku, Quantity)
Closed sets of states/eventsSealed interfaces, enums
Role annotationsjMolecules DDD
Executable languageJUnit 5 tests named in domain terms, Cucumber (optional)
GlossaryMarkdown/Backstage docs versioned with code
Table 7 — Toolkit

6Practical Examples

6.1 Lost in translation

A retailer's developers called every price change "update product"; merchandisers distinguished list price, promo price and override with different approvals. After a glossary session, the code gained PriceOverride with an approval rule, eliminating a class of margin-leakage defects.

6.2 Language drift

A team's code used Customer, the business said Account holder, support said Member. Adopting the business term in code and APIs reduced misunderstandings in requirements reviews noticeably.

6.3 Worked example — glossary entry

TermDefinitionCode
AllocationReservation of physical stock for an order lineAllocation
Back-orderAccepted line without available stockBackOrderPolicy
Table 8 — Glossary as a living artefact

Scenario. In ShopNow's fulfilment project, warehouse staff, customer service and developers used "allocated", "reserved" and "assigned" to mean different things. Bugs kept appearing where one group's meaning was implemented instead of another's.

Walkthrough. The team started a glossary in the repository, with one entry per term: the term, an agreed definition and the code element that implements it. "Allocation" became the reservation of physical stock for an order line, implemented by the Allocation class. "Back-order" became an accepted order line without available stock, governed by a BackOrderPolicy. Every new term discussed in a modelling session was added in the same week, and changes to definitions went through pull requests, so domain experts could review them.

TEXT
docs/glossary.md
| Term       | Definition                                         | Code            |
| Allocation | Reservation of physical stock for an order line   | Allocation      |
| Back-order | Accepted line without available stock              | BackOrderPolicy |

Result. Within two months, arguments about meaning in stand-ups largely disappeared, and code reviews started rejecting names that were not in the glossary.

Takeaway. Keep the ubiquitous language as a living artefact next to the code, with a link from each term to its implementation. A glossary that nobody maintains is worse than none, so make updating it part of normal work.

6.4 Worked example — anaemic vs rich

AnaemicRich
order.setStatus(PLACED) in serviceorder.place(policy, stock)
Table 9 — Behaviour in the model

Scenario. ShopNow's order service had an Order class with only getters and setters. Rules for placing an order, such as checking stock and applying the back-order policy, were spread across three service classes, and one of them forgot to check stock.

Walkthrough. In an anaemic model, services manipulate data, for example order.setStatus(PLACED), so nothing stops code from skipping a rule. In a rich model, the entity owns its behaviour: order.place(policy, stock) checks the stock, applies the back-order policy, changes the status and records an OrderPlaced event, all in one place. The setters disappear, so the only way to place an order is the method that enforces the rules.

JAVA
public void place(BackOrderPolicy policy, StockLevels stock) {
    if (status != Status.DRAFT) throw new IllegalStateException("already placed");
    lines.forEach(l -> l.allocateOrBackOrder(stock, policy));
    status = Status.PLACED;
    events.add(new OrderPlaced(id, total()));
}

Result. The stock-check bug could no longer happen, because no code path could change the status without going through place(). Unit tests for ordering rules moved into simple tests of the Order class.

Takeaway. Put business behaviour into the domain model so rules are enforced in one place. If entities only hold data, every service that touches them must remember every rule.

6.5 Worked example — value objects add language

JAVA
public record Quantity(int value) {
    public Quantity {
        if (value <= 0) throw new IllegalArgumentException("quantity > 0");
    }
}

Scenario. Quantities in ShopNow's code were plain ints. Negative quantities, zero-item order lines and quantities mixed up with prices appeared in production data from different code paths.

Walkthrough. A Quantity record gives the concept a name in the code and enforces its rule in the compact constructor: a quantity must be greater than zero. Methods that take a Quantity can no longer receive a price or a negative number by mistake, because the compiler checks the type and the constructor checks the value. The record also gives value-based equality for free.

Result. Invalid quantities were rejected at the edge of the domain with a clear error, rather than discovered later in reports. Method signatures such as allocate(Sku, Quantity) also became self-documenting.

Takeaway. Use value objects to add domain language and rules to primitive values. Small records like Quantity, Money or Sku prevent whole classes of bugs and make code read like the business.

6.6 Worked example — event storming output

EventCommandAggregate
Order PlacedPlace OrderOrder
Stock AllocatedAllocate StockStock Item
Table 10 — From sticky notes to model

Scenario. ShopNow ran a two-day event storming workshop for returns, with orange sticky notes for events, blue for commands and yellow for aggregates covering a long wall.

Walkthrough. After the workshop, the team turned the wall into a table that links each event to the command that causes it and the aggregate that decides it. "Order Placed" results from the "Place Order" command handled by the Order aggregate; "Stock Allocated" results from "Allocate Stock" handled by the Stock Item aggregate. The table became the first draft of the domain model: aggregates became classes, commands became methods and events became event types.

JAVA
class StockItem {                                 // aggregate
    StockAllocated allocate(AllocateStock cmd) {  // command -> event
        ensureAvailable(cmd.quantity());
        available = available.minus(cmd.quantity());
        return new StockAllocated(sku, cmd.orderLineId(), cmd.quantity());
    }
}

Result. Developers started coding with a model that domain experts had already agreed, and the event names were used directly in the code and in Kafka topics.

Takeaway. Capture event storming results in a durable form and translate them directly into code. Workshops create shared understanding only if their output is used afterwards.

6.7 Worked example — hot spot

Hot spotResolution
"What if only part of the stock exists?"Split lines; partial allocation rule
Table 11 — Discovering rules

Scenario. During event storming for allocation, a warehouse manager asked, "What if only part of the ordered stock exists?" Nobody had an answer, so the facilitator marked it with a pink hot-spot note.

Walkthrough. Hot spots are questions, conflicts or unknowns found during modelling. Instead of guessing, the team scheduled a short session with the business. The answer was a new rule: split the order line into an allocated part and a back-ordered part, and notify the customer. The rule was added to the glossary, modelled as a method on the order line, and covered by tests written in business language.

Result. Without the hot spot, developers would have chosen a behaviour on their own, probably rejecting the whole line, and customers would have lost orders that could have been partly fulfilled.

Takeaway. Make unknowns visible during modelling and resolve them with domain experts, not in code. Hot spots are where the most valuable business rules are discovered.

6.8 Worked example — expert review of code

PracticeEffect
Read domain tests aloud with expertsValidate rules early
Table 12 — Code as communication

Scenario. ShopNow's developers wanted to confirm that their allocation rules matched the warehouse's expectations, but domain experts could not read Java.

Walkthrough. The team wrote domain tests with names and structure in business language, and read them aloud with warehouse experts every two weeks. A test named "back-orders the remaining quantity when stock is insufficient" describes a rule experts can confirm or correct. The test body uses domain terms, such as stock levels, order lines and allocations, so the conversation stays in the experts' language.

JAVA
@Test
void backOrdersTheRemainingQuantityWhenStockIsInsufficient() {
    var stock = StockLevels.of(sku("SKU-1"), 3);
    var line  = OrderLine.of(sku("SKU-1"), Quantity.of(5));
    line.allocateOrBackOrder(stock, BackOrderPolicy.ALLOW);
    assertThat(line.allocated()).isEqualTo(Quantity.of(3));
    assertThat(line.backOrdered()).isEqualTo(Quantity.of(2));
}

Result. Experts found two misunderstood rules in the first session, long before release. Fixing them took minutes instead of becoming production incidents.

Takeaway. Use code, especially tests, as a communication tool with domain experts. Rules validated early with experts are far cheaper than rules corrected after release.

6.9 Worked example — naming smells

SmellBetter
OrderManager, ProcessorHelperOrderPlacement, ReturnAuthorisation
Table 13 — Business verbs and nouns

Scenario. ShopNow's code base contained classes such as OrderManager, ProcessorHelper and ReturnUtil. New developers could not guess what any of them did without reading every method.

Walkthrough. Generic technical names hide business meaning. The team renamed classes after the business concepts and activities they implement: OrderManager split into OrderPlacement and OrderCancellation, ProcessorHelper became ReturnAuthorisation, and methods were renamed with business verbs such as authorise, allocate and dispatch. The renames followed the glossary, and code review guidelines now reject names ending in Manager, Helper or Util in domain packages.

Result. New developers could navigate the code by business concepts they already knew from the glossary, and domain experts could follow discussions about class names.

Takeaway. Name domain code with business nouns and verbs. Names like Manager or Helper usually signal unclear responsibilities and should prompt a design discussion.

6.10 Worked example — model scope

IncludeExclude
Order lines, pricing at placementWarehouse bin locations (other context)
Table 14 — Model is purposeful

Scenario. The order model in ShopNow's first DDD attempt included warehouse bin locations, picking routes and courier details, because "orders eventually involve all of these". The model became huge and hard to change.

Walkthrough. A model is a purposeful abstraction for specific problems, not a copy of reality. The ordering model needs order lines and the prices at placement, because ordering rules depend on them. Bin locations and picking routes belong to the warehouse context, which has its own model and language. The team removed them from the order model and exchanged only the necessary information through events.

Result. The order model shrank to the concepts ordering decisions need, and the warehouse team could change bin management without affecting ordering.

Takeaway. Decide deliberately what each model includes and excludes, based on the problems it must solve. A model that tries to represent everything ends up serving nothing well.

6.11 Worked example — example mapping

RuleExample
Promo cap 40%30% + 20% promo → capped at 40%
Table 15 — Examples sharpen rules

Scenario. ShopNow's rule "promotions are capped at 40%" seemed clear, but developers and marketing disagreed on what happened when a 30% discount and a 20% promotion applied together.

Walkthrough. Example mapping makes rules concrete. For each rule, the team wrote concrete examples with inputs and expected outcomes, reviewed them with marketing, and turned them into tests. The example "30% discount plus 20% promotion is capped at 40%" settled the question: discounts are added, then capped, rather than applied one after the other.

JAVA
@ParameterizedTest
@CsvSource({"30, 20, 40", "10, 15, 25", "40, 10, 40"})
void combinedDiscountIsCappedAt40Percent(int discount, int promo, int expected) {
    assertThat(Pricing.combined(pct(discount), pct(promo))).isEqualTo(pct(expected));
}

Result. The ambiguity was resolved before any code was written, and the examples became regression tests that marketing could read.

Takeaway. Sharpen every non-trivial rule with concrete examples agreed with domain experts, and turn the examples into tests. Abstract rule descriptions hide disagreements; examples expose them.

6.12 Worked example — refactoring toward deeper insight

BeforeAfter insight
discount fieldPromotion with eligibility + stacking rules
Table 16 — Model evolves

Scenario. ShopNow's Order class had a single discount field. As marketing introduced stacked promotions, eligibility rules and exclusions, the code around that field grew into a mess of conditionals.

Walkthrough. During a modelling session, the team realised that "discount" was not one number but the result of applying promotions, each with eligibility rules and stacking behaviour. This deeper insight changed the model: a Promotion concept with its own eligibility and stacking rules, and an order that records which promotions were applied. The refactoring was done in small steps, with existing tests protecting behaviour.

Result. New promotion types became new Promotion configurations rather than new conditionals, and pricing bugs dropped sharply.

Takeaway. Models evolve as understanding deepens. When code around a concept keeps getting more complex, look for a missing domain concept and refactor towards it.

6.13 Worked example — API language

BadGood
POST /orders/{id}/statusPOST /orders/{id}/placement
Table 17 — Language in APIs

Scenario. ShopNow's order API exposed POST /orders/{id}/status with a body {"status":"PLACED"}. Clients could set any status, and the API said nothing about the business actions it supported.

Walkthrough. The domain language was carried into the API. Instead of a generic status update, the API offers business operations: POST /orders/{id}/placement to place an order, and similar endpoints for cancellation and dispatch. Each endpoint maps to a domain method that enforces the relevant rules, so clients can only perform valid actions.

Result. Invalid status jumps were no longer possible, the API documentation read like a list of business capabilities, and client teams understood the order life cycle from the endpoint names.

Takeaway. Express the ubiquitous language in APIs, not only in internal code. Generic CRUD-style APIs leak data structures; business-oriented APIs expose capabilities and protect rules.

6.14 Worked example — sealed states

JAVA
public sealed interface ReturnDecision permits Approved, Rejected, NeedsInspection { }

Scenario. Return requests at ShopNow could be approved, rejected or sent for physical inspection. Code used a status string plus several nullable fields, and every handler had to remember which fields applied to which status.

Walkthrough. A sealed interface, ReturnDecision, permits exactly three implementations: Approved, Rejected and NeedsInspection. Each can be a record carrying only the data relevant to it, such as a rejection reason or an inspection location. A switch expression over ReturnDecision must handle all three cases, and the compiler checks this.

JAVA
String message(ReturnDecision d) {
    return switch (d) {
        case Approved a        -> "Refund of " + a.amount() + " approved";
        case Rejected r        -> "Rejected: " + r.reason();
        case NeedsInspection n -> "Please send the item to " + n.location();
    };
}

Result. When a fourth outcome, partial refund, was added later, the compiler pointed to every switch that needed updating, so no handler was missed.

Takeaway. Model a closed set of business outcomes with sealed types. The compiler then helps keep the domain language and its handling complete.

6.15 Worked example — domain story

ActorWork objectActivity
CustomerReturn requestsubmits
AgentReturn requestauthorises
Table 18 — Domain Storytelling

Scenario. Before modelling returns, ShopNow wanted a quick shared picture of how returns actually worked, told by the people who did the work.

Walkthrough. In a Domain Storytelling session, a customer service agent told the story while a moderator drew it with icons: the customer submits a return request; the agent authorises it; the warehouse receives the item; finance issues the refund. Each sentence has an actor, a work object and an activity. The resulting pictures became the first input to event storming and the glossary.

Result. Developers understood the process in one hour, and two steps that existed only in an agent's head, such as escalation for high-value items, were captured for the first time.

Takeaway. Use Domain Storytelling to capture how work is really done, in the words of the people doing it. It is a fast way to start a model and to find undocumented steps.

6.16 Worked example — language in events

Event nameLanguage quality
OrderStatusChangedWeak
OrderDispatchedStrong
Table 19 — Facts in business terms

Scenario. ShopNow's order service published a single OrderStatusChanged event with a status field. Consumers had to inspect the status and guess what had happened.

Walkthrough. Event names should state business facts in domain language. The team replaced the generic event with specific ones: OrderPlaced, OrderDispatched and OrderCancelled, each with the data relevant to that fact. Consumers subscribe to the facts they care about, and the event catalogue now reads like a description of the order life cycle.

Result. Consumer code became simpler, because each handler dealt with one clear fact, and new consumers understood the events without asking the orders team.

Takeaway. Name events as business facts in the past tense. Generic change events push interpretation onto consumers and hide the domain language.

6.17 Worked example — measuring model health

SignalHealthy
Business rules in domain classesMost
Technical terms in domain packageNone
Table 20 — Code review lens

Scenario. ShopNow's architects wanted to know whether DDD practices were actually reflected in the code, not just in workshop photos.

Walkthrough. They added two questions to code review. Do business rules live mostly in domain classes, such as entities, value objects and domain services, rather than in controllers or application services? Are domain packages free of technical terms, such as JPA annotations, HTTP classes or framework types? An ArchUnit rule checks the second question automatically.

JAVA
@ArchTest
static final ArchRule domainIsTechnologyFree = noClasses()
    .that().resideInAPackage("..domain..")
    .should().dependOnClassesThat().resideInAnyPackage(
        "org.springframework..", "jakarta.persistence..", "jakarta.servlet..");

Result. Reviews found several services where rules had drifted into controllers, and the automated check prevented new technical dependencies in the domain.

Takeaway. Measure model health in code reviews and automated checks. DDD succeeds only if the language and rules actually live in the code.

6.18 Worked example — experts access

ArrangementEffect
Weekly modelling session with merchandisersFaster learning
Table 21 — DDD needs expert time

Scenario. ShopNow's merchandising team was too busy to attend modelling sessions, so developers guessed at rules for promotions and assortments, and releases were repeatedly sent back.

Walkthrough. Leadership agreed a fixed weekly one-hour modelling session with two merchandisers, treated as part of their job rather than a favour. Developers prepared questions and examples in advance, and decisions were recorded in the glossary and as tests.

Result. Rework on promotion features fell noticeably, and merchandisers began proposing simplifications to rules once they saw how the model worked.

Takeaway. DDD needs regular access to domain experts. Secure that time explicitly with management, because guessed rules are far more expensive than an hour a week.

6.19 Worked example — when not to

ModuleApproach
Admin lookup tablesSimple CRUD
Table 22 — Apply selectively

Scenario. Enthusiastic after a DDD training, a team planned to model ShopNow's admin lookup tables, such as country lists and store opening hours, with aggregates, value objects and domain events.

Walkthrough. The architect pointed out that these modules have almost no business rules; they are simple data maintenance. Applying full DDD there would add complexity without benefit. The team used simple CRUD with Spring Data repositories for the lookup modules and saved the rich modelling effort for complex areas such as pricing, allocation and returns.

Result. The admin modules were delivered in days, and the team's modelling effort went where business complexity was highest.

Takeaway. Apply DDD selectively. Use rich models where business complexity and change justify them, and simple CRUD where the problem is mostly data entry.

6.20 Worked example — onboarding

ArtefactUse
Glossary + event-storm photos + domain testsNew joiners learn domain fast
Table 23 — Knowledge retention

Scenario. When an experienced developer left ShopNow's fulfilment team, the team worried that much domain knowledge would leave with her.

Walkthrough. Over the previous year the team had built durable artefacts: the glossary, photos of event-storming walls with notes, and domain tests written in business language. New joiners were asked to read the glossary, walk through the event-storm photos with a team member and run the domain tests, reading their names as a description of the rules.

Result. The next new developer became productive in about three weeks instead of the usual two months, and the team found that most of the departing developer's knowledge was already captured.

Takeaway. DDD artefacts are an investment in knowledge retention. Keep them current and use them deliberately for onboarding.

6.21 Worked example — interview answer

ConceptAnswer
DomainBusiness area & problems
ModelPurposeful abstraction, in code
Ubiquitous languageShared terms in talk & code
Table 24 — Structured answer

Scenario. In interviews for senior roles, ShopNow asks candidates to explain the core ideas of domain-driven design in two minutes.

How to answer. A clear answer explains three concepts and how they connect. The domain is the business area and its problems, such as fulfilment. The model is a purposeful abstraction of that domain, expressed primarily in code, capturing the concepts and rules needed to solve specific problems. The ubiquitous language is the shared vocabulary used by experts and developers in conversation, documents and code within a context. Then give one concrete example, such as how a term like "allocation" appears in a conversation, a class and an event.

How to finish. Finishing with when DDD is worth its cost, in complex domains with available experts and not for simple CRUD, shows practical judgement.

Takeaway. Explain DDD through connected concepts plus a real example. Interviewers want to see that you have applied it, not just read about it.

7Common Problems & Mistakes

  • DDD = patterns only, skipping collaboration.
  • Anaemic models with logic in services.
  • Technical vocabulary in the domain.
  • No expert access.
  • Glossary written once, never maintained.
  • DDD for trivial CRUD.

8Performance & Production Considerations

AspectGuideline
CollaborationRegular sessions with experts
LanguageGlossary versioned with code
ModelBehaviour-rich, purposeful
TestsWritten in domain language
ScopeApply where complexity justifies
Table 25 — Operational guidelines

9Architecture & Design Considerations

  • Invest in shared understanding before structure.
  • Make the code the model's primary expression.
  • Keep the language consistent within a boundary.
  • Use DDD where business complexity dominates.

10Interview Questions & Answers

Q1Basic

What is domain-driven design?

Domain-driven design is an approach to software development that puts a model of the business domain at the centre of the design. Developers and domain experts build that model together, using a shared ubiquitous language, and express it directly in code. DDD has two parts. Strategic design divides a large domain into bounded contexts with their own models and defines how they relate. Tactical design provides building blocks such as entities, value objects, aggregates and domain events. It is most valuable for complex business domains, where understanding the rules is the main challenge, rather than for simple data-entry applications.

Q2Basic

What is the ubiquitous language?

The ubiquitous language is a shared vocabulary that domain experts and developers agree on and use consistently, in conversations, documents, tests and code, within one bounded context. If warehouse experts say "allocation", the code has an Allocation class and an allocate method, and events are named accordingly. This removes translation between business and technical language, which is where misunderstandings and bugs arise. The language evolves as understanding deepens, and when a term changes, the code is renamed too. Different bounded contexts may use the same word with different meanings, which is why the language is scoped to a context.

Q3Intermediate

What is a model in DDD?

A model in DDD is a purposeful abstraction of the business domain that captures the concepts, relationships and rules needed to solve specific problems. It is not a complete picture of reality: it deliberately leaves out details that do not matter for its purpose. Although it may be sketched in diagrams, the primary expression of the model is the code, especially the domain layer with entities, value objects, aggregates and domain services. The model and the ubiquitous language reinforce each other, and both are refined continuously as the team learns more about the domain.

Q4Intermediate

What is knowledge crunching?

Knowledge crunching is the iterative process through which developers and domain experts distil domain knowledge into a model. Developers ask questions, experts explain how the business works, and together they test ideas against concrete scenarios, often using techniques such as event storming, example mapping and domain storytelling. The team proposes model elements, tries them in code, finds gaps and refines them. Over time, the noise of everyday details is filtered out and the essential concepts and rules remain. It is a continuous activity, not a phase completed at the start of a project.

Q5Senior Engineer

How do you keep code aligned with the language?

Use the glossary's terms everywhere in the code: class names, method names, event names, API endpoints and database tables. Write tests whose names and bodies read as business rules, and review them with domain experts. Avoid generic technical names, such as Manager or Helper, in the domain layer, and add code-review checks or ArchUnit rules that keep technical concepts out of domain packages. When the language changes, for example a term is renamed or split, refactor the code to match promptly. A mismatch between conversation and code is a signal of a modelling problem.

Q6Senior Engineer

What is an anaemic domain model?

An anaemic domain model consists of entities that only hold data, with getters and setters, while all business logic sits in separate service classes. It looks object-oriented but is essentially procedural: rules are scattered across services, nothing stops code from putting entities into invalid states, and the same rule is often implemented in several places. Martin Fowler described it as an anti-pattern for complex domains. DDD prefers rich models, where entities and value objects own the behaviour that changes their state, enforcing invariants in one place. Anaemic models are acceptable for simple CRUD applications.

Q7Technical Architect

When is DDD not appropriate?

DDD is not worth its cost when business complexity is low. Simple CRUD applications, such as maintaining reference data or admin screens, rarely have rules that justify rich models and extensive modelling sessions. Purely technical problems, such as building a file converter or a monitoring agent, have little business domain to model. DDD also struggles when domain experts are not available, because the model would be based on guesses. In these cases, simpler architectures, such as transaction scripts or straightforward CRUD with Spring Data, deliver faster. Strategic ideas such as bounded contexts can still help at the system level.

Q8Solution Architect

How would you introduce DDD in an organisation?

Start small with a business area that is both complex and valuable, where better modelling will visibly pay off. Secure regular access to domain experts with management support. Run event storming or domain storytelling sessions to build shared understanding, and capture the results in a glossary and a first model expressed in code and tests. Deliver working software and show the results, such as fewer defects or faster changes. Then spread practices gradually through training, pairing and communities of practice, and introduce strategic design, such as context maps, as more teams adopt DDD.

Newsletter

Get new tutorials every week

New ArchitectMind tutorials and interview questions, once a week. No spam, unsubscribe anytime.

Almost done! Check your inbox and click the confirmation link to complete your subscription.
Keep going in Book 7

Application Architecture & Distributed Systems

Volume I · Topic 1.1

Software Architecture

Architecture Fundamentals & Architectural Thinking

Volume I · Topic 1.2

Architecture Responsibilities

Architecture Fundamentals & Architectural Thinking

Volume I · Topic 1.3

Architect vs Developer

Architecture Fundamentals & Architectural Thinking

Volume II · Topic 1.1

Monolith Fundamentals

Application Architecture Styles & Patterns

Volume II · Topic 1.2

Types of Monoliths

Application Architecture Styles & Patterns

Volume II · Topic 1.3

Monolith Advantages

Application Architecture Styles & Patterns

Volume III · Topic 1.2

Strategic vs Tactical DDD

Domain-Driven Design & Business Boundaries

Volume III · Topic 1.3

Domain Complexity

Domain-Driven Design & Business Boundaries

Volume IV · Topic 1.1

What Is a Distributed System?

Distributed Systems Fundamentals

Volume IV · Topic 1.2

Why Distributed Systems Exist

Distributed Systems Fundamentals

Volume IV · Topic 1.3

Distributed System Complexity

Distributed Systems Fundamentals

Volume V · Topic 1.1

What Makes a Microservice?

Microservices Architecture

Volume V · Topic 1.2

Service Granularity

Microservices Architecture

Volume V · Topic 1.3

Microservice Boundaries

Microservices Architecture

Volume VI · Topic 1.1

Event Fundamentals

Event-Driven & Asynchronous Architecture

Volume VI · Topic 1.2

Event Types

Event-Driven & Asynchronous Architecture

Volume VI · Topic 1.3

Event-Carried State

Event-Driven & Asynchronous Architecture

Volume VII · Topic 1.1

Scalability

Resilience, Scalability & Distributed Data

Volume VII · Topic 1.2

Stateless Architecture

Resilience, Scalability & Distributed Data

Volume VII · Topic 1.3

Load Balancing

Resilience, Scalability & Distributed Data

Volume VIII · Topic 1.1

ADR

Architecture Decision-Making & System Evolution

Volume VIII · Topic 1.2

Architecture Decision Process

Architecture Decision-Making & System Evolution

Volume VIII · Topic 1.3

Reversible vs Irreversible Decisions

Architecture Decision-Making & System Evolution