Что такое DDD (Domain-Driven Design) и каковы принципы его применения в Go?

Ответ

Domain-Driven Design (DDD) — это подход к разработке программного обеспечения, который фокусируется на предметной области (домене) и бизнес-логике. Главная цель — смоделировать сложный домен в коде, используя терминологию и концепции, понятные как разработчикам, так и экспертам в предметной области.

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

Ключевые концепции DDD

  1. Ubiquitous Language (Единый язык): Создание общего языка, который используется всеми участниками проекта (разработчиками, менеджерами, экспертами). Этот язык отражается в коде: в названиях классов, методов, модулей.

  2. Bounded Context (Ограниченный контекст): Четкое определение границ, в рамках которых модель и Единый язык имеют смысл. Например, в контексте "Продажи" Товар может иметь цену и скидку, а в контексте "Склад" — вес и местоположение.

  3. Layers (Слои): Классическая слоистая архитектура в DDD:

    • Domain Layer: Сердце приложения. Содержит бизнес-логику, сущности, агрегаты. Не зависит от других слоев.
    • Application Layer: Оркестрирует выполнение бизнес-сценариев (use cases), делегируя работу объектам домена.
    • Infrastructure Layer: Технические детали: работа с базами данных, файловой системой, внешними API. Реализует интерфейсы, определенные в доменном и прикладном слоях.
    • Presentation/UI Layer: Взаимодействие с пользователем (API-эндпоинты, веб-интерфейс).

Применение в Go (Тактические паттерны)

Поскольку в Go нет классов, паттерны DDD реализуются с помощью структур и интерфейсов.

  • Entity (Сущность): Объект с уникальным идентификатором и жизненным циклом. В Go это структура с полем ID.

    type User struct {
        ID    uuid.UUID
        Email string
        // ... другие поля
    }
  • Value Object (Объект-значение): Неизменяемый объект без идентификатора, характеризуемый своими атрибутами (например, Money, DateRange).

    type Money struct {
        Amount   decimal.Decimal
        Currency string
    }
  • Aggregate (Агрегат): Кластер из связанных сущностей и объектов-значений, который рассматривается как единое целое. У агрегата есть корень (Aggregate Root), который является единственной точкой входа для изменения агрегата.

  • Repository (Репозиторий): Интерфейс для доступа к агрегатам, имитирующий коллекцию объектов в памяти. Он скрывает детали хранения (SQL, NoSQL).

    // В доменном слое (domain/user.go)
    package domain
    
    type UserRepository interface {
        FindByID(ctx context.Context, id uuid.UUID) (*User, error)
        Save(ctx context.Context, user *User) error
    }
    
    // В инфраструктурном слое (infrastructure/postgres_user_repo.go)
    package infrastructure
    
    type PostgresUserRepo struct { db *sql.DB }
    
    func (r *PostgresUserRepo) FindByID(ctx context.Context, id uuid.UUID) (*domain.User, error) {
        // ... логика запроса к PostgreSQL
    }

Преимущества и недостатки в Go

  • Преимущества: Четкая структура, тестируемость, масштабируемость, бизнес-логика изолирована от технических деталей.
  • Недостатки: Может быть избыточным для простых CRUD-приложений. Требует дисциплины и глубокого понимания предметной области.