Як створити новий агрегатний корінь у CQRS?


10

Як нам створити нові сукупні корені в архітектурі cqrs? У цьому прикладі я хочу створити новий сукупний корінь AR2, який має посилання на перший AR1.

Я створюю AR2, використовуючи метод AR1 як вихідну точку. Поки що я бачу кілька варіантів:

  1. Метод всередині AR1 createAr2RootOpt1я міг викликати new AR2()та зберегти цей об'єкт до db imediatelly, використовуючи доменний сервіс, який має доступ до сховища.
  2. Я міг би випустити подію в першому сукупному корені, наприклад. SholdCreateAR2Eventа потім мати сагу без громадянства, яка реагує на це і видає команду, CreateAR2Commandяка потім обробляється і фактично створює AR2 і випускає AR2CreatedEvent. У разі використання джерела події SholdCreateAR2Eventне зберігатимуться в магазині подій, оскільки це не впливає на стан першого кореневого кореня. (Або ми все-таки повинні зберігати це в магазині подій?)

    class AR1{
        Integer id;
        DomainService ds;
    
        //OPTION 1
        void createAr2RootOpt1(){
            AR2 ar2 = new AR2();
            ds.saveToRepo(ar2);
        }
    
        //OPTION 2
        void createAr2RootOpt2(){
            publishEvent(new SholdCreateAR2Event());    //we don't need this event. Shoud it still be preserved in event store?
        }
    }
    
    class AR2{
        Integer id;
        Integer ar1Id;
    
        void handle(CreateAR2Command command){
            //init this AR with values and save
            publishEvent(AR2CreatedEvent());    //used for projections afterwards and saved inside AR2 event store
        }
    }
    
    class Saga{
        void handle(SholdCreateAR2Event ev){
            emitCommand(new CreateAR2Command());
        }
    }
    

Який правильніший спосіб зробити це?

Відповіді:


2

Я думаю, що такого варіанту немає. 2 - це рішення з невеликою, але важливою модифікацією: AR1не повинно випромінювати подію, метою якої є створення AR2, натомість вона повинна випромінювати AR1WasCreatedподію. Цю подію слід зберігати у магазині подій, оскільки це важлива подія, що знаменує народження AR1. Потім Sagawhould listent для AR1WasCreatedподії і сформувати команду для створення AR2: CreateAR2Command.

Варіант №1 дуже неправильний. Ви ніколи не повинні вводити таку послугу домену в Aggregate. Aggregatesмає бути чистим, без побічних ефектів, інших, ніж покоління подій.

PS Я ніколи не випромінюю події від конструктора а, Aggregateоскільки існує різниця між створенням екземпляра об'єкта (в мові програмування) та створенням (народження, якщо ви хочете) Aggregate. Я випромінюю події лише з handleметоду (при обробці a command).


Що ви маєте на увазі під AR1WasCreated? Чи має бути AR2WasCreated? Крім того, якщо я використовую вашу логіку, я випускаю подію ще AR2WasCreatedдо того, як вона фактично створена? І збереження цієї події всередині журналу подій AR1 видається проблематичним, оскільки я фактично не потребую цих даних у AR1 (він нічого не змінює в AR1).
Боян Вукасович

Гаразд, через 3 роки. Він іде AR1WasCreated-> SAGA (має правило, якщо створено A1, тоді створити A2) -> CreateAR2Command-> AR2WasCreated.
Боян Вукасович

@BojanVukasovic Я радий, що це працювало так, як я писав :)
Константин Гальбену

2

Як нам створити нові сукупні корені в архітектурі cqrs?

Шаблони створення дивні .

Уді Дахан має сказати кілька корисних речей щодо загальної проблеми: Не створюйте корінних коренів . Основним моментом є те, що сукупність не просто з'являється з нізвідки, і що існує мова домену, яка описує, як вони з'являються, і яку слід зафіксувати у вашій моделі домену.

Там, де вона, як правило, перекручена, це те, що суб'єкт у вашій доменній моделі, що обробляє команду, не є об'єктом, що змінюється транзакцією. Це не неправильно; це просто дивно (порівняно з випадками, коли ви вимагаєте від організації змінити себе.

Ваш другий підхід також добре. "Події, які ми збираємо без фактичного збереження в базі даних", іноді називають "подіями домену"

Основна ідея полягає в тому, що в рамках однієї транзакції обробник команд піднімає подію, яка рухається по шині до обробника подій, що дозволяє другому агрегату створювати себе. Можливо, ви отримаєте дещо кращу згуртованість коду.

Примітка. У системах, що базуються на подіях, ви зазвичай не використовуєте події таким чином.

У разі використання джерела події ShouldCreateAR2Event не зберігатиметься у сховищі подій, оскільки це не впливає на стан першого кореневого корінця.

Примітка: імена подій зазвичай є в минулому часі - ShouldCrateAR2 має неправильне написання.

Так, якщо ви просто кидаєте подію на синхронну шину для запуску віддаленого коду, вам не слід зберігати цю подію в книзі записів. Це просто деталізація реалізації в такому масштабі.

Або ми все-таки повинні зберігати це в магазині подій?

Уникайте модифікації двох різних потоків подій в одній транзакції. Якщо це створення також являє собою зміну AR1, то звичайною відповіддю було б змінити AR1 в цій транзакції з асинхронним абонентом тих подій, який відповідає за виконання команди для створення AR2.

Тут допомагає безвідмовна робота з командами.


дякую за відповідь. Ще одне, що не на 100% зрозуміло - пізніше, якщо мені доведеться використовувати AR2 як аргумент AR1, як мені це передати - оскільки CQRS заявляє, що AR слід використовувати лише для запису, а не для запитів. Але у мене немає іншого варіанта, ніж використовувати, AR1.doSmthn(AR2 param)оскільки будь-яка створена проекція читання не має повних потрібних мені даних (лише AR2 має повні дані).
Боян Вукасович

> "Так, якщо ви просто кидаєте подію на синхронну шину для запуску віддаленого коду, вам не слід зберігати цю подію в книзі записів." Я думаю, що збереження цього має справжню цінність, оскільки ви знаєте, що процес було розпочато, щоб щось сталося, тепер ви також можете відслідковувати, наскільки це фактично завершено. Але я думаю, це залежить від випадку використання
Chaosekie
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.