Whenever I take a look at Axon Bank I start wondering whether I should follow a set of design rules for events and commands.
In Axon Bank both events and commands exclusively consist of primitives. In my applications I tend to avoid primitive usage as much as possible, mainly to build an expressive domain and to have type safety wherever I can get it.
Axon itself comes around with some DDD references but no matter which documents I browse, not a single example makes use of compound objects as part of event/command payloads.
Which confuses me. There is built-in-support for full-blown xml and json serialization capable of more than just having some key-value pairs.
I understand that especially events tend to be small and simple structures since they only reflect incremental state changes but there will always be some kind of gap between a complex domain model and an event (entry).
In my domain I could have a bunch of Classes like OverdraftLimit, CurrentBalance, Deposit and AccountIdentifier.
Now there are two possible ways to design events and commands:
1. Primitives and extensive converting
- Treat Events as raw data with a nice label on it
- Convert raw data to powerful objects as soon as it "enters" the application
When creating events simply strip them down again.
public class BankAccountcreatedEvent { private final String accountIdentifier; private final int overdraftLimt; // ... }And somewhere else:
public void on (BankAccountCreatedEvent event) { this.accountIdentifier = AccountIentifier.fromString(event.getAccountIdentifier()); this.overdraftLimit = new OverdraftLimit(event.getOverdraftLimit()); }
Pros:
- Simple command/event API that does not have any weird dependencies
- Makes distribution easier
- Upcasters will only be needed if the actual event structure changes and therefore can be anticipated easily.
Cons:
- A huge conversion layer needs to be written and maintained
- Decoupling events/commands and the rest of the domain model for mainly technical reasons introduces a new, artificial, contextual gap
2. Expressive Payloads
Use sophisticated types directly as attributes
public class BankAccountCreatedEvent { private final BankAccountIdentifier bankAccountIdentifier; private final OverdraftLimit overdraftLimit; //.. }
Pros:
- Less to write, easier to read
- Keep together what naturally belongs together
Cons:
- Domain logic influences event structure indirectly, upcasting will be needed more frequently and will be less predictable.
I need a second opinion. Is there a recommended way?