In the world of software development, there are phrases that sound like an incantation from a magician. For example, Event sourcing and Command Query Responsibility Segregation (CQRS). They may seem like arcane incantations, but they are actually super useful architectural principles for designing and creating scalable and evolutionary software systems.
In this article, we are going to unravel two magical concepts of the Event sourcing world: Event Upcasting and Event Replaying. Understanding these concepts is vital because as companies expand and respond to new market demands, event structure and content must evolve with them. We will delve into these concepts, reveal their mysteries and illustrate how they benefit technical wizards and business wizards alike. Our journey will be guided by concrete examples, using the Axon Framework to demonstrate their practical application.
Building on the foundation
Before embarking on this magical journey of Event Upcasting and Event Replaying, it is essential to establish a strong foundation in event sourcing and CQRS.
- ๐ง F or business professionals: No crystal ball necessary. Understanding the basics of event sourcing and CQRS will help you make informed decisions about your digital kingdom. It's your ticket to fluency in the language of technical wizards. For more mystical insights, check out lectures like "Capture, Keep, and Unlock All Your Data," by Michael Schoenmaekers, designed just for you.
- ๐ง F or developers: Imagine event sourcing and CQRS as the spells that create powerful and resilient systems. To become the wizards of the digital realm, mastering these incantations is a must. Don't worry; you don't need a Hogwarts letter. Just go to Axon Academy to unlock these magical skills and take their free courses.
What is event upcasting?
Imagine upgrading your favorite enchanted sword without losing any of its magical properties. That's exactly what Event Upcasting does. It transforms historical events into a shiny new format while retaining all their original charm.
- ๐ก๏ธVoor business professionals: Upcasting allows your system to handle changes seamlessly, reduces the risk of data inconsistencies and preserves historical data.
- ๐ก๏ธVoor developers: Event Upcasting allows you to evolve your event schedule without breaking existing systems. This flexibility is crucial for adapting to changing requirements.
What is Event Replaying?
Picture this: it's movie night in your castle and you decide to rewatch your favorite epic story. Event Replaying is similar, but it involves reconstructing the story of your entire kingdom by re-enacting events from the past. It's like summoning all the magical creatures and characters from your kingdom's history to relive their adventures.
- ๐ฌ F or business professionals: Allows you to perform historical analysis, audit trails and ensure regulatory compliance, improving data management and decision-making.
- ๐ฌ F or Developers: Event Replaying is a powerful tool for debugging, checking and maintaining data consistency in your systems. Moreover, you can extend existing code more easily because you can delete your data tables and refill them through the event store or create new modules based on existing events and data.
How Event Upcasting and Event Replaying Work Together
These two magical practices often join forces, especially when you are on a quest to migrate data from one event server version to another.
- ๐ค F or business professionals: This collaboration ensures seamless transitions between data structures and improves data harmony and reliability in the kingdom. It keeps your digital kingdom agile and ready to respond to the ever-changing demands of the market and your loyal subjects.
- ๐ค F or Developers: The collaboration between Upcasting and Replaying is your secret weapon to effectively manage data changes. It's like adding new spells to your repertoire, encouraging innovation while ensuring compatibility with your old charms.
Let's see this collaboration in action through the story of the potion delivery boy "Bubbles"
A story of beverage delivery - the delicious expansion
In the beginning, our store delivered only in the Netherlands, with a specific event in their order list:
โEvent For Business Professionals:
Potion Order Placed Event (Version 1.0)
Order
ID- Customer Name
- Dutch Address
Event For Developers:
@Revision("1.0")
public class PotionOrderPlacedEvent{
private String orderId;
private String customerName;
private DutchAddress address;
// ...
}
โ
public class DutchAddress{
private String postalCode;
private String houseNumber;
private String addition;
// ...
public String getAddress() {
return postalCode + " " + houseNumber + " " + addition;
}
}
โ
The search
Now the Dutch address had some peculiar characteristics: zip code, house number and addition. But lo and behold! News came that customers across the border in Belgium were also craving drinks. The challenge? Belgian addresses needed street names; Dutch did not. This is a concrete example of how companies expand and respond to new market demands. This means that the structure and content of the PotionOrderPlacedEvent must evolve with it.โ
How could "Bubbles" serve both the Dutch and Belgian countries without disrupting their current corporate event? The answer: upcasting!
Step 1: create address, the shape-shifter
First, "Bubbles" conjured up a new 'Address' object that could shapeshift into a Dutch or Belgian address, thanks to the 'getCountry' and 'getAddress' spells:
Step 1 for business professionals:
๐ช Address (The shapeshifter):
- `getAddress()` spell => shows the full address in one sentence
- `getCountry()` spell => show whether it is a Dutch or Belgian address by returning the country Belgium or Netherlands
Next, the shape shifter is used:
๐ก DutchAddress:
- postal code
- house number
- addition
- `getAddress()` spell => shows the full Dutch address in one sentence
- `getCountry()` spell => shows that it is a Dutch address by giving NETHERLANDS
๐ก BelgianAddress:
- postal code
- street name
- house number
- addition
- `getAddress()` spell => shows the full Belgian address in one sentence
- `getCountry()` spell => shows that it is a Belgian address by giving BELGIUM
Step 1 for developers:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "country")
@JsonSubTypes({
@JsonSubTypes.Type(value = DutchAddress.class, name = "NETHERLANDS"),
@JsonSubTypes.Type(value = BelgianAddress.class, name = "BELGIUM"),
})
public interface Address {
String getAdress();
String getCountry();
}
โ
public class DutchAddress implements Address {
private String postalCode;
private String houseNumber;
private String addition;
// ...
public String getAddress (){
return postalCode + " " + houseNumber + " " + addition;
}
@Override
public String getCountry () {
return "NETHERLANDS";
}
}
โ
public class DutchAddress implements Address {
private String postalCode;
private String houseNumber;
private String addition;
// ...
public String getAddress (){
return postalCode + " " + houseNumber + " " + addition;
}
@Override
public String getCountry () {
return "BELGIUM";
}
}
โ
Step 2: create an event update
We used this newly found "Address" shapeshifter in a new version of the event, so we could deliver to both Dutch and Belgian addresses.
Step 2 for business professionals:
Potion Order Placed Event (Version 2.0):
- Order ID
- Customer Name
- Shape shifter Address
Step 2 for Developers:
@Revision ("2.0")
public class PotionOrderPlacedEvent {
private String orderId;
private String customerName;
private Address address;
//...
}
โ
Step 3: behold the Upcaster Spell - transform the old into the new
The Order Scroll contains our history with the older events and we do not want to overwrite our history. A history is in the past and therefore should never be changed. However, all order scrolls should clearly state whether the deliverers should fly to Belgium or the Netherlands. This is where the transformation (upcaster) spell comes in that allows the order scroll, our history, to be used in all our delivery lists. This is done by adding the country property when reading old events from the order scroll. This change allowed "Bubbles" to extend their magic kingdom to Belgium without losing sight of their Dutch adventures.
Step 3 for business professionals:
๐ง Magical Upcaster Spell
- Detects old order events (version 1.0)
- Transforms them into new order events (version 2.0) with the 'country' spell set to "NETHERLANDS".
Step 3 for developers:
@Component
@Order(0)
public class PotionOrderPlacedEvent1to2Upcaster extends SingleEventUpcaster {
public static final SimpleSerializedType TARGET_TYPE =
new SimpleSerializedType("com.bubbels.events.PotionOrderPlacedEvent", "1.0");
@Override
public boolean canUpcast (IntermediateEventRepresentation intermediateRespresentation){
return intermediateRepresentation.getType().equals(TARGET_TYPE);
}
@Override
public IntermediateEventRepresentation doUpcast(
IntermediateEventRepresentation intermediateRepresentation)}
return intermediateRepresentation.upcastPayload(
new SimpleSerializedType (TARGET_TYPE.getName(), "2.0"),
ObjectNode.class,
event -> {
event.set ("country", "NETHERLANDS");
return event;
}
);
}
}
โ
Step 4: pronounce the upcaster spell and make the delivery lists
But wait, the party isn't over yet! Although we can now read our history as if we have always had a country house, we need to update our delivery lists so we can see which country we are delivering to. What should we do? Replay event!
The old delivery list looked like this:
We burn all the data in the delivery list and call the order scroll (our history). We tell the scroll that we deleted the data (in developer language => by removing the token) and the scroll will use the upcaster spell to refill the delivery list with the land field.
Now when we receive new orders, our list looks like this:
๐ฎ Looking for more knowledge?
If you:
- Eager to dive deeper into the world of Event Upcasting and Event Replaying
- Or if you have specific questions
- Or crave more enchanting content
- or are looking for a system you can use like a reliable Swiss army knife