Comments 16
Любая нетривиальная система состоит из нескольких слоев (в порядке кто кого вызывает): application layer (controller) -> business services -> core domain -> infrastructure\libraries. Слои могут быть пропущены и app layer может напрямую звать core или инфраструктуру. А еще есть DI, чтобы композиция была не жестко задана в коде, а управлялась из вне самих слоев.
Но как только не извращаются разные авторы, пытаясь эту линейную конструкцию завернуть в разные геометрические формы. Сначала придумали onion architecture, там все тоже самое, но никто не понял что это. Теперь hexagonal, который от onion только формой отличается. А еще постоянно существует DDD в которой люди пытаются core domain вытащить на самый верх и построить все на нем, используя определенный набор паттернов.
Но все это не несет никакой добавленной ценности к процессу разработки. Как были контроллеры, сервисы, классы предметной области и библиотеки, так и остаются, и цепочка вызова от «применения» hexagonal\onion\domain-driven не меняется.
Но как только не извращаются разные авторы, пытаясь эту линейную конструкцию завернуть в разные геометрические формы. Сначала придумали onion architecture, там все тоже самое, но никто не понял что это. Теперь hexagonal, который от onion только формой отличается. А еще постоянно существует DDD в которой люди пытаются core domain вытащить на самый верх и построить все на нем, используя определенный набор паттернов.
Но все это не несет никакой добавленной ценности к процессу разработки. Как были контроллеры, сервисы, классы предметной области и библиотеки, так и остаются, и цепочка вызова от «применения» hexagonal\onion\domain-driven не меняется.
+14
Это вам понятно, а для большинства неокрепших умов не понятно даже зачем нужно отделять инфраструктуру от приложения. По сути все эти луковые/гексагональные архитектуры несут в себе одни и те же идеи, вопрос в предпосылках.
Луковая — концентрирует внимание на главенстве core domain/domain layer, восхваляет persistence ignorance и т.д.
Гексагональная — мы берем луковую архитектуру и привносим концепцию портов и адаптеров как более четкое определение границы между слоями. То есть тут мы концентрируем больше внимания на границах слоев а не на том что внутри оных.
То есть оно как бы все о том же, но добавляет чуть больше конкретики на разных уровнях.
Луковая — концентрирует внимание на главенстве core domain/domain layer, восхваляет persistence ignorance и т.д.
Гексагональная — мы берем луковую архитектуру и привносим концепцию портов и адаптеров как более четкое определение границы между слоями. То есть тут мы концентрируем больше внимания на границах слоев а не на том что внутри оных.
То есть оно как бы все о том же, но добавляет чуть больше конкретики на разных уровнях.
-4
Я вот вижу ровно обратную картину.
Под воздействием подобных статей неокрепшие умы начинают отделять когда еще нечего отделять. Получается очень over-engineered код, в котором за тоннами «паттернов» не видно какая же логика работает. И такая болезнь иногда поражает даже опытных программистов.
Если посмотреть на эту статью, то в ней такой же пример оверинжиниринга, как обычно я вижу в приложениях. Куча кода, чтобы вызвать sendmail. Понятно что в реальном коде будет сложнее чем просто sendmail, понятно что в большом масштабе это даст выигрыш. Но если начинать любое приложение с такого кода, то его невозможно будет поддерживать.
Поэтому я категорически против навязывания архитектуры в таких статях.
Под воздействием подобных статей неокрепшие умы начинают отделять когда еще нечего отделять. Получается очень over-engineered код, в котором за тоннами «паттернов» не видно какая же логика работает. И такая болезнь иногда поражает даже опытных программистов.
Если посмотреть на эту статью, то в ней такой же пример оверинжиниринга, как обычно я вижу в приложениях. Куча кода, чтобы вызвать sendmail. Понятно что в реальном коде будет сложнее чем просто sendmail, понятно что в большом масштабе это даст выигрыш. Но если начинать любое приложение с такого кода, то его невозможно будет поддерживать.
Поэтому я категорически против навязывания архитектуры в таких статях.
+6
Поэтому я категорически против навязывания архитектуры в таких статях.
Так никто ж и не навязывает.
Если вы не только просмотрите код, но и почитайте сопровождающий текст, там ясно написано, что это то к чему нужно стремиться, но не нужно слепо следовать этим правилам. Скажем в примере с добавлением логирования ясно написано что это может быть и ок, особенно если реализация нотификатора у нас одна, но это увеличивает технический долг. Просто добавляются еще и примеры, почему это увеличивает технический долг, при каких ситуациях нам это скажется и как вообще быть.
В целом я решил перевести эту статью как раз таки из-за весьма прагматичных подходов применимых в реальности, а не потому что там расписана клевая архитектура.
Но в целом я с вами согласен, что люди могут неверно истолковать идеи и броситься из одной крайности в другую.
0
когда возникает желание что-то выделить в отдельный слой, абстракцию и тд вспоминаю цитату
если желание осталось — значит действительно надо выделять :)
а вообще с каждым годом МакКоннел открывается в новом свете
Управление сложностью — самый важный технический аспект разработки ПО. По-моему, управление сложностью настолько важно, что оно должно быть Главным Техническим Императивом Разработки ПО
если желание осталось — значит действительно надо выделять :)
а вообще с каждым годом МакКоннел открывается в новом свете
+2
UFO just landed and posted this here
UFO just landed and posted this here
— Допустим RegisterUserCommand должен создавать пользователя и отправлять ему на почту письмо. Нормально ли реализовывать это требование прямо в обработчике команды, или нужно создавать ещё событие UserRegisteredEvent?
Я обычно такие вещи разруливаю на уровне ивент листенеров по postFlush (когда пользователь на самом деле создался). То есть по postFlush я могу достать все сущности из unit of work учавствовавшие в транзакции. Потом из всех сущностей собираются все доменные ивенты и отправляются на выполнение в ивент диспатчер. Но это при условии если у вас есть такая возможность.
У автора статьи ситуация чуть отличается в силу использования active record. Тут уже есть другие варианты, но в принципе опять же, ваш сервис отправки нотификашек может не сразу отправлять нотификашку, а запоминать что ее надо отправить, и реализация будет ожидать окончания транзакции (какой-то системный ивент).
Опять же идея юз кейсов идет из луковой архитектуры и там суть в том что на каждый юз кейс приложения есть метод, один и только один. А как вы это дело будете реализовывать — все зависит от задачи которую вы решаете.
Не глупо ли создавать события и их обработчики в одном слое, а связывать их в другом?
обработчики событий обычно будут находиться в application layer, и может быть иногда во framework layer для каких-то задач связанных с инфраструктурой (например синхронизация write model и read model если мы практикуем CQRS и при этом у нас данные в разных хранилищах но это детали реализации, инфраструктура).
Связываются они опять же диспетчером событий. Так как интерфейс оного определен в application layer то значит и принадлежит он application layer, а уж где его реализуют и как он работает — дело третье.
Где валидировать сущности которые зависят от данных? Например нужно проверять E-Mail на уникальность.
Сущности не должны иметь в принципе возможности войти в невалидное состояние. Валидировать вы должны входящие данные (например, как рассматривалось в статье, декоратор над CommandBus реализующий валидацию).
Альтернатива — попробовать достать из репозитория чувака с таким email-ом и если он найден, уже как-то реагировать (от бросания исключения до предложения восстановить пароль + нотификация владельцу аккаунта). Это будет более правильный способ так как таким образом в нашем хэндлере будет явно описано это бизнес правило.
Мне кажется или бизнес правила разносятся по всему коду?
Все бизнес правила инкапсулируются в виде сервисов или же в методах сущностей (общие бизнес правила для конкретной сущности).
Как потом с такой кашей работать?
Никакой каши, все красиво и удобно. Особенно если писать это все с применением TDD (вся бизнес логика при таком подходе вообще не зависит от инфраструктуры или фреймворка, что означает что мы можем спокойно покрыть все быстрыми юнит тестами).
Не слова о транзакциях. Не должна ли команда оборачиваться в транзакцию?
Можно, в частности можно обернуть нашу шину команд декоратором, который начинает транзакцию, коммитит ее по завершению обработки и, в случае возникновения ошибки, откатывает.
Так же мы можем разделить различные команды по типам (Command, Query или write-command и read-command). Словом… это относится уже к реализации. Как я уже говорил — гексагональная архитектура делает больше упор на взаимодействие между слоями, описание зависимостей. Она потому еще и называется архитектурой «портов и адаптеров».
0
Если вам интересно узнать подробнее как организуются бизнес правила, рекомендую посмотреть вот этот доклад от Mathias Verraes: Unbreakable Domain Models. Опять же в условиях простой бизнес логики все это может быть оверхэдом, потому не стоит впадать в крайности.
0
Кому интересно, пример простенького приложения организованного с разделением на 4 слоя + behat + phpspec.
github.com/norzechowicz/mydrinks
github.com/norzechowicz/mydrinks
+2
Выглядит интересно. Может быть есть сопровождающий пост к этому репозиторию?
0
Неа, только код. На реддите вчера было обсуждение, но там большинство восприняло это все в штыки так как код вопервых не идеален (главное там показать структуру всетаки), и выглядит все слишком сложно для такой задачи.
В целом там идеи те же что описываются в этой статье, но просто идут более классические для многослойной архитектуры термины (вроде инфраструктура вместо framework layer), а так тоже самое, та же инверсия зависимостей для снижения связанности слоев.
В целом там идеи те же что описываются в этой статье, но просто идут более классические для многослойной архитектуры термины (вроде инфраструктура вместо framework layer), а так тоже самое, та же инверсия зависимостей для снижения связанности слоев.
0
вчера проревьюировал приложение, а сейчас прочел ветку реддита — согласен с вашим комментарием. Это самый подробный и более-менее реальный пример многослойного приложения.
Соглашусь и с комментариями противников такого подхода в контексте констатации факта — код приложения очень многословен. Но нужно понимать для чего это делается и что мы благодаря этому получаем.
Соглашусь и с комментариями противников такого подхода в контексте констатации факта — код приложения очень многословен. Но нужно понимать для чего это делается и что мы благодаря этому получаем.
+1
Sign up to leave a comment.
Articles
Change theme settings
Гексагональная архитектура