I was using the example of Joda Money being used in the event to illustrate a greater problem with storing any value object in an event. Joda Money is used in the domain because it perfectly encapsulates the business rules for expressing money with currency, calculations and exchange rates.
Now let’s try this with the value objects used to describe other attributes in our domain. For example, let’s create a “org.example.Description” which currently wraps a String with the class attribute name of “value”. The constructor takes a String and validates that: a) is not blank; b) is a maximum length of 100 characters. And finally let’s store this value object in our DomainEvent. The default XStream serialiser will format this as:
<org.example.Description>Here is my description</org.example.Description>
It’s simple and by using XStream capabilities we could reduce the verbosity by removing the package name.
Let’s make a change to our domain such that we don’t need the org.example.Description value object and we remove it from our domain vocabulary. The event as stored above cannot be deserialised. The domain is now held hostage to an old requirement as it must keep the unused value object around.
Let’s make a different change in that org.example.Description must store the Charset. So instead of one class attribute there are now two. I could assume in the Description class that a null Charset is UTF-8 if that was always true for how the domain was being used. The decision of which Charset to use may also be based upon the date in which the event was created, for example; using UTF-8 vs US-ASCII. In the first case the value object is cluttered with historical knowledge. In the second case the event handler that is called in the aggregate during deserialisation would be replacing the Description value object with one that used a Charset for a particular time – and it is desirable to have that logic located here but we’re instantiating a replacement object for the additional detail.
Either example may by alleviated by using an Axon upcaster. However this adds additional complexity to the code base and is moving logic outside of the domain. Events are meant to be versioned. The domain is not. Events are immutable by their very nature and adding objects with behaviour is counter-intuitive.
By using primitive values in the DomainEvent the attribute could simply be a String named “description”. The serialised version of the DomainEvent would be:
Here is my description
With this our domain is free to remove the Description value object. And version 2 of the event would accept the Charset. My legacy event handler in the aggregate is free to choose the Charset based upon the event creation date. I can also take this stream and directly serve it to a consumer that wasn’t using Java or had a copy of our API. In this case " … " has more descriptive value than “<org.example.Description>…</org.example.Description>”.