0

I'm on a crusade to learn CQRS (ala Greg Young). Currently reading CQRS The Example by Mark Nijhof and working with his example for the book. The first thing i'm starting to get confused with is domain events and replaying the events to arrive at any state of any object at any point. As Mark has stated in his book only the final state of a domain object should be recorded because of changing business logic. Buisness logic can change and you might not arrive at the same answer and also you don't want to kick out external domain events. So your essentially decoupling business logic & the actually data changes their self. So you essentially serialize the event object (which is essentially a DTO) into a datastore.

I can see this working really well if your data/object schema never changes but what do you do for object/data schema changes? So for example a really simple contrived example would be an address. Say when you first developed your app it was localized to the united states. You decide at some point to internationalize your app. You add a "Country" field and make the field required. So if your working with a static language (like Java or C#) as soon as you try to deserialize an old object for event replay then it will blow up. The only way i can see getting around this would be to either store different versions of your objects (seems to be very messy) or store your events as something more unstructured (like xml). Of course this would probably work really well with a dynamic language. I guess in c# you could probably use the DLR (dyanmic) but i think this could get messy too. Is there any other way?

coding4fun
  • 8,038
  • 13
  • 58
  • 85
  • For the 2nd part of your question you might want to look at my event-versioning strategy with C# + JSON + NEventStore (but the concept should work with Gregs event-store too): https://stackoverflow.com/questions/17521153/event-up-conversion-with-keeping-event-class-name – David Rettenbacher Jun 26 '15 at 09:34

3 Answers3

2

The thing to remember is that what has passed is history. Event are history. Just coz your current view of the world has changed, doesn't mean something that happened in some form in the past didn't happen.

In terms of storage, yes, some form of serialised storage is usually handy. In fact, have loose type-tags that let you interpret the serialised events (rather than straight deserialisation) can be quite helpful when it comes to versioning. As with any form of communication (in this case, your ability to communicate across verions), remember Postel's Law: http://en.wikipedia.org/wiki/Robustness_principle . Favour the tolerant reader approach. In other words, consider your past events when hydrating. If an event seems to change semantic meaning, create a different event. Commands only act on the present state of the aggregate. The aggregate hydrates itself from its previous events. As such, your current version gets to choose how it will interpret past events (perhaps taking on a default value for past events - ala any event not having country code would correspond to UK, or something).

Lastly, only the state necessary to act on commands should be part of aggregates. They're not meant for querying purposes. They just need to emit enough information in the form of events so that downstream consumers can build the data they need (possibly correlating data from multiple streams / sources).

Does that help?

ashic
  • 6,367
  • 5
  • 33
  • 54
  • "Lastly, only the state necessary to act on commands should be part of aggregates. They're not meant for querying purposes. They just need to emit enough information in the form of events so that downstream consumers can build the data they need (possibly correlating data from multiple streams / sources)." This last sentence is all but clear, cant really make any sense out of it. – MeTitus Apr 11 '14 at 08:06
  • Say you have a rule that says that an account can have three consecutive overdraft attempts before being locked. In this case, the only data needed to enforce the rule is the current number of overdraft attempts, and the current balance. If this is the transactional boundary (i.e. an aggregate), then those are the only values you care about inside the aggregate. Other values may be pass-throughs for projections, or part of other aggregates. Don't think of aggregates as "things", think of them as bits of data that needs to be transactionally consistent to enforce some business rule. – ashic Apr 11 '14 at 08:11
  • I know what aggregates are, have being using it for a while now. The problem is that the OP is not talking about DDD whatsoever, no idea why you are bring it it to the table. Not saying I agree or not with what you are saying, just that, the question was not related to DDD. – MeTitus Apr 11 '14 at 16:40
  • The term Domain Events in the title made me think this was about DDD. Also, Greg's approach usually entails cqrs and even sourcing as tactics for DDD. – ashic Apr 12 '14 at 20:46
0

Events are history. They are persisted data, just like a file on disk, or column values in a database. You have to deal with upgrades and schema changes. There are a variety of methods to deal with this. In your particular example, you'd clearly need to make Country an optional field, and figure out what to do with the old events, for instance assuming they were all in the USA.

Sebastian Good
  • 6,310
  • 2
  • 33
  • 57
0

The only way i can see getting around this would be to either store different versions of your objects (seems to be very messy) or store your events as something more unstructured (like xml). Of course this would probably work really well with a dynamic language. I guess in c# you could probably use the DLR (dyanmic) but i think this could get messy too. Is there any other way?

You can and should version or events, that is how we get away with those changes. You would end up with two versions of the same event:

public class AddressAdded : IEvent
{
    public string Street {get; set;}
    public string Number {get; set;}
    public string PostCode {get; set;}
    public virtual string Region { get; set; }
}

public class AddressAddedV1 : AddressAdded 
{
    [Obsolete]
    public override string Region { get; set; }
}

A new version of the event might add or remove properties, so you can use the "Obsolete" attribute to make sure old properties are not used anymore.

MeTitus
  • 3,390
  • 2
  • 25
  • 49