Что такое Domain-Driven Design (DDD)

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

Ответ

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

Ключевые концепции и строительные блоки:

  1. Ubiquitous Language (Единый язык):

    • Создание общего словаря терминов между разработчиками, экспертами предметной области (domain experts) и всеми участниками проекта.
    • Этот язык используется в коде, документации и обсуждениях, устраняя недопонимание.
    • Пример: В банковской сфере термины "Счет", "Клиринг", "Овердрафт" имеют четкие, согласованные определения.
  2. Bounded Context (Ограниченный контекст):

    • Явное определение границ, внутри которых конкретная модель и единый язык являются согласованными и применимыми.
    • Один и тот же термин в разных контекстах может иметь разное значение.
    • Пример: Сущность Product в контексте "Каталог" имеет атрибуты Name, Description, Price. В контексте "Логистика" та же сущность Product имеет атрибуты Weight, Dimensions, StorageRequirements.
  3. Aggregate (Агрегат) и Aggregate Root (Корень агрегата):

    • Агрегат — это кластер из связанных объектов (сущностей и value objects), который трактуется как единое целое для операций изменения данных.
    • Aggregate Root (AR) — единственная точка входа в агрегат. Внешние объекты могут ссылаться только на AR, а не на внутренние сущности. AR отвечает за инварианты (бизнес-правила) всего агрегата.

      
      // Aggregate Root
      public class Order : AggregateRoot
      {
      public Guid Id { get; private set; }
      public OrderStatus Status { get; private set; }
      private readonly List<OrderLine> _lines = new(); // Внутренняя сущность
      public IReadOnlyCollection<OrderLine> Lines => _lines.AsReadOnly();
      
      // Все изменения идут через методы AR
      public void AddLine(Product product, int quantity)
      {
          // Проверка инварианта: нельзя добавлять товары в завершенный заказ
          if (Status == OrderStatus.Completed)
              throw new DomainException("Cannot modify a completed order.");
      
          var line = new OrderLine(product.Id, product.Price, quantity);
          _lines.Add(line);
      
          // Генерация Domain Event
          AddDomainEvent(new OrderLineAddedEvent(Id, product.Id, quantity));
      }
      
      public void MarkAsCompleted() { ... }
      }

    // Value Object (неизменяемый) public record OrderLine : ValueObject { public Guid ProductId { get; } public Money Price { get; } // Другой Value Object public int Quantity { get; } public Money Total => Price * Quantity; // Конструктор и equality на основе всех полей }

  4. Repository (Репозиторий):

    • Абстракция, которая инкапсулирует логику доступа к агрегатам (обычно через персистентность).
    • Работает только с Aggregate Roots.
    • IOrderRepository.GetByIdAsync(id) возвращает Order (AR) со всеми его OrderLines.
  5. Domain Event (Доменное событие):

    • Факт, что в домене что-то значимое произошло (например, OrderPlacedEvent, PaymentReceivedEvent).
    • Позволяет разным частям системы (внутри или даже вне bounded context) реагировать на изменения, не создавая жестких зависимостей.
  6. Слоистая/Гексагональная архитектура:

    • DDD часто реализуется с четким разделением на Domain Layer (чистая бизнес-логика, без зависимостей от инфраструктуры), Application Layer (координация задач, транзакций), Infrastructure Layer (реализация репозиториев, вызов внешних сервисов).

Когда использовать DDD?

  • Проекты со сложной и нетривиальной бизнес-логикой.
  • Когда требуется долгосрочная поддержка и активная эволюция продукта.
  • Когда критично точное соответствие программной модели реальным бизнес-процессам.

Когда НЕ использовать DDD?

  • Простые CRUD-приложения без сложных бизнес-правил.
  • Прототипы или проекты с очень коротким сроком жизни.
  • Ситуации, где накладные расходы на сложность архитектуры не окупаются.