#domain-driven-design #race-condition #cqrs #event-sourcing
#проектирование, управляемое доменом #условие гонки #cqrs #источник событий
Вопрос:
Я создаю приложение для управления медицинской клиникой.
Мы находим случай состояния гонки, когда назначена встреча, и до сих пор ни один из членов команды не пришел к решению.
Когда назначена встреча, необходимо проверить некоторые бизнес-правила:
- не может быть назначено на то же время, что и другое, с тем же врачом или тем же пациентом.
- врачи могут посещать только N приемов в месяц
- в течение недели врачи могут посещать только N приемов
Итак, первый подход, который мы считаем, заключается в создании агрегата, который будет содержать все встречи, ответственный за их планирование, но этот агрегат будет огромным и технически неприемлемым.
Второй подход, и текущий, заключается в создании назначения в качестве совокупного корня, а затем его проверке с помощью доменной службы (интерфейс на уровне домена и реализация на уровне инфраструктуры), которая запрашивает сторону чтения.
Сегодня он выглядит так:
-
Внутри обработчика команд создайте экземпляр нового назначения, передав доменную службу в ее конструкторе
-
Назначение вызывает службу домена, которая запрашивает сторону чтения и проверяет правила. Однако здесь могут возникать условия гонки (одновременно назначаются две встречи, поскольку они не видят друг друга, будут созданы обе).
-
Если служба домена проверяет правила, то назначение создается, но со статусом ОЖИДАНИЕ, и запускается событие домена AppointmentRequested.
-
На стороне чтения это событие было подписано, и проекция вставляется в базу данных чтения (статус = ОЖИДАНИЕ). В той же транзакции команда CompleteAppointmentSchedule вставляется в мой почтовый ящик и вскоре отправляется и принимается асинхронно стороной записи.
-
сторона записи обрабатывает назначение вызова команды.CompleteSchedule(DomainService). Та же служба домена, переданная при создании экземпляра нового назначения, снова передается назначению. Но теперь назначение уже будет в базе данных для чтения, и можно будет проверить бизнес-правила.
Правильно ли использовать сторону чтения таким образом? Мы не можем придумать другой способ проверки этих правил без использования стороны чтения. Член команды предположил, что мы могли бы создать частную сторону чтения для нашей стороны записи и использовать ее вместо стороны чтения в этих случаях, но, поскольку мы используем EventStore DB, нам пришлось бы создать другую базу данных, подобную той, которую мы используем на стороне чтения (pgsql)чтобы иметь возможность делать это таким образом на этой частной стороне чтения.
Комментарии:
1. Вы можете использовать SAGA для управления этим. Опубликованы два события, созданные назначением, затем вам нужно обновить сторону чтения, но на стороне чтения у вас есть уникальные ограничения, поэтому одно из них невозможно сохранить, затем опубликуйте другое событие для отмены второго события, созданного назначением
Ответ №1:
Я создаю приложение для управления медицинской клиникой.
Зарезервируйте офис, соберите всю команду и посмотрите Trench Talk: разработка модели. Ив Рейнхаут занимается (и говорит о) дизайном, управляемым доменом, и его область — планирование встреч для здравоохранения.
Когда назначена встреча, необходимо проверить некоторые бизнес-правила:
- не может быть запланирован на то же время, что и другой, с тем же врачом или тем же пациентом
- врачи могут посещать только N встреч в месяц через неделю,
- врачи могут посещать только N назначений
Одна из вещей, которые вам нужно обсудить с экспертами вашего домена; нужно ли предотвращать конфликты планирования или вам нужно выявлять конфликты планирования и разрешать их?
Рекомендуемое чтение:
- Условий гонки не существует — Уди Дахан, 2010
- Воспоминания, догадки и извинения — Пэт Хелланд, 2007
Тем не менее, вы действительно близки к общему ответу.
Вы выполняете свои проверки по кэшированной копии календаря, чтобы избежать наиболее распространенных коллизий (обратите внимание, что все еще существует условие гонки, когда вы проверяете расписание в то же время, когда кто-то другой пытается отменить конфликтующую встречу). Затем вы помещаете сообщение с запросом на встречу в очередь.
Подписка на очередь — это услуга как в SOA, которая является техническим полномочием для всей информации, связанной с планированием. Эта служба имеет свою собственную базу данных и проверяет свою собственную достоверную копию всего, прежде чем вносить изменения.
Критическое отличие здесь заключается в том, что когда служба работает напрямую с заблокированными экземплярами данных. Это может быть связано с тем, что обработчик событий в службе является единственным процессом, который имеет разрешения на запись достоверных данных (и сам обрабатывает только одно сообщение за раз), или это может быть связано с тем, что обработчик событий блокирует все данные, необходимые для обеспечения согласованности результата записис бизнес-правилами (конфликтующие записи конкурируют за одну и ту же блокировку, что гарантирует контроль изменений данных).
По сути, все попытки изменить достоверные данные календаря (логически) сериализуются, чтобы гарантировать, что записи не могут конфликтовать друг с другом.
На языке CQRS вся эта блокировка происходит в модели записи службы календаря. Все остальные работают с разблокированными копиями данных, которые предоставляются моделью чтения (с некоторыми скромными изменениями, связанными с копированием изменений данных из модели записи в модель чтения).