Domain-Driven Design Guide: Mastering Complex Business Logic
Domain-Driven Design (DDD) is an approach to software development that places the business domain at the center. Popularized by Eric Evans' 2003 book, DDD is an indispensable design philosophy for projects with complex business logic.
Why DDD Matters
Most software projects fail not because of technical complexity, but because the business domain was modeled incorrectly. DDD addresses this by unifying developers and domain experts around a common language.
Core Concepts
1. Ubiquitous Language
Creating a single language that everyone understands between the technical team and the business unit:
- ❌ "Update the row in the User table" → Technical jargon
- ✅ "Update the customer's address information" → Ubiquitous language
This language must be consistent across code, documentation, and meetings.
2. Bounded Context
Dividing a large system into sub-domains, each managed with its own model. The same concept can mean different things in different contexts:
Sales Context: Customer = Person who places orders
Support Context: Customer = Person who opens tickets
Billing Context: Customer = Person who receives invoices
Each context manages its own model. Inter-context communication happens through defined interfaces.
3. Entity
Objects with identity (ID) that can change throughout their lifecycle:
Customer { id: "C001", name: "Alice", address: "..." }
Two customers with the same name are still different entities because they have different IDs.
4. Value Object
Objects without identity, defined by their values:
Money { amount: 100, currency: "USD" }
Address { city: "New York", zip: "10001", street: "..." }
Two Money(100, "USD") objects are equal — no identity needed.
5. Aggregate
A group of entities and value objects that must change together. Each aggregate has an Aggregate Root:
Order Aggregate:
├── Order (Root Entity)
├── OrderItem (Entity)
├── DeliveryAddress (Value Object)
└── Payment (Value Object)
External access is only through the Aggregate Root.
6. Repository
An interface managing the persistent storage of aggregates:
interface OrderRepository {
find(id: OrderId): Order
save(order: Order): void
delete(id: OrderId): void
}
7. Domain Event
Significant events that occur in the business domain:
OrderCreated { orderId, customerId, amount, date }
PaymentReceived { orderId, paymentMethod, date }
OrderShipped { orderId, trackingNumber, date }
When combined with Event Sourcing, this creates a very powerful combination.
Strategic DDD
Context Mapping
A map defining relationships between Bounded Contexts:
- Shared Kernel — Two contexts share a common model
- Customer-Supplier — One context provides services to another
- Anti-Corruption Layer — Translates external system to your own model
- Conformist — One context accepts another as-is
BilgeOne Example
Bounded contexts in the BilgeOne platform:
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Restaurant │ │ Finance │ │ CRM │
│ Context │──│ Context │──│ Context │
│ (Menu,Table)│ │(Invoice,Rev.)│ │ (Customers) │
└─────────────┘ └──────────────┘ └──────────────┘
Each industry module is its own bounded context. Shared modules like Finance and CRM integrate across all industries.
When to Use DDD
✅ Use It
- Projects with complex business logic
- Long-lived, growing systems
- Projects with close collaboration with domain experts
❌ Skip It
- Simple CRUD applications
- Short-lived prototypes
- Technically complex but business-simple projects (e.g., game engines)
Conclusion
DDD teaches us to see software not as a technical problem, but as a business problem. When applied correctly, it ensures both business and technical teams speak the same language, resulting in more accurate and sustainable systems.
Learn DDD interactively on LabLudus. The Software Architecture 3.0 book covers DDD in depth.