An Overview of Domain‑Driven Design Patterns and Practices
Domain-Driven Design (DDD) focuses on modeling core business complexity by building a ubiquitous language, bounded contexts, and layered architectures, and the article explains key DDD patterns such as model‑driven design, aggregates, repositories, factories, services, and hexagonal architecture, illustrated with diagrams and Java code examples.
Domain‑Driven Design (DDD)
Enterprise applications are increasingly complex and rely on specialized technologies such as persistence, AJAX, and web services. DDD, introduced by Eric Evans, shifts attention to the core domain, distinguishing the core business model from supporting sub‑domains and encouraging design work to concentrate on the core.
Code and Model
In DDD the model is the code; the model‑driven design principle states that changes in the model should directly drive changes in the code and vice‑versa. The model can be visualised with UML, ER diagrams, or tool‑generated views.
Ubiquitous Language
DDD requires a shared language between domain experts and developers. Concepts that cannot be expressed easily indicate missing elements in the model, prompting the team to refine the ubiquitous language.
Model and Bounded Context
Each model exists within a bounded context (BC), which aligns with a specific user group or system. Relationships between BCs are classified as published language, open host service, shared kernel, customer/supplier, conformist, or anti‑corruption layer, forming a spectrum of integration strategies.
Layered and Hexagonal Architecture
DDD stresses a layered architecture (presentation, application, domain, infrastructure) while also supporting hexagonal (ports‑and‑adapters) architecture, where the core domain is surrounded by adapters for UI, external services, and persistence.
Building Modules
Typical DDD building blocks include entities, value objects, and modules (packages or namespaces). Value objects should be immutable and encapsulate behavior, e.g., a Money type that includes currency and rounding rules.
Money m1 = new Money("GBP", 10);
Money m2 = new Money("GBP", 20);
Money m3 = m1.add(m2); // m1 remains unchangedAggregates and Aggregate Roots
An aggregate groups related entities under a single aggregate root, which enforces invariants and is the only entry point for external references. Examples include Order (root) with OrderItems, or Country as a simple aggregate.
Repositories, Factories and Services
Repositories abstract persistence for aggregate roots, often defined as interfaces in the domain layer with implementations in the infrastructure layer. Factories create new aggregates, and domain services encapsulate business logic that does not naturally belong to an entity.
public class Customer {
private OrderFactory orderFactory;
public void setOrderFactory(OrderFactory orderFactory) { this.orderFactory = orderFactory; }
public Order placeOrder(...) { return orderFactory.createOrder(); }
}Problems and Obstacles
Implementing Layered Architecture
Maintaining strict layer boundaries is difficult; business logic often leaks into the presentation or application layers.
Presentation Layer Obscures Domain Layer
Creating a ubiquitous language can be hindered when domain experts discuss UI screens rather than domain concepts.
Repository Pattern Implementation
Novices may conflate repository interfaces with their concrete persistence implementations, missing the benefit of interchangeable storage mechanisms.
Service Dependency Implementation
Debate exists over whether entities should depend on domain services; common solutions include dependency injection or service locators.
Inappropriate Modularity
Over‑ or under‑modularising a system leads to “big ball of mud” or unmanageable module graphs; typical modules contain 5‑9 concepts.
Getting Started
Many frameworks exist for presentation and persistence, but few assist with the domain layer. The Naked Objects framework fills this gap by generating UI directly from domain objects.
Naked Objects
Naked Objects treats domain objects as the sole source of behavior; the framework renders them in a rich UI without additional presentation code.
public class Claim extends AbstractDomainObject {
private String description;
private Date date;
private String status;
// getters and setters omitted for brevity
public void addItem(int days, double amount, String description) {
ClaimItem ci = newTransientInstance(ClaimItem.class);
Date d = new Date();
d = d.add(0, 0, days);
ci.setDateIncurred(d);
ci.setDescription(description);
ci.setAmount(new Money(amount, "USD"));
persist(ci);
addToItems(ci);
}
public void submit(Approver approver) {
setStatus("Submitted");
setApprover(approver);
}
}Actions such as addItem and submit appear automatically as UI commands, turning the application into more than a CRUD system.
Next Steps
Read Eric Evans’s book “Domain‑Driven Design”, explore the DDD newsgroup, and consider the book “Domain‑Driven Design using Naked Objects” for practical guidance.
References
Domain‑Driven Design Community – http://domaindrivendesign.org/
FitNesse – http://fitnesse.org
Hexagonal Architecture – http://alistair.cockburn.us/Hexagonal+architecture
Naked Objects for Java – http://nakedobjects.org
Naked Objects for .NET – http://nakedobjects.net
Architects Research Society
A daily treasure trove for architects, expanding your view and depth. We share enterprise, business, application, data, technology, and security architecture, discuss frameworks, planning, governance, standards, and implementation, and explore emerging styles such as microservices, event‑driven, micro‑frontend, big data, data warehousing, IoT, and AI architecture.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.