Два агрегата и одна транзакция — пример из Красной книги

В Красной книге Вернон моделирует объекты BacklogItem и Sprint как отдельные агрегаты. Я вижу преимущества этого подхода, но есть один случай, который я не могу понять. Например, мне нужен агрегат Sprint, чтобы обеспечить соблюдение максимального количества элементов. Более того, BacklogItem должен знать о факте назначения, чтобы убедиться, что он не назначен более чем одному спринту. Таким образом, назначение BacklogItem для Sprint изменяет два агрегата в одной транзакции, а это не то, что мы хотим делать. Я не вижу хорошего подхода, который решает эту проблему. Расширение агрегата подразумевает создание BacklogItem внутренней части Sprit. Что не имеет смысла из-за необходимости использования внутри других агрегатов (Release, Schedule). Еще один способ, который я придумал, — использовать возможную согласованность и просто предупредить администратора о двойном назначении BacklogItem. Но я воспринимаю это как важный агрегатный инвариант, и я хотел бы получить возможность навязать это явно.


person ayeo    schedule 04.06.2017    source источник


Ответы (1)


Еще один способ, который я придумал, - использовать конечную согласованность и просто предупредить администратора о двойном назначении BacklogItem.

Я предполагаю, что это правильный ответ.

Мне нужен мой агрегат Sprint, чтобы обеспечить соблюдение максимального количества назначенных элементов.

Есть важное соображение, которое вы, возможно, упускаете из виду: кто решает, какие элементы невыполненной работы добавлять в спринт? Является ли это решением, которое модель принимает для себя, или это решение принимается человеком (или какой-либо другой сущностью вне модели)?

Потому что по большей части модель не должна отбрасывать решения, принимаемые людьми-операторами; и это не должно заставлять людей-операторов менять контекст, чтобы прыгать через обручи модели.

Нет смысла запускать эту команду, если мы заранее знаем, что превышен лимит элементов Sprint. Если обработчик команд выполнит эту проверку: $sprint->hasSpaceFor($item); не будет ли это считаться утечкой знаний?

Есть несколько вещей, чтобы рассмотреть здесь.

Во-первых, данные, не являющиеся частью агрегата, являются устаревшими; может быть другой обработчик команд, изменяющий эти данные, пока вы их проверяете. Это не означает, что проверка его неверна, но это означает, что проверка его в обработчике команд явно не лучше, чем проверка где-то еще.

Во-вторых, ваша проверка инварианта зависит от порядка поступления сообщений. Человек-оператор, возможно, решил удалить элемент из спринта, чтобы освободить место для нового, но если порядок сообщений изменяется при передаче (ненадежный обмен сообщениями), то модель в конечном итоге отклоняет то, что должно быть допустимой командой. . Это не хорошо.

В качестве альтернативы сообщения могут приходить в том порядке, в котором они были отправлены. Человек-оператор знает, что две операции отменяются, и поэтому не имеет значения, в каком порядке это происходит, но модель настаивает на том, чтобы о решениях сообщалось в определенном порядке. Это прыжки с обручем — модель усложняет, а не облегчает работу.

Кроме того, ценность для бизнеса от включения нужного элемента в спринт может быть более ценной, чем выяснение того, какой из них следует удалить.

Основная идея: человек-оператор работает с приоритетами бизнеса сейчас, но модель, работающая в производственной среде, отражает приоритеты на момент ее написания — другими словами, модель отражает прошлые приоритеты. . Таким образом, вы должны быть осторожны, чтобы модель не наложила вето на оператора.

Более того, вы хотите понять, как возникает коммерческая ценность при наложении вето на сообщения, которые вы получаете от оператора-человека, и решить, где следует управлять этой ответственностью — может быть, это проблема пользовательского интерфейса (попытка снизить высокую стоимость ввода данных). ошибки?), а не проблема модели предметной области.

person VoiceOfUnreason    schedule 05.06.2017
comment
Это великое понимание! Вы совершенно правы :) Но это приводит к другому соображению. Для нужд этого разговора предположим, что я запускаю свою команду в корне агрегата BackLogItem: - person ayeo; 05.06.2017
comment
$item-›assignToSprint($sprint); Элемент Backlock проверит, не назначен ли он другому спринту. Но должна быть проведена другая проверка. Нет смысла запускать эту команду, если мы заранее знаем, что превышен лимит элементов Sprint. Если обработчик команд выполнит эту проверку: $sprint-›hasSpaceFor($item); не будет ли это считаться утечкой знаний? - person ayeo; 05.06.2017
comment
Я согласен со всем, что говорит VoiceOfUnreason. По сути, ваш агрегат спринта может просто иметь параметр, который сообщает, есть ли у него переполнение. Это может использоваться в пользовательском интерфейсе для информирования пользователя, но не должно использоваться где-либо еще. Для закрытия спринта вы можете отклонить это, если в этот момент еще есть открытые элементы. Конечно, возможно, что закрытие элемента все еще приближается, но разумно потребовать, чтобы все элементы были закрыты. С другой стороны, если вы перемещаете невыполненную работу, которая уже находится в одном спринте, ваша команда может быть «MoveBacklog» и требовать исходного и целевого спринта. - person Arwin; 06.06.2017
comment
Спасибо за отличные развернутые ответы. Вы привели меня в правильном направлении. Но иногда синхронизированный поток между несколькими агрегатами кажется неизбежным. Насколько я понимаю, когда именно в игру вступают саги (или менеджеры процессов)? Это правильно? - person ayeo; 06.06.2017