What is DDD?
Domain-driven design is not a technology or a methodology. DDD provides a structure of practices and terminology for making design decisions that focus and accelerate software projects dealing with complicated domains.
Some of the major DDD concepts
- Understanding the core elements of Domain.
- Ubiquitous Language.
- Contexts and Bounded Contexts.
- Entities and Value Objects.
- Aggregates and Aggregate Roots.
- Persistence Ignorance.
- Anticorruption Layers.
- Domain Service.
DDD #1: Interaction with Domain Experts
DDD #2: Focus on a subdomain at a time.
DDD #3: Implementing the subdomain
Benefits of DDD
- Customer’s vision/perspective of the problem
- Path through a very complex problem
- Well-organized and easily tested code
- Business logic lives in one place
- Many great patterns to leverage
Drawbacks of DDD
- Time & Effort
- Big Learning Curve
- Only makes sense when their is complexity in the problem
- Term or Company Buy-In to DDD
Sub-domain vs Bounded Context
Sub-domain is a problem space concept, where as Bounded context is a solution space concept. In other words, the sub-domain is describing something about the way you've chosen to break down your business, or whatever the domain activity is, whereas the boundary context describes how the software and the software development has been broken down.
General Glossary of Terms for DDD:
- Problem Domain : The specific problem the software you’re working on is trying to solve
- Core Domain : The key differentiator for the Customer’s business- something they must do well and cannot outsource
- Sub-domains : Separate applications or features your software must support or interact with.
- Bounded Context : A specific responsibility , with explicit boundaries that separate it from other parts of the system
- Context Mapping : The process of identifying bounded contexts and their relationships to one another
- Shared Kernel : Part of the model that is shared by two or more terms, who agree not to change it without collaboration
- Ubiquitous Language : A language using terms from the domain model that programmers and domain experts use to discuss the system
Elements of DDD
Domain : The Domain Layer is responsible for representing concepts of the business, information about the business situation, and business rules. State that reflects the business situation is controlled and used here, even though the technical details of storing it are delegated to the infrastructure. This layer of the domain is the heart of business software. Just to reiterate, the domain model is the heart of the business software. We need to give important to the behaviors not the attributes
Types of Objects
- Entities : have Identity and are Mutable
- Value Objects : A value object has very specific characteristics. It is an object that is used to measure, quantify, or describe something in your domain. Rather than have an identity key, its identity is based on the composition of the values of all of its properties. Because the property values define a value object it should be immutable. In other words, you shouldn't be able to change any of the properties once you've created one of these objects. Instead you would simply create another instance with the new values. If you need to compare two value objects to determine if they are equal, you should do so by comparing all of the values. Value objects may have methods and behavior, but they should never have side effects. Any methods on the value object should only compute things, they shouldn't change the state of the value object since it's immutable, or the system. If a new value is needed, a new value object should be returned. EG: DateTimeRange
Associations (Relationships) : try to avoid unwanted navigation.
Domain Service: Important operations that don’t belong to a particular Entity or Value Object, A good service is
- not a natural part of an Entity or Value Object
- Have an interface defined in terms of other domain model element
- are stateless, but may have side effects
- live in the core of application
Aggregates & its roots
Avoid unwanted bidirectional relationship
An aggregate is a cluster of associated objects that we treat as a unit for the purpose of data changes.
Data changes to the Aggregate should follow ACID, that is they should be Atomic, Consistent, Isolated, and Durable. It's also the Responsibility of the Aggregate Root to maintain its invariants
Aggregates serve as boundaries between logical groupings within our application.
We enforce these boundaries by prohibiting direct references to objects within an aggregate that aren't the root of the Aggregate. Aggregates and Aggregate Roots only apply to objects not data, and when we're talking about references we're talking about object references, properties that use an object directly. This is especially important with ORMs.
Remove the navigation property and use the foreign key ID instead. It's a little more work, but removing some of the ORM magic results in more control over the behavior and this aligns perfectly with the fact that one common way to enforce the aggregates is to replace direct navigation properties in the models non-root types with key references, and this reduces the number of dependency relationships within the model.
A repository represents all objects of a certain type as a conceptual set…like a collection with more elaborate querying capability.
- a repository should have the illusion of a collection of a specific type of object – i,e “Think of it as an in-memory collection”
- setup repositories access through a well-known global interface
- repository should have methods to add and remove objects to encapsulate the underlying data insertion and deletion operations.
- provide methods in your Repository classes that select objects based on certain criteria.
- repositories should only for Aggregate Roots that require direct access
- keep the clients focused on the model while delegating all of the object storage and excess concerns to the repositories
Repositories & Factories
For simple entities and aggregates having a standard set of CRUD operations on your repository makes a lot of sense. However, for less standard aggregates, it may not make sense to implement these common operations
Generic Repositories in DDD
It is always good to create a generic repository interface just for Aggregate Root, which would comply with DDD recommendations. However, it can be convenient to create a generic repository of T implementation class that you can then use with any entity. We definitely don't want to use this in a DDD implementation because it means that it's allowed to bypass the Aggregate Root for interacting with data.
If you've really liked the code reuse you get from having a generic repository implementation, one way to keep it from allowing too much access to the internals of your aggregates would be to use a marker interface (IAggregateRoot), perhaps one that simply extends the entity interface. Then, you could update your generic repository to require this interface, rather than working with any entity. At that point, client code will not be able to instantiate the generic repository with NonRoot entities, so we are able to use our repository to restrict access to NonRoot entities from clients of our model.
Domain Events provide a way to describe important activities or state changes that occur in the system. Then, other parts of the domain can respond to these events in a loosely coupled manner
Domain Event Boundaries
- Create separate events objects for specific client
- Create cross-system events for external client
- Define specific application events for the UI layer
this is a layer which can minimize the amount of work involved when two sub-systems, using two different models, need to communicate with one another. Let's get started.
Job of the Anti-corruption Layers is simply to translate between the foreign systems model and your own. In addition to translating the objects themselves, the anti-corruption layer can also clean up the way in which you must communicate with the other system
Why do we need them?
Eric Evans notes why that's important. "Even when the other system is well designed, it is not based on the same model as the client. And often the other system is not well designed."