Ответ
Чистая архитектура (Clean Architecture) — это набор принципов проектирования программного обеспечения, предложенный Робертом Мартином. Её главная цель — создание систем с четким разделением ответственностей, которые легко поддерживать, тестировать и развивать.
Основной принцип — Правило Зависимостей (The Dependency Rule):
Зависимости в исходном коде могут быть направлены только внутрь — от внешних слоев к внутренним. Ничего во внутреннем слое не может знать о внешнем.
Это достигается за счет разделения системы на концентрические слои:
- Entities (Сущности): Самый внутренний слой. Содержит объекты и бизнес-правила, общие для всего предприятия. Этот слой не зависит ни от чего.
- Use Cases (Сценарии использования): Содержит бизнес-логику, специфичную для приложения. Оркестрирует поток данных к сущностям и от них. Зависит только от Entities.
- Interface Adapters (Адаптеры интерфейсов): Слой преобразования данных. Здесь находятся контроллеры, презентеры, шлюзы (gateways). Этот слой адаптирует данные из наиболее удобного для Use Cases формата в формат, удобный для внешних систем (базы данных, UI, веб).
- Frameworks & Drivers (Фреймворки и Драйверы): Самый внешний слой. Здесь находятся все внешние детали: фреймворки (Gin, Echo), драйверы баз данных, UI и т.д. Этот слой является "клеем", который соединяет все вместе.
Пример на Go, иллюстрирующий правило зависимостей:
// 1. Entities (внутренний слой, ни от чего не зависит)
type User struct {
ID int
Name string
}
// 2. Use Cases (определяет интерфейс, зависит от Entities)
type UserUseCase struct {
repo UserRepository
}
// Интерфейс репозитория определяется здесь, во внутреннем слое!
type UserRepository interface {
GetByID(id int) (*User, error)
}
func (uc *UserUseCase) GetUser(id int) (*User, error) {
return uc.repo.GetByID(id)
}
// 3-4. Interface Adapters / Frameworks (внешний слой, реализует интерфейс)
// Эта структура зависит от интерфейса UserRepository, а не наоборот.
type PostgresUserRepository struct {
db *sql.DB
}
// Реализация интерфейса из слоя Use Cases
func (r *PostgresUserRepository) GetByID(id int) (*User, error) {
// ... логика получения пользователя из PostgreSQL
return nil, nil
}
Ключевые преимущества:
- Независимость от фреймворков и БД: Бизнес-логику можно легко перенести на другой фреймворк или СУБД.
- Высокая тестируемость: Внутренние слои можно тестировать в изоляции, без поднятия веб-сервера или подключения к реальной БД.
Ответ 18+ 🔞
А, чистая архитектура! Ну это ж, блядь, как священный грааль для программиста, который устал от спагетти-кода, где всё перемешано хуже, чем в студенческом холодильнике после затяжных выходных.
Слушай, суть-то проще, чем кажется. Представь себе матрёшку, только не простую, а ебаную, с правилами. Самая маленькая и ценная — в центре. Это Сущности (Entities). Твои главные бизнес-понятия: Пользователь, Заказ, Деньги. Они нихуя не знают про базы данных, HTTP или твой любимый фреймворк. Они святые и неприкасаемые.
Дальше идёт слой Сценариев использования (Use Cases). Это уже конкретные действия: «Создать заказ», «Начислить бонусы». Они знают про сущности и крутят ими как хотят, но тоже, блядь, в вакууме! Им похуй, откуда данные пришли и куда уйдут. Они просто говорят: «Дай-ка мне репозиторий для пользователя» — через интерфейс, естественно.
А вот теперь, внимание, главное правило, его хуй забудешь: зависимости идут ТОЛЬКО внутрь! Внутренние слои НЕ ЗНАЮТ НИ ХУЯ про внешние. Это как будто твоя бизнес-логика — это царь, а всё остальное — холопы, которые ей прислуживают, но царь даже имён холопов не помнит. Он просто приказывает: «Подай мне данные!», а уж как холоп их достал — из базы, из файла, из своей жопы — царю похуй.
Самый внешний слой — это уже Фреймворки и Драйверы (Frameworks & Drivers). Веб-сервер, база данных, кэш, UI. Это обслуга, которая реализует те интерфейсы, которые нарисовали внутренние слои. Вот смотри, как это выглядит в коде, чтобы не быть пустословом:
// 1. Entities — царь в своём дворце. Ни от кого не зависит.
type User struct {
ID int
Name string
}
// 2. Use Cases — приближённые царя. Знают только царя и свои интерфейсы для приказов.
type UserUseCase struct {
repo UserRepository // Говорим: "Нужен кто-то, кто умеет давать пользователей"
}
// Интерфейс объявляем ЗДЕСЬ, во внутреннем мире! Царь диктует условия.
type UserRepository interface {
GetByID(id int) (*User, error)
}
func (uc *UserUseCase) GetUser(id int) (*User, error) {
// Царское повеление: "Принеси пользователя!"
return uc.repo.GetByID(id)
}
// 3-4. Interface Adapters / Frameworks — холопы снаружи. Они ЗАВИСЯТ от интерфейса царя.
type PostgresUserRepository struct {
db *sql.DB
}
// Холоп покорно реализует царский указ (интерфейс).
func (r *PostgresUserRepository) GetByID(id int) (*User, error) {
// ... лезет в свою постгресную берлогу, достаёт данные, кланяется...
row := r.db.QueryRow("SELECT id, name FROM users WHERE id = $1", id)
var u User
err := row.Scan(&u.ID, &u.Name)
return &u, err
}
Видишь магию? Слой UseCase зависит от абстракции (UserRepository), а конкретная реализация (PostgresUserRepository) зависит от этой абстракции. Зависимость инвертирована, правило соблюдено. Царь (бизнес-логика) чист и незапятнан.
А нахуя это всё? Да затем, ёпта, чтобы завтра, когда начальник придёт и скажет «Меняем Постгрес на Монгу, а Джин на Эхо», ты не обосрался, а просто поменял холопов на других. Бизнес-логика, тесты к ней — они остались нетронутыми, потому что они нихуя не знали про эти внешние штуки. Это и есть независимость от фреймворков и БД.
И тестировать — одно удовольствие. Хочешь протестировать UseCase? Подсунь ему заглушку (mock), которая реализует UserRepository. Никаких поднятий баз, никаких веб-серверов. Чистая, быстрая, изолированная проверка логики. Красота, а не жизнь.
Короче, идея в том, чтобы система не превращалась в большой ком грязи, где всё связано со всем. А чтобы была как луковица — слоёная и чтобы от неё не плакали те, кто её потом поддерживает.