Я начинаю с DDD и понимаю, что совокупные корни используются для обеспечения транснациональной согласованности. Мы не должны изменять несколько агрегатов в одном сервисе приложений.
Однако я хотел бы знать, как справиться со следующей ситуацией.
У меня есть совокупный корень под названием Продукты.
Существует также совокупный корень, называемый группой.
Оба имеют идентификаторы и могут редактироваться независимо.
Несколько продуктов могут указывать на одну и ту же группу.
У меня есть служба приложений, которая может изменить группу продуктов:
ProductService.ChangeProductGroup(string productId, string groupId)
- Проверка группы существует
- Получить продукт из хранилища
- Установить свою группу
- Написать продукт обратно в хранилище
У меня также есть служба приложений, где группа может быть удалена:
GroupService.DeleteGroup(string groupId)
1. Получить продукты из репозитория, для которого groupId установлен на предоставленный groupId, убедиться, что счетчик равен 0 или прервать 2. Удалить группу из репозитория групп 3. Сохранить изменения
Мой вопрос следующий сценарий, что произойдет, если:
В ProductService.ChangeProductGroup мы проверяем, что группа существует (она существует), затем сразу после этой проверки отдельный пользователь удаляет группу ProductGroup (через другой GroupService.DeleteGroup). В этом случае мы устанавливаем ссылку на продукт, который был только что удален?
Является ли это недостатком моего дизайна в том, что я должен использовать другой дизайн домена (при необходимости добавляя дополнительные элементы), или мне придется использовать транзакции?
источник
Почему вы не храните идентификаторы продуктов в группе? Таким образом, вы имеете дело только с одним агрегатом, который облегчит задачу.
Затем вам нужно будет реализовать некоторый тип шаблона параллелизма, например, если вы выбираете оптимистичный параллелизм, просто добавьте свойство версии к объекту группы и сгенерируйте исключение, если версии не совпадают при обновлении, т.е.
Добавить товар в группу
Многие ORM имеют оптимистичный параллелизм, встроенный с использованием идентификаторов версий или временных меток, но вы легко можете сделать свою собственную. Вот хороший пост о том, как это делается в Nhibernate http://ayende.com/blog/3946/nhibernate-mapping-concurrency .
источник
Хотя транзакции могут помочь, есть и другой способ, особенно если ваш сценарий ограничен несколькими случаями.
Вы можете включить проверки в запросы, которые выполняют мутации, эффективно превращая каждую пару check-and-mutate в атомарную операцию.
Внутренний запрос на обновление продукта присоединяется к (вновь ссылающейся) группе. Это заставляет его ничего не обновлять, если эта группа исчезла.
Запрос на удаление группы объединяет любые продукты, указывающие на него, и имеет дополнительное условие, что (любой столбец) результат объединения должен быть нулевым. Это приводит к тому, что он ничего не удаляет, если на него указывают какие-либо продукты.
Запросы возвращают количество строк, которые были сопоставлены или были обновлены. Если результат равен 0, то вы проиграли условие гонки и ничего не сделали. Он может выдать исключение, и прикладной уровень может обработать то, что он хочет, например, повторив попытку с самого начала.
источник