How To: Domain Driven Design

J.D. Meier, Alex Homer, David Hill, Jason Taylor, Prashant Bansode, Lonnie Wall, Rob Boucher, Akshay Bogawat.

Applies To

  • Object Oriented design using the Domain Model design pattern.
  • Microsoft .NET Framework 3.5
  • ADO.NET Entity Framework.

Summary

Domain driven design is an object oriented architectural style focused on modeling a business domain and defining business objects that are based on entities within the business domain. One of the challenges with an object oriented design is handling the impedance mismatch between object oriented structures in your application and relational structures in a database. There are many products that provide Object Relational/Mapping (OR/M) support including the ADO.NET Entity Framework (EF). As a result, this document will focus on a design that uses EF to provide OR/M support along with common patterns associated with domain driven design.

Contents

  • Objectives
  • Overview
  • Summary of Steps
  • Step 1 – Create a Domain Model
  • Step 2 – Identify Aggregates and Aggregate Roots
  • Step 3 – Design the Data Access Layer
  • Step 4 – Design the Business Layer
  • Additional Resources

Objectives

  • Define a domain model
  • Create a logical view with aggregates
  • Design the data access layer
  • Design the business layer

Overview

When it comes to implementing a domain driven design the first task is to use object oriented modeling techniques to define a domain model. To create an effective domain model you start by examining dynamic behavior and then generalize that behavior into a static structure that defines classes used to represent the domain. Once you have a domain model the next task is to design how that model will be used within your application. A common approach is to use Abstract Factory and Repository design patterns to create, get, and persist domain model entities.

Summary of Steps

  • Step 1 – Create a Domain Model
  • Step 2 – Identify Aggregates and Aggregate Roots
  • Step 3 – Design the Data Access Layer
  • Step 4 – Design the Business Layer

Step 1 – Create a Domain Model

There are many different approaches to analysis and design from waterfall where everything is designed up-front to agile where you focus on adding value as you go and expect to have changes throughout the application development lifecycle. Regardless, of the approach you are using within your organization, when it comes to domain driven design you will still need to perform some analysis of the business domain in order to correctly model the domain entities.

The process used to create a domain model starts by setting the context, observing specific instances of entities within the domain, and extrapolating those instance to the general population. This process is repeated until you have completed documentation of a domain model that defines the attributes of entities within context and describes how those entities interact with each other.

DomainModel.PNG
Figure 1: Process to Create Domain Model

The actual process is broken down into two sub-tasks that also follow the same process of observing instances and extrapolating to the general population. In other words, at a high level you are identifying objects within the system, extrapolating those objects to create a static definition, and repeating that cycle until the entities within context have been modeled. However, within this cycle there are two tasks that use a similar process. The first one is modeling dynamic behavior, which is done by observing behavior and extrapolating that behavior to the general population. Next, a logical view is created by taking a snapshot of specific instances and extrapolating that snapshot to a class diagram.

Set the Context

The first thing you should do is set the context for the model, which describes the boundary of what is being modeled. This includes the central actor, breadth, and depth. The central actor is the system or company that the model focuses on. The breadth represents the functionality covered by the model. Depth identifies the level of detail for the model.

Model Dynamic Behavior

Once the context is set the next task is to model dynamic behavior within the system. You start by observing how people or entities within the domain interact during the execution of an activity. Next, you extrapolate the behavior into an activity that can be applied to the general population and document it. This cycle is repeated until all of the activities that need to be modeled, which is based on your development approach, have been documented.
  • Observe Specific instances within the context. For example Jane places an order. This is done using structured English and object models that represent dynamic behavior such as a collaboration diagram.
  • Extrapolate to General population. For example, a person places an order. This is done using structured English, activity diagrams, or use case diagrams.

Create the Logical View

Once you have modeled the dynamic behavior the last task is to create a logical view. The process used to create a logical view is similar to modeling dynamic behavior. You start with a snapshot of objects within the system and then extrapolate to the general population. The final class diagram represents your logical view of the system, which is used to define business objects within the system.
  • Take a snapshot of the system using an object diagram. The snapshot is a static observation of the objects identified by the dynamic modeling process. This diagram should include major properties of the objects and relationships between the objects.
  • Extrapolate the object diagram to a class diagram. The classes represent business entities in your domain model design.

Identify the Bounded Context

Before moving to the next step there is one additional concept that you should understand with relation to the domain model. There are many cases where multiple models are used within an organization or even within an application. The models may be completely different or they may represent variations of the same model. As a result, you need to establish the context in which a model applies, which is considered the bounded context. This is not the same as setting the context for modeling as shown above. Instead, the bounded context refers to where the model will be used within an application design.

Step 2 – Identify Aggregates and Aggregate Roots

Once you have a domain model the next step is to identify how objects within the domain will be grouped together in order to process requests. This grouping of objects is called an aggregate and the root of the aggregate is the single object that identifies the entire aggregate. The grouping of objects is driven by the usage patterns defined by the dynamic modeling process. Typically, the data on each user interface form correspond to a single aggregate, although the designer should be careful not to define aggregates that are too fine-grained, which might result in excessive network chatter, or too coarse-grained, which might result in slow load and save times. In order to define the aggregates and aggregate roots, the designer must also determine the navigability of each association in the model. This is indicated by adding arrowheads to each association. One must always be able to navigate from a root object to its aggregate parts. It is also frequently necessary to be able to navigate back from the aggregate parts to the root and from one aggregate to another.

The following class diagram depicts a domain model with several aggregates:

SampleClassDiagram.PNG
Figure 2: Sample Class Diagram

A typical approach to defining heads and bodies might follow these steps:
  • For a basic application that performs Create, Read, Update, and Delete (CRUD) operations, 80% of the aggregate boundaries should be fairly obvious and will conform to the use cases. For example, everything on a single web page or form will usually be part of the same aggregate.
  • In some cases, especially for web applications, these boundaries will result in aggregates that are too fine-grained. For example, consider an aggregate that has many editable attributes. The user interface designer may decide that there are too many attributes to fit on a single page, so they are split across several pages instead. In this case, the corresponding aggregate should span multiple pages. Note that CRUD might not occur until the last page is submitted.
  • For complex rich-client apps with a sophisticated "Save" mechanism, the aggregates can become much coarser – the application preserves changes in memory until the user explicitly commits them to the database. However, a large aggregate can lead to performance and design problems. In this case, the business layer designer can split up the aggregate so that a single "Save" operation contains multiple aggregates in a single transaction.

In advanced situations, the presence of polymorphism in the class model can affect the aggregate boundaries as well. This occurs when multiple aggregate root classes all share the same base class, for example, class A in the diagram above. Lacking use cases, the identification of aggregates and aggregate roots in the above class model is arbitrary. Let’s assume that the three concrete subclasses of class A (A1, A2, and A3) are all roots, as is class X. This would result in four aggregates as shown below.

Aggregates.PNG
Figure 3: Aggregates and Aggregate Roots

As previously mentioned, interaction with these aggregates should always done through the aggregate root. As a result, the remainder of this guide will focus on data access and business layer components that are used to perform operations against the aggregate roots.

Step 3 – Design the Data Access Layer

The primary interface into the data access layer is through repository objects, which provide an illusion that business objects are being maintained in memory. For example, business and factory objects in the business layer can use repository objects to perform get, save, and update operations on business objects without having any knowledge of the underlying data store used to persist the objects. The ADO.NET Entity Framework is used to support Object/Relational mapping between the domain model and relational model in the database.

Create an Entity Data Model

The ADO.NET Entity Framework (EF) is responsible for managing the database schema and providing a mapping layer between the database and data entities. Now that you have the domain model you can use that as a reference to create an Entity Data Model (EDM) using a visual designer provided by EF. The Entity Data Model provides a mapping layer between the conceptual model and storage model.

This will generate partial classes that represent data entities used to perform entity based data access operations with EF. These data entities represent domain entities in the domain model and they will be extended in the business layer to create business objects.

The Entity Data Model (EDM) is actually composed of three separate language types. The store schema definition language (SSDL) is used to define the storage, or database, schema. The conceptual storage definition language (CSDL) is used to define the entity types, complex types, associations, entity containers, entity sets, and association sets in the application domain. Finally, the mapping specification language (MSL) is used to describe the mapping between the conceptual model and target database.

Design the Repository

Identify aggregate root objects in your domain model and define a separate repository object for each one. Aggregate root objects represent the top level object in a in a group of objects that are related. If you have a one-to-one mapping between objects and tables or views in the database you will normally create a repository object for each table or view object.

Define operations that use Entity Framework data entities, or domain entities, as parameters and return values.
public class AccountRepository
{
    public Account GetAccountById( string id );
    public void Save( Account account );
}

For objects that can be retrieved using multiple complex queries consider using a criteria object to define the queries instead of defining a separate operation for each query. A typical approach is to define a common criteria interface that all criteria objects implement.
public Interface ICriteria
{
  public void Equals( string name, object value );
  public void GreaterThan( string name, object value );
  public void LessThan( string name, object value );
  public void NotEqual( string name, object value );
}

Now that you have an entity data model, and repository classes that are based on aggregate roots, the next step is to design the business layer.

Step 4 – Design the Business Layer

Once you have an Entity Data Model (EDM) that was produced in the previous step you can define business objects that are based on data entities in the EDM. Business objects contain data and implement behavior used to support business operations. In order to create or retrieve business objects an Abstract Factory design pattern can be used. Factory objects represent an initial interface into the business layer that presentation components use to create or get business objects.

Define Business Objects

Define business objects that are based on data entities in the Entity Data Model (EDM). Data entities are partial classes that define properties and support interaction with the database. Partial classes defined in the business layer with the same name can be used to add behavior, which makes the combined class definition a business object when instantiated.
public partial class Account
{
    public bool IsActive
    {
        // _isActive is defined in the data entity
        get { return _isActive; }
    }
    public bool Add( money value )
    {
        bool added = false;
        if( value > 0 ) 
        {
            _balance = _balance + value;
            added = true;
        } 
        return added;
    }
}

Now that you have the business objects defined the last step is to define factory classes that will be used to create and get instances of the business objects.

Define the Abstract Factory (Optional)

The use of a factory is optional but strongly recommended in order to maintain strict layering guidelines and separation of concerns. In other words, without the factory, components in the presentation layer would be required to access components in the data access layer. A common approach for defining an abstract factory is to used an interface definition that provides common operations to create and get domain entities. The following code snippet provides an example of a abstract factory definition.
public Interface IFactory
{
    object Create();
    object GetById( string ID );
}

Once you have an interface defined you’ll need to define concrete factory classes for each aggregate root that implements the factory interface. The following code snippet provides an example of an account factory that can be used to create new instances of an Account object or retrieve an initialized instance from the repository in the data access layer.
public class AccountFactory : IFactory
{
    public object Create()
    {
        return new Account();
    }
    public object GetById( string Id )
    {
        using( AccountRepository repository = 
            new AccountRepository())
        {
            return repository.GetAccountById( Id );
        }
    }
}

You now have a business layer that exposes business objects that can be used by the presentation layer along with an interface that can be used to create and get new instances of business objects.

Additional Resources

Last edited Jan 15, 2009 at 11:01 PM by prashantbansode, version 3

Comments

Nirvana Jan 31, 2009 at 11:11 PM 
Is this DDD?
Bad Guide

colinjack Jan 24, 2009 at 9:08 PM 
> The grouping of objects is driven by the usage patterns defined by the dynamic modeling process. Typically, the data on each user interface form correspond to a single aggregate, although the designer should be careful not to define aggregates that are
> too fine-grained, which might result in excessive network chatter, or too coarse-grained, which might result in slow load and save times. "

I think its possibly worth saying that when you are practicing DDD your domain model design is not directly influenced by the GUI, you have to be careful to protect it from user interface concerns (and other seperate concerns such as reporting).

The network chatter bits doesn't make sense to me, are you suggesting that a client would be doing RPC style calls to an aggregate?

Also I'm wondering why you do not mention any of the guidance Evans gives about defining aggregates?what he sees as DDD.

Ergel Jan 24, 2009 at 11:07 AM 
I think that you habe broken the SRP ( Single Responsibility Principle) by defining Creation and Querying Methods in the public Interface IFactory? Factory classes are responsible for creation of objects not querying and finding objects.

mrpmorris Jan 23, 2009 at 6:58 PM 
This isn't DDD, it's "How do we say Entity Framework is DDD so that we can cash in on a phrase that is popular at the moment?"

And why is your factory not only doubling-up as a Repository, but simply passing on the Repository-like calls directly to the Repository anyway?

colinjack Jan 23, 2009 at 5:13 PM 
> The process used to create a domain model starts by setting the context, observing specific instances of entities within the domain, and extrapolating
> those instance to the general population.

As a user of the ideas/patterns in DDD I'm surprised you expressed it like this, I'd have focussed on the analysis of the problem domain with the domain expert (as Evans does). This is, to me, too technical an approach.

Also where is the mention of the ubiquitous language?


> Additional Resources

I'm amazed that the last resource you come up with is the only one related to the author of DDD and to DDD in general.