The journeylism of @yreynhout

On CQRS, DDD(D), ES, …

Value objects in an event sourced domain model

A question that comes up from time to time is what role value objects can play in an event sourced domain model and how they contribute to and interact with the events produced by said domain model. Value objects come in many shapes and sizes, but obvious ones like money (amount and currency) and period (range of time values) come to mind. Fields that change together or are used together in the ubiquitous language are prime suspects of information “clustering” and can usually be represented as value objects when they clearly lack any form of identity. They are an ideal place to DRY up some of the validation, security and contextual capturing code that surrounds these fields.
Below you’ll find a stripped version of a value object from my own domain. Things you can’t easily spot from the code below is the clusivity of the lower and upper boundary of this range-like value type, partially because I omitted the arithmetic operations. The finer details, such as which methods are required and who to collaborate with, surface as you discuss them with domain experts.

namespace Domain {
  public class RolloutPeriod {
    readonly DateTime _from;
    readonly DateTime _to;

    public RolloutPeriod(DateTime from, DateTime to) {
      if(from > to)
        throw new ArgumentException("The from value of the rollout period must be less than or equal to the to value.", "from");
      _from = from;
      _to = to;
    }

    //Arithmetic members omitted for brevity

    public bool Equals(RolloutPeriod other) {
      if (ReferenceEquals(null, other)) return false;
      if (ReferenceEquals(this, other)) return true;
      return other._from.Equals(_from) && other._to.Equals(_to);
    }

    public override bool Equals(object obj) {
      if (ReferenceEquals(null, obj)) return false;
      if (ReferenceEquals(this, obj)) return true;
      if (obj.GetType() != typeof (RolloutPeriod)) return false;
      return Equals((RolloutPeriod) obj);
    }

    public override int GetHashCode() {
      return _from.GetHashCode() ^ _to.GetHashCode();
    }
  }
}

Something you do notice in the above code is that there are no getters nor setters. This is deliberate. On one hand, it allows me to redefine how I represent values on the inside (i.e. the from and to value). On the other hand, I decouple any consuming code from the internals and gently steer that code into a Tell Don’t Ask style of interaction. It makes you think about the question: “Why do you want access to my internals? What is it that you want to achieve with them? Just tell me what you want to do.”.
Once you get that right, you bump into the next hurdle: using value objects in an event sourced domain model.

namespace Domain {
  public class Schedule : AggregateRootEntity {
    public void Rollout(RolloutPeriod period) {
      //Guards ommitted for brevity

      ApplyEvent(Build.RolledoutSchedule.ForPeriod(period));
    }
  }

  public abstract class AggregateRootEntity {
    Guid _id;
    long _version;

    protected void ApplyEvent(IEventBuilder<IEvent> builder) {
      var @event = builder.Build(_id, _version++);
      //The usual stuff as found below goes here
      //https://github.com/gregoryyoung/m-r/blob/master/SimpleCQRS/Domain.cs
    }
  }
}

namespace Messaging {
  public static class Build {
    public static RolledoutScheduleBuilder RolledoutSchedule { get { return new RolledoutScheduleBuilder(); } }
  }

  public class RolledoutScheduleBuilder : IEventBuilder<RolledoutScheduleEvent> {
    RolloutPeriod _rolloutPeriod;

    public RolledoutScheduleEvent Build(Guid id, long version) {
      return new RolledoutScheduleEvent(id, version, _rolloutPeriod);
    }

    public RolledoutScheduleBuilder ForPeriod(RolloutPeriodBuilder builder) {
      _rolloutPeriod = builder.Build();
      return this;
    }
  }

  public interface IEventBuilder<out TEvent> where TEvent : IEvent {
    TEvent Build(Guid id, long version);
  }

  public class RolledoutScheduleEvent : IEvent {
    public RolledoutScheduleEvent(Guid id, long version, RolloutPeriod period) {
      Id = id;
      Version = version;
      Period = period;
    }

    public RolloutPeriod Period { get; private set; }

    public long Version { get; private set; }

    public Guid Id { get; private set; }
  }

  public interface IEvent { }

  public class RolloutPeriod {
    public DateTime From { get; private set; }
    public DateTime To { get; private set; }

    public RolloutPeriod(DateTime from, DateTime to) {
      From = from;
      To = to;
    }
  }

  public class RolloutPeriodBuilder {
    DateTime _from;
    DateTime _to;

    public RolloutPeriodBuilder From(DateTime value) {
      _from = value;
      return this;
    }

    public RolloutPeriodBuilder To(DateTime value) {
      _to = value;
      return this;
    }

    public RolloutPeriod Build() {
      return new RolloutPeriod(_from, _to);
    }
  }
}

The rollout period passed into the Schedule rollout method, is a value object part of the domain model. The rollout period of the event – a bit obfuscated by the syntax ‘Build.RolledoutSchedule.ForPeriod’ above – is part of the messaging bits. How do these two meet without exposing internals?

One thing I’d like to make clear: the domain value object IS NOT the data structure used inside the event. It’s a different type.

By applying double dispatch to the domain rollout period and adding a builder extension method – inside the domain model – that is domain rollout period aware, we get a nice translation going.

namespace Domain {
  public class RolloutPeriod {
    //... see above for other members

    internal void BuildValue(RolloutPeriodBuilder builder) {
      builder.From(_from).To(_to);
    }
  }

  public static class BuildExtensions {
    public static RolledoutScheduleBuilder ForPeriod(this RolledoutScheduleBuilder builder, RolloutPeriod period) {
      var valueBuilder = new RolloutPeriodBuilder();
      period.BuildValue(valueBuilder);
      builder.ForPeriod(valueBuilder);
      return builder;
    }
  }
}

The reverse, going from an event (or data structure within that event) to a value object, is just as easy.

namespace Domain {
  public class RolloutPeriod {
    //... see above for other members

    internal static RolloutPeriod FromEvent(Messaging.RolloutPeriod period) {
      return new RolloutPeriod(period.From, period.To);
    }
  }

  public class Schedule : AggregateRootEntity {
    void Apply(RolledOutScheduleEvent @event) {
      _rolloutPeriod = RolloutPeriod.FromEvent(@event.RolloutPeriod);
      //...
    }
  }
}

So, really, there’s no excuse to NOT use value objects inside an event sourced domain model nor to expose the internal state of those value objects for event sourcing purposes. Happy coding …

P.S. More information on event builders can be found here.

Advertisements

8 responses to “Value objects in an event sourced domain model

  1. Yoann June 20, 2012 at 07:53

    Nice post Yves. one thing though, I did not get clearly. you add :
    internal void BuildValue(RolloutPeriodBuilder builder) {
    builder.From(_from).To(_to);
    }
    to RolloutPeriod and it makes sense to add a behavior to create the value object on the messaging side since it’s a general concern for RolloutPeriod. But if I had to use this value object in some specific AR to make validation between these dates (to and from) and another date my AR might have. I would have to create a new RolloutPeriod for this specific class and add a method to put this specific validation in this new ValueObject? is this how you do? or am I missing something?
    And thanks for this share and your tweets, it is nice to have a glimpse of your whereabouts, it gives nice hints … 😉

  2. Leonardo January 28, 2014 at 17:48

    Good one mate… This is something not many people realize when going with an ES solution, and NONE of the samples you can find in github show this kind of implementation. They say DDD/CQRS but in the end are still normal anemic domain models.

    For me there is a big concept gap when using event sourcing, the business method ChangeName/ApplyTaxes/whatever should modify the state of the aggregate root (including entities, collections and value objects within) and the resulting event being raised should grab the data from the new state of the aggregate root and not from the parameters (how it’s being shown usually).

    On the Apply/Handle/When of the event the private members should be set bypassing all validations.

    For example:

    • František Maša (@FrantisekMasa) August 7, 2015 at 02:14

      I disagree. Now you have “duplicate” code in When and ChangeName methods. Method for applying an event should make no more checks, just modify state. “Bussiness” method (ChangeName) should be one to protect invariants. Imagine method with more parameters, your way is neither DRY or cleaner imho.

      • Leonardo August 7, 2015 at 10:05

        Hi František. Thanks for your comment. I do agree with you that repetition on name assignment is not clean. But having the business logic only in change name wouldn’t solve the problem either as Name is set on the constructor as well. You need to check the business rules for Name there too.

        If we then think that Name is a Value Object, and we wrap it in a Domain Value Object where the validation happens (Minimum length, not null, not empty, etc..) then we could just do the apply event without going through the private setter. We could safely instantiate the domain VO on construction and in Change Name and then use the VO for the Domain Event.

        What do you think?

  3. Pingback: Important links on Event Sourcing and CQRS – Techtonica Blog

  4. Oguzhan Eren April 4, 2017 at 16:06

    Hi,
    I read this and https://seabites.wordpress.com/2011/07/27/eventbuilders/
    but I have a question?

    Why you simply add VO -> Event Object conversion inside to RolloutPeriod VO class like ToRolloutEventObject?

    Thanks,


    public class RolloutPeriod
    {
    readonly DateTime _from;
    readonly DateTime _to;

    ...

    internal static RolloutPeriod FromEvent(Messaging.RolloutPeriod period)
    {
    return new RolloutPeriod(period.From, period.To);
    }

    internal Messaging.RolloutPeriod ToRolloutEventObject()
    {
    return new Messaging.RolloutPeriod(_from,_to);
    }
    }

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: