Home > Uncategorized > Cheeky CQRS

Cheeky CQRS

Code for this post: http://bitbucket.org/earwicker/cheekycqrs

In CQRS (put on your architecture buzzword helmets now) domain objects are replaced by aggregate roots. The in memory representation of aggregate roots is never directly persisted. It only has getter properties. It can also respond to messages, sometimes called domain events, that update its state. These events can be persisted, and are appended to the end of a event store, which therefore records the history of everything that has happened to the aggregate root.

So business logic operates like this:

  1. Instantiate a “blank” object of a class that represents the aggregate root for the domain.
  2. Replay all its history of events to it to bring it up to the present state.
  3. Use its getter properties (in concert with any other sources of information) to decide what you need to do with it.
  4. Send it events to tell it how to update its state. Also append those events to the event store.
  5. Repeat steps 3 and 4 until you’re satisfied.
  6. Discard the object and only keep the new events.

The last part is the big shocker, but is fine because the next time anything is done with this object, we will start at step 1, and in step 2 we will be replaying the events we just produced to get us up to date first, so it wasn’t all a total waste of time.

The fiddly part of all this is the notion that you aren’t allowed to set properties or even call methods on the class representing the aggregate root. It implies some complex system where each event has to be represented by a special class, and the aggregate root class has to have handlers that respond to those events.

But hang on a minute… Aren’t those event handlers basically methods?

To keep things very simple and concrete, here’s my aggregate root:

public interface IRoom
{
    // Queries
    IEnumerable<string> PeopleInRoom { get; }
    bool IsLightOn { get; }
    bool IsWindowOpen { get; }

    // Messages
    void CloseWindowAndSwitchLightOn();
    void OpenWindowAndSwitchLightOff();
    void AdmitPerson(string name);
    void EjectPerson(string name);
}

Here I’ve provided getter properties (which better not have side-effects) and called them “Queries”, and also some methods that only return void (and have no out/ref parameters) and called them “Messages”. (In an earlier version of this post, I labelled them “Commands” as that’s what they were called in the original CQS, but that’s a loaded term in CQRS. A command is allowed to have random side-effects, a message is not.)

I’ve purposely made the message manipulate multiple facets of the state, to clarify that these are “high level” operations rather than mere individual assignment operations on data. So for example, in a Room class implementing this interface, EjectPerson would make sure that the light was turned out when the last person is ejected:

public void EjectPerson(string name)
{
    _peopleInRoom.Remove(name);

    if (_peopleInRoom.Count == 0)
    {
        _isWindowOpen = false;
        _isLightOn = false;
    }
}

It’s vitally important that messages only modify the internal state of this object. They must not have other visible side-effects. The reason for this will become clear later on.

So now how can I get all that “event store” goodness? Easy. By intercepting the calls to the void methods and using them to generate event objects automatically. For example:

var room = new Interceptor<IRoom>(new Room());

This would build me a proxy around the Room class, providing me with a way to hook into events happening on the object:

var roomEvents = new List<DomainEvent>();
room.NewEvent += (s, e) => roomEvents.Add(e);

Here for fun I’m just buffering up the events in a list. Now I can send some messages to the room, and check that they have had the expected side-effects:

room.Object.AdmitPerson("Daniel");
room.Object.OpenWindowAndSwitchLightOff();
room.Object.AdmitPerson("Melvyn Bragg");
room.Object.AdmitPerson("Stevie Wonder");
room.Object.EjectPerson("Daniel");

Debug.Assert(!room.Object.IsLightOn);
Debug.Assert(room.Object.IsWindowOpen);
Debug.Assert(room.Object.PeopleInRoom.SequenceEqual(
        new[] { "Melvyn Bragg", "Stevie Wonder" }));

Note that I have to use a special Object property of room to get at the interface (if this reminds you of using Moq, there’s a good reason for that.)

Now lets say I want to persist what I’ve done. Let’s use JSON.NET:

var json = JsonConvert.SerializeObject(roomEvents);

That wasn’t too hard, was it? The JSON looks like this:

[
    { "Name": "AdmitPerson", "Arguments": ["Daniel"] },
    { "Name": "OpenWindowAndSwitchLightOff", "Arguments": [] },
    { "Name": "AdmitPerson", "Arguments": ["Melvyn Bragg"] },
    { "Name": "AdmitPerson", "Arguments": ["Stevie Wonder" ] },
    { "Name": "EjectPerson", "Arguments": ["Daniel"] }
]

Nice and self-describing, easy for some other client to listen in on. A browser, perhaps? Then I can pass my JSON to another method and it can pick up exactly where I left off:

var roomEvents = JsonConvert.DeserializeObject<List<DomainEvent>>(json);

var room = new Interceptor<IRoom>(new Room());
room.Play(roomEvents);

Debug.Assert(!room.Object.IsLightOn);
Debug.Assert(room.Object.IsWindowOpen);
Debug.Assert(room.Object.PeopleInRoom.SequenceEqual(
        new[] { "Melvyn Bragg", "Stevie Wonder" }));

The Play method can accept one or many events. This “replayability” is the reason why messages are methods that have no visible side-effects except on the object they are called on. When replaying the messages we only want to get the object back into the required state; we probably don’t really want to cause a bunch of logging to occur, for example.

Now for some behind the scenes stuff.

A domain event is always represented by:

public class DomainEvent : EventArgs
{
    public string Name { get; set; }
    public object[] Arguments { get; set; }
}

This is what we saw serialized in the JSON version of an event log. I also need to do some pretty heavy-duty validation of the interface type, because we don’t accept any old riff-raff. We have certain standards, you know. So I have a ValidationException class with a handy Check method:

public static void Check(string message, IEnumerable<string> fails)
{
    if (fails.Any())
        throw new ValidationException(message, fails);
}

This lets me query the interface with reflection for any rule-breakage:

ValidationException.Check("Properties must not have setters", interfaceType
    .GetProperties()
    .Where(p => p.CanWrite)
    .Select(p => p.Name));

ValidationException.Check("Methods must not have outputs", interfaceType
    .GetMethods()
    .Where(m => !m.IsSpecialName &&
                ((m.ReturnType != typeof(void)) ||
                m.GetParameters().Any(p => p.IsOut)))
    .Select(m => m.Name));

ValidationException.Check("Events are not allowed", interfaceType
    .GetEvents()
    .Select(e => e.Name));

ValidationException.Check("Overloaded methods are not allowed", interfaceType
    .GetMethods()
    .Where(m => !m.IsSpecialName)
    .GroupBy(m => m.Name)
    .Where(g => g.Count() > 1)
    .Select(g => g.Key));

The reason overloading is banned is so that we can identify the handler for a domain event by using the method name, and keep them as simple name strings; not all languages support overloading anyway.

The final thing of interest is the Interceptor itself. It uses Castle.DynamicProxy to do the hard stuff. Calls to property getters are forwarded directly onto the real object, whereas other method calls merely cause a CLR event called NewEvent to be fired, sending out a DomainEvent to anyone interested. (This is what we wired up our event log list to in the example).

The interceptor itself enlists on its own event, and calls its own Play method, so this ensures that the method runs, and also that it runs with exactly the same information that has been sent out to other listeners.

Code for this post: http://bitbucket.org/earwicker/cheekycqrs

Here’s all the code in one long splat:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.DynamicProxy;
using Castle.Core.Interceptor;
using Newtonsoft.Json;
using CheekyCqrs;
using System.Diagnostics;
using System.Reflection;

namespace CheekyCqrs
{
    public class DomainEvent : EventArgs
    {
        public string Name { get; set; }
        public object[] Arguments { get; set; }
    }

    public class ValidationException : Exception
    {
        public IEnumerable<string> Fails { get; private set; }

        public ValidationException(string message, 
              IEnumerable<string> fails)
            : base(message + ": " + string.Join(", ", fails.ToArray()))
        {
            Fails = fails;
        }

        public static void Check(string message, 
                 IEnumerable<string> fails)
        {
            if (fails.Any())
                throw new ValidationException(message, fails);
        }
    }

    public abstract class Interceptor : IInterceptor
    {
        private object _wrapper;
        private object _instance;

        public Interceptor(object instance, Type interfaceType)
        {
            ValidationException.Check("Properties must not have setters",
                interfaceType.GetProperties()
                .Where(p => p.CanWrite)
                .Select(p => p.Name));

            ValidationException.Check("Methods must not have outputs",
                interfaceType.GetMethods()
                .Where(m => !m.IsSpecialName &&
                            ((m.ReturnType != typeof(void)) ||
                            m.GetParameters().Any(p => p.IsOut)))
                .Select(m => m.Name));

            ValidationException.Check("Events are not allowed",
                interfaceType.GetEvents()
                .Select(e => e.Name));

            ValidationException.Check("Overloaded methods are not allowed",
                interfaceType.GetMethods()
                .Where(m => !m.IsSpecialName)
                .GroupBy(m => m.Name)
                .Where(g => g.Count() > 1)
                .Select(g => g.Key));

            _instance = instance;
            _wrapper = new ProxyGenerator()
                .CreateInterfaceProxyWithoutTarget(interfaceType, this);
            NewEvent += (s, e) => Play(e);
        }

        void IInterceptor.Intercept(IInvocation invocation)
        {
            if (invocation.Method.Name.StartsWith("get_"))
            {
                invocation.ReturnValue = _instance.GetType()
                    .GetMethod(invocation.Method.Name)
                    .Invoke(_instance, invocation.Arguments);

                return;
            }

            NewEvent(this, new DomainEvent
                            {
                                Name = invocation.Method.Name,
                                Arguments = invocation.Arguments
                            });
        }

        public object Object
        {
            get { return _wrapper; }
        }

        public void Play(DomainEvent evt)
        {
            _instance.GetType().GetMethod(evt.Name)
                         .Invoke(_instance, evt.Arguments);
        }

        public void Play(IEnumerable<DomainEvent> events)
        {
            foreach (var evt in events)
                Play(evt);
        }

        public event EventHandler<DomainEvent> NewEvent;
    }

    public sealed class Interceptor<TInterface> : Interceptor
        where TInterface : class
    {
        public Interceptor(TInterface instance)
            : base(instance, typeof(TInterface)) { }

        public new TInterface Object
        {
            get { return (TInterface)base.Object; }
        }
    }
}

public interface IRoom
{
    // Queries
    IEnumerable<string> PeopleInRoom { get; }
    bool IsLightOn { get; }
    bool IsWindowOpen { get; }

    // Messages
    void CloseWindowAndSwitchLightOn();
    void OpenWindowAndSwitchLightOff();
    void AdmitPerson(string name);
    void EjectPerson(string name);
}

public class Room : IRoom
{
    private bool _isLightOn;
    private bool _isWindowOpen;

    public List<string> _peopleInRoom = new List<string>();

    public IEnumerable<string> PeopleInRoom
    {
        get { return _peopleInRoom; }
    }

    public bool IsLightOn
    {
        get { return _isLightOn; }
    }

    public bool IsWindowOpen
    {
        get { return _isWindowOpen; }
    }

    public void CloseWindowAndSwitchLightOn()
    {
        _isWindowOpen = false;
        _isLightOn = true;
    }

    public void OpenWindowAndSwitchLightOff()
    {
        _isWindowOpen = true;
        _isLightOn = false;
    }

    public void AdmitPerson(string name)
    {
        if (!_isWindowOpen)
            _isLightOn = true;

        _peopleInRoom.Add(name);
    }

    public void EjectPerson(string name)
    {
        _peopleInRoom.Remove(name);

        if (_peopleInRoom.Count == 0)
        {
            _isWindowOpen = false;
            _isLightOn = false;
        }
    }
}

namespace CheekyTest
{
    class Program
    {
        static string SetupRoom()
        {
            var room = new Interceptor<IRoom>(new Room());

            var roomEvents = new List<DomainEvent>();
            room.NewEvent += (s, e) => roomEvents.Add(e);

            room.Object.AdmitPerson("Daniel");
            room.Object.OpenWindowAndSwitchLightOff();
            room.Object.AdmitPerson("Melvyn Bragg");
            room.Object.AdmitPerson("Stevie Wonder");
            room.Object.EjectPerson("Daniel");

            Debug.Assert(!room.Object.IsLightOn);
            Debug.Assert(room.Object.IsWindowOpen);
            Debug.Assert(room.Object.PeopleInRoom.SequenceEqual(
                    new[] { "Melvyn Bragg", "Stevie Wonder" }));

            var json = JsonConvert.SerializeObject(roomEvents);
            Console.WriteLine(json);
            return json;
        }

        static void CheckRoom(string json)
        {
            var roomEvents = 
                JsonConvert.DeserializeObject<List<DomainEvent>>(json);

            var room = new Interceptor<IRoom>(new Room());
            room.Play(roomEvents);

            Debug.Assert(!room.Object.IsLightOn);
            Debug.Assert(room.Object.IsWindowOpen);
            Debug.Assert(room.Object.PeopleInRoom.SequenceEqual(
                    new[] { "Melvyn Bragg", "Stevie Wonder" }));
        }

        static void Main(string[] args)
        {
            CheckRoom(SetupRoom());
        }
    }
}
Advertisements
Categories: Uncategorized Tags: , ,
  1. April 20, 2010 at 11:09 pm

    I’m not sure how serious you’re being based on the title of the article, but I did enjoy reading it.

    It is serious, it just felt cheeky to be reducing the whole event sourcing part down to mere method intercepting on a restricted kind of interface.

    I do feel the need to point out that you’re confusing CQRS with Event Sourcing. Lots of people make that mistake because Greg Young has difficulty describing one without the other. I know form experience, that CQRS (as an architectural principle) is very useful even though I’ve never used Event Sourcing on a real project.

    Udi Dahan has more info on CQRS here: http://www.udidahan.com/2009/12/09/clarified-cqrs/

    I find that emerging ideas like this are like early religions, with opposing prophets handing out Revealed Truth to their followers! So I’m sure the followers of Greg Young would tell you that if you leave out Event Sourcing then you’re missing the whole point of CQRS, etc. Personally I’m not (yet) convinced by either, having only just begun exploring the subject. But I’m diving in with event sourcing too because otherwise how will I know whether it’s worth it or not?

    With respect to Event Sourcing, you’re not actually implementing it correctly. You don’t replay commands (which you’re doing) when re-loading your aggregates, you replay events.

    That’s just down to my confusing mixing of terminology. I’ve described the methods on IRoom as “commands”, per Bertrand Meyer. They are not “commands” in the sense you mean – they are the replayable events. I just hate calling them events, because they are clearly the “command methods” (in the Meyer sense) on the domain object.

    Commands could trigger behavior that interacts with other aggregates/services. You certainly don’t want to be doing that when re-loading. The events that you record and replay represent the state changes in your aggregate. That’s why it’s safe to replay them. The events have to represent logical changes in state, though, and not what properties changed to what values.

    Absolutely. I will fix the terminology to say that the methods are “messages”, to avoid using the loaded (in this context) term “command”; I also need to clarify what state is allowed to change. In particular, methods on the domain object must only modify the state of that object. They must not have externally visible side-effects, because those side-effects will get “replayed” whenever the object is reconstituted.

    Also, for performance reasons, it’s possible to store snapshots of your aggregates once they reach a certain threshold to avoid having to replay all events since their birth to get them to their current state. This is more of a physical thing, though, which you could always discard and recreate if the physical representation of your aggregates changed.

    Indeed – but I didn’t need to specify how to do that in this mini-library, because the state of the Room class can be captured or restored with JSON serialization already. Problem solved…

    I’m curious where you got the rule that you can only have getters and no setters or methods. I don’t think I’ve ever read anything from Greg saying that. In fact, he advocates banning both getters and setters and only having methods. There’s nothing preventing events from being recorded internally by invoking methods.

    Well, opinion is divided on the subject… 🙂 When implementing a command (business logic which needs to read in and manipulate domain objects), we go to the bother of reconstituting its state somehow first. Why? Because what we do next will depend on that state. However that is implemented, it is logically equivalent to having visibility of the state, and the convenient way to allow that in .NET is through property getters. So I conclude property getters are a Good Thing. And as for methods, I’m basically pointing out that (or asking whether) these “events” are in fact just methods that modify only the state of the object and so can be automatically recorded/replayed to reconstitute the state at any point in the past.

    By the way, aggregates are a DDD term which doesn’t really have anything to do with CQRS or Event Sourcing. Greg tries to claim that proper DDD is done with both CQRS and Event Sourcing, but I don’t believe that’s true.

    Also, if I’m not mistaken, Udi coined the term domain events, which he didn’t intend to have anything to do with Event Sourcing.

  1. No trackbacks yet.

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

%d bloggers like this: