Учтите следующие бизнес-требования:
У нас есть игроки, которые могут играть в игры. Игрок может играть только в одну игру за раз. Для игры нужны два игрока.
В системе будут миллионы игроков, а игры займут около двух минут. Вероятны проблемы с параллелизмом.
Мы хотим соответствовать правилу, согласно которому одна транзакция включает единую совокупность. Кроме того, возможная согласованность не должна приводить к принятию игр, которые должны быть впоследствии отменены (даже если на короткий период времени) из-за проблем с параллелизмом. Таким образом, конечная согласованность не совсем подходит.
Как нам нужно определять агрегаты и их границы, чтобы обеспечить соблюдение этих бизнес-правил?
Я задумал два подхода:
1. Рукопожатие на основе событий
Совокупный Player
, агрегированный Game
.
Когда игра запрашивается, она отправляет GameRequested
-событие. Player
s подписываются на это событие и отвечают соответствующим событием, либо GamePlayerAccepted
, либо GamePlayerRejected
. Только если оба Player
s приняли, Game
запускается (GameStarted
).
Плюсы:
- Агрегат
Player
отвечает за управление своей собственной доступностью, которая соответствует модели предметной области.
Минусы:
- Ответственность за запуск
Game
разбросана по нескольким агрегатам (это похоже на «фальшивую» согласованность событий) - Значительные накладные расходы на связь
- Необходимые меры согласованности, например освобождение
Player
s, если что-то пошло не так
2. Коллекция-агрегат
Агрегат Player
, агрегат GamesManager
(с набором объектов-значений ActiveGamePlayers
), агрегат Game
.
GameManager
запрашивается, чтобы начать новый Game
с двумя заданными Player
. GameManager
может гарантировать, что Player
будет воспроизводиться только один раз, поскольку это единый агрегат.
Плюсы:
- Нет событий, обеспечивающих согласованность, таких как
GamePlayerAccepted
,GamePlayerRejected
и т. Д.
Минусы:
- Модель предметной области кажется неясной
- Сменилась ответственность
Player
за управление доступностью - Мы должны гарантировать, что создается только один экземпляр
GameManager
, и ввести доменные механизмы, которые позволят клиенту не беспокоиться о посредническом агрегате. - Независимые
Game
-старты мешают друг другу, потому чтоGameManager
-агрегат блокирует себя - Необходимость оптимизации производительности, поскольку
GameManager
-aggregate собирает всех активных игроков, которых будут десятки миллионов.
Похоже, что ни один из этих подходов не подходит для решения проблемы. Я не знаю, как установить границы, чтобы обеспечить как строгую согласованность и ясность модели, так и производительность.