Skip to content

Chapter 6: Domain-Driven Design as an Immune System

A foundation language model, by its statistical nature, converges toward the most average continuation for any given prompt—the statistically most probable token sequence across its training corpus. In production software engineering, the “most average” solution rarely reflects the nuanced business invariants of your proprietary domain. And when an unanchored model lacks requisite domain knowledge, it does not gracefully pause: it generates fluent, plausible, and completely unfounded code—the hallucination phenomenon cataloged across academic literature as a foundational vulnerability of foundation models Ji et al., 2023. Early academic critique characterized this unsparingly: absent external grounding, a language model is a stochastic parrot repeating linguistic forms without semantic commitment to truth Bender et al., 2021. While scholars debate the philosophical boundaries of that critique, its systems engineering consequence is inescapable: grounding is non-negotiable in production.

Domain-Driven Design (DDD) (Evans, 2003) is the systems methodology that establishes a Ubiquitous Language (a unified vocabulary shared between domain experts and software code) and explicit architectural perimeters (Bounded Contexts) within which that vocabulary possesses immutable meaning. Codified two decades before the advent of modern foundation models, DDD proves to be the exact operational artifact an autonomous agent demands: a machine-readable business ontology that programmatically segregates valid operations from fatal system corruptions.

When an autonomous agent is constrained by a formal Bounded Context, it reliably differentiates between:

  • A bank transfer (which mandates balance checks, AML/KYC validations, and statutory transaction limits), and
  • An arbitrary arithmetic mutation of numeric records in a relational database.

Absent that bounded context, the agent defaults to the latter: fast, cheap, and potentially illegal. This is why we characterize Domain-Driven Design as the immune system of the Synthetic Enterprise: it does not accelerate generation velocity; it prevents autonomous agents from synthesizing toxic code at industrial scale.

The Class Diagram: Structural Architecture of a DDD Aggregate

Section titled “The Class Diagram: Structural Architecture of a DDD Aggregate”

Design Justification: Class Diagrams for Structural Domain Modeling

In Domain-Driven Design, systems modeling centers on the static domain structure: which entities exist, which function as Aggregate Roots, and what invariants govern their transactional consistency boundaries. A class diagram illustrates precisely that: domain hierarchies, responsibilities, and consistency perimeters.

A state diagram models when an aggregate transitions; a component diagram models where it is deployed. Here, systems design demands what an aggregate is and which entity functions as its root—the solitary gateway for all state mutations.

Class diagrams reappear in Chapter 5 (agentic role structures) and Volume 4, Chapter 6 (Clean Architecture and Ports), where classes model structural invariants that remain stable over time.

Diagram 6.1: Structure of a DDD Aggregate: Root Entity, Sub-Entities, and Domain Events

► TECHNICAL TRACK: Feeding the Bounded Context to the Agent

# Example 6.1: Injecting domain context as a semantic immune system.
# The bounded context functions as a semantic guardrail: invariants,
# ubiquitous language, and prohibited anti-patterns, maintained as versioned markdown.
DOMAIN_CONTEXT_FINTECH = """
BOUNDED CONTEXT: Bank Transfers — MANDATORY Business Invariants
INVARIANTS (Never Violate):
1. A transfer can only be created if available_balance >= amount + fee
2. Maximum transaction amount is USD 50,000 (verify via ConfigService)
3. Transfers exceeding USD 10,000 require recipient AML/KYC verification
4. Transfer state is immutable once transitioned to PROCESSING status
UBIQUITOUS LANGUAGE:
- "Account" = entity with IBAN, available_balance, pending_balance, and status
- "Transfer" = aggregate root with unique_id, origin, destination, amount, fee, status
- "Fee" = computed by CommissionService based on account tier and transfer volume
- "Available" = total_balance - pending_balance (NEVER use raw total_balance)
EXTERNAL CONTEXTS (Do NOT implement within this module):
- Authentication -> AuthContext
- Push Notifications -> NotificationContext
- Regulatory Auditing -> ComplianceContext
PROHIBITED ANTI-PATTERNS:
- Do not access database drivers directly (use Repository interfaces)
- Do not expose core domain entities in REST contracts (use DTOs)
- Do not trigger transactional notifications within this domain context
"""
import anthropic
client = anthropic.Anthropic()
def code_with_domain_guard(technical_task: str) -> str:
"""Generates code with the bounded context injected as an architectural guardrail."""
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=4096,
system=f"""You are a Principal Software Engineer specialized in Domain-Driven Design and Hexagonal Architecture.
MANDATORY DOMAIN CONTEXT — you must read and strictly enforce these rules before writing code:
{DOMAIN_CONTEXT_FINTECH}
DIRECTIVE: Synthesize code that STRICTLY complies with the declared bounded context.
If the requested task violates any domain invariant, HALT execution immediately and explain which invariant is breached and why.""",
messages=[{"role": "user", "content": technical_task}],
)
return next(b.text for b in response.content if b.type == "text")

Three software engineering practices elevate this pattern into production infrastructure:

  1. Domain Context Resides in the Repository, colocated alongside the code it governs (domain/CONTEXT.md), reviewed and merged within the same pull requests. Stale domain documentation is worse than no documentation: it breeds false confidence.
  2. Context Functions as a Cacheable Prefix. Modern foundation model APIs support prompt prefix caching; an extensive enterprise domain context reused across recurring requests costs a fraction of standard pricing starting on the second call (see Volume 4, Chapter 1). Clean systems architecture (positioning static domain rules first and transient task inputs last) happens to be both the most reliable and the most cost-effective—an alignment every architect should exploit.
  3. Every Invariant Is Covered by an Automated Test. The bounded context instructs the agent what invariants must never be breached; automated regression tests verify they remained intact. Volume 4, Chapter 3 operationalizes this testing loop.

From Static Documentation to Automated Runtime Gates

Section titled “From Static Documentation to Automated Runtime Gates”

The ultimate maturity milestone is deriving automated static analysis rules directly from the domain context: if Invariant 1 dictates that every financial transfer requires balance verification, a deterministic linter or AST checker verifies that all execution paths creating transfers traverse the balance check. The code review agent from Chapter 5 (reviewer) inspects domain rules in its prompt while static checkers execute in CI/CD: two complementary immune layers—one probabilistic, one deterministic. Defense-in-depth is not cynicism toward models; it is sound systems engineering.

■ EXECUTIVE TRACK

The executive question regarding agentic Domain-Driven Design is capital expenditure: reverse-engineering and codifying bounded contexts across legacy enterprise systems demands weeks of labor from the most senior engineers in the company. The direct ROI justification: that documentation is no longer shelf-ware; it is an active production runtime asset. It is injected into every inference call executed by every autonomous agent touching that domain, multiplying first-pass code acceptance rates and eradicating the rework that currently consumes those exact senior engineers. Enterprises that practiced rigorous DDD enter the agentic era with a massive structural moat; those that neglected domain modeling now have the compelling economic incentive to repay that technical debt.

Worked Exercise 6.1: Author an Inventory Bounded Context

Section titled “Worked Exercise 6.1: Author an Inventory Bounded Context”

Problem Statement. Draft a structured domain context (following the schema of Example 6.1) for an enterprise e-commerce inventory subsystem governed by this non-negotiable business invariant: “an item cannot be sold unless physically in stock, and items reserved within active checkout sessions are unavailable for sale.”

Walkthrough Solution.

BOUNDED CONTEXT: Inventory Management — MANDATORY Business Invariants
INVARIANTS (Never Violate):
1. available_stock = physical_stock - reserved_stock; NEVER permit checkout if available_stock < requested_quantity
2. An active reservation expires after exactly 30 minutes, returning quantity to available_stock
3. physical_stock mutates exclusively via warehouse intake, confirmed order dispatch, or audited adjustments
4. Manual stock adjustments are rejected without an audited audit_log_id
UBIQUITOUS LANGUAGE:
- "Available" = physical_stock - reserved_stock (NEVER inspect raw physical_stock to authorize sales)
- "Reservation" = stock locked by an active user checkout session with an explicit TTL expiration
- "Adjustment" = audited ledger reconciliation of physical_stock (shrinkage, count discrepancy, damaged inventory)
EXTERNAL CONTEXTS (Do NOT implement within this module):
- Pricing & Dynamic Discounts -> PricingContext
- Payment Gateway Processing -> PaymentContext
PROHIBITED ANTI-PATTERNS:
- Do not inspect physical_stock directly to authorize a sale (always query available_stock)
- Do not instantiate inventory reservations lacking an explicit expiration TTL

Key architectural merits to benchmark against your own implementation: (1) the business requirement was decomposed into atomic, testable invariants: each maps directly to a deterministic unit test (Volume 4, Chapter 3); (2) the deceptive terminology trap (physical_stock vs. available_stock) was defined within the Ubiquitous Language and explicitly forbidden within Anti-Patterns—deliberate semantic redundancy defending against the “average” model output that would naively query physical stock; (3) reservation TTL expiration, which was implicit in business reality but unmentioned in the raw requirement, was made explicit: drafting domain contexts is the precise engineering activity that surfaces unspoken domain rules, which constitutes half the value of the exercise.

  • Foundation models synthesize the “average” solution; your proprietary enterprise domain is never average. Absent grounding, models hallucinate with total fluency (Ji et al., 2023; Bender et al., 2021).
  • Domain-Driven Design (Evans, 2003) provides the machine-readable ontology an agent requires: invariants, ubiquitous language, and bounded contexts.
  • Domain contexts live in version control, serve as cacheable prompt prefixes (~90% cost reduction), and back automated test assertions: three practices forming a cohesive immune system.
  • Enforce defense-in-depth: semantic context in the prompt (probabilistic defense) paired with static AST checkers and unit tests in CI/CD (deterministic defense).
  1. Does your enterprise possess a formalized, machine-readable domain specification that an autonomous agent could consume today? If not, whose mind holds that institutional knowledge, and what is your business exposure if they resign?
  2. Which business invariant in your core product, if breached by synthetic code operating at machine speed, would trigger the most catastrophic financial outage? Is that invariant documented anywhere a machine can parse it?