Привет, Хабр! Меня зовут Иван Русин, я старший разработчик группы модернизации платформы Flowwow. Именно к нашей команде приходят, когда надо перенести часть функционала со старого бэкенд-проекта в новый. Сейчас у нас два проекта: один мы ведём на базе фреймворка Yii-1, а во втором реализуем современные архитектурные принципы на Yii-2.

Flowwow — это маркетплейс цветов и подарков: более 20 000 магазинов, свыше 5 миллионов пользователей и десятки категорий товаров. В обычные дни RPS составляет от 400 до 1 000 запросов, а в пиковые дни (8 марта, День матери и т. д.) этот показатель может увеличиваться до 7 000 RPS и больше.

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

Когда монолит становится проблемой

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

Классические признаки деградации архитектуры проявились в полной мере:

  • высокий coupling: компоненты сильно зависели друг от друга,
    границы размывались;

  • низкий cohesion: не было фокуса на задачи контекста в компоненте;

  • скрытые зависимости, которые трудно отслеживались по коду;

  • отсутствие зон ответственности для разработчиков.

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

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

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

Всё это приводило к росту стоимости разработки. Некоторые фичи могли реализовываться месяцами, тестирование становилось сложным, а любой сбой на проде означал прямые финансовые потери.

Почему не микросервисы

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

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

Чтобы этот подход действительно заработал на масштабах Flowwow, нам пришлось перестроить сразу несколько направлений: от организации слоев и контроля зависимостей до работы с базой данных и процессов внутри команд. Ниже мы детально рассмотрим каждый из этих пунктов.

Выбор архитектуры

За основу мы взяли onion-архитектуру, но адаптировали её под практические задачи команды.

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

В классическом виде она состоит из трех слоев: Domain, Application и Infrastructure. Мы упростили модель, убрав слой инфраструктуры, и фактически оставили два уровня: публичный слой Application и приватную область, в которую входят Domain и остальные внутренние слои.

Слой Infrastructure в привычном виде мы убрали. Главной причиной для отказа стала скорость разработки — работать с двумя слоями вместо трёх гораздо быстрее и проще. Это решение ускорило миграцию компонентов в новый проект и сэкономило команде сотни часов. Однако слой Infrastructure все еще можно найти в новой схеме, но уже с другой трактовкой. Для нас он обозначает внешние сервисы, например карты, почтовые отправления, email и СМС-рассылки. Инфраструктура нужна не для каждого модуля, поэтому зачастую этого слоя нет вовсе.

Onion архитектура и архитектура Flowwow
Onion архитектура и архитектура Flowwow

На практике это означает, что любой внешний вызов к модулю проходит через Application, вместо классического взаимодействия посредством Infrastructure. Application слой становится точкой контроля и границей модуля.

Разделение на контексты

Выбрать архитектурный паттерн и обозначить границы модулей — это лишь половина дела. Нужно было решить, по какому принципу логически делить сам монолит на эти независимые блоки. Здесь мы обратились к Domain Driven Design (DDD). Согласно его принципам, в основе всего лежит контекст, он же Domain. Это те области бизнеса, с которыми он работает. Для нас было важно прийти к единой терминологии. Нам было критично, чтобы не было двух разных сфер деятельности компании с одинаковым названием.

Для начала мы определили и выделили контексты, которые сформулировали на основе задач бизнеса:

  • бизнес-процессы и термины, которыми он оперирует;

  • таблицы базы данных;

  • существующая функциональность.

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

Мы разбили разработчиков на команды и закрепили за каждой свой контекст. Обозначив, какая команда за какой модуль отвечает, приступили к переносу кода.

Распределение контекстов по модулям
Распределение контекстов по модулям

Как взаимодействуют модули

Ключевым правилом для работы команд стало отсутствие прямого доступа к внутренним слоям чужих модулей. Взаимодействие возможно только через Application-слой. В нем находятся менеджеры (по сути, сервисы), которые предоставляют публичный API модуля. Именно через них происходит вся коммуникация.

Типичный сценарий выглядит следующим образом:

  1. Один модуль вызывает менеджер другого.

  2. Передает необходимые параметры (например, идентификатор магазина).

  3. Менеджер уходит во внутренние слои — в Domain.

  4. Собирает данные и возвращает результат.

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

Направление подключаемых зависимостей внутрь модуля

Изоляция данных через DTO — это лишь половина дела. Чтобы избежать повреждений модуля от внешних воздействий, важно соблюдать ещё одно правило: очередность подключения (Use) классов-зависимостей должна быть строго последовательной и направленной внутрь модуля. Он должен зависеть только от самого себя, и при этом внешние слои — только от внутренних.

Механика работы следующая: когда внешний компонент или модуль обращается к вашему модулю, он может делать это, только используя в своём коде ваш менеджер из Application. Этот слой первым встречается на пути его запроса. Дальше работает уже код из Application, который подключает у себя компоненты со слоя Domain. Мы строго придерживаемся этой последовательности: в коде Domain не используются классы Application, равно как и Application не обращается к классам внешних модулей в обход Anti-Corruption.

Anti-Corruption Layer

Даже DTO не передаются напрямую. На границе модуля используется Anti-Corruption Layer. Когда модуль получает данные извне, происходит двойное преобразование. Сначала данные формируются в DTO в исходном модуле, затем в принимающем модуле, внешнее DTO преобразуется во внутреннее, из доменного слоя, что даёт возможность работать с таким «своим» объектом внутри модуля на любом слое.

В результате у одного и того же объекта существуют разные представления в разных модулях — это осознанная изоляция.

Такой подход решает сразу несколько задач:

  • обеспечивает взаимную защиту модулей от изменений друг друга (изменения в наших модулях не влияют на соседние и наоборот)

  • разработчики команды, ответственной за модуль, сфокусированы только на своей предметной области;

  • помогает сделать зависимости видимыми, не скрывать их, локализовать в одном месте, на слое Application.

Структура модуля

Каждый модуль делится на две большие зоны: публичную и приватную. Публичная часть — это Application, через который происходит всё взаимодействие с внешним миром. Приватная часть включает Domain и остальные внутренние слои.

Схема модуля
Схема модуля

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

Это даёт важный эффект: каждый блок становится устойчивым к изменениям в системе. Если команда меняет внутреннюю реализацию, это не влияет на соседей. Дополнительно все внешние связи локализованы. Чтобы понять, от чего зависит конкретный участок кода, достаточно посмотреть на слои Application и Anti-Corruption. Зависимости больше не размазаны по проекту.

Контроль зависимостей через Deptrac

Чтобы архитектура не деградировала, мы автоматизировали контроль зависимостей с помощью Deptrac. Он запускается при деплое и проверяет взаимодействия между слоями, допустимость обращений, связи между модулями.

Конфигурация описывается в YAML-файлах. В одном файле мы задаем слои: например, где находится Domain, где Application. Это делается через пути и регулярные выражения, которые описывают структуру каталогов.

Во втором файле мы описываем сами модули и их взаимосвязи. По сути, это явный список: какой модуль от какого зависит. Если в коде одного модуля появляется несанкционированное подключение другого модуля, которое не описано в конфигурации, деплой упадёт. Например, если модуль SEO внезапно начинает использовать модуль Shop без явного разрешения, такой вызов блокируется.

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

Важно понимать, что Deptrac решает только задачу контроля архитектуры. Мы не рассматривали альтернативы, потому что он полностью покрывает эту потребность, хотя существуют похожие инструменты: PHPat, dePHPend, Arkitect. Некоторые из них позволяют даже визуализировать структуру проекта и зависимости модулей, но нам это не требовалось: для визуализации мы используем модель C4 и ведём документацию по этому стандарту.

Работа с базой данных

Если на уровне PHP-кода мы жёстко разграничили модули и автоматизировали проверки через deptrac, то с данными всё оказалось сложнее. База данных осталась общей. Мы не стали делить её физически, а сосредоточились на уровне кода. Модели распределены по модулям: каждый из них работает только со своими моделями, а использовать чужие напрямую — запрещено. Мы пока не можем обеспечить полный автоматический запрет технически, поэтому часть контроля лежит на договоренностях и ревью.

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

Производительность

Обилие слоёв, DTO и отказ от прямых SQL-запросов могут вызвать вопрос: как всё это сказывается на скорости? Несмотря на усложнение архитектуры, система остаётся производительной. Это достигается за счёт комплекса решений:

  • кэширование, включая статические страницы через Varnish;

  • использование Redis для ресурсоёмких операций;

  • очереди для асинхронной обработки;

  • репликация базы данных (master/slave);

  • оптимизация индексов и запросов.

Дополнительно используется распределение нагрузки по кластерам. Чтение и запись разделены: изменения идут в master, а SELECT-запросы направляются в slave. Это реализовано на уровне движка через фильтрацию запросов.

DBA-команда постоянно мониторит долгие запросы, анализирует сложные JOIN и приходит к разработчикам с конкретными рекомендациями по оптимизации. В результате система стабильно выдерживает нагрузку более 7 000 RPS.

Организация разработки

Технические ограничения и автоматические проверки — это лишь половина успеха. Чтобы новая архитектура жила и не деградировала, нам потребовалось изменить процессы внутри самих команд. У нас за каждым модулем закреплены команда и ответственные разработчики. Любые изменения проходят обязательное ревью. Если нужно изменить чужой модуль, это делается только через согласование с его владельцем. Без этого код не попадет в релиз. У каждого файла есть владелец, прописанный в CODEOWNERS. При этом сам файл мы не обновляем руками: он генерируется автоматически. Мы организовали отдельный архитектурный репозиторий. В нём храним информацию о взаимосвязях файлов и контекстов, из него генерируем актуальный список владельцев контекстов — CODEOWNERS.

Луковичная архитектура позволяет удерживать архитектурные границы не только технически, но и организационно. Разработчики лучше понимают свой контекст, глубже в него погружаются, не отвлекаются, сохраняют фокус на нём и реже допускают ошибки. Мы также упростили онбординг. Новичку стало проще погружаться в работу: он изучает один модуль, а не весь проект.

Где архитектура не решает все проблемы

Несмотря на преимущества, модульный монолит не универсален. Для нас он стал идеальным решением, но не обошлось и без минусов.

Во-первых, остаются общие зависимости. Например, сам фреймворк и часть библиотек подключены для всех модулей. При необходимости вынести модуль в отдельный сервис он поедет вместе с этими зависимостями.

Во-вторых, возникают трудности с полным запретом доступа к данным на уровне базы, raw query, прямых обращений. Так как запрет влечёт за собой дублирование моделей данных в модулях. Поэтому часть правил держится на дисциплине команды.

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

И наконец, часть общих компонентов (например, common, Shared Kernel) остается общими и не всегда удобно изолируется.

Миграция с легаси

Переход на новую архитектуру — длительный процесс. Мы переносим функциональность постепенно, не останавливая развитие продукта.

Процесс выглядит так:

  1. Функциональность реализуется в новом модуле.

  2. В аннотациях классам либо методам добавляем теги переноса, сообщающие, куда и откуда переносили.

  3. Пишем проверки — консольные команды или тесты.

  4. Результаты сравниваем со старой реализацией.

  5. После совпадения фронт переключается на новый код.

  6. Старый функционал удаляем.

Иногда для проверки мы запускаем один и тот же метод в старом и новом проекте на разных наборах данных и сравниваем результаты. Это позволяет убедиться, что поведение полностью совпадает. Только после этого можно безопасно отключать старую реализацию.

Результаты

После внедрения модульной архитектуры система стала более предсказуемой. Связанность между модулями снизилась, зависимости стали явными и контролируемыми.

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

Дополнительно система стала готова к дальнейшему развитию. Любой модуль при необходимости можно вынести в отдельный сервис без радикальной переработки. Также чёткое разграничение позволило нам настроить автоматическое распределение возникающих ошибок на ответственных хозяев контекста.

Ещё одним неочевидным плюсом стало то, что модульная система оказалась отлично приспособлена для написания ИИ-документации: за счёт разбиения кодовой базы на более мелкие модули. Мы можем добавлять в каждый модуль .md файлы-подсказки для LLM, с описанием особенностей работы именно этого контекста.

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

Модульный монолит — это практический инструмент. Он позволяет навести порядок в легаси, снизить стоимость изменений и создать основу для дальнейшего развития системы.

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

Комментарии (28)


  1. SolidSnack
    18.06.2026 00:42

    У вас не луковичная архитектура.

    У вас код разбит по модулям, которые в свою очередь содержат "луковичную" архитектуру.

    Луковичную я беру в кавычки потому что не до конца понимаю вашу интерпретацию, убрать 1 слой, чтобы добавить его в доменный, ну и 3 слоя внутри 1 слоя не выглядят по луковичному)

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

    Тут кстати назревает организационный вопрос, разве много бизнес задачи которые затрагивают 1 конкретный модуль? Большинство задач превращается в тим билдинги?

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

    Я очень сильно надеюсь что вы здесь не про инверсию зависимостей))))

    Мне кажется у вас в статье противоречие:

    Во-первых, остаются общие зависимости. Например, сам фреймворк и часть библиотек подключены для всех модулей. При необходимости вынести модуль в отдельный сервис он поедет вместе с этими зависимостями.

    Дополнительно система стала готова к дальнейшему развитию. Любой модуль при необходимости можно вынести в отдельный сервис без радикальной переработки.


    1. SolidSnack
      18.06.2026 00:42

      Ну и кстати, такой подход будет работать на проектах с простой бизнес логикой, потому что при таком подходе к организации кода не о каком моделировании бизнес процессов в домене речи идти не может)


      1. SolidSnack
        18.06.2026 00:42

        Кто мне не верит - попросите автора вынести слой домена из приложения, замокать его и протестировать бизнес кейсы.

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


    1. IvanRsn Автор
      18.06.2026 00:42

      Спасибо за комментарий! Давайте по порядку.

      убрать 1 слой, чтобы добавить его в доменный

      Мы взяли классический Onion лишь за основу и адаптировали его под реалии Flowwow. В итоге у нас получилась своя архитектура: слой Application заменил Infrastructure, став публичным.

      Мы не переносили Infrastructure в доменный слой — мы от него полностью отказались. Точкой входа и границей модуля стал Application. Да, в некоторых модулях внутри приватной области можно встретить раздел Infrastructure, но он несет совершенно другой смысл. Там мы пишем “провайдеры” для связи со сторонними внешними сервисами.

      Как видится мне - разделением на модули вы получаете дублирование кода

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

      у вас на каждый юзер кейс сколько обращений к базе?

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

      У каждого модуля свои обращения? А как на счёт сложных join запросов?

      Каждый модуль работает только со своими таблицами в базе, через модели, расположенные внутри модуля. Эти модели не дублируются в других модулях. Получить от них данные можно только через менеджеры. Да и сами модели менеджеры не вернут, они преобразуют их в DTO.
      Сложные join разрешаем по разному: иногда, в рамках одного контекста, когда обе таблицы ему принадлежат, прописываем связи между моделями, как заложено в ActiveRecord. В других случаях, когда модели из разных контекстов, рассматриваем отдельно. 

      Зачастую связи прописывать не нужно вовсе, подобные задачи решаются в несколько запросов: достаточно отдельно получить id из одной таблицы и добавить их в запрос к другой, а вторым запросом, уже в своем модуле, можно получить результат. Только в исключительных, редких кейсах, когда действительно нужна оптимизация, мы договариваемся о применении прямых запросов к базе. Что согласовывается с архитектором, либо DBA командой отдельно.

      разве много бизнес задачи которые затрагивают 1 конкретный модуль? Большинство задач превращается в тим билдинги?

      Полностью изолированных задач, которые решаются внутри одного модуля, у нас много. Если же для бизнес-задачи нужны данные из соседнего контекста, разработчик просто вызывает менеджер чужого модуля. Благодаря единой структуре всех модулей чужой код читается легко. Идти к соседям в чат нужно только в одном случае — если нужного метода еще не существует. И это скорее плюс: владельцы контекста всегда подскажут наиболее оптимальное решение для своей зоны ответственности.

      Любой модуль при необходимости можно вынести в отдельный сервис без радикальной переработки.

      Модуль действительно уедет в отдельный сервис вместе со своим фреймворком. Но главное преимущество в том, что теперь появилась возможность сделать это за пару дней, а не за месяц. Четкая структура, локализованные внешние связи в менеджерах и зависимости, описанные в deptrac, делают процесс переноса значительно проще.

      Я очень сильно надеюсь что вы здесь не про инверсию зависимостей

      Да, в тексте описано направление подключения классов. О том, что обращение к модулю опирается на application, а он на domain, только в таком порядке.


      1. SolidSnack
        18.06.2026 00:42

        Мы взяли классический Onion

        Ну нет же конечно, одного названия и картинок не достаточно.

        Да, в тексте описано направление подключения классов. О том, что обращение к модулю опирается на application, а он на domain, только в таком порядке.

        Про инверсую зависимостей совсем грустно с точки зрения технического специалиста, рекомендую всетаки вам прочитать книжку "чистая архитектура" Роберта Мартина

        Потом можно Мартина Фаулера, но у него шрифт более мелкий и чтиво потяжелее ;)


    1. rukhi7
      18.06.2026 00:42

      У вас не луковичная архитектура.

      вот откуда вы знаете? Может она вызывает слезы.


      1. SolidSnack
        18.06.2026 00:42

        Спросонья рассмешили меня))


        1. rukhi7
          18.06.2026 00:42

          После внедрения модульной архитектуры система стала более предсказуемой. Связанность между модулями снизилась, зависимости стали явными и контролируемыми.

          удивительно сколько людей, теперь, верят (или получат деньги за то что внушают это другим, как можно не верить тогда?) что достаточно произнести заклинания и это сразу помогает, Кашпировский отдыхает, а его дело живет. Кстати, может это Кашпировский виноват?

          Сравните:

          Слушайте мой голос — он поможет вам обрести внутренний покой.

          Чувствуйте, как с каждым вдохом уходит напряжение, а с каждым выдохом приходит лёгкость и спокойствие.

          Я даю установку: ваше тело расслабляется, разум успокаивается, все тревоги отступают.

          ... система стала более предсказуемой. Связанность между модулями снизилась, зависимости стали явными и контролируемыми...


          1. SolidSnack
            18.06.2026 00:42

            Если честно моё мнение что в ИТ очень многие решения принимаются эмоционально, некий массовый психоз проплаченный гигантами индустрии (микросервисы, джаваскрипт). Необходимо воздействовать не только на мозг инженера, но и на людей которые принимают финансовые решения. Поэтому микросервисы и джаваскрипт должны быть везде и под сомнения их ставить не должно быть принято)

            P.S. перечитал ваш комментарий - а ведь действительно, по итогу сам разработчик по той или иной причине становится лоббистом всяких странных решений)

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

            Вот и получаеются микросервисы с луковичной архитектурой)

            Или вот кэш, я думаю знающие люди понимают как сложно сделать прослойку кэша для бд. Сколько раз я видел redis которые по факту стоит в стороне от системы (торгует лицом, осваивает бюджет) а по бумагам редис чуть ли не из главных хранилищь, когда я указал на это тимлиду и начальнику департамента, что подрядчик редисом просто бабки в договоре отмывает, как же на меня тогда накинулись мои же коллеги)) Какое-то массовое помещательство)


            1. rukhi7
              18.06.2026 00:42

              я тут писал тоже про микросервисы . Может в тему ссылка будет. Но я только с локальными БД в своей файловой системе работал, это у меня совсем не главное, я маленько с другой стороны смотрю.


              1. SolidSnack
                18.06.2026 00:42

                Да, я полностью разделяю вашу точку зрения, и приём с подменой компонента на микросервис это показательно)

                Обязательно прочту книгу которую вы рекомендуете.

                В "чистой архитектуре" автор тоже достаточно подробно разбирает этот момент, но я уже не знаю, может и правда большинство книги не читают, а если читают то перелистывают на самое "главное")

                Только и повторяя себе под нос - зачем разбиратья если не важно как, главное вчера))


                1. AlexViolin
                  18.06.2026 00:42

                  Может есть смысл от обсуждения монолит vs микросервис, перейти к обсуждению вопроса компонент vs микросервис ?


                  1. SolidSnack
                    18.06.2026 00:42

                    Примерно такие рассуждения и ведёт Роберт Мартин.

                    Микросервис это слишком дорого в сравнение с компонентом, до неприличного дорого, от вызова функционала до инфраструктуры.

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


                1. rukhi7
                  18.06.2026 00:42

                  может и правда большинство книги не читают, а если читают то перелистывают на самое "главное"

                  по моему проблема в том что то что написано в той книге в том числе - очень сложная тема. По мне так сравнимо с положениями и применениями из квантовой физики.

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

                  Мне кажется что те кто читает это без практического опыта думают что сложность того что там написано, не адекватна реальным задачам, и просто пытаются придумать какую-то свою теорию о том как все просто, но это большая ошибка. Реальность сложна, в основе реальности лежат противоречия с их единством и борьбой. Реальность начинает бить по голове за пренебрежение своими фундаментальными законами, во всей красоте их противоречий.


                  1. AlexViolin
                    18.06.2026 00:42

                    Когда-то пришлось поработать с СОМ-технологией и СОМ компонентами. Как на меня это полный ужас. Её создали очень сложные люди, которые не стремились к простоте и красоте того, что они делают.


                    1. rukhi7
                      18.06.2026 00:42

                      Её создали очень сложные люди, которые не стремились к простоте и красоте того, что они делают.

                      так я про это и написал же! Это точно не вопрос простоты! Это вопрос соответствия сложности И полноты решения сложности задачи. А задача там очень сложная была по крайней мере с чем я работал: DirectX, видео-кодеки, база данных по видео-кодекам, сборка пайплайна из видео кодеков под произвольный формат видео стрима, ... . СОМ оказался очень сложным, но совершенно адекватным решением этой задачи обладающим полнотой решением. Не было проблем которые нельзя было решить с помощью хитростей-заумностей (не знай как это назвать!) СОМ-технологии! Но понимание этих хитростей-заумностей, а главное их необходимости в определенных ситуациях, приходит конечно далеко не сразу!


                  1. SolidSnack
                    18.06.2026 00:42

                    Согласен с вами, рассуждаете точно в цель.

                    Знаете, сейчас призадумавшись я понимаю что мой совет прочитать техническую литературу и получить из неё знания это наивно. И не потому что кто-то не пойдёт читать или будет читать сквозь пальцы. А потому что я сам перечитывал эту книгу несколько раз и если бы не опыт, боль, шишки, а самое главное - собственный интерес, наверное я бы не стал так глубоко во все это погружаться


                    1. rukhi7
                      18.06.2026 00:42

                      вот тут вот, кстати, мой комментарий к моей же статье про СОМ

                      https://habr.com/ru/articles/752478/#comment_25884588

                      где я нашел аналогию с теорией относительности, может тоже будет интересно, и статья и комментарий.


  1. Dr10s
    18.06.2026 00:42

    У вас один менеджер но контекст/модуль? Не превратится это в класс на 100500 строк и методов?


    1. Dr10s
      18.06.2026 00:42

      Обычно в монолитах без out of process зависимостях для общения между модулями используют application какраз как ACL над доменом что бы не делать +1 прослойку над командами/кверями. Единственное правило: application input/output должены содержать примитивы яп а не доменные классы


      1. stdpmk
        18.06.2026 00:42

        Не понятно...


    1. IvanRsn Автор
      18.06.2026 00:42

      Наоборот, благодаря такому подходу кода в файлах становится меньше. Внутри одного модуля мы используем не один глобальный менеджер, а несколько небольших.

      Каждый из них создается под узкий круг задач и отвечает за конкретную таблицу в БД или отдельную сферу. Иногда встречаются менеджеры всего с одним методом — это удобно для будущего расширения логики в этой конкретной области. Нам предпочтительнее создать 10 разных специализированных менеджеров внутри одного контекста, чем плодить один гигантский класс на 100500 строк.


  1. AlexViolin
    18.06.2026 00:42

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

    Один модуль вызывает менеджер другого

    Из схемы модуля видно что при обращении к модулю идёт вызов к Anti-Corruption Layer, а не к менеджеру модуля.

    затем в принимающем модуле, внешнее DTO преобразуется во внутреннее, из доменного слоя

    Внутреннее DTO наверное надо назвать доменным объектом.

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

    На схеме модуля на мой взгляд не указаны или перепутаны порядок использования слоёв и направления взаимодействия между слоями.

    Anti-Corruption Layer скорее всего это и есть слой Presentation через который идёт вызов к функционалу модуля и при вызове ему передаётся объект dto. Если в Presentation используется cron, то вызов проходит без передачи объекта dto.

    На схеме не показано откуда идёт вызов к Application. Можно предположит, что вызов идёт от Anti-Corruption Layer.

    Непонятно что именно понимается под блоком Components и где находится блок доменной логики?

    Для наглядности архитектуру удобно разделить на 2 части - схема взаимодействия между слоями/функциональными блоками модуля и схема переноса данных между моделями данных модуля. Сейчас на схеме вызовы идут и через функциональные блоки и через модели данных.


    1. IvanRsn Автор
      18.06.2026 00:42

      Из схемы модуля видно что при обращении к модулю идёт вызов к Anti-Corruption Layer, а не к менеджеру модуля.

      В тексте и изображении схемы всё верно. При обращении к другому модулю ACL нашего модуля вызывает их менеджер.

      Anti-Corruption Layer скорее всего это и есть слой Presentation

      Слой Presentation о другом: он содержит view-хи и прочие представления данных. Задача ACL создать собственный dto, класс которого расположен у нас, в нашем модуле. Это позволяет использовать его у себя в модуле без порождения внешних зависимостей. Такой подход дополнительно обеспечивает безопасность от изменений в других модулях: у них в ответе от менеджера 10 свойств, мы используем только 2, любые изменения остальных 8 никак не отразятся на нашем модуле. Оставшиеся 2 свойства, которые используются, не дадут повредить статические анализаторы.

      Ещё один плюс такого подхода, который хочется отметить, это то, что все “мапперы”, реализующие ACL, располагаются системно. Всегда знаешь где их искать у себя и в других модулях. Узнать список модулей, от которых зависит твой, просто и быстро.

      Непонятно что именно понимается под блоком Components и где находится блок доменной логики? 

      Components и есть блок доменной логики. Как видно из схемы, блоки Components, Dto, Models и прочие расположены внутри объединяющего их, группирующего блока Domain. Все они находятся внутри директории Domain каждого модуля.


  1. SolidSnack
    18.06.2026 00:42

    Флоу бау уау юпи ёй юпи ей

    Интересно как так получается что по заверениям СТО ваша компания выпустила больше сотни "отличных" инженеров, но которые, по всей видимости, не знают что такое инверсия зависимости :)


  1. vsinyavsky
    18.06.2026 00:42

    Проблема точно не в монолите, а в отсутствии структуры - недавно работал с похожим случаем: 10-летний легаси монолит, горы компонентов , очень слабая структура, незадокументированные решения живущие только в коде, high coupling, low cohesion, старый стек, вот это всё. И на горячую нужно поднять альтернативную версию 60% функциональности ядра на новом стеке. Тоже сталкивались со всеми этими проблемами...

    Хочу накинуть тему для дискуссии: архтесты хороши, чтобы поддерживать уже правильно проведённые границы, но архтесты не помогают их найти, и вы сами отмечаете это как пробел. Тут ловушка: как только конфиг зелёный, неудачно проведённая граница застывает в коде - инструмент защищает именно её и переразбить контекст становится дороже, чем было бы без него. На моём опыте изоляция контекстов это заслуга не архтестов, а проектной работы до них (мы границы искали Event Storming'ом). Я бы так и формулировал: сначала найти границы и проверить их на реальных сценариях изменений, а потом цементировать тулами, иначе рискуешь намертво закрепить не ту нарезку.


  1. SolidSnack
    18.06.2026 00:42

    Интересно конечно почему не стоит очередь из перформеров в компанию "сил добра", чтобы пробиться через кучу собеседований и начать за 100-200к обучать тимлидов инверссии зависимости))

    Think about вроде фраза такая есть)


  1. Dhwtj
    18.06.2026 00:42

    Flowwow — это маркетплейс цветов и подарков: более 20 000 магазинов, свыше 5 миллионов пользователей и десятки категорий товаров

    Эти 5 миллионов пользователей они с нами в одной комнате? ©