Существует ли общепринятый способ обработки событий, содержащих идентификатор другого агрегата?

Допустим, для клиента был создан счет-фактура. Это конкретное событие будет выглядеть примерно так:

invoice.raised {
   "id": "4dbcff82-6f35-4155-9aec-f8185c1f932f",
   "total": "50.00",
   "description": "Order 01133",
   "customer_id": "c2206843-414d-454f-9894-57c6b11b9c00"
}

Это довольно простой пример, и он относится к совокупности клиентов, к которой принадлежит счет.

Сложность проявляется, когда я хочу создать представление «Счета» и хочу вставить имя клиента в счет. Я мог бы сделать две вещи: добавить в исходное событие имя клиента или загрузить представление «Клиенты», чтобы узнать, какое это имя, когда я создаю представление «Счета». Теперь это не очень сложный пример, но в некоторых случаях становится почти неуправляемым обогащать события, поскольку я получаю множество свойств различных агрегатов, скопированных в событие, которое очень специфично для какого-то другого агрегата.

Есть ли общепринятый способ справиться с этим, помимо обогащения событий? Поскольку теперь каждый раз, когда возникает событие invoice.cancelled, мне также придется снова включать сумму счета, чтобы я мог, например, обновить представление «Клиенты», указав новый баланс.


person smokeybear    schedule 08.09.2016    source источник


Ответы (2)


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

Правильно, это делает ваши мероприятия менее управляемыми.

Итак, я бы сказал, что достаточно иметь только customer_id и запрашивать имя клиента при построении модели чтения:

  • Это может быть во время проектирования, когда строится денормализованное представление.
  • Это может быть во время запроса, когда результирующая модель представления строится на основе счетов и клиентов (для этого варианта использования у вас может быть специальный список клиентов только с customer_id и customer_name)

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

Я полагаю, что событие invoice.cancelled инициируется корнем агрегата Invoice, и ему принадлежит amount, поэтому естественно поместить amount в событие invoice.cancelled.

Событие invoice.raised также запускается корневым каталогом агрегации счета-фактуры, но ему не принадлежит имя клиента. В любом случае он не может проверить, соответствует ли имя клиента в определенный момент времени. Вот почему вместо встраивания имени клиента вы можете просто запросить его при построении модели чтения.

Кстати, есть хорошая статья о разработке событий — 6 Code Запахи с вашими событиями CQRS — и как их избежать

person Ilya Palkin    schedule 08.09.2016

Сложность проявляется, когда я хочу создать представление «Счета» и хочу вставить имя клиента в счет.

Есть ли общепринятый способ справиться с этим, помимо обогащения событий?

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

Методы композиции пользовательского интерфейса могут указать вам полезное направление. О них неоднократно писал Уди Дахан. Некоторые примеры

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

InvoiceView (e : invoice.raised) {
    InvoiceWidget(e.id)
    CustomerWidget(e.customer_id)
}
person VoiceOfUnreason    schedule 08.09.2016