
Sergey
27.12.2017
08:10:35
так что если у тебя логика чувствительна к порядку возникновения доменных ивентов - то сингелтон может быть разумным вариантом.
если не чувствительна - у тебя больше вариантов
но зашквара я как не видел так и не вижу

Bohdan
27.12.2017
08:11:00
(ивент рекооордер)

Google

Maksim
27.12.2017
08:11:10
если асинхронная, толку от синглтона чуть меньше 0)

Sergey
27.12.2017
08:11:15

Bohdan
27.12.2017
08:11:30
тут да, пофиг как называть

Sergey
27.12.2017
08:11:54
у тебя могут несколько логических транзакций паралельно идти но тут уже сагами надо обмазываться, это совсем другая история

Maksim
27.12.2017
08:13:02
да нету никакого профита, если честно)
у тебя в сущности всё равно события по очереди идут) как зарайзил, так и отправишь)
либо засейвишь

Sergey
27.12.2017
08:13:11
то есть все упирается в то как у тебя сущности организованы, дробишь ты на конексты или нет, и что тебе нужно от доменных ивентов. Если email-ы отправлять - то вообще плевать как делать
и по сути только он их должен генерить, но... жизнь не всегда так сладка)

Maksim
27.12.2017
08:15:24

Sergey
27.12.2017
08:16:17
event_stream (aggregate id) -> one-to-many -> events
да да, а что делать если у тебя в пределах одной бизнес транзакции несколько логических в разных контекстах, причем все не настолько плохо что надо сагами обмазываться. и вот у тебя уже будет больше одного агрегата задействовано

Maksim
27.12.2017
08:17:23
у меня каждое событие в разном контексте.
и если что-то пошло не так, то на совести программиста что с этим делать. Так как уж обмазались сагами, пускай сагами и исправляют

Google

Sergey
27.12.2017
08:17:51

Maksim
27.12.2017
08:17:57
исправим)
всех по колено в саги посадим)


Roman
27.12.2017
08:18:09
обработчики доменных событий - это что? не является ли это тоже инфраструктурой?
Ну вот давай пример с юзером. Сверху вниз.
Прилетает POST-запрос на /api/v1/users
Запрос суть DTO. Далее эта DTO через автомаппер преобразуется в команду "Создать юзера".
Эта команда улетает в диспетчер команд. Диспетчер, внутри себя, через какую-нить фабрику (которая скроет работу с IoC контейнером) - срезолвит обработчик (1 команда - 1 обработчик) и вызовет какой-нить метод Handle, в который передаст команду.
Обработчику в конструктор приходят:
- контекст бд
- сервис юзеров
Через контекст БД открывается транзакция, через сервис создаётся юзер. По факту создания юзера, из сервиса юзеров возвращается ивент "Создан юзер". В контексте БД сохраняются изменения (но транзакция не коммитится). Если изменения сохранились - то вызывается диспетчер доменных ивентов, куда передаётся ивент "Юзер создан". Далее, диспетчер находит обработчики (1 ивент - много обработчиков) доменного ивента "Юзер создан" и вызывает их. В обработчиках происходит создание инфраструктурных событий, например, формирование имейлов для нотификации. Т.е. ивент "Юзер создан" превращается в команду "Отправить имейл с текстом таким-то туда-то". Сами обработчики эти инфраструктурные команды отправляют в шину. Если все обработчики успешно отработали - происходит коммит транзакции и возврат созданного юзера из команды, который вернётся в ответе на POST-запрос.
Параллельно с этим пачка инфраструктурных сервисов разгребает очередь ивентов в рэбите и делает нотификации.


Maksim
27.12.2017
08:19:36


Sergey
27.12.2017
08:22:15
Ну вот давай пример с юзером. Сверху вниз.
Прилетает POST-запрос на /api/v1/users
Запрос суть DTO. Далее эта DTO через автомаппер преобразуется в команду "Создать юзера".
Эта команда улетает в диспетчер команд. Диспетчер, внутри себя, через какую-нить фабрику (которая скроет работу с IoC контейнером) - срезолвит обработчик (1 команда - 1 обработчик) и вызовет какой-нить метод Handle, в который передаст команду.
Обработчику в конструктор приходят:
- контекст бд
- сервис юзеров
Через контекст БД открывается транзакция, через сервис создаётся юзер. По факту создания юзера, из сервиса юзеров возвращается ивент "Создан юзер". В контексте БД сохраняются изменения (но транзакция не коммитится). Если изменения сохранились - то вызывается диспетчер доменных ивентов, куда передаётся ивент "Юзер создан". Далее, диспетчер находит обработчики (1 ивент - много обработчиков) доменного ивента "Юзер создан" и вызывает их. В обработчиках происходит создание инфраструктурных событий, например, формирование имейлов для нотификации. Т.е. ивент "Юзер создан" превращается в команду "Отправить имейл с текстом таким-то туда-то". Сами обработчики эти инфраструктурные команды отправляют в шину. Если все обработчики успешно отработали - происходит коммит транзакции и возврат созданного юзера из команды, который вернётся в ответе на POST-запрос.
Параллельно с этим пачка инфраструктурных сервисов разгребает очередь ивентов в рэбите и делает нотификации.
> из сервиса юзеров возвращается ивент "Создан юзер"
прям вот сервис вернет тебе событие? не сущность?
> В обработчиках происходит создание инфраструктурных событий
зачем так сложно? Просто pub/sub и нужные штуки уже пусть обрабатывают.
> Сами обработчики эти инфраструктурные команды отправляют в шину.
зачем если они просто могут метод вызвать? или ты думаешь что наличие шины волшебным образом связанность уменьшит? Оно только сделает это неявным.
> Если все обработчики успешно отработали - происходит коммит транзакции
то есть транзакция не считается успешной если письмо небыло отправлено? а если было отправлено но зависло в баунсед на smtp сервере?
если у тебя выкинулось доменное событие на обработку - это уже факт, то есть все, финито ля транзакция. Все остальное - это уже второстепенные действия которые не влияют на предыдущую.
если тебе надо что бы влияло - добро пожаловать в клуб Maksim любителей саг


Bohdan
27.12.2017
08:24:19
если ты хочешь добиться обязательной отправки всех сообщений (в этом примере) - это нужно делать иначе

Roman
27.12.2017
08:24:34
> то есть транзакция не считается успешной если письмо небыло отправлено? а если было отправлено но зависло в баунсед на smtp сервере?
нет. меня волнует факт порождения инфраструктурных событий и отправку их в рэбит.
А вот если там smtp будет тупить или телега упадёт - меня уже волновать не будет. Это проблемы инфраструктуры.


Sergey
27.12.2017
08:25:29

Roman
27.12.2017
08:25:55

Sergey
27.12.2017
08:26:00
> нет. меня волнует факт порождения инфраструктурных событий и отправку их в рэбит.
почему бы в рэббит не кинуть доменный ивент? Что за инфраструктурые события? зачем они?

Roman
27.12.2017
08:26:30

Sergey
27.12.2017
08:26:30

Maksim
27.12.2017
08:26:45

Sergey
27.12.2017
08:26:48
короч.... тут проблема не с доменными событиями

Google

Roman
27.12.2017
08:27:04

Sergey
27.12.2017
08:27:32

Maksim
27.12.2017
08:27:32
и чё?)

Roman
27.12.2017
08:27:49
Инфраструктурные сервисы.
Я вот такое только придумал.

Sergey
27.12.2017
08:28:31
но связанность то не исчезла - кто-то теперь должен знать о инфраструктуре и к какому сервису это будет относиться?

Maksim
27.12.2017
08:28:35
нету никаких инфраструктурных сервисов, событий и команд

Sergey
27.12.2017
08:28:54
или у тебя 10 сервисов будут знать о сервисе нотификаций?)
короч - микросервисы это про связанность а то что ты описываешь это какой-то трэш
короч... https://www.youtube.com/watch?v=Fuac__g928E
пойду работать а вам вот видяшки

Roman
27.12.2017
08:29:44

Sergey
27.12.2017
08:30:34
Почему трэш то?
вот есть у тебя ну не 10, пускай 5 сервисов. Каждый генерит какие-то доменные ивенты на которые надо отправлять email-ы. для имейлов у тебя есть "инфраструктурные события". кто и где будет преобразовывать доменное событие в инфраструктурное?

Roman
27.12.2017
08:30:58

Sergey
27.12.2017
08:31:11
все в каше

Roman
27.12.2017
08:31:18
Я же писал в длиннопосте
Появилось событие "Создан юзер". Оно бросается в диспетчер, который хэндлит обрботчики этого события. Эти обработчики создают команды для инфраструктуры и пуляют их в рэбит. Если не пульнулось - не создалось - рэбит лёг - качу транзакцию обратно.

Sergey
27.12.2017
08:33:02
1. получили команду - нашли обработчик
2. обработчик попросил сервис юзеров что-то сделать на что тот вернул результат (то что ты называешь "событием"). Какой-то факт. что он вернет в случае неудачи? Чем это отличается от "вернуть юзера"?
3. обработчик взял этот "доменный ивент" (который оным по факту не является) и сгенерил "инфраструктурные события" - это не события скорее а команды, то есть описание что сделать и данные для этого. так?

Google

Sergey
27.12.2017
08:33:31
типа ProductPurchased -> [SendProductPurchasedNotification]`
по факту "доменный ивент" тут не особо нужен

Roman
27.12.2017
08:33:58

Sergey
27.12.2017
08:34:09
точно так же как нет разницы между "инфраструктрным" событием и просто RPC вызовом

Roman
27.12.2017
08:34:18

Sergey
27.12.2017
08:34:29
все сервисы которые генерият "инфраструктурные события" по факту неявно знают о инфраструктуре

Roman
27.12.2017
08:34:31

Sergey
27.12.2017
08:34:33
связанность системы повышается
добро пожаловать в ад через пол годика

Roman
27.12.2017
08:35:20
кролик умеет RPC)
Я не хочу дожидаться отправки имейла. Я хочу кинуть в кролик команду и работать дальше.

Sergey
27.12.2017
08:35:22
проблема со связанностью - у тебя с таким подходом она выходит дико огромной и в целом во всем этом нет смысла

Roman
27.12.2017
08:35:38

Sergey
27.12.2017
08:35:41

Roman
27.12.2017
08:35:59
Получается инфраструктура должна знать о доменных ивентах?

Sergey
27.12.2017
08:36:01
RPC не означает что ты должен ждать результат

Roman
27.12.2017
08:36:25

Sergey
27.12.2017
08:36:47
короч... посмотри видосик) тебе понравится)

Google

Roman
27.12.2017
08:37:44

Sergey
27.12.2017
08:38:18

Roman
27.12.2017
08:38:24
Тут короче 2 варианта - либо инфраструктура знает про домен, либо домен про инфраструктуру)

Sergey
27.12.2017
08:38:45
инфраструктура знает о тригерах - это норм. Домен знает о инфраструктуре - это не ок

Roman
27.12.2017
08:39:04

Sergey
27.12.2017
08:39:10
вся проблема в том что твои "доменные события" это не доменные события

Roman
27.12.2017
08:39:36

Sergey
27.12.2017
08:39:46
А Ъ доменные события тогда это как?
1. обработчик попросил сервис создать юзера
2. юзер в контроллере запомнил событие что "я жив"
3. кто-то кто знает что транзакция завершилась возьмет у сущност и события и пробросит их в кролик. Это может и хэндлер делать но это не удобно

Maksim
27.12.2017
08:42:06

Sergey
27.12.2017
08:42:43
потому янг (или уди, не помню кто из них) и топили за сингелтон EventStore так как в этом случае у тебя при выходе из обработчика команды можно:
- закоммитить транзакцию
- пробросить все события в кролик
доменное событие может быть выкинуто в кролик ТОЛЬКО когда транзакция завершена и доменное событие становится фактом
ай не, работать

Maksim
27.12.2017
08:44:35
асунковый эвент соурсинг кафно)
я не могу придумать как красиво блокировки и согласованность выстроить :( уже месяц возни, а работающей схемы даже на горизонте нету :(

Roman
27.12.2017
14:26:39
Я тут почитал немного. Сага это грубо говоря - обработчик, начинающийся с определённого доменного события, описывающий бизнес-процесс что ли?
Или неправильно понял?

Aleh
27.12.2017
14:27:56

Roman
27.12.2017
14:28:25