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

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

Ответ

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

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

  1. Ubiquitous Language (Единый язык) — строго определенный набор терминов, общий для разработчиков, экспертов предметной области и документации. Имена классов, методов и переменных в коде напрямую соответствуют этому языку.
  2. Bounded Context (Ограниченный контекст) — четкая граница, внутри которой действует определенная модель и единый язык. Это основной принцип декомпозиции большой системы. Например, контексты "Продажи", "Доставка", "Каталог товаров".
  3. Entity (Сущность) — объект, который имеет уникальную идентичность и жизненный цикл. Две сущности с одинаковыми атрибутами, но разными ID — это разные объекты.
    class Order {
        let id: UUID // Уникальный идентификатор — ключевое свойство сущности.
        var status: OrderStatus
        // ...
    }
  4. Value Object (Объект-значение) — объект, который определяется исключительно значениями своих атрибутов. Не имеет идентичности, неизменяем (immutable).
    struct Money: Equatable {
        let amount: Decimal
        let currency: String // Например, "USD"
        // Две суммы в 10 USD равны, независимо от того, один это объект или два разных.
    }
  5. Aggregate (Агрегат) — кластер из связанных сущностей и объектов-значений, который рассматривается как единое целое для операций изменения. У агрегата есть корень (Aggregate Root) — единственная сущность, через которую происходит всё взаимодействие с агрегатом извне.

    // Order — корень агрегата.
    class Order {
        let id: UUID
        private(set) var items: [OrderItem] // OrderItem — сущность внутри агрегата.
        var customerInfo: CustomerInfo // Value Object
    
        func addItem(productId: UUID, quantity: Int) {
            // Вся бизнес-логика добавления товара инкапсулирована здесь.
            // Проверяет правила агрегата (например, не добавить товар в завершенный заказ).
            let newItem = OrderItem(productId: productId, quantity: quantity)
            items.append(newItem)
        }
    }
  6. Domain Service (Доменный сервис) — операция или логика, которая по своей природе не принадлежит ни одной сущности или объекту-значению. Часто координирует работу нескольких агрегатов.
  7. Repository (Репозиторий) — абстракция для доступа к агрегатам, скрывающая детали хранения (база данных, сеть). Обычно предоставляет методы типа findById, save, delete.

Польза DDD:

  • Создает понятную и тесно связанную с бизнесом кодовую базу.
  • Помогает управлять сложностью больших приложений через ограниченные контексты.
  • Делает код более тестируемым, так как ядро (домен) не зависит от инфраструктуры (UI, БД, сеть).