Clean Architecture: простыми словами
В Clean Architecture, как учит Роберт Мартин (он же Uncle Bob), работает чёткое правило: внешний код всегда зависит от внутреннего, но не наоборот. Всё просто — если у тебя есть сервисы или репозитории, они должны взаимодействовать с бизнес-логикой через абстракции (интерфейсы), которые определяет ядро приложения. Это значит, что доменная или прикладная логика сами формулируют, что им нужно (например, интерфейс получения пользователя), а вот как это будет реализовано — не их забота, этим занимается инфраструктура.
Кратко про слои:
- Домен (Domain/Entities): тут живёт твоя предметная область, бизнес-правила и сущности. Никаких технических деталей, только чистая логика.
- Приложение (Application/Use Cases): здесь оркестрируются сценарии использования (use-cases), работающие с доменом и абстракциями для работы с внешним миром.
- Инфраструктура (Infrastructure): наружный слой, где происходят все «грязные» вещи: доступ к БД, интеграция с файловыми системами, внешними API и т.п.
- Презентация (UI): здесь у тебя контроллеры, Web API, десктоп/мобильный фронт и прочее, всё, что общается с пользователем.
Куда класть интерфейсы?
Есть два подхода:
- В Application-слой — так рекомендует сам Uncle Bob, и это рабочий стандарт. Тут интерфейсы объявляются там, где нужны для use-case'а, а домен остаётся вообще не в курсе о том, что у тебя есть какая-то база или хранилище.
- В Domain-слой — если ты строго следуешь DDD и в предметной области есть понятие репозитория для агрегатов, можешь описывать интерфейс прямо в домене. Так домен остаётся «чистым», но явно подразумевает, что кто-то должен реализовать хранение.
Классика — интерфейсы, реализуемые инфраструктурой, — часть ядра, а не внешнего слоя. Главное — зависимости должны идти внутрь.
Принципы зависимостей: Dependency Rule и DIP
Всё это базируется на Dependency Inversion Principle (DIP) — той самой «D» из SOLID. В книге у Мартина формулировка такая: «Все зависимости в коде должны быть направлены внутрь, к политике более высокого уровня». То есть бизнес-логика не зависит от деталей, например, базы данных, а наоборот — детали реализуют абстракции, определённые в бизнес-логике.
Как это работает на практике:
Вместо того чтобы класс домена знал, как работать с SQL или MongoDB, он видит только интерфейс, скажем, IRepository. А вот реализация (например, SqlRepository) — уже знает все детали, и лежит в Infrastructure. При запуске приложения мы скармливаем DI-контейнеру:
services.AddScoped<IUserRepository, UserRepository>();
и всё, магия — везде в приложении будет именно эта реализация, а код ядра останется нетронутым при любом рефакторинге инфраструктуры.
Важно: не путай DIP с IoC!
- DIP — это правило про зависимости от абстракций, а не деталей.
- IoC — шире, это про то, что объекты не создают свои зависимости сами, а получают их снаружи (через DI, например).
SOLID-принципы и Clean Architecture
Clean Architecture по сути реализует все принципы SOLID на уровне всей системы, а не только классов:
SRP (Single Responsibility): Каждый слой отвечает только за своё — бизнес-логика живёт своей жизнью, детали хранения — своей.
OCP (Open/Closed): Чтобы добавить новый способ хранения данных — реализуй новый класс, интерфейсы трогать не надо.
LSP (Liskov Substitution): Все реализации интерфейса можно взаимозаменять — хоть база, хоть in-memory-реализация.
ISP (Interface Segregation): Лучше много маленьких интерфейсов, чем один монстр на все случаи.
DIP (Dependency Inversion): Уже обсудили выше, именно на нём всё строится.
Архитектура выстраивается так, что изменения в одной части не затрагивают другие: хочешь сменить СУБД — меняешь только инфраструктурный слой, всё остальное не трогаешь.
Domain-Driven Design и Clean Architecture
DDD и Clean Architecture отлично сочетаются: DDD заставляет центрировать проект вокруг домена, а Clean Architecture гарантирует, что домен защищён от деталей.
В DDD принято, что домен — самая стабильная и важная часть системы. Он не должен зависеть от технических подробностей — и Clean Architecture этому идеально способствует.
Интерфейсы на доменном языке:
Если твой репозиторий называется IOrderRepository, он работает с сущностями Order, а не с какими-то абстрактными строками или SQL-командами. Такой подход делает архитектуру более читабельной и поддерживаемой.
Application Layer в DDD:
По Эвансу, Application Layer — тонкая прослойка, которая координирует действия домена. Здесь удобно определять нужные интерфейсы, например, для рассылки писем или интеграции с внешними сервисами.
Обычно это выглядит так:
- Отдельный проект Domain — вся бизнес-логика и сущности.
- Application — сценарии, DTO, сервисы.
- Infrastructure — все реализации и интеграции.
- Web/API — контроллеры, UI.
Главное — все внешние зависимости подключаются через абстракции, и эти абстракции находятся внутри ядра, а не снаружи.
В итоге:
DDD помогает грамотно выделять домен и строить архитектуру вокруг него, Clean Architecture — гарантирует, что этот домен будет надёжно защищён от технических изменений. Система становится гибкой, понятной и легко тестируемой. Любые изменения — хоть в бизнес-логике, хоть в инфраструктуре — происходят локально и не разрушают всё остальное.