Но что, если одно из событий должно привести к тому, что внешняя система, не находящаяся под вашим контролем, «отправит товар» клиенту, если вы просто воспроизведите события, товар будет отправлен дважды.
Чтобы выбрать конкретный пример, давайте рассмотрим, как может работать «хотя бы один раз» подход к побочным эффектам.
State currentState = State.InitialState
for(Event e : events) {
currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
performSideEffect(s)
Таким образом, модель предметной области отслеживает, что необходимо сделать; но оставляет фактическое выполнение для приложения
В контексте выполнения команды основная идея выглядит одинаково. Фактические побочные эффекты происходят вне транзакции, которая обновляет модель.
Так что юнит-тесты для вашей модели могут выглядеть примерно так
{
// 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()
}
Основными моментами здесь являются
- Обновление модели без побочных эффектов; фактические побочные эффекты происходят вне транзакции, которая обновляет модель.
- Событие, описывающее исход побочного эффекта, должно вернуться.