лежит наверху потом идут модули, из UI мы дергаем необходмые модули:
Банально пришел заказ, мы должны создать заказ, потом проверить что товар все еще есть в наличии, потом проверить что цена не изменилась после отправки и если все ок регнуть заказ.
И обычно я это делал в контроллере, вызавал все необходимые сервисы и все ок.
Но модульный монолит предлагает это перенести в события, преимущества
А какие преимущества взаимодествия через события?
Я вижу только одно что мы можем вписывать туда какие то обработки заказа и не переживать что их надо где то вызвать.
не обязательно использовать события в модульном монолите
Я уже плохо себе представляю как без событий если честно
Декорацией через контейнер)
мне кажется, что мухи отдельно, котлеты отдельно. особенно если речь об обучении, а не о реальном проекте. иначе потом появляются такие выводы "модульный монолит предлагает это перенести в события". хотя мы и в обычном монолите можем многое перенести в события, и в модульном монолите не использовать события совсем
события это внутреннее общение модулей, создалась страница, кидаем событие, все модули узнают, что появилась страница и могут как то проработать ее, например проставить теги через нейронку, типографию поправить и т.д.
Преимущества очередей с событиями в транзакционности и терпимости ко временным проблемам. Если модули делают только изменения в БД, то несколько вызовов в контроллере можно обернуть в транзакцию БД. Если где-то вылетит ошибка, то внешняя транзакция откатится и всё отменится. Но в реальной жизни модули помимо работы с БД начинают отправлять письма или дёргать по API сторонние сервисы. Например, при успешной оплате заказа надо сходить по API в платёжную систему, отправить письмо, отправить кассовый чек по API кассы, добавить клиента в рассылку по API сервиса рассылок, отправить заказ в API сервиса доставки и т.п. Тогда есть большой риск, что в момент дёрганья этой кучи модулей из контроллера какой-то сторонний сервис не будет доступен. И тогда непонятно, что с этим делать прямо здесь в контроллере. Другое дело когда есть очереди. Если сервис пока недоступен и через минуту отвиснет, то из очереди задача повторно свою работу доделает. Об это рассказывал в https://youtu.be/E8jKRp751ZY
Это не настолько хорошо скейлится
Модульный монолит это обычный монолит, необычный это распределённый монолит и big ball of mud
я всегда считал, что обычный монолит - это и есть big ball of mud
Я с вашего стрима и начал, вот мне в коде не совсем понятна у вас все модули между собой через очередь общаются?
У меня тож через очередь.
Я понимаю очереди там где используется внешние сервисы, но когда два модуля лежат рядом, почему бы их просто не запустить в одном контроллере
потому что другой может пойти в стороне апи и отвалиться
А зачем мы на модули разделяли? Ну мол где-то в этой плоскости ограничения которые мы выбираем. Ещё может быть тебе хочется ограничивать радиус взрыва, или что б голова не болела что будут по ивенту делать
А евент вызывается в контроллере или дергается команда которая вызывает евент
Команда меняет агрегат и он порождает событие
А чем отличается контракт синхронного вызова, от ивента? Не понимаю, почему вызов одного модуля другого напрямую - это жесткая связь, а через очередь - связи нет. Если мы изменим структуру ивента - всё так же похерится. Не, конечно можно не подтверждать изменный ивент, убрать на рейтрай - это плюс, да, но это речь о другом. Все равно есть зависимость от контракта, ивент - тоже контракт.
кто сломается если кто поменяется и почему может сломаться. Примерно так ты меряешь уровень связи.
Фесор говорил не шарить данные в ивентах!
Ну один из профитов асинка - это уменьшение связанности. Но как она уменьшается? Контракт то есть (ну структура ивента/сообщения)
Ну значит можно сделать синк команду с одним id :)
можно, и если речь про команды то там не оч много разницы
Это профит от связи событиями, а не от синка/асинка. Контракт событий один и тот же хоть с синхронным обменом событиями, хоть с асинхронным.
Ну вот да, я про синк/асинк а не про события.
Есть ещё нюансы. С синхронными штуками ты ограничен синхронной моделью запрос ответ. Даже если ответ тебе не нужен ты его ждёшь + надо ошибки хэндлить. С асинхронной ты можешь просто слать сообщение, делать запрос ответ, или запрос и множество ответов... Оно про гибкость. С асинеом можно легко в ногу выстрелить. Но если уже разобрался как его готовить то нет желания возвращаться И да, есть ещё такая гадкая штука как temporal coupling..
При прямой связи первый модуль напрямую вызывает второй и третий и не может работать без них. При внешней оркестрации на событиях или вызовах из контроллера никакой модуль ничего не знает об остальных. При хореографии на событиях следующие модули знают только про события предыдущих.
Ну тут вы справниваете ивент vs асинк. Синк ивенты внутри модульного молита будут иметь такие же плюсы по "не знают друг о друге"
Могут, вопрос что делать если обработчик упал, как хэндлить ретраи и вот это все
Ну ретраи это плюс асинка, с этим вопросов нет. Опять же если все это нормально работает. В среднем проекте first fail может быть даже полезнее
Я не говорю, что синк эвенты отличаются от асинк. Я говорю, что синк система будет более хрупкой и ненадёжной из-за возможных глюков своего кода или внешних API и отсутствия долгих ретраев.
Ну если сравнивать с идеально отлаженной и работающей асинк, то да, вопросов нет.
Товар зарезервировался, деньги списались, а онлайн-касса глюкнула и... остальное не доделалось. Система оказалась в незавершённом состоянии, поставив на штраф за невыданный чек. Не знаю, кому это полезнее.
ну с ретраем не все так просто. Надо учитывать кучу условий, если происходят те же мутации с данными и тд. В этом плане концепция оркестратора/workflow оч удобно, снимает головную боль, начинает ретраить код с того место где упал
Вы выбрали определенный удобный кейс, где есть зависимость от внешней системы :) вопросов по нему нет. А если мы свою систему делаем асинк везде и вся (без внешнего взаимодействия) , то система может оказаться не в консистентом состоянии, если какая то распределенная транзакция прервется и не откатится верно. Значит надо везде оркестраторы и аутбоксы делать. Аутобокс ещё плюс нагрузка на бд.
> Вы выбрали удобный кейс... Да, я предпочитаю универсальный надёжный асинхронный вариант, который покрывает 100% всех кейсов. А не беру синхронный, который применим только для 10% проектов, которые без внешнего взаимодействия. В развивающемся проекте почти всегда появляются интеграции с платёжными системами, рассылками, CRM и прочими штуками. Поэтому логично советовать сразу очереди с ретраями. А вы предлагаете сначала спрограммировать всё синхронно, а потом переписывать на асинхронный при первой же интеграции стороннего сервиса.
тебе надо отправлять нотификашки - внешняя система. у тебя появляется разделение на первичные и вторичные функции - уже может возникнуть желание разделить их явно что бы одна меньше афектала другую. у меня в основном параноя в сторону "челы сломали фичу - что случится"
> Если какая-то распределённая транзакция прервётся или не откатится верно... Это произойдёт только один раз если логика отката неверно мной спрограммирована и какое-то событие зависнет. Я исправлю логику и после деплоя операция пойдёт дальше. А если прерывание будет по технической причине из-за ошибки сервиса или подключения, то по ретраю всё продолжится и в конце концов дойдёт до консистентного состояния.
Нет, я предлагаю делать там где от этого виден профит :) Выше был вопрос, про ваш стрим, что у вас все модули общаются через очередь и ивенты. Тип юзер зарегался, а еще в N местах создаются юзеры асинхронно. А там нет никаких сложностей, нет каких внешних систем, но уже прикручены ивенты, прикручен аутбокс, опять же асинк не спасет если проблема в коде, все отвалится после N ретраев из очереди и система будет в неконситетном состоянии. И тут вопрос основной, а нужно ли все это на проекте такого объема? Я понимаю что это урок и хорошие интересные примеры для реализациях в сложных системах. Но есть ли реальный профит, кроме доп сложности и возможных точек отказа.
Ну это самый наверное первый кейс для изучения асинхрона - отправить емейл после реги)
> Нет, я предлагаю делать там где от этого виден профит :) Я и говорю, что профит асинхрона виден в 90% проектов с внешними взаимодействиями, а экономия от синхрона возможна только в 10% проектов без взаимодействий.
Так у нас внутри проекта может быть 95% кода работать без внешнего взаимодействия, а 5% с внешним. Этот проект каким считается?
> Аутобокс ещё плюс нагрузка на бд. Во-первых, аутбокс применяется при операциях на модификацию БД, а не при чтении. Если у поста в соцсети на 1 000 000 просмотров (чтение) всего 10 000 лайков (запись), то чтений в 100 раз больше, чем операций лайка. Во-вторых, не во всех проектах нужно заморачиваться на хайлоаде. Если программируем корпоративный софт для автоматизации завода железобетонных изделий со 100 клиентами в год, то нет смысла переживать, что он не выдержит миллиард посетителей в день.
Да, только на 1 запись мы добавляем еще несколько записей аутбокса :) Вот и х2 нагрузка на БД. > Если программируем корпоративный софт для автоматизации завода железобетонных изделий со 100 клиентами в год Так может там обернуть все в транзакцию и не придумывать себе сложности и лишнюю инфру?
Там и монолит с нагрузкой справится. Не совсем понимаю зачем заводу на 100 юзеров рокет-сайнс)
Ну мы вообще не про нагрузку сейчас)
Так а изначально какую проблему решали?
Ну вот из-за этих 5% уже понадобится очередь с ретраями. Иначе привет глюкам от этих взаимодействий.
Ну так можно только для них и сделать если это критически необходимо
> Так может там обернуть все в транзакцию и не придумывать себе сложности и лишнюю инфру? Ну так оборачивайте, если умеете отправку писем и хождения в чужие API в транзакцию оборчивать. Я не умею.
Так с внешними системами все понятно и их можно отправлять на асинк, банально чтобы ответ приходил быстрее клиенту.
Теперь у вас в проекте есть две системы событий. Синхронная без аутбокса и асинхронная с аутбоксом.
Обсуждают сегодня