During a discussion around some of the code examples in Vaugn Vernon’s book Implementing Domain Driven Design we got stuck at what first appeared to be a tiny detail: where the domain events are published. Is it part of the domain or not? But more importantly, what is the programming model for publishing events? I think this issue highlights how object oriented programmers are so used to carelessly mutating state that they don’t even consider any alternatives. I will not go into monads or anything, but only show how using normal return values can simplify your code.

In many DDD examples of code that generates domain events you often find a EventPublisher (or MessageBus, DomainEventsRaiser etc) that is injected into the domain object or worse accessed statically as argued by Udi Dahan. The aggregate then use the EventPublisher to send all the events that are generated when processing the command. When reading the code many people think it is very clear and easy to understand. However, there are many problems with this approach.

I will use my current favorite domain rock-paper-scissors. For a more complete example that also uses event sourcing, have a look at Aggregates + Event Sourcing distilled. (In the following example I will not use event sourcing and will in fact mutate state directly in the command handler. The reason for doing this is that in this blog post I target traditional object oriented programmers. If you are already into functional programming, you should probably read Event Sourcing in Clojure instead.)

public void performMove(String playerId, Move move) { if (gameOver) { throw new IllegalStateException("Game is already over"); } if (playerId.equals(this.lastPlayer)) { throw new IllegalStateException("Already made your move"); } this.eventPublisher.publish(new MoveMadeEvent(playerId, move)); if (lastMove != null) { generateWinningEvent(playerId, move); gameOver = true; } lastMove = move; lastPlayer = playerId; } private void generateWinningEvent(String playerId, Move move) { if (this.lastMove.beats(move)) { this.eventPublisher.publish(new GameWonEvent(this.lastPlayer, playerId)); } else if (move.beats(this.lastMove)) { this.eventPublisher.publish(new GameWonEvent(playerId, this.lastPlayer)); } }

The idea is that once the game is started either player can make their move first and when both players have made their move the game is over.

Can you spot the bug?

No event is generated when “lastMove” equals “move”, that is no GameTiedEvent is generated! Since the event publishing for the winning event occurs in a separate method it can be easy to miss this.

First, we write a test case to try to find the bug:

InMemoryEventPublisher ep = new InMemoryEventPublisher(); Game game = new Game(ep); @Test public void same_move_should_result_in_tie() { // given game.performMove("ply1", Move.rock); ep.removeEvents(); // when game.performMove("ply2", Move.rock); // then List events = ep.removeEvents(); assertThat(events, hasItem(new GameTiedEvent())); }

To make the test work we need to do the following:

Know that events are published

Create an EventPublisher and inject into Game

Remove all events after the first command since we don’t want to verify these

Since the test fails you start the debugger. You step over the first “eventPublisher.publish” method in the debugger and then think to yourself “hmm, the MoveMadeEvent have been published, maybe it is a concurrency issue. Maybe somewhere else some weird behavior is triggered by this MoveMadeEvent”….

However, as you will realize after an hour or so of debugging is that the event has not at all been published. The EventPublisher only publishes the events when the entire command has been processed! Otherwise the command will not be atomic! This is typically setup in the Application Service that start the transaction.

Of course, one solution to this is rename the EventPublisher to EventTracker or something, but that is only a workaround and still makes it hard to understand for new developers what is going on. Even worse, since your domain depends on this code it means that you might have to explain event publishing/tracking to a domain expert. From their perspective event publishing/tracking is a technical detail that they couldn’t care less about.

Now lets consider a revolutionary new approach: use return values! :-)

Implementing the Game logic becomes:

public List performMove(String playerId, Move move) { if (gameOver) { throw new IllegalStateException("Game is already over"); } if (playerId.equals(this.lastPlayer)) { throw new IllegalArgumentException("Already made your move"); } List events = new ArrayList<>(); events.add(new MoveMadeEvent(playerId, move)); if (lastMove != null) { events.add(generateWinningEvent(playerId, move)); gameOver = true; } lastMove = move; lastPlayer = playerId; return events; } private Event generateWinningEvent(String playerId, Move move) { if (this.lastMove.beats(move)) { return new GameWonEvent(this.lastPlayer, playerId); } else if (move.beats(this.lastMove)) { return new GameWonEvent(playerId, this.lastPlayer); } }

Immediately we get a compiler error saying that generateWinningEvent is missing a return value! Let’s fix it:

private Event generateWinningEvent(String playerId, Move move) { if (this.lastMove.beats(move)) { return new GameWonEvent(this.lastPlayer, playerId); } else if (move.beats(this.lastMove)) { return new GameWonEvent(playerId, this.lastPlayer); } else { return new GameTiedEvent(); } }

And the test case:

Game game = new Game(); @Test public void same_move_should_result_in_tie() { // given game.performMove("ply1", Move.rock); // when List events = game.performMove("ply2", Move.rock); // then assertThat(events, hasItem(new GameTiedEvent())); }

The domain has no knowledge about event publishing or event tracking. The test only have to check the return value it cares about. Clean and simple. It is easy to understand and easy to test.

But a Command returning values, isn’t that a violation of Command Query Separation? Or perhaps Tell, don’t ask? Perhaps, but the reason is not the fact that I’m returning events, instead the reason is I am returning events AND mutating state. By using event sourcing you separate the mutation to when the event is handled. But, it gets even better. Event sourcing is not object oriented, instead it is functional. Then “Tell, don’t ask” does not apply as it is a object oriented advice. Michael Feathers also talks about Tell above, ask below as a way to combining OO and functional. Another good point is made by Robert Harvey:

If you are using the result of a method call to make decisions elsewhere in the program, then you are not violating Tell Don’t Ask. If, on the other hand, you’re making decisions for an object based on a method call to that object, then you should move those decisions into the object itself to preserve encapsulation.

So where are the events actually published? In the Application Service of course:

@Transactional public void performMove(String gameId, String playerId, Move move) { Game game = this.gameRepository.load(gameId); List events = game.performMove(playerId, move); this.eventPublisher.publish(events); }

This code is just boiler plate, see Aggregates + Event Sourcing distilled how you can in fact use a completely generic Application Service for all command handlers using aggregates.

Sure, there are cases where injecting something like an EventPublisher or even accessing it statically may be useful. However, typically this is because you are either writing your own framework or working around someone else’s framework. I hope I have convinced you how normal return values can be a lot simpler and easier to understand!