Domain-Driven Design in Python — Core Concepts
What Domain-Driven Design is
Domain-Driven Design (DDD) is a software design approach introduced by Eric Evans in 2003. Its central thesis: complex software should be modeled around the business domain, not around technical infrastructure. The code should reflect the problem space, not the solution space.
Ubiquitous language
The most impactful DDD concept is ubiquitous language — a shared vocabulary between developers and domain experts. If the business calls it a “policy,” the code should have a Policy class, not a RuleSet. If they say “underwriting,” the method should be underwrite(), not evaluate_risk_v2().
This is not just about naming. When the code and the business speak different languages, requirements get lost in translation. Bugs emerge from misunderstandings, not from logic errors.
Strategic patterns
Bounded contexts
A large system has multiple subdomains, and each subdomain has its own model. In an e-commerce system, “Product” means something different to the catalog team (title, description, images) than to the warehouse team (weight, dimensions, shelf location). Each team works within a bounded context — a boundary where a particular model applies.
Context mapping
Bounded contexts need to communicate. Context maps define how: shared kernel (common code), customer-supplier (one depends on the other), or anti-corruption layer (translation between incompatible models).
Tactical patterns
Entities
Objects with a unique identity that persists across time. A Customer with ID “C-1234” is the same customer even if they change their name and address. Identity, not attributes, defines equality.
Value objects
Objects defined by their attributes, with no identity. A Money(amount=100, currency="USD") is equal to another Money(amount=100, currency="USD"). They are immutable and interchangeable.
Aggregates
Clusters of entities and value objects treated as a single unit for data changes. An Order aggregate might contain OrderLine entities and an Address value object. The Order is the aggregate root — all modifications go through it, ensuring business rules are enforced consistently.
Domain events
Records of something that happened in the domain: OrderPlaced, PaymentReceived, ShipmentDispatched. Events enable loose coupling between bounded contexts.
Repositories
Interfaces for retrieving and storing aggregates. The domain defines what a repository does (get, save); the infrastructure layer decides how (SQL, NoSQL, in-memory).
How DDD maps to Python
| DDD Concept | Python Implementation |
|---|---|
| Entity | Dataclass with id field, __eq__ based on id |
| Value Object | Frozen dataclass or NamedTuple |
| Aggregate | Class with methods that enforce invariants |
| Domain Event | Frozen dataclass with timestamp |
| Repository | Protocol class |
| Bounded Context | Separate Python package or service |
Common misconception
“DDD is only for microservices.” DDD works perfectly well inside a monolith. Bounded contexts can be separate Python packages within a single codebase. Microservices are one deployment option, not a prerequisite.
When DDD is worth the investment
DDD shines when the domain is complex — insurance, logistics, finance, healthcare. If the hardest part of your project is understanding the business rules rather than the technology, DDD will pay for itself.
For simple CRUD applications where the domain is thin, DDD adds ceremony without proportional benefit.
The one thing to remember: DDD is fundamentally about aligning your code with the real business domain so that changes in business rules translate directly into changes in code, with minimal friction and miscommunication.
See Also
- Python Aggregate Pattern Why grouping related objects under a single gatekeeper prevents data chaos in your Python application.
- Python Bounded Contexts Why the same word means different things in different parts of your code — and why that is perfectly fine.
- Python Bulkhead Pattern Why smart Python apps put walls between their parts — like a ship that stays afloat even with a hole in the hull.
- Python Circuit Breaker Pattern How a circuit breaker saves your app from crashing — explained with a home electrical fuse analogy.
- Python Clean Architecture Why your Python app should look like an onion — and how that saves you from painful rewrites.