We had a discussion about CQRS and event sourcing, and there was a concern raised about cost of event versioning. Yes, with event sourcing we can get rid of relational database schema on the write side of the system, saving us from the maintenance of the SQL schema, but won’t the saved effort be used on the maintenance of events?
I don’t think so. Or at least I don’t think the cost will be as high. Let’s see why.
First, one of the frequent reasons to change data schema is refactoring tables to improve extensibility and expressiveness of the relational model. It does not however necessarily change any events. For example, the first version of the Internet shop could have all customer details placed in a table Customer:
In the second version it was decided to extract address details into a separate table, because in the future customers should be able to register several address:
In a system based on a relational database this change will require writing an upgrade script. But a system based on event sourcing will handle same events:
void CustomerRegistered(CustomerRegistrationDetails details); void CustomerAddressUpdated(CustomerAddressDetails details);
I believe that systems where entities represent state transition rather than state are less affected by changes of internal internal data model. What is essential for such systems is define events right, so they reflect state transitions as seen by parties involved in a use case, and not state changes of internal data structures.
Second, and more interesting case is when changes to the system require event versioning and may or may not require versioning of a relational database schema. For example, let’s say in the third version of the Internet shop customers are required to register both shipping and billing addresses:
You will most likely use the same “Address” table to store all address types. Then you will have to upgrade existing customer data. How will you interpret old Address records that don’t specify the address type? You will either introduce a new address type (“Unspecified”) or have to make a qualified guess, for example treat all addresses without types as shipping addresses. But you will have to run another upgrade script, this time it will be data upgrade, not schema upgrade.
In case of event sourcing system you may want to be more expressive with events:
void CustomerRegistered(CustomerRegistrationDetails details); void CustomerShippingAddressUpdated(CustomerAddressDetails details); void CustomerBillingAddressUpdated(CustomerAddressDetails details);
But we missed the old event here: CustomerAddressUpdated. We could also make a qualified guess here, assume all already registered addresses are shipping addresses and rename the event, but I don’t think this is a right apporach. We should not redefine semantics of old events and instead include them in the event list:
void CustomerRegistered(CustomerRegistrationDetails details); void CustomerAddressUpdated(CustomerAddressDetails details); void CustomerShippingAddressUpdated(CustomerAddressDetails details); void CustomerBillingAddressUpdated(CustomerAddressDetails details);
So the code that builds customer object from the event stream should understand both obsolete CustomerAddressUpdated event and new CustomerShippingAddressUpdated and CustomerBillingAddressUpdated events. Will it really require greater development effort than upgrading existing customers in a relational database? I don’t think so. But retaining historical events in the system has an important advantage of being able to reproduce exactly what happenned to the customer with option to correct possible inconsistency on the fly (for example by asking him to specify the type of previously registered address) rather than bending its shape to fit an updated relational data model.