
Being domain driven
Domain driven design is a great approach for building reasonably complex systems. The learning curve is steep, but the payoff is that you'll become a better software developer.
Introduction
Domain driven design (DDD) has been around for several years, but seems to be gaining momentum in corporate software development. It's interesting to see this because DDD is a vendor and technology agnostic approach to software design, so there are no vested interests behind DDD, just the experience of many skilled software developers who have coalesced around a good way to approach the design of complex software.
As such, don't expect to find a DDD Wizard in the next edition of Visual Studio. DDD is an approach that encompasses a number of design principes and patterns and as a result doesn't lend itself to a single tool for implementation. I think this makes it somewhat inaccessible to developers unfamiliar with the approach, which is a shame since DDD can deliver tremendous benefits on a project. A commitment to learn DDD is a commitment to understand object-oriented analysis, design and coding, along with a number of key patterns and practices.
I was initially exposed to domain driven design in 2005 by the technical architect on a project I was leading. We applied some aspects of Domain Driven Design (DDD) on that project, but it was a year or so later before I started to really 'get' the benefits of the approach. I had the opportunity to help a customer model a complex business domain, helping them reflect implementation considerations and design in the conceptual model. That's when the 'ubiquitous language' notion really took off for me. Since then I've held that DDD is the 'default' design approach to take for any reasonably complex system.
DDD is all about focusing more on the business aspect of software, and less so on the technical aspects. While the technical aspects are important, DDD allows the domain model to be isolated from technical considerations and designed in conjunction with a highly engaged user or product owner team.
Software development relies on abstractions. Humans rely on metaphors to deal with abstractions. We often struggle on projects to find the right metaphors that have value for both the business and technical teams. DDD put the focus on metaphors that are shared by project stakeholders, including the business users, by encouraging the creation of a 'Ubiquitous Language'. The Ubiquitous Language is shared by all project stakeholders and allows for productive collaboration between technical and non-technical team members regarding the design of the heart of the software - the domain model. The core idea of Ubiquitous Language is to "reflect in code how users think and speak about their work".
Focusing mostly on the data in a software development project is to downplay these metaphors and to devalue shared understanding among project stakeholders. Data is an enabler of system behavior, but the 'management' of data is not a substitute for understanding the important behaviors that support the metaphors the system is based on. When faced with a data-centric approach, the question to ask is: "If this data is the answer, then what's the question?"
DDD requires a fair amount of technical sophistication, but also relies on simplicity. In that sense it aligns closely with the best principles of object-oriented programming.
What follows is a brief overview of DDD, from an implementation perspective.
DDD Project Considerations
Domain driven design (DDD) is an approach that pays off when applied to reasonable complex systems, that have emergent qualities. As such, not all software projects are good candidates for this approach.
Suggested pre-requisites for DDD projects:
- interaction and collaboration with domain experts is critical. customer / product owner (or team) is highly available, can communicate well (not always a given!), and is willing to work with the implementation team to create the Ubiquitous Language
- implementation team have solid OO skills (analysis, design, coding). Teams with minimal OO experience can struggle with DDD, and may end up incurring significant development overhead with minimal benefit
- organization is willing/ready to not have the data model or database be the primary focus of the project. Organizations that want to focus primarily on the data model (typically in a relational database) will often struggle with DDD
- project timeline and approach support the creation and refactoring of a rich domain model as deeper understanding emerges. Agile projects that are highly automated, test-driven, and iterative are best suited for DDD. Traditional waterfall projects with manual QA and/or 'test-after' approaches are not good candidates for DDD.
Gotcha's (a.k.a. bad smells) for DDD projects:
- under tight deadlines it's not hard to lapse into the anemic domain model anti-pattern
- DDD is not about UML class diagrams. These diagrams can be a communication tool, but the true representation of the domain model is in the software. If required, seek to generate other artifacts (like class diagrams) from the software
- using DDD can be challenging on a project that wraps or modifies a legacy system. Strict attention to interface design and implementation of anti-corruption layers is critical on these projects.
- treating the model as static (i.e. "that's too much work to change") defeats the purpose of using DDD
Construction
Implementation of domain driven design relies on object oriented (OO) programming techniques. Loose coupling (typically via interfaces) and separation of concerns are applied with a layered architecture.
Presentation Layer
-> Application Layer
-> Domain Layer
-> Infrastructure Layer
Presentation layer provides the user interface, which may or may not be a human user interface (could be another system).
Application layer provides access to the jobs or tasks that the system supports, uses the domain layer to perform work. The application layer is kept as thin as possible and delegates work to domain objects. It should not contain state or business logic. Things like tasks, event logging and security are typically implemented in the application layer.
Domain layer is the heart of the system and represents the working model of the business, including state and business rules. Technical details of instantiation and storage are delegated to the infrastructure layer. The separation of the domain layer from other layers is what enables domain driven design.
Infrastructure layer provides technical features to support higher layers such as object persistence. Supertypes for other layers (see layer supertypes pattern) are typically provided by the infrastructure layer.
The separation of concerns implied in this layered approach allows a much cleaner design of each layer. Domain objects are freed from concerns like storage and display, allowing them to evolve along with the domain model as deeper insights into the system are formed.
Common DDD Patterns
Several patterns are ubiquitous in DDD projects:
Entity. An entity is a domain object whose definition is based primarily on identity and less so on it's attributes. Entities have attributes, but they are not critical to determine equality. Entity responsibilities and associations are revolve around who they are, rather than what attributes they have. For example, rail car # 117 has attributes that may or may not change, such as color, but it's unique number will always be how it is referred to. In a typical domain model most key objects are entities. Some guidelines for entites are as follows:
- keep the class definition simple and focused on on life cycle continuity and identity. The basic responsibility of an entity is to establish continuity so that behaviour can be predicatable
- define a means to distinguish each object regardless of form or history
- add only behaviour that is essential to the concept, and add only attributes that are required by the behaviour. Look to remove behaviour and attributes into other objects associated with the core entity
- when there is no unique key made up of attributes of the object, an artificial identity attribute (typically ID) can be used which is unique within the class.
Value object. A value object is an object that has no identity, but instead describes some characteristic of a thing. An address is a common example of a value object - it has no integral identity, but is instead an attribute of something else (i.e. a building). Value objects can simplify implementation by removing the need to track identity for all objects. Some guidelines for value objects are as follows:
- value objects must be immutable if they are shared
- value objects should only be shared to save space and if communication overhead is low
- bidirectional assocations between value objects should not be used
Aggregate. An aggregate is a cluster of associated domain objects that is treated as a unit for the purposes of data changes. It can be difficult to ensure the consistency of changes to objects in the domain model when there are complex associations. Aggregates help to mitigate this by encapsulating references within the model. The 'parent' object of the aggregate is known as the Root Entity. For example, a Purchase Order root entity may have a collection of Line Item entities, each of which in turn may have a Product entity and a Quantity value object. Several rules apply in the use of aggregates:
- root entity has global identity
- entities (non-root) within the aggregate have local identity, unique only within the aggregate
- aggregates are treated as atomic for purposes of creation, saving, deletion
- when a change to any object within the aggregate boundary is applied, all invariants (rules) of the whole aggregate must be satisfied
- root entity is only object accessible from outside the aggregate
- root entity can pass transient references for internal objects to external objects. External objects do not hold the reference after operation is finished
Layer supertype is used to create 'base classes' for key object types in each layer. For example, an application framework for DDD may provide layer supertypes for classes such as Entity, Root Entity, Repository, Factory and Task.
Factory classes are used to manage the creation and reconstruction aspects of an objects lifecycle. Aggregates may have complex business rules (or invariants) that must be enforced for proper object creation. These invariants may be difficult to properly implement in a constructor, so a Factory is used to instantiate the full aggregate and ensure that all invariants, or rules, are satisfied.
Repository is used to abstract object storage and retrieval. A repository allows domain objects (via root entities or aggregates) to be stored and retrieved as though they exist as an in-memory collection. The repository implementation hides the actual storage, retrieval and instantiation of domain objects from the physical store, typically a relational database. Data mapper or an Object Relational Mapper (ORM) such as Hibernate are often used in Repository implementation.
Unit of Work is used at the job/task level (Application Layer) to coordinate changes that span aggregates (root entities). Since each aggregate type has it's own repository, tasks that involve changes to different types of aggregate may require coordination across repositories. Unit of work provides this coordination.
Identity Map is used to cache entities, preventing excessive or redundant retrieval from storage. Some ORM's implement this pattern internally as an entity cache.
Getting Started with DDD
Where to begin? The bad news is that there are no shortcuts to learning DDD. The good news, however, is that knowledge gained along the road to DDD will make you a better software developer.
I think anyone new to the subject should start off with the Eric Evans book (see link below). The book is a long read but is packed with useful insights based on hard-won experience in enterprise software development. There's also a shorter treatment of the book available from InfoQ (link below).
Once you have a basic grasp of the concepts you should just try to apply them on a sample application. It might take a while, but eventually you'll come to see the benefits and wonder how you built apps before using DDD.
References
Domain Driven Design website.
"Domain Driven Design: Tackling Complexity in the Heart of Software", book by Eric Evans
"Domain Driven Design Quickly", book from InfoQ
"Applying Domain Driven Design And Patterns: With Examples in C# and .NET", book by Jimmy Nilsson
".NET Domain Driven Design with C#", book by Tim McCarthy
DDD on Yahoo Groups



