Dealing with non-trivial command and event payloads in Axon

Viewed 658

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?

1 Answers
Related