Привет Хабр! В этой статье мне бы хотелось рассказать про чистую архитектуру Роберта Мартина. Чистая архитектура это набор правил и идей, которые делают систему независимой от фреймворков, UI, баз данных и любых внешних агентов. Цель — создать систему, которую легко тестировать, поддерживать и изменять. Попытаюсь рассказать вам все кратко и понятно.

1. Фундаментальное правило: Зависимости направлены внутрь

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

2. Принцип разделения на слои (Концентрические круги)

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

От внутренних к внешним:

  1. Entities (Сущности): Ядро системы. Это бизнес-объекты и правила, которые не меняются при изменении внешних обстоятельств. Например, класс BankAccount с методом calculateInterest().

  2. Use Cases (Сценарии использования): Содержат специфическую бизнес-логику для конкретного приложения. Они определяют, как внешний мир взаимодействует с сущностями для достижения цели (например, TransferMoneyUseCase). Они ничего не знают о UI или базе данных.

  3. Interface Adapters (Адаптеры интерфейсов): Преобразуют данные между форматами, удобными для Use Cases и Entities, и форматами, удобными для внешних агентов (например, базы данных, веб-фреймворков). Сюда входят:

    Контроллеры (Presenters, ViewModels)

    Шлюзы (Gateways) / Репозитории (Repositories) — интерфейсы, которые определяют, какие данные нужны Use Cases, но не как они будут получены.

  4. Frameworks & Drivers (Фреймворки и драйверы): Внешний слой: база данных (PostgreSQL, MongoDB), веб-фреймворки (Spring, Express), UI, внешние API, библиотеки и т.д.

3. Принцип зависимостей (Dependency Rule)

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

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

  • Внешний круг реализует интерфейсы, объявленные во внутреннем круге (например, UserRepository интерфейс объявлен в слое Use Cases/Entities, а его реализация MySQLUserRepository — во внешнем слое). Это достигается с помощью инверсии зависимостей (DIP).

4. Принцип инверсии зависимостей (Dependency Inversion Principle — DIP)

Модули верхнего уровня (бизнес-логика) не должны зависеть от модулей нижнего уровня (детали реализации). Оба должны зависеть от абстракций (интерфейсов).

  • Бизнес-логика объявляет, какие данные ей нужны (например, "мне нужен способ сохранить пользователя" → интерфейс UserRepository).

  • Внешний слой предоставляет реализацию этой абстракции (например, UserMongoRepository).

5. Принцип единственной ответственности для слоёв

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

6. Принцип тестируемости

Бизнес-правила (Entities и Use Cases) должны быть полностью тестируемыми без UI, базы данных, веб-сервера или любого другого внешнего элемента. Их можно тестировать с помощью модульных тестов, используя заглушки (stubs) и моки (mocks) для интерфейсов репозиторий и презентеров.

Ключевые выгоды (Почему это стоит делать):

  1. Независимость от фреймворков: Ядро приложения не привязано к Spring, Django, React и т.д. Их можно заменить с минимальными затратами.

  2. Тестируемость: Бизнес-логику можно тестировать в изоляции.

  3. Независимость от UI: UI может меняться (веб, консоль, мобильное приложение) без изменения бизнес-правил.

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

  5. Независимость от внешних агентов: Бизнес-правила ничего не знают о внешних API, сторонних сервисах и т.д.

Делитесь своими впечатлениями от статьи в комментариях!

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


  1. jlllk
    11.01.2026 14:07

    Шо, опять?! ©


  1. Emelian
    11.01.2026 14:07

    UI может меняться (веб, консоль, мобильное приложение) без изменения бизнес-правил

    А GUI (оконные приложения)? – Не, не, не слышали!


    1. Akon32
      11.01.2026 14:07

      "Чистая архитектура" по идее позволяет относительно легко подключать любые ui к приложению, в т.ч. gui. Взаимодействие с gui выполняется через интерфейсы. Бизнес-логика, работа с БД вынесены в отдельные классы, поэтому смена ui ни на что не влияет (при "правильной" реализации, естественно). Возможна ли такая реализация во всех случаях - отдельный вопрос, но на небольших проектах у меня получалось подключать cli и gui через единый интерфейс.


      1. Emelian
        11.01.2026 14:07

        Взаимодействие с gui выполняется через интерфейсы.

        Практически, так никто не делает. Хотя я, в качестве эксперимента, пробовал этот вариант (см. мою статью: «Модульное программирование в C++. Статические и динамические плагины» в https://habr.com/ru/articles/566864/ ). Там как раз использовались интерфейсы (статические и динамические плагины) для создания GUI. Идея интересная, но народ её воспринял в штыки, поскольку это не вписывалось в парадигму: «Есть только два мнения: моё и неправильное!».

        Бизнес-логика, работа с БД вынесены в отдельные классы, поэтому смена ui ни на что не влияет (при "правильной" реализации, естественно).

        В своей статье: «Новая компьютерная программа для запоминания иностранных слов и фраз» ( https://habr.com/ru/articles/848836/ ) я, практически, этим и занимался.

        Только я бы не сказал, что это всё просто. В данном случае, вместо «бизнес-логики» использовалась «программная логика», в качестве «БД» – «Sqlite», Для классов, главной проблемой была организация их в иерархию. Нужно было, чтобы одна база данных выдавала разные данные для шести режимов работы в разных «видах» (т.е., дочерних окнах) плюс дополнительный выбор на фильтрацию и сортировку данных, с учетом выбранных пунктов меню.

        В MFC, подобная парадигма называлась «Документ-Вид», т.е., одному «окументу» (порции данных из БД) сопоставлялась несколько дочерних окон («видов»), в которых они могли быть представлены по-разному. Причем управление этими данными, осуществлялось через обработчики сообщений, которые имеют свою иерархию в циклах сообщений (от родительского окна к его потомкам). При этом, теоретическим, могут еще использоваться потоки (хотя режим «Видео», для своих данных, я реализовал с помощью таймера),

        Да, MFC я не использовал из-за его трудно обходимых недостатков, а применял WTL, очень близкому к WinAPI, что не слишком упрощало разработку.

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

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

        Поэтому, насчет «не влияет», я бы не сказал. GUI менять на UI / web / консоль и т.п., не нужно от слова «совсем». Нужно, просто добиться разной работы программной логики в разных видах (по факту, режимах работы). Эти «режимы работы» можно добавлять (в которых одни и те же порции данных из БД будут разными и отображаться по-разному в разных видах), но не более. «Кросплатформа» тоже не актуальна, тратить на нее силы не вижу смысла.

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

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


  1. errorcost
    11.01.2026 14:07

    Независимость

    Это как купить УАЗ, чтобы ездить на работу, потому что «а вдруг дорогу размоет (или снег пойдет), а я готов». В итоге 10 лет стоишь в пробке, а дороги так ни разу и не размыло.


  1. Stanislav_Z
    11.01.2026 14:07

    Такой информации довольно много в открытом доступе. Было бы интереснее, если бы Вы взяли реальную компанию/бизнес, описали бы её подробно человеческим языком, а потом показали бы архитектуру для неё. Хочется больше авторства в таких статьях, а не копипаст


    1. Akon32
      11.01.2026 14:07

      Боюсь, такое обычно под NDA... Впрочем, можно попробовать соорудить pet project, но эта "чистая архитектура" не слишком приятна при реализации.


  1. mityukov
    11.01.2026 14:07

    Не первый раз вижу эту тему, как тут все кристально, по полочкам, идеально.. но вот только начал читать, и сразу же возник вопрос: почему "посчитать процент" это метод у сущности, а "перевести деньги" - это use case? Кто определяет границу?


  1. Akon32
    11.01.2026 14:07

    Чистую архитектуру уже кто только не разбирал... Например тут - https://habr.com/ru/articles/905148/

    как правильно сами знаете что
    как правильно сами знаете что

    Картинка оттуда показывает, что любой hello world должен состоять минимум из 13 классов/интерфейсов. На практике мало кто так делает :) Обычно проект гвоздями прибивают ко фреймворку, интерфейсы тоже в принципе не обязательны... и т.д. И вроде нормально работает.

    На мой взгляд, очень полезны следующие идеи из "чистой архитектуры": инверсия зависимостей (и всё, что говорится о зависимостях), тестируемость, слои (как хоть какая-то структура кода), и также идея о том, что "СУБД, UI, интеграции - это маловажные детали реализации, а более важен домен и его работа". Этим идеям нужно следовать в большинстве проектов, без этого даже средне-малый проект может оказаться тяжёлым для разработки.

    Остальные идеи тоже хороши, но порождают переусложнение на ровном месте.