Various topics from software architecture part 5: aggregate roots

Introduction

In the previous post we looked at the second installment of the unit of work pattern. We designed a mock implementation of the unit of work and unit of work repository interfaces.

In this post we’ll look at a key concept from Domain Driven Design, the aggregate root. This topic was discussed in detail as part of the DDD series but it deserves a dedicated post.

Recall from the post on the Repository pattern that we marked a domain object with the interface IDomain:

public class Customer : IDomain
{
	public int Id { get; set; }
	public string Name { get; set; }
	public Address Address { get; set; }	
}

IDomain is just an empty interface to mark domain objects:

public interface IDomain
{
}

It really should have been called IAggregateRoot but I didn’t want to introduce that concept right there. The scope of the post would have exploded.

Aggregates and their roots

Objects are seldom “independent”: they have other objects attached to them or are part of a larger object graph themselves. A Person object may have an Address object which in turn may have other Person objects if more than one person is registered there. It can be difficult to handle in software: if we delete a Person, then do we delete the Address as well? Then other Person objects registered there may be “homeless”. We can leave the Address in place, but then the DB may be full of empty, redundant addresses. Objects may therefore have a whole web of interdependency. Where does an object start and end? Where are its boundaries? There’s clearly a need to organise the domain objects into logical groups, so-called aggregates. An aggregate is a group of associated objects that are treated as a unit for the purpose of data changes. Every aggregate has a root and clear boundaries. The root is the only member of an aggregate that outside objects are allowed to hold references to.

Here comes an example:

Take a Car object: it has a unique ID, e.g. the registration number to distinguish it from other cars. Each tire must also be measured somehow, but it’s unlikely that they will be treated separately from the car they belong to. It’s also unlikely that we make a DB search for a tire to see which car it belongs to. Therefore the Car is an aggregate root whose boundary includes the tires. Therefore consumers of the Car object should not be able to directly change the tire settings of the car. You may go as far as hiding the tires entirely within the Car aggregate and only let outside callers “get in touch with them” through methods such as “MoveCarForward”. The internal logic of the Car object may well modify the tires’ position, but to the outside world that is not visible.
An engine will probably also have a serial number and may be tracked independently of the car. It may happen that the engine is the root of its own aggregate and doesn’t lie within the boundaries of the Car, it depends on the domain model of the application.

Car aggregate root

An important term in conjunction with Aggregates is invariants. Invariants are consistency rules that must be maintained whenever data changes, so they represent business rules.

In code

The simplest way to introduce aggregate roots in your domains is to mark them as such like in the case of IDomain above. As discused above 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. Here’s the marker interface for aggregate roots:

public interface IAggregateRoot
{
}

So it looks exactly like the IDomain interface. The Customer domain can now be marked as an aggregate root:

public class Customer : IAggregateRoot
{
	public int Id { get; set; }
	public string Name { get; set; }
	public Address Address { get; set; }	
}

After refactoring the name of the interface you’ll see that the repositories we’ve seen in this series all work with aggregate roots, e.g.:

public interface IRepository<DomainType, IdType> where DomainType : IAggregateRoot
{
	DomainType FindBy(IdType id);
	void Update(DomainType aggregate);
	void Insert(DomainType aggregate);
	void Delete(DomainType aggregate);
}

This post finishes the series.

View the list of posts on Architecture and Patterns here.

Advertisements

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

5 Responses to Various topics from software architecture part 5: aggregate roots

  1. Pingback: Architecture and patterns | Michael's Excerpts

  2. Ranga says:

    Hi Andras,
    Awesome article. Thanks for this.
    I have a question to clarify;
    If we consider implementing UnitOfWork pattern,
    should we require implementing Update(…)/Save(…) in each Repository ? Shouldn’t it belongs to UnitOfWork implementation?
    Within UnitOfWork implementation, each repository ( which represent aggregate root) behave like collection, and it should have a collection type interface ( methods like Add, Remove, Find and etc)

    Appreciate your feedback in this regard.

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 )

Google+ photo

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

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

iReadable { }

.NET Tips & Tricks

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

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

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: