Ответ 1
Если я правильно понимаю ваш дизайн-рисунок, тогда команды, связанные с временным подключением пользователей, то есть запросы на изменение, и когда пользователь повторно соединяет команды в очереди, отправляются вместе; существует только один орган базы данных (который обработчики команд запрашивают для загрузки самых последних версий своих агрегитов); для клиентов синхронизируется только модель представления.
В этой настройке Сценарий 2 трижды автоматически объединяется по вашему дизайну, если вы правильно выбираете свои команды, читайте: сделайте их мелкозернистыми: для всех возможных измените, выберите одну команду. Затем, при повторном соединении клиента, команды обрабатываются в любом порядке, но поскольку они влияют только на поля disjunct, нет проблем:
- Клиент находится на уровне v20.
- A отключен, редактирует изменения против устаревшей модели v20.
- B отключен, редактирует изменения в сравнении с устаревшей моделью v20.
- A выходит онлайн, пакет отправляет команду в очередь
ChangeName
, Клиент v20 загружается и сохраняется как v21. - B выходит онлайн, пакет отправляет команду в очередь
ChangeAddress
, клиент v21 загружается и сохраняется как v22. - В базе данных есть пользователь с правильным именем и адресом, как и ожидалось.
В Сценарий 1 с этой настройкой оба сотрудника перезапишут изменения других сотрудников:
- Клиент находится на уровне v20.
- A отключен, редактирует изменения против устаревшей модели v20.
- B отключен, редактирует изменения в сравнении с устаревшей моделью v20.
- A отправляется онлайн, пакет отправляет команду queue
ChangeName
в "John Doe" , клиент v20 загружается и сохраняется как v21 с именем "John Doe" - B выходит онлайн, пакет отправляет команду queue
ChangeName
в "Joan d'Arc" , клиент v21 (с именем "John Doe" ) загружается и сохраняется как v22 (с именем "Joan d'Arc" ). - База данных содержит пользователя с именем "Joan d'Arc" .
Если B входит в сеть до A, то это наоборот:
- Клиент находится на уровне v20.
- A отключен, редактирует изменения против устаревшей модели v20.
- B отключен, редактирует изменения в сравнении с устаревшей моделью v20.
- B выходит онлайн, пакет отправляет команду queue
ChangeName
в "Joan d'Arc" , клиент v20 загружается и сохраняется как v21 (с именем "Joan d'Arc" ). - A приходит в сеть, пакет отправляет команду queue
ChangeName
в "John Doe" , клиент v21 загружается и сохраняется как v22 с именем "John Doe" . - База данных содержит пользователя с именем "John Doe" .
Существует два способа разрешения обнаружения конфликтов:
- Проверьте, не указана ли дата создания команды (т.е. время изменения сотрудников) после последней даты изменения
Customer
. Это отключит функцию автоматического слияния сценария 2, но даст вам полное обнаружение конфликтов против одновременных изменений. - Проверьте, не изменилась ли дата создания команды (т.е. время изменения сотрудников) после последней даты изменения отдельного поля
Customer
. Это оставит автоматическое слияние сценария 2 без изменений, но даст вам автоматическое обнаружение конфликтов в сценарии 1.
Оба легко реализуются с использованием источников событий (поскольку, вероятно, известны временные метки отдельных событий в потоке событий).
Что касается вашего вопроса "Кто изменения, мы сохраняем в сценарии 1?" - это зависит от вашего бизнес-домена и его требований.
EDIT-1: ответить на вопрос о разъяснении:
Да, вам понадобится одна команда для каждого поля (или группы полей, соответственно), которые могут быть изменены индивидуально.
Что касается вашего макета: то, что вы показываете, является типичным пользовательским интерфейсом "CRUD", то есть несколькими полями формы и, например, одной кнопкой "Сохранить". CQRS обычно и естественно сочетается с "пользовательским интерфейсом на основе задачи", где будет отображаться поле Status
(только для чтения), и если пользователь хочет изменить статус, один щелчок, скажем, Кнопка "Изменить статус", которая открывает диалоговое окно/новое окно или другой элемент пользовательского интерфейса, в котором можно изменить статус (в веб-системах также возможно редактирование на месте). Если вы работаете с пользовательским интерфейсом на основе задачи, где каждая задача влияет только на небольшое подмножество всех полей, то мелкозернистые команды для ChangeName, ChangeSupplier и т.д. Естественны.