Сохранение согласованности между агрегатами

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

Представьте, что вы продаете коровье молоко. У вас несколько коров, каждая из которых дает определенное количество литров молока в день. У вас должна быть служба, способная получить необходимое количество молока на складе. Кроме того, вы также должны иметь возможность заказывать молоко. На основе этой информации вы можете создать три агрегата: Cow, Stock и Order. Каждый раз, когда заказывается определенное количество молока, одно из бизнес-правил - проверять, есть ли это количество в наличии, а если нет, сразу же сообщать пользователю. Как этого добиться, если два пользователя одновременно запрашивают и заказывают 150 литров молока, в то время как доступно только 130 литров? Моя первая идея заключается в том, что вы можете добиться этого с помощью оптимистической / пессимистической блокировки, но в этом случае один агрегат зависит от другого. Есть ли какой-то способ решить эту конкретную проблему, или это просто плохая совокупная конструкция?


person Frank Levering    schedule 04.04.2017    source источник
comment
Может, стоит объединить агрегаты Cow и Stock в один агрегат? Кажется, это те части, которые вам нужно постоянно поддерживать. На мой взгляд, нет необходимости моделировать Cow как отдельный агрегат.   -  person Mike Wojtyna    schedule 04.04.2017
comment
Это подтверждает мою мысль о плохом дизайне. Действительно имеет смысл объединить Cow и Stock в один агрегат, поскольку бизнес требует, чтобы это было согласовано.   -  person Frank Levering    schedule 04.04.2017
comment
Это просто мое мнение, основанное на приведенном вами кратком описании. Если ваш домен представляет собой реальную проблему, он может быть гораздо более сложным, и вам определенно следует многократно повторять свою модель с вашей командой, чтобы найти лучший вариант. Однако, если вас устраивает мой комментарий, я могу сделать из него ответ, поэтому вопрос не помечается как оставшийся без ответа.   -  person Mike Wojtyna    schedule 04.04.2017
comment
Я это понимаю. Я в основном искал подтверждение того, что всякий раз, когда вы сталкиваетесь с проблемой согласованности транзакций между несколькими агрегатами, ваш дизайн неверен. Я не уверен, что этот короткий пример полностью подтверждает эту точку зрения, но мне он кажется верным.   -  person Frank Levering    schedule 04.04.2017
comment
В таком случае я оставлю ссылки на материалы Вона Вернона по моделированию агрегатов. Бывают ситуации, когда вы вынуждены создавать транзакцию, охватывающую агрегаты, но этого следует определенно избегать, поскольку они противоречат всей идее агрегирования. Вот ссылка - я считаю эти статьи действительно хорошими dddcommunity.org/library/vernon_2011   -  person Mike Wojtyna    schedule 04.04.2017


Ответы (2)


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

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

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

На основе этой информации вы можете создать три агрегата: «Корова», «Запас» и «Заказ».

Примечание: Cow - паршивая совокупность - если предположить, что мы говорим о настоящих в мире поедающих травяных коровах. Да, это сущность, но она находится вне влияния модели. Если модель говорит, что корова пуста, а корова говорит, что она полна молока, корова права.

Точно так же, если коровы настоящие, то большая часть вашего поголовья также настоящая. Если модель говорит, что есть семь полных канистр молока, а фермер считает шесть, то шесть - правильный ответ.

Агрегаты - это информационные ресурсы.

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

Важно понимать, что «сразу»; вы здесь, пользователь (покупатель?) там. Связь имеет определенную задержку, а это означает, что информация, которую вы отправляете покупателю, уже устарела, когда она поступает. (Технически он уже устарел на момент отправки.)

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

В вашем примере параллельной обработки это будет выглядеть как две команды для резервирования 130 литров молока, работающих одновременно с агрегатом запаса, имеющим 150 литров молока. Используя оптимистичный параллелизм, обе команды обнаружат, что молока достаточно для удовлетворения заказа, и попытаются обновить книгу записей. Однако это обновление сериализуется - подумайте о транзакции или сравните и поменяйте местами - поэтому одна из команд выполняется успешно, а другая вместо этого получает исключение одновременной модификации. Таким образом, эта вторая команда пытается еще раз, перезагружая ложу в ее новом состоянии. На этот раз он обнаруживает, что имеющихся запасов недостаточно, и действует соответствующим образом (перемещает заказ в список ожидания, уведомляет покупателя об ожидаемой дате выполнения и т. Д.).

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

person VoiceOfUnreason    schedule 04.04.2017

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

Я пойду в другом направлении и скажу: не обязательно. Вы всегда собираетесь требовать согласованности между агрегатами в какой-то момент времени. Вопрос только в том, когда. Вы можете обнаружить, что в некоторых ситуациях сталкиваетесь с политикой. Мне пришлось реализовать 100% немедленную согласованность между целой кучей агрегатов просто потому, что я получил указание сделать это от тех, кто владеет строками кошелька. В идеальном мире мы были бы свободны выбирать. Но в моем реальном мире вы можете поспорить, что мы проиграем. В любом случае, если отбросить лирику, безусловно, можно уйти от немедленной согласованности.

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

В вашем примере вы можете проверить уровни запасов, но, как уже отмечалось, одновременное чтение приводит к вводящей в заблуждение информации. Однако вы можете разместить заказ и еще до того, как вы начнете процесс, вы можете «зарезервировать» количество, используя то, что станет идентификатором вашего менеджера процесса и вашим общим «идентификатором корреляции». если вы не можете зарезервировать запас, вы можете отказаться от заказа. Однако вам придется зарезервировать несколько товаров для определенных заказов с более чем одним товаром.

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

Просто мысли :)

person Eben Roux    schedule 05.04.2017