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

Как в DDD управляют зависимостями?

2.3 Middle🔥 141 комментариев
#Архитектура и паттерны

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

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

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

Управление зависимостями в Domain-Driven Design (DDD)

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

Ключевые принципы и паттерны

1. Инверсия зависимостей через Dependency Inversion Principle (DIP)

Вместо того чтобы доменный код зависел от инфраструктуры, мы делаем инфраструктуру зависимой от абстракций, определённых в домене. Это реализуется через интерфейсы (портs) в доменном слое.

// Доменный слой: определяет интерфейс (порт)
protocol OrderRepository {
    func save(_ order: Order) async throws
    func findById(_ id: OrderId) async throws -> Order?
}

// Доменная сущность использует только абстракцию
class OrderProcessingService {
    private let repository: OrderRepository
    
    init(repository: OrderRepository) {
        self.repository = repository
    }
    
    func process(order: Order) async throws {
        // Бизнес-логика
        try await repository.save(order)
    }
}

2. Чёткое разделение слоёв (Layered Architecture)

DDD традиционно предполагает разделение на:

  • Доменный слой (сущности, агрегаты, доменные сервисы, репозитории интерфейсы)
  • Слой приложения (App Services, координирующие доменные операции)
  • Инфраструктурный слой (реализации репозиторий, внешние API, persistence)
  • Слой представления (UI, API контроллеры)

Зависимости направлены внутрь: внешние слои зависят от внутренних, но не наоборот.

3. Использование Dependency Injection (DI)

Конкретные реализации внедряются в доменные сервисы или App Services через инъекцию зависимостей, обычно на уровне композиции корня приложения.

// Инфраструктурный слой предоставляет реализацию
class SQLOrderRepository: OrderRepository {
    private let database: DatabaseConnection
    
    func save(_ order: Order) async throws {
        // SQL-специфичная реализация
    }
}

// Композиция корня (например, в SwiftUI или отдельном модуле)
let repository = SQLOrderRepository(database: myDB)
let service = OrderProcessingService(repository: repository)

4. Порт и Адаптер (Hexagonal/Ports and Adapters Architecture)

Это более современный подход, расширяющий layered architecture:

  • Порт — интерфейс в домене, определяющий контракт (например, OrderRepository).
  • Адаптер — реализация порта в инфраструктурном слое (например, SQLOrderRepository или GraphQLOrderAdapter).

Такой подход позволяет домену быть полностью независимым от способов взаимодействия с внешним миром.

Практические механизмы управления в iOS/Swift

Для модульности и тестирования:

  • Протоколы (интерфейсы) как основные абстракции.
  • Фреймворки/модули для физического разделения слоёв (можно использовать Swift Package Manager).
  • Тестирование: легко создавать mock-реализации для доменных интерфейсов.
// Mock для тестирования
class MockOrderRepository: OrderRepository {
    var savedOrders: [Order] = []
    
    func save(_ order: Order) async throws {
        savedOrders.append(order)
    }
}

let mockRepo = MockOrderRepository()
let service = OrderProcessingService(repository: mockRepo)
// Тестируем бизнес-логику без инфраструктуры

Конкретные примеры зависимостей в DDD

Что НЕ должно попадать в доменный слой:

  • Конкретные классы баз данных (CoreData, Realm, SQL).
  • Сетевые фреймворки (Alamofire, URLSession напрямую).
  • Фреймворки UI (SwiftUI, UIKit).
  • Системы аналитики или логирования.
  • Любые технические детали реализации.

Что определяет доменный слой:

  • Интерфейсы репозиториев для персистентности.
  • Интерфейсы для внешних сервисов (например, PaymentGateway).
  • Доменные события и их обработчики (через интерфейсы).

Выводы и лучшие практики

Управление зависимостями в DDD сводится к строгой защите доменного модели от внешних влияний. Это достигается через:

  • Абстракции, определённые доменом.
  • Инверсию направления зависимостей.
  • Чёткие границы слоёв с правилами взаимодействия.
  • Инъекцию конкретных реализаций только на уровне композиции.

Такое подход не только сохраняет чистоту бизнес-логики, но и делает систему более тестируемой, модульной и адаптируемой к изменениям инфраструктуры без переписывания доменного кода. В iOS разработке это особенно важно для долгосрочной поддержки проекта и обеспечения его эволюции без нарушения работы ядра приложения.