Strategic design is the part of DDD that decides how to divide a large domain into manageable and coherent pieces. In the previous lesson we learned how to classify subdomains; now we are going to draw the concrete boundaries of the model within the software through the most important concept in all of DDD: the Bounded Context. Alongside it we will study the Ubiquitous Language, the shared vocabulary that keeps business and development aligned. This lesson matters because it is where DDD connects directly with architecture: the boundaries we draw here will determine, for example, the frontiers of our microservices. Getting these boundaries wrong is one of the most expensive causes of failure in distributed systems.
Contents
- The problem: the same term, different meanings
- Ubiquitous Language
- Bounded Context
- Relationship between Bounded Contexts and microservices
- Integrated example in an insurance company
- The problem: the same term, different meanings
In any medium-sized organization, the same words mean different things depending on the department. Consider the word "Customer" in an insurance company:
- For the Sales area, a Customer is a lead with contact details and a history of sales interactions.
- For the Underwriting area, a Customer is a risk profile with age, occupation, and prior claims history.
- For the Billing area, a Customer is an account with a direct debit setup, receipts, and debts.
If we try to create a single giant Customer class that serves everyone, we get a huge object, full of fields that only some use, with contradictory rules, and that no one can modify without fear of breaking another area. This is the classic "single model for the entire company," and it always fails in complex domains.
DDD proposes the opposite: accept that meaning depends on the context and draw explicit boundaries.
- Ubiquitous Language
The Ubiquitous Language is a common, rigorous vocabulary shared between developers and domain experts, used everywhere: in conversations, in documentation, and—crucially—in the code.
Principles:
- Each term has a single, precise meaning within its context.
- If the business says "underwrite a policy," the code has an
underwrite()method, notcreate()orsave(). - The language evolves: when a nuance is discovered, the spoken language and the code are updated at the same time.
Let's see the difference it makes in the code:
// WITHOUT Ubiquitous Language: generic technical names
public class PolicyManager {
public void process(PolicyData data) { /* ... */ }
public void update(Long id, int status) { /* ... */ }
}Problems with the previous fragment:
PolicyManager,process, andupdatesay nothing about the business. What does it process? What does it update?- The state is represented with an
int: no one knows whatstatus = 2means. - A domain expert could not read this code or validate that it does the right thing.
// WITH Ubiquitous Language: the code speaks the language of the business
public class Policy {
public void underwrite(Subscriber subscriber) { /* ... */ }
public void rejectForExcessiveRisk(RejectionReason reason) { /* ... */ }
public void renew() { /* ... */ }
}Here:
- The names
underwrite,rejectForExcessiveRisk, andreneware exactly the verbs the business uses. - The
SubscriberandRejectionReasontypes are domain concepts, not loose primitive data. - A domain expert can read and understand what the class does, which allows modeling errors to be detected in conversation, without running anything.
The Ubiquitous Language is the bridge that eliminates the constant translation between "what the business says" and "what the programmer writes."
- Bounded Context
A Bounded Context is an explicit boundary within which a domain model and its Ubiquitous Language have a single, consistent meaning. Outside that boundary, the same terms may mean something different, and that is fine.
Each Bounded Context has:
- Its own domain model.
- Its own Ubiquitous Language (the word "Customer" can be modeled differently in each one).
- Its own business rules and, usually, its own database.
Returning to the "Customer" example:
| Bounded Context | How it models the "Customer" | Relevant attributes |
|---|---|---|
| Sales | Lead / Prospect |
Contact details, funnel stage, assigned salesperson |
| Underwriting | Insured / Risk Profile |
Age, occupation, claims history |
| Billing | CustomerAccount |
Payment method, receipts, outstanding balance |
The radical idea is: there is no single "Customer". There are three distinct models, each optimal for its context, that share a referenced identity (a common identifier) but not the same structure. How those contexts communicate is something we will see in lesson 06-04 (Context Mapping).
graph LR
subgraph BC_Ventas[Bounded Context: Sales]
L[Lead]
end
subgraph BC_Suscripcion[Bounded Context: Underwriting]
A[Insured]
end
subgraph BC_Facturacion[Bounded Context: Billing]
C[CustomerAccount]
end
L -. same real customer .-> A
A -. same real customer .-> CAbout this Mermaid diagram (graph LR, left to right):
- Each
subgraphrepresents a Bounded Context with its explicit boundary. - Within each context lives a different model of the "customer":
Lead,Insured,CustomerAccount. - The dotted arrows (
-.->) indicate that they refer to the same real person, but do not share the same code model: they are connected by identity, not by structure.
- Relationship between Bounded Contexts and microservices
There is a natural—but not mandatory—correspondence between Bounded Contexts and microservices:
| Concept | Bounded Context (DDD) | Microservice (architecture) |
|---|---|---|
| Nature | Logical boundary of a model | Physical unit of deployment |
| Defines | Consistent meaning of terms | Independent process, API, own DB |
| Relationship | Is a good candidate for a microservice | Ideally, aligns its boundary with a BC |
The good practice is: a microservice should contain one or more complete Bounded Contexts, never split a Bounded Context across several microservices. If you split a context, you distribute its model and its rules across different services, which generates extremely strong coupling and constant communication.
Bounded Contexts offer the decomposition criterion that microservice teams often miss. Without DDD, many split services by technical entities ("user service," "order service") and end up with a distributed monolith.
Example of how a Bounded Context translates into the configuration of an independent microservice:
# deployment of the microservice that implements the Underwriting Bounded Context
apiVersion: apps/v1
kind: Deployment
metadata:
name: underwriting-service # one BC = one deployable service
spec:
replicas: 2
template:
spec:
containers:
- name: underwriting
image: registry.fiatc/underwriting:1.4.0
env:
- name: DB_URL
value: jdbc:postgresql://db-underwriting:5432/underwriting # own DBComments on this Kubernetes YAML:
name: underwriting-servicemakes it clear that the service corresponds to a single Bounded Context (Underwriting).imagereferences the container image of that context, versioned independently.- The
DB_URLvariable points todb-underwriting: a dedicated, exclusive database for the context. Other contexts do not access it directly; that preserves the boundary.
- Integrated example in an insurance company
Let's put it all together. An insurance company could organize its system into these Bounded Contexts:
graph TD
V[Sales] --> S[Underwriting]
S --> P[Policy Management]
P --> SI[Claims Management]
P --> F[Billing]
SI --> FReading the diagram:
- Sales captures interested parties; when one accepts, they move to Underwriting.
- Underwriting assesses the risk and, if it approves, Policy Management creates the policy.
- Claims Management and Billing depend on the active policy.
Each of these contexts has its own Ubiquitous Language: in Sales they speak of "Lead" and "opportunity"; in Claims, of "claim report," "loss adjuster," and "indemnity." Forcing a single vocabulary across all of them would be a mistake.
Common Mistakes and Tips
- The single corporate model. Trying to make a single
CustomerorProductclass serve the entire company. Result: an unmanageable object. Accept the multiplicity of models. - Bounded Contexts that are too small. If every entity is its own context, you will have hundreds of tiny services talking to each other nonstop. The context must be cohesive, not atomic.
- Confusing logical boundary with physical boundary. A Bounded Context is a logical concept of DDD; a microservice is a deployment decision. You can start with several contexts within the same monolith (a modular monolith) and separate them into services only when needed.
- Not keeping the Ubiquitous Language alive. If the business changes a term and the code does not, the bridge breaks. Refactor the names when the language changes.
- Tip: organize your Java packages by Bounded Context (
com.fiatc.underwriting,com.fiatc.claims), not by technical layer (controllers,services). The package should shout the domain.
Exercises
Exercise 1. Choose the term "Product" in an online store and describe how you would model it differently in at least two Bounded Contexts (for example, Catalog and Inventory).
Exercise 2. Rewrite the following method signature applying the Ubiquitous Language of the claims domain: void update(Long id, int type, String text).
Exercise 3. A team has divided its system into "database-service," "logic-service," and "frontend-service." Explain why this division does not correspond to Bounded Contexts and propose an alternative.
Solutions
Solution 1. In the Catalog context, a Product is marketing content: name, description, photos, sale price, categories. In the Inventory context, the same product is a StockItem: SKU, available units, warehouse location, reorder point. They share the product identifier, but their attributes and rules are different.
Solution 2. A possible rewrite: void registerClaimReport(ClaimId id, ClaimType type, DamageDescription description). The names reflect the business concepts and the types replace the generic primitives.
Solution 3. That division is technical, by layers, not by domain: each "service" needs the others to do anything, which produces total coupling and constant communication (a distributed monolith). An alternative based on Bounded Contexts would be to divide by business capabilities—for example, "Underwriting," "Policies," "Claims"—where each service contains its own logic, data, and interface for its part of the domain.
Conclusion
We have seen that strategic design is about drawing boundaries: the Ubiquitous Language ensures that business and code speak the same language, and the Bounded Context delimits the scope within which that language and its model are consistent. We have verified that the same real-world concept ("Customer," "Product") is modeled differently in each context, and that these contexts are the best candidates for defining the boundaries of microservices, avoiding the dreaded distributed monolith.
In the next lesson, "Tactical Design: Entities, Aggregates, and Repositories", we will descend from the strategic map to the implementation detail: we will see with Java code how to build the rich model within a Bounded Context using the tactical patterns of DDD.
Application Architecture Course
Module 1: Fundamentals of Application Architecture
- What Is Application Architecture?
- The Role of the Software Architect
- Quality Attributes and Non-Functional Requirements
- Architectural Decisions and Trade-offs
- Architecture Documentation: Views and the C4 Model
Module 2: Design Principles and Tactics
- Coupling, Cohesion and Separation of Concerns
- SOLID Principles Applied to Architecture
- DRY, KISS, YAGNI and Other Design Principles
- Architectural Tactics for Quality Attributes
- Managing Technical Debt
Module 3: Architectural Styles and Patterns
- Monolithic Architecture
- Layered Architecture (N-Tier)
- Client-Server Architecture
- Hexagonal Architecture (Ports and Adapters)
- Clean and Onion Architecture
Module 4: Distributed Architectures and Microservices
- Introduction to Distributed Systems
- Microservices Architecture
- Service Decomposition and Bounded Contexts
- API Gateway, Service Discovery and Inter-Service Communication
- Resilience Patterns: Circuit Breaker, Retry and Bulkhead
- The CAP Theorem and Data Consistency
Module 5: Event-Driven Architectures and Messaging
- Fundamentals of Event-Driven Architecture
- Asynchronous Messaging: Queues and Brokers
- Event Patterns: Event Sourcing and CQRS
- Managing Distributed Transactions: The Saga Pattern
- Real-Time Data Streaming
Module 6: Domain-Driven Design (DDD)
- Core DDD Concepts
- Strategic Design: Bounded Contexts and Ubiquitous Language
- Tactical Design: Entities, Aggregates and Repositories
- Context Mapping
Module 7: Data and Persistence
- Persistence Strategies: SQL vs NoSQL
- Data Access Patterns: Repository, Unit of Work and DAO
- Database per Service and Distributed Data Management
- Caching and Invalidation Strategies
Module 8: Cloud Architecture and Deployment
- Cloud Computing Fundamentals (IaaS, PaaS, SaaS)
- Containers and Orchestration with Docker and Kubernetes
- Serverless Architecture
- Cloud-Native Design Patterns
- Infrastructure as Code (IaC)
Module 9: Quality, Security and Observability
- Scalability: Horizontal vs Vertical and Load Balancing
- High Availability and Fault Tolerance
- Security by Design and Authentication/Authorization
- Observability: Logging, Metrics and Tracing
- Performance and Load Testing
