Aggregate root serialization problem with joda Money class for Event Sourced Aggregate

For Event Sourced JPA aggregate…

Aggregate root with fields of joda Money stores fine… retreival is the problem with error below when trying to load aggregate by applying domain eventhttp://joda-money.sourceforge.net/apidocs/org/joda/money/Money.html

Joda Money is serializable design.

2011-12-29 16:56:57,353 [btpool0-2] ERROR org.axonframework.commandhandling.SimpleCommandBus - Processing of a [ContactCreateCommandAction] resulted in an exception:

com.thoughtworks.xstream.converters.ConversionException: Cannot construct class java.lang.Class : Class com.thoughtworks.xstream.converters.reflection.ExternalizableConverter can not access a member of class org.joda.money.Ser with modifiers “public”

---- Debugging information ----

message : Cannot construct class java.lang.Class

cause-exception : java.lang.IllegalAccessException

cause-message : Class com.thoughtworks.xstream.converters.reflection.ExternalizableConverter can not access a member of class org.joda.money.Ser with modifiers “public”

class : com.server.domain.ContactCreatedEvent

required-type : org.joda.money.Ser

path : /com.server.domain.ContactCreatedEvent/contact/fixedFee

line number : 1

Anyone come across this or what may be missing or any ideas on how to address or workaround?

Cheers

Hi,

unfortunately, XStream doesn’t support all of the advanced features of Java Serialization. It seems that readResolve() is just a step too far for XStream.

I had a look at the (Big)Money API, and your best workaround is a simple custom XStream converter. You can simply “toString()” the money value, and use Money.parse(…) to get the money instance back. It will serialize to a much more compact format, too. The JodaTimeConverter in Axon is an example of how to create a simple converter.

Hope that helps.

Cheers,

Allard

Thanks Allard ..

You read my mind...

Thats exactly what i did. Stored as string and money.parse to reconstruct and money.tostring as instance variable update.

Cheers...

I find that the use of converters is acceptable for the metadata element as that is part of the application and framework. However my solution for the domain attributes in the event is to unwrap and store as primitives without the domain specific value object. Today the domain is using Joda Money but tomorrow it might be Acme Money or even worse the construction constraints are changed for an upgraded version of Joda Money. Yes there are event upcasters available in the framework that can be used to version the domain – at the cost of additional complexity and deserialisation resources/performance.

My suggestion would be to construct the event by passing and storing String representations of the Joda Money object (value and currency as separate construction attributes). It’s the simple truth of what happened and frees your domain to change as required. This construction of the event has the additional benefit to be shared outside of a Java environment.

Hi Seamus,

you make a very valid point here. Storing “pure” values makes it much easier in the long term to maintain compatibility with the stored data. Java serialization typically fails at that. XStream does it a bit better by serializing data only, and allowing for configuration of the (de)serialization.

In the Money example, I looked at how XStream serializes the data. It stores a number of fields, most of which don’t make any sense to me when talking about money. The only thing I would care about (and it’s how Money is described in the JavaDoc), is the currency and the amount. A custom serializer helps you to serialize to such a construct. In the serialized event, there is (typically) no reference at all to the object type stored. So there is no reference to the fact that Joda Money is used.

So using the Joda Money in the Event with a custom converter to serialize it as a simple string seems like a suitable way to me. After all, I do the same in the framework with Joda Time. If I’d change to another Time library (which I don’t think I will, by the way), all I need is to parse the ISO 8601 Strings to a date.

Cheers,

Allard

Hi Allard,

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>”.

Seamus

Hi Seamus,

your point about striving for library-independence in serialized objects is very valid. In fact, that’s the point I was also making, but in another form.

Your example of the Description object is missing one thing: typically, these objects are included in an Event. That means you won’t have “org.example.Description” mentioned anywhere. You’ll have something like this:
<org.myapp.MyAppsEvent>Bladibla bla </org.myapp.MyAppsEvent>
Where description is the name of the field used in MyAppsEvent, not the name of the class.

Still, this is quite ugly, and I guess we all agree, because “” seems a bit out of the ordinary here. The serialized form we want is the following:
<org.myapp.MyAppsEvent>Bladibla bla </org.myapp.MyAppsEvent>

Now here comes the clue: it doesn’t matter what kind of object your using in the MyAppsEvent class. Whether it’s a String or a Description. As long as there is an appropriate Converter for it, you’ll be fine.

I always have a look at the EventStore on my local dev environment to look at how events are serialized, before committing any code changes. That way, I make sure my Events are flexible and don’t need an upcaster for every minor refactoring we make.

You should always be weary about using thrid-party libraries in Events, especially when it comes to serialization. But converting everything to a String in the Event is not the only way to achieve the goal.

Cheers,

Allard

Hi Allard,

I do stand corrected.

I took an existing event and returned my value objects to the class. I also created a converter that is passed a single-value value-object class from my domain. These converters are then registered with XStream. The value objects in my domain have a static “create” signature and a public “asString” method.

I created two tests to first review the output of this serialised event and to take an existing serialised event which had used primitives. Both tests passed without issue.

As you pointed out its best to review the local dev event store prior to committing changes. Now I have my code simplified (no more useless conversions) and I get my small and portable event stream.

Thanks!
Seamus

Thanks for all the responses…

I was doing the straightforward primitive storage without a converter and converter makes a lot of sense. I am not very familiar with registration of converter to Axon serializer… seems straightforward with a native xstream instance… but code to point to for learning for Axon specific instance attachment…

Reviewed the JodaTimeConverter code in Axon and looks straightforward… question is how to wire a converter like JodaTimeConverter in Axon to the xstream instance used by Axon from a axon based App so Axon picks it up when Money fields are getting persisted by aggregate.

Thanks and Cheers…

Using Spring and its Java Config it’s as easy as:

@Bean
public JpaEventStore eventStore() {
XStreamEventSerializer eventSerializer = new XStreamEventSerializer();
XStream xStream = eventSerializer.getXStream();

xStream.registerConverter(new JodaMoneyConverter());

JpaEventStore eventStore = new JpaEventStore(eventSerializer);
return eventStore;
}

And your implementation of JodaMoneyConverter just needs to implement “com.thoughtworks.xstream.converters.Converter”.

Works like a charm… Thanks for the tip… Cheers…