But, what if one of the events should cause an external system not in your control to "ship an item" to the customer if you just replay the events the item would be shipped twice.
To choose a specific example, let's consider how an "at least once" approach to side effects might work.
State currentState = State.InitialState
for(Event e : events) {
currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
performSideEffect(s)
So the domain model tracks what needs to be done; but leaves the actual doing to the application
In the context of running a command, the basic idea looks the same. The actual side effects happen outside of the transaction that updates the model.
So the unit tests for your model could looks something like
{
// Given
State currentState = State.InitialState
// When
Events events = List.of(OrderPlaced)
// Then
List.of(SendEmail) === currentState.applyAll(events).querySideEffects()
}
{
// Given
State currentState = State.InitialState
// When
Events events = List.of(OrderPlaced, EmailSent)
// Then
List.EMPTY === currentState.applyAll(events).querySideEffects()
}
The main points here being
- Updating the model is side effect free; the actual side effects happen outside of the transaction that updates the model.
- An event describing the outcome of the side effect needs to come back.