2017-02-09 10 views
0

Итак, я запускаю команду на агрегированном корне, и в результате этой команды произошло 10 событий. Эти события являются внутренними, и поскольку внешние системы нуждаются в агрегации этих событий, я решил сделать проекцию (в основном, прочесть прогноз). Чтобы сделать этот прогноз из 10 событий (внутренних) в 1 событие (внешнее), я должен применить некоторые бизнес-правила (бизнес-правила, касающиеся слияния событий). Где я должен помещать эти правила, поскольку он кажется частью домена, но я создаю прогнозы внутренних событий?Прогнозирование модели CQRS - бизнес-логика

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

ОБНОВЛЕНИЕ

Так, внутри одного агрегатного корня, у меня есть, например, 3 события (внутренние) в качестве ответа на одну команду (aggregate.createPaintandwashatsametime (id, red)), которая отправляется агрегируемому корню и распространяется через все агрегированные корневые объекты, такие как CarCreated (Id), CarSeatColored (Red), CarWashed () и т. д. (все эти 3 события происходят из-за одиночной команды). Внешняя система ожидает получить одно внешнее событие в качестве CarMaintainenceDone (Id, repaintted = true, wash = true, somevalue = 22);

Теперь, если у меня есть какая-то сложная логика, чтобы сделать это CarMaintainenceDone события (например, если (цвет == красный, то в проекции SomeValue == 22 в противном случае 44) - это должно идти в проекции кодах или быть частью домена

UPDATE 2

Позвольте мне дать вам новый пример просто игнорировать, как моделируется домен, так как это просто пример:.

как вы можете видеть, что мы имеем AggregateRoot, который содержит умножитель, который существует только называть вещи правильным именем. Когда мы делаем умножение, мы сначала отправьте целое число 1 в ObjectA, у которого есть некоторая логика, чтобы установить внутреннее состояние и испустить событие ObjectAHasSetParam. То же самое происходит с ObjectB. Наконец, ObjectC прослушивает все эти события, а на paramsHasBeenSet будет выполнять фактическое умножение.

В магазине событий в этом случае я бы сохранить список событий:

[ObjectAHasSetParam , ObjectBHasSetParam , ObjectCHasMultiplied ] 

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

class AggregateRoot{ 
    Multiplier ml; 

    void handle(MultiplyCommand(1,2)){ 
     ml.multiply(1,2); 
    } 
} 

class Multiplier{ 
    ObjectA a; 
    ObjectB b; 
    ObjectC res; 

    void multiply(1,2){ 
     a.setParam(1); 
     b.setParam(2); 
     publish(paramsHaveBeenSet()); 
    } 
} 

class ObjectA{ 
    int p; 

    void setParam(1){ 
     p = 1 + 11; 
     publish(ObjectAHasSetParam(12)); 
    } 
} 

class ObjectB{ 
    int p; 

    void setParam(2){ 
     p = 2 + 22; 
     publish(ObjectBHasSetParam(24)); 
    } 
} 

class ObjectC{ 
    int p1; int p2; 
    int res; 

    listen(ObjectAHasSetParam e1){ 
     p1 = e1.par; 
    } 

    listen(ObjectBHasSetParam e2){ 
     p2 = e2.par; 
    } 

    listen(paramsHaveBeenSet e3){ 
     res = p1 * p2; 
     publish(ObjectCHasMultiplied(288)); 
    } 
} 
+0

Это странная проблема; вы должны предоставить больше информации о контексте; что происходит, что вы объединяете, и так далее. – VoiceOfUnreason

+0

Что такое «событие внешнего домена» ?! –

+0

Что происходит после события 'CarMaintainenceDone'? Использует ли это 'Car' Aggregate это событие, чтобы изменить его поведение для будущих команд, которые он получает? –

ответ

0

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

Так, в вашем случае потребитель (реализованный как сага, например) должен объединить эти события по своим бизнес-правилам, а затем сделать что-то (например, сага может создать новую команду), а не Агрегат.

UPDATE (в ответ на вопрос обновляется)

Если вы считаете, что обслуживание автомобиля является обязанностью Car агрегата, то Car агрегат должен поднять событие. Это зависит от того, как влияет на поведение Car Aggregate на это событие CarMaintainenceDone. В этом конкретном контексте я бы сгенерировал событие из агрегата Car, чтобы сделать код более простым.

+0

Это просто, что CarMaintainenceDone на самом деле является проекцией этих трех событий. Итак, вы в основном говорите, что я должен также поднять это событие CarMaintainenceDone из одного и того же агрегата (Car), хотя это только проекция этих трех событий? Поскольку я также использую источник событий, эти 3 события будут храниться в хранилище событий. CarMaintainenceDone в данном случае - это просто событие, которое должно быть передано внешним потребителям. – bojanv55

+0

«Не должно быть понятия внешнего события» Это неверно. См. Мой ответ. – plalx

+0

@plalx С моей точки зрения, все события сохраняются в хранилище событий. Затем эти события публикуются во внешний мир любому потребителю, который их хочет. Интересна ваша идее с агрегатором событий. Эти «агрегированные события» сохраняются в хранилище событий? –

0

Часто бывает 2 модели событий, внутренние события (только видимые внутри BC) и внешние события (публикуются во внешнем мире). Вы могли бы решить сделать все по-внешнему, но тогда вам нужно все переделать.

Подробнее о внутренних и внешних событиях можно узнать в Patterns, Principles, and Practices of Domain-Driven Design book p.408 (прокрутите немного по ссылке).

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

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

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

Альтернативой было бы позволить потребителю разобраться с агрегацией. Это может быть правильным подходом, если потребитель сможет наложить вето на то, что означает «CarMaintenanceDone».

Наконец, вы также можете опубликовать дополнительное событие из самого агрегата. Событие не может быть использовано самим АР, но иногда лучше просто делать то, что более практично (так же, как обогащение событий данными, потребляемыми только моделью чтения). Такой подход также имел бы преимущество в том, что не нужно было менять логику, если добавлено больше событий.

+0

Yup.Это стало менее понятным. Просто, если я использую этот внешний процесс, как вы упомянули в последней части, я фактически создаю проекцию событий в это новое событие для внешних систем. Также кажется, что вы должны внести часть бизнес-логики в поколение Q-части CQRS. – bojanv55

+0

Как «событие публикации агрегации событий» работает для перестройки проекции? Я думаю, что правильный метод для этой агрегации событий будет с сагой, создающей команду –

+0

@ bojanv55 Я бы не назвал ее проекцией. Это процессор событий, который преобразует внутренний поток событий во внешний поток событий. У этого есть другая цель, чем проекция и не будет считаться частью Q. – plalx

1

Внешняя система ожидает получить одно внешнее событие в качестве CarMaintainenceDone (Id, redainted = true, wash = true, somevalue = 22);

A ha! Короткий ответ - диспетчер процессов.

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

Большой намек на то, что где-то есть еще одна совокупность: у вас есть это событие CarMaintenanceDone, без агрегата, ответственного за его создание. Все события имеют «совокупность» где-то, что их производит.Агрегатом может быть реальный мир или прокси для реального мира (HttpRequestReceived) или цифровая вещь в каком-то другом ограниченном контексте; но событие говорит вам, что что-то, где-то, изменило состояние.

То есть у вас есть своя совокупность, которая знает правило о том, когда выполняется техническое обслуживание. Это информационный ресурс, журнал работы. Когда CarWashed публикуется (автомобилем или стиральной машиной или что-то еще), обработчик событий, подписанный на событие CarWashed, отправляет команду агрегированию Maintenance для его информирования. Агрегат Maintenance обновляет свое собственное состояние, запускает его логику и публикует событие MaintenanceCompleted, когда все индивидуальные шаги были учтены.

Большинство вещей, которые являются такими процессами, могут быть реализованы как Агрегаты; странный бит заключается в том, что «команды» имеют тенденцию выглядеть как обработчики событий. Но у них есть своя история (основанная на том, что они наблюдали), которая описывает, как изменилась государственная машина в ответ на каждое наблюдаемое событие.

Это может быть более двух, в зависимости от сложности процессов.

Ринат Абдуллин написал хорошее introduction to process managers, что я часто упоминаю.

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

От того, что я видел - нет, нет. Литература не делает этого очень ясным.

Например, Udi Dahan wrote

Вот самый сильный признак я могу дать вам знать, что вы делаете CQRS правильно: Ваши агрегатные корни сказания.

Сага, здесь, будучи эквивалентной процессу.

+0

Это интересная идея. Разве нет четкого различия между агрегатом и менеджером процессов? Я думал, что менеджеры процессов будут только координировать и жить в мире сервисов приложений, отправляя соответствующие команды на агрегаты на основе полученных событий. – plalx

+0

Я бы увидел больше обработчика событий, который вызывает агрегат (например, обработчик «CarWashed») в качестве диспетчера процессов, нет? – plalx

+0

Я планирую более подробно писать в своем блоге; Я буду ссылаться на него здесь, когда будет опубликовано. – VoiceOfUnreason

Смежные вопросы