Вид из тысячи футов
Могу ли я поделиться с вами небольшой историей ...Когда-то давно кто-то хотел сохранить события для совокупности (из славы управляемой доменом разработки) в ответ на данную команду.Этот человек хотел убедиться, что агрегат будет создан только один раз и что любая форма оптимистичного параллелизма может быть обнаружена.Для решения первой проблемы - что агрегат должен быть создан только один раз - он сделал вставку в транзакционный носитель, который выбрасывал при обнаружении дублирующего агрегата (или, точнее, его первичного ключа).Он вставил агрегатный идентификатор в качестве первичного ключа и уникальный идентификатор для набора изменений.Коллекция событий, создаваемых агрегатом при обработке команды, - это то, что здесь подразумевается под набором изменений.Если кто-то или что-то еще побеждает его, он рассматривает совокупность, уже созданную, и оставляет это на этом.Набор изменений будет сохранен заранее на носителе по его выбору.Единственное обещание, которое этот носитель должен дать, - вернуть то, что было сохранено как есть, когда его спросят.Любой сбой сохранения набора изменений будет считаться провалом всей операции.Чтобы решить вторую проблему - обнаружение оптимистичного параллелизма в последующем жизненном цикле агрегата - он, после написания еще одного набора изменений, обновит запись агрегата в среде транзакций, если и только если никто не обновил ее за его спиной (то есть по сравнению с тем, что он последний раз читал непосредственно перед выполнением команды).Транзакционная среда уведомит его, если такое случится.Это заставит его перезапустить всю операцию, перечитав агрегат (или его наборы изменений), чтобы на этот раз команда была выполнена успешно.Конечно, теперь он решил проблемы с письмом, вместе с проблемами чтения.Как можно было бы прочитать все наборы изменений агрегата, который составлял его историю?В конце концов, он имел только последний зафиксированный набор изменений, связанный с совокупным идентификатором в этой транзакционной среде.И поэтому он решил вставить некоторые метаданные как часть каждой ревизии.Среди метаданных, которые нередко встречаются как часть набора изменений, был бы идентификатор предыдущего последнего подтвержденного набора изменений.Таким образом, он мог «пройти линию» наборов изменений своего агрегата, как, скажем, связанный список.В качестве дополнительного бонуса он также будет хранить идентификатор командного сообщения как часть метаданных набора изменений.Таким образом, при чтении наборов изменений он мог заранее знать, является ли команда, которую он собирался выполнить в агрегате, уже частью ее истории.Все хорошо, что хорошо кончается ...
PS1. Транзакционный носитель и носитель изменений набора могут быть одинаковыми,2. Идентификатор набора изменений НЕ ДОЛЖЕН быть идентификатором команды,3. Не стесняйтесь пробивать дыры в сказке :-),4. Хотя это не имеет прямого отношения к хранилищу таблиц Azure, я успешно реализовал описанную выше историю, используя AWS DynamoDB и AWS S3.