Продукт
Archito НовоеУчится на merge request и показывает, что застряло Интеграции 25
Решения
По роли
Почему AppArch
Ресурсы
Клиенты Цены Контакты
Язык
Связаться с нами

Справочник по архитектуре.

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

14 глав

Что такое архитектура (и чем она не является)

«Архитектура - это совокупность решений, которые дорого менять позже.» - Ralph Johnson

Архитектура - это не «какой фреймворк мы использовали»: это деталь реализации. Архитектура - это та форма, которую вы ощущаете, перемещаясь по кодовой базе: как разбиты модули, в каком направлении текут зависимости, какой код что может вызывать.

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

? Архитектура или качество? Практический тест: насколько дорого это откатить? Часы → качество. Недели → архитектура.

Цена решений

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

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

Прежде чем закреплять решение, спросите: если через три месяца я пойму, что оно ошибочно, насколько болезненным будет откат?

Замысел против реализации

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

Задача не в том, чтобы один раз нарисовать идеальную диаграмму. Задача в том, чтобы держать замысел и реализацию достаточно близко, чтобы диаграмма по-прежнему предсказывала поведение кода.

Слоистая (n-уровневая)

Классическое разделение: представление → приложение → предметная область → данные. Каждый слой может вызывать только тот, что под ним. Это самая простая для обучения модель - и самая простая для нарушения: один вызов из UI напрямую в слой данных, и слоистость превращается в вымысел.

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

Гексагональная · Порты и адаптеры

Предметная область находится в центре и ничего не знает о внешнем мире. Всё внешнее - база данных, HTTP, очередь сообщений - общается с ней через порты (интерфейсы), которые реализуют адаптеры.

Отдача - тестируемость и заменяемость: в тестах вы можете подменить Postgres на заглушку в памяти или REST на gRPC, не тронув ни строки кода предметной области.

Clean Architecture

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

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

DDD - предметно-ориентированное проектирование

DDD - это не фреймворк, это дисциплина словаря. «Aggregate», «bounded context», «anti-corruption layer» - общий язык, чтобы говорить о задаче.

Когда применять DDD?

  • Предметная область сложна - её часто путают с технической сложностью.
  • Несколько команд работают над одной и той же предметной областью.
  • Бизнес и инженеры говорят на разных языках.

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

Большой ком грязи

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

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

Антипаттерн: модуль-бог

«Модуль-бог» - тот, что вызывается отовсюду и содержит всё подряд. Имена вроде Utils, Common, Shared - тревожные признаки.

# правило, которое это обнаруживает
- id: GOD-001
  max_fan_in: 25
  message: "Module imported by >25 files - split or rename"

Решение: разбейте примерно на три части. Первая группа - реальная инфраструктура (logger, config). Вторая - примитивы предметной области (money, id). Третья - скорее всего, скрытый там отдельный модуль.

Циклические зависимости

Модуль A нуждается в B, а B - в A. Теперь ни один из них нельзя понять, протестировать или развернуть по отдельности, а порядок сборки превращается в бросок монеты. Циклы - самый надёжный признак модуля, к которому никто не хочет прикасаться.

Разорвите цикл, вынеся общее понятие в третий модуль, от которого зависят оба, или инвертировав одно из направлений через интерфейс. AppArch помечает циклы в тот момент, когда они появляются в merge request.

Дырявые абстракции

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

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

Архитектурное ревью

Ревью - это не шлагбаум, блокирующий слияния; это разговор о том, приближает изменение систему к её задуманной форме или удаляет от неё. Лучшие ревью чаще спрашивают «что это усложнит потом?», чем «правильно ли это сейчас?».

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

Записи об архитектурных решениях

ADR фиксирует одно решение: контекст, рассмотренные варианты, выбор и его последствия - абзац-другой, версионируемый рядом с кодом. Через полгода он отвечает на единственный важный вопрос: почему это устроено именно так?

Пишите ADR, когда решение принято, а не после. Заменяйте, а не удаляйте - след того, почему вы передумали, так же ценен, как и текущий ответ.

Крупный рефакторинг

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

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

Подпишитесь на нашу рассылку

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