Skip to content
This repository has been archived by the owner on May 29, 2024. It is now read-only.

Aggregate roots

Samuel Debruyn edited this page Dec 24, 2015 · 11 revisions

An aggregate root is an important kind of object that can work independently of other objects. It may contain other objects, and those contained objects are owned by the root. The root and everything it contains is called an aggregate. The contained objects are often called value types.

All interaction with the aggregate goes through the aggregate root, thus allowing the root to function as a facade to whatever is going on beneath it, providing a nice encapsulated way of modeling things.

This way, an aggregate can and must function as a consistency barrier, making sure that its integrity is sustained i.e. its invariants are preserved.

Creating an aggregate root with Cirqus is easy: you just create a class that inherits from AggregateRoot, like so:

public class MyRoot : AggregateRoot { }

The AggregateRoot base class will bring a couple of useful things with it:

  • An Id property - since all aggregate roots must be uniquely addressable, they will have a globally unique ID. The ID is a string which enables you to come up with suitable ID schemes for various types, e.g. "customer/324" and "case/ab678".
  • An Emit method - the root must only change itself by using the Emit Apply Pattern, and to do that the root must use the Emit method. More about this in Emit Apply Pattern.

Internally it keeps track of some stuff regarding events and sequence numbers.

How do I create an instance?

You create a new aggregate root instance either by calling Create<ThatRoot>("an ID") on the ICommandContext handed to you in a Command or an ExecutableCommand, simply by executing a Command<ThatRoot> (which will create/update as needed), or, finally, you can create an instance from within another aggregate root by calling Create<ThatOtherRoot>("another ID") on the AggregateRoot base class.

For example like this in an ExecutableCommand:

public class CreateNewRoot : ExecutableCommand
{
    public string RootId { get; set; }

    public override void Execute(ICommandContext context)
    {
        context.Create<Root>(RootId); //< throws an exception if the root already exists!
    }
}

It should be noted that the root is considered as existent if it has emitted an event - this also means that, in this case, the only way that the context.Create call will actually have an effect, is when the Created method of the aggregate root has been overridden to emit an event.

How do I delete an instance?

It's not possible to delete an instance. To achieve this, we recommend using soft deletes. You keep track of the instance's state using a boolean or a nullable property and you update this property when the instance should be deleted.

public class MyRoot: AggregateRoot
{
    private bool _isDeleted;
    
    public bool IsDeleted { get { return _isDeleted; } }
    
    public void Apply(MyDeleteEvent deleteEvent)
    {
        _isDeleted = true;
    }
    
    public void Delete()
    {
        Emit(new MyDeleteEvent());
    }
}

You could also use something like DateTimeOffset? so that you can keep track of when the instance was deleted.

Make sure to read Emit Apply Pattern to get a grasp of how you can update a property or field of an aggregate root.

Clone this wiki locally