Domain Driven Design with Web API revisited Part 3: shared kernel, bounded context and some initial code

Introduction

In the previous post we discussed 3 fundamental concepts in DDD: entities, value objects and aggregates with aggregate roots. We said that an entity is an object whose identity is defined by its ID key for the purpose of tracking its state through time. Its properties can change but its ID – whether a simple integer or a composite – should never be modified after the object has been created. Value objects on the other hand are defined by their properties and are immutable. We can say that a value object is also a type of entity but its ID is a composite of all its constituent properties. As the IDs are immutable per se, value objects are consequently also immutable. Finally, aggregates are groups of objects that are treated as a unit for purposes of data persistence. The “boss” of the group is the aggregate root and it’s through the aggregate root that other aggregates communicate with other aggregates.

In this post we’ll look at a concept called the shared kernel and how the above mentioned concepts can be represented in code.

Shared kernel and bounded context

If you read the original DDD series starting here then you’ve come across something that we called the infrastructure layer. Here’s how we defined it:

The infrastructure layer is a place for all sorts of cross-cutting concerns and objects that can be used in any project. They are not specific to any single project or domain. Examples: logging, file operations, security, caching, helper classes for Date and String operations etc. Putting these in a separate infrastructure layer helps if you want to employ the same logging, caching etc. policy across all your projects. You could put these within the project solution but then when you start your next project then you may need to copy and paste a lot of code.

There’s something very similar for the domains of your business. Let’s see what Eric Evans writes about a concept called the shared kernel:

Designate some subset of the domain model that the two teams agree to share. Of course this includes, along with this subset of the model, the subset of code or of the database design associated with that part of the model. This explicitly shared stuff has special status, and shouldn’t be changed without consultation with the other team.”

In order to understand this statement better we need to consider another DDD concept: the bounded context. Let’s see what the blue book of DDD tells us about bounded contexts:

Explicitly define the context within which a model applies. Explicitly set boundaries in terms of team organization, usage within specific parts of the application, and physical manifestations such as code bases and database schemas. Keep the model strictly consistent within these bounds, but don’t be distracted or confused by issues outside.

A BOUNDED CONTEXT delimits the applicability of a particular model so that team members have a clear and shared understanding of what has to be consistent and how it relates to other contexts. Within that context, work to keep the model logically unified, but do not worry about applicability outside those bounds.”

What this definition tells us is that all domain objects will be valid within a specific, well-defined context. All objects for the demo app we mentioned in the previous post belong to the context of load testing. We have a timetable of load tests, we have customers who execute load tests, we have a set of URLs that define a load test scenario and so on. Those objects belong to the bounded context of load testing and have a set of rules that they need to follow within that context.

A company can have multiple sub-domains which will often translate to separate bounded contexts in practice. I also mentioned that the company I currently work for offers web site monitoring as well. That is a sub-domain that’s separate from load testing. However, the web site monitoring sub-domain can also have domain objects that are similar to those in the load testing sub-domain. A classic example is a customer. Nearly all businesses will need to have customers or clients in order to survive on the market. So it’s reasonable that a domain called Customer or Client will appear in multiple bounded contexts. However, a web monitoring client may or may not be the same as a load testing client. They can be the same people in reality but from a contextual point of view in the domain level they may be different. They can have different properties and behaviour attached to them. The load testing bounded context may very well have different requirements and rules for the client domain than the web monitoring context. Similarly a load test will require a set of steps, typically URLs that make up a load test scenario and web site monitoring will also need one or more URLs to be monitored. However, the instructions for load tests may be entirely different from the instructions for a web site monitoring case.

Therefore it’s important to define these bounded contexts early on in the domain planning. We’ll sort out the exact terms of the load testing bounded context in the next post. The section where the different contexts overlap will be the shared kernel that the contexts can refer to. In code this will simply be a project, such as a C# class library which is referenced by the different contexts. We’ll soon see an example for this.

Abstractions for entities and value objects in code

OK, we’ve seen enough text, it’s time to start coding the foundations. In fact we’ll only insert 3 abstractions in the shared kernel to begin with as we’ll need to go through the exact definitions of our load testing domain. However, we can create these abstractions already now.

Create a new blank solution in VS 2012 or 2013 and call it WebSuiteDDD.Demo. Add a new C# class library called WebSuiteDDD.SharedKernel. Remove Class1 and add a new folder called Domain. In that folder add a new class called EntityBase. This abstract class will be a common class for all the entities:

public abstract class EntityBase<IdType> : IEquatable<EntityBase<IdType>>
{
	private readonly IdType _id;

	public EntityBase(IdType id)
	{
		_id = id;
	}

	public IdType Id
	{
		get
		{
			return _id;
		}
	}	

	public override bool Equals(object entity)
	{
		return entity != null
		   && entity is EntityBase<IdType>
		   && this == (EntityBase<IdType>)entity;
	}

	public override int GetHashCode()
	{
		return this.Id.GetHashCode();
	}

	public static bool operator ==(EntityBase<IdType> entity1, EntityBase<IdType> entity2)
	{
		if ((object)entity1 == null && (object)entity2 == null)
		{
			return true;
		}

		if ((object)entity1 == null || (object)entity2 == null)
		{
			return false;
		}

		if (entity1.Id.ToString() == entity2.Id.ToString())
		{
			return true;
		}

		return false;
	}

	public static bool operator !=(EntityBase<IdType> entity1, EntityBase<IdType> entity2)
	{
		return (!(entity1 == entity2));
	}

	public bool Equals(EntityBase<IdType> other)
	{
		if (other == null)
		{
			return false;
		}
		return this.Id.Equals(other.Id);
	}
}

This is almost identical to the abstract class we had in the original DDD series. However, we won’t add the code related to validation in this revised version. We’ll perform the validation directly in the domain classes when they are constructed. I think it will be a better approach, but feel free to review the code in the referenced blog post.

Another difference is that the ID field can be set once in the constructor and not changed through a public modifier.

Next, we’ll need a similar abstraction for value objects. Add a class called ValueObjectBase to the SharedKernel layer:

public abstract class ValueObjectBase<T> : IEquatable<T> where T : ValueObjectBase<T>
{
	public abstract bool Equals(T other);
	public abstract override bool Equals(object obj);
	public abstract override int GetHashCode();
}

This is markedly different from the ValueObjectBase abstract class we had previously. Here we only force the implementing classes to provide their logic for equality. There’s no abstract method for validation, so we take the same approach here as for the EntityBase class.

Aggregate root

We also discussed aggregates and aggregate roots in the previous post. Recall that aggregates are handled as one unit where the aggregate root is the entry point, the “boss” of the aggregate. This implies that the data access layer should only handle aggregate roots. It should not accept objects that lie somewhere within the aggregate. We haven’t yet implemented any code regarding aggregate roots, but we can take a very simple approach. Insert the following empty interface in SharedKernel:

public interface IAggregateRoot
{
}

Conceptually it would probably be better to create a base class for aggregate roots, but we already have one for entities. As you know an object cannot derive from two base classes in C# so we’ll indicate aggregate roots with this interface instead. At present it is simply an indicator interface with no methods, such as the ISerializable interface in .NET. We could add common properties or methods here but I cannot think of any right now.

Just to recap we have the following structure of the demo app at this point:

State of application after three abstractions in shared kernel

It’s not much yet but it’s at least something. It is often the case with DDD projects that you need to discuss a lot of things with the other stakeholders of the project before you can write any code.

In the next post we’ll start define our domain objects and discuss the importance of another DDD concept called the ubiquitous language.

View the list of posts on Architecture and Patterns here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

2 Responses to Domain Driven Design with Web API revisited Part 3: shared kernel, bounded context and some initial code

  1. Stg says:

    Hello, I have a short question:
    Let’s suppose that we have an Entity named Customer: EntityBase
    and entity name Vehicle:EntityBase.

    According to the equality code if we have a Vehicle with Id 1 and a Customer with Id 1, we will get that this is objects are equal, since they have the same Id.
    Is this correct? It is assumed, that each entity will have a unique Id across the whole domain?

    Thank you in advance,
    Stefanos

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: