← Назад к вопросам

Какие три основные принципы DDD?

2.8 Senior🔥 201 комментариев
#Микросервисы и архитектура

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Три основных принципа Domain-Driven Design (DDD)

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


1. Фокусировка на ядре предметной области (Domain-Centric Design)

Первый и главный принцип — это смещение фокуса всей разработки на предметную область (домен) и её бизнес-логику. Технические аспекты (базы данных, фреймворки, API) становятся вторичными. Разработчики, архитекторы и эксперты-доменщики (business experts) должны говорить на одном языке и совместно создавать модель, отражающую реальные бизнес-процессы.

Ключевые практики:

  • Глубокое погружение в домен (Deep Modeling): Недостаточно поверхностного понимания. Нужно вникать в нюансы, исключения и правила бизнеса.
  • Постоянное обучение: Домен постоянно эволюционирует, и модель должна эволюционировать вместе с ним.
  • Разделение на поддомены (Subdomains): Сложный домен делится на более мелкие части:
    *   **Core Domain:** Самая ценная и уникальная часть бизнеса, ради которой существует система. На неё тратится основное внимание и лучшие ресурсы.
    *   **Supporting Subdomain:** Вспомогательные процессы, специфичные для бизнеса, но не дающие конкурентного преимущества (например, расчёт сложных налогов).
    *   **Generic Subdomain:** Стандартные, решаемые готовыми решениями задачи (например, аутентификация, отправка email).

Пример на Go, показывающий фокус на домене, а не на технике (например, на JSON или БД):

package order

// Доменный объект (Entity) "Заказ". Его структура и поведение определяются бизнес-правилами.
type Order struct {
    ID         OrderID
    CustomerID CustomerID
    Items      []Item
    Status     OrderStatus // (Created, Paid, Shipped, Cancelled) - важные для бизнеса состояния
    Total      Money
}

// Доменный сервис, инкапсулирующий бизнес-правило.
// Внимание: логика связана с доменом "Заказ", а не с технической реализацией.
type OrderService struct {
    repo OrderRepository
}

func (s *OrderService) CancelOrder(orderID OrderID, reason string) error {
    order, err := s.repo.Get(orderID)
    if err != nil {
        return err
    }

    // Бизнес-правило: отменить можно только заказы со статусом "Created" или "Paid"
    if !order.CanBeCancelled() {
        return domain.ErrOrderCannotBeCancelled
    }

    order.Cancel(reason) // Вызов метода доменного объекта, который меняет состояние
    return s.repo.Save(order)
}

2. Использование единого языка (Ubiquitous Language)

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

Как это работает:

  • Имена типов, функций, методов, пакетов и переменных напрямую берутся из единого языка.
  • Это устраняет дорогостоящие преобразования ("на жаргоне бизнеса это X, а в коде у нас поле abc").
  • Код становится самодокументируемым и понятным для всех.

Пример на Go:

// Плохо: техноцентричные имена, не отражающие домен.
type DataRecord struct {
    ID     int
    State  string
    Amount float64
}

// Хорошо: имена взяты из единого языка домена "Торговля".
type PurchaseOrder struct { // "Заказ на закупку" - конкретный термин бизнеса
    ID           PurchaseOrderID
    Status       PurchaseOrderStatus // "Статус заказа" - "Submitted", "Approved", "Rejected"
    TotalCost    Money               // "Общая стоимость" - тип Money явно указывает на домен
}

// Метод назван в соответствии с бизнес-процессом.
func (po *PurchaseOrder) Approve(approverID EmployeeID) error {
    // Логика утверждения, соответствующая бизнес-правилам
    if po.Status != Submitted {
        return domain.ErrCannotApproveNonSubmittedOrder
    }
    po.Status = Approved
    po.ApproverID = approverID
    return nil
}

3. Стратегическое проектирование через ограниченные контексты (Bounded Contexts)

Это ключевой принцип для управления сложностью больших систем. Ограниченный контекст (Bounded Context) — это явная граница, внутри которой действует конкретная модель и её единый язык. Один и тот же термин в разных контекстах может иметь разное значение.

Зачем это нужно:

  • Предотвращает создание одной огромной, противоречивой "единой модели" для всей системы, что неизбежно ведёт к хаосу.
  • Позволяет разным командам независимо разрабатывать и развивать свои контексты.
  • Четко определяет, где заканчивается ответственность одной модели и начинается другая.

Пример: В системе электронной коммерции термин "Продукт (Product)" имеет разное значение в разных контекстах:

  • Каталог (Catalog Context): Product — это товар с описанием, фото, категориями.
  • Заказы (Ordering Context): Product — это идентификатор (SKU) и цена на момент заказа.
  • Доставка (Shipping Context): Product — это физический объект с весом и габаритами.

На уровне кода и архитектуры это разделение можно отразить через разные Go-модули/пакеты:

// Контекст "Каталог"
package catalog

type Product struct {
    ID          ProductID
    Name        string
    Description string
    Images      []Image
    Categories  []Category
    // Атрибуты, важные для каталога
}

// Контекст "Заказы"
package ordering

// Локальная модель продукта в контексте заказов.
// Может содержать только необходимые данные.
type OrderProduct struct {
    SKU      ProductSKU // Идентификатор, связывающий с каталогом
    Name     string     // Возможно, упрощённое имя для чека
    Price    Money      // Цена НА МОМЕНТ ЗАКАЗА (важное бизнес-правило!)
    Quantity int
}

// Связь между контекстами осуществляется через идентификаторы или антикоррупционный слой (ACL),
// а не через прямую передачу объектов.

Итог: Эти три принципа взаимосвязаны. Фокус на домене задаёт направление, единый язык обеспечивает эффективную коммуникацию и точное отражение домена в коде, а ограниченные контексты предоставляют практический инструмент для структурирования сложной системы, не позволяя ей превратиться в "большой комок грязи" (Big Ball of Mud). Внедрение DDD требует значительных усилий и особенно оправдано в проектах с высокой сложностью бизнес-логики.

Какие три основные принципы DDD? | PrepBro