In the last post, I published the DomainTypes library: a set of .NET interfaces & base classes to provide the building blocks for DDD (source code included).
In this post I’ll describe the interfaces and supporting design decisions.
Represents any type of object within the Domain model. Currently this is simply used as a marker, signalling to developers that the implementation has DDD semantics.
Represents a unique Entity within the system. The ID property is used to identify the instance during it’s lifetime. Using a GUID allows identity to be established without having to talk to a persistence store (i.e. database).
Represents a non-unique object within the system. Note that it’s an object, and not a struct. Structs may seem like a better semantic fit with DDD, but too many instances can cause memory problems (structs – like value types – are created on the stack).
Represents a list of Domain Objects. It’s crucial that the implementing class raises events before items are added or removed from the list. This allows consumer code to execute any domain rules, and cancel the action if necessary.
This is one that Eric Evans mentioned earlier this year. It is used to record & identify domain events, and must be immutable. For example, a child’s birth should be captured, be identifiable, but never modified.
Most people understand Aggregate Roots as a cluster of closely associated entities. However, there are 2 additional concerns that an Aggregate Root has:
- It knows how to validate the entire aggregate
- It is used to lock it’s contents in a multi-user environment
The IsConsistent() method is a placeholder for all rules/invariants that apply to the aggregate. The locking concerns are handled by IRepository<T> (see below).
Used to create Domain Objects/Entities/Aggregate Roots that are complex to build. Only use factories when the code within your constructors becomes unweildy.
Used as a facade to a persistence store. Strictly speaking, repositories should only deal with Aggregate Roots – to access other entities you would navigate through the Aggregate.
In addition to the typical Load()/Save()/Delete() methods, I’ve also included Lock() and Unlock() methods. This is a crucial function in a multi-user system, and should be considered when designing your Aggregates.
Currently this is simply used as a marker. Only consider using Services if :
- Try as you might, you can’t decide on which Domain Object to place your logic
- The logic requires the use of several unassociated Domain Objects
- You’re communicating with another module/tier/component
The remaining interfaces aren’t DDD specific, but are to aid real-world implementations. I’ll discuss those in the near future.