Что такое чистая архитектура (Clean Architecture)?

«Что такое чистая архитектура (Clean Architecture)?» — вопрос из категории Архитектура, который задают на 32% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Чистая архитектура (Clean Architecture) — это архитектурный подход, предложенный Робертом Мартином (Дядя Боб), который ставит бизнес-логику и правила предметной области в центр системы, делая их независимыми от внешних деталей: баз данных, фреймворков, UI или внешних сервисов.

Основной принцип — правило зависимостей (Dependency Rule):

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

Типичные слои (концентрические круги):

  1. Entities (Сущности): Ядро. Содержат критичные бизнес-правила и объекты предметной области. Это POCO-классы, не знающие ни о чем внешнем.
  2. Use Cases (Сценарии использования): Содержат специфичную логику приложения. Они оркестрируют поток данных от/к сущностям, координируя работу репозиториев и других интерфейсов.
  3. Interface Adapters (Адаптеры интерфейсов): Преобразуют данные между форматами, удобными для Use Cases и внешним миром (например, MVC-контроллеры, ViewModels, репозитории как абстракции).
  4. Frameworks & Drivers (Фреймворки и драйверы): Внешний слой. Базы данных, веб-фреймворки (ASP.NET Core), UI, файловые системы. Сюда подключаются конкретные реализации.

Пример структуры проекта и кода:

MyApp.Domain/          // Слой Entities (ядерные бизнес-правила)
    Entities/
        Order.cs
        Product.cs
    Interfaces/
        IRepository.cs // Абстракция объявлена здесь!

MyApp.Application/     // Слой Use Cases
    UseCases/
        CreateOrderUseCase.cs
    Interfaces/
        IEmailService.cs // Абстракция объявлена здесь!

MyApp.Infrastructure/  // Слой Frameworks & Drivers (конкретные реализации)
    Persistence/
        OrderRepository.cs // : IRepository (зависит от Domain!)
    Services/
        SmtpEmailService.cs // : IEmailService (зависит от Application!)

MyApp.WebApi/         // Слой Interface Adapters & Frameworks
    Controllers/
        OrderController.cs // Использует CreateOrderUseCase

Код сущности (Domain):

// MyApp.Domain/Entities/Order.cs
namespace MyApp.Domain.Entities;

public class Order // НИКАКИХ ссылок на EF, API и т.д.
{
    public int Id { get; private set; }
    public decimal TotalAmount { get; private set; }
    public OrderStatus Status { get; private set; } = OrderStatus.Pending;

    public void ApplyDiscount(decimal discount)
    {
        if (discount <= 0) throw new ArgumentException("Discount must be positive.");
        if (discount >= TotalAmount) throw new InvalidOperationException("Discount exceeds total.");
        // Бизнес-правило!
        TotalAmount -= discount;
    }

    public void MarkAsPaid() => Status = OrderStatus.Paid; // Еще одно правило
}

Преимущества:

  • Независимость: Бизнес-логика не зависит от выбора базы данных, веб-фреймворка или UI.
  • Тестируемость: Ядро можно тестировать изолированно, с моками всех внешних зависимостей.
  • Гибкость: Замена внешних компонентов (например, миграция с SQL Server на PostgreSQL) требует изменений только во внешнем слое.
  • Долгосрочная поддержка: Четкое разделение ответственности облегчает понимание и развитие кодовой базы.

Недостаток: Большой объем шаблонного кода (boilerplate) и повышенная сложность для маленьких проектов.

Видео-ответы