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

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

2.4 Senior🔥 121 комментариев
#Архитектура и паттерны

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

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

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

Основной принцип DDD

Делай код похожим на язык бизнеса, не наоборот. Если у вас есть термины вроде "заказ", "покупатель", "платёж" — они должны быть видны в коде как классы и методы.

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

1. Ubiquitous Language (Всеобщий язык)

Все участники проекта (разработчики, бизнес-аналитики, клиенты) используют один и тот же словарь. Это предотвращает недопонимание.

# Неправильно — назва не отражает бизнес
class Order:
    def process(self):
        self.status = "processed"

# Правильно — названия из Ubiquitous Language
class Order:
    def confirm_payment(self) -> None:
        """Подтверждает платёж заказа в системе"""
        self.status = OrderStatus.PAYMENT_CONFIRMED
        self.paid_at = datetime.now(UTC)

2. Entities (Сущности)

Объекты с уникальным идентификатором. Их личность важнее состояния.

from dataclasses import dataclass
from uuid import UUID

@dataclass
class Customer:
    """Клиент системы — сущность с уникальным ID"""
    id: UUID
    email: str
    phone: str
    
    def __eq__(self, other):
        # Два клиента — один и тот же, если у них одинаковый ID
        if not isinstance(other, Customer):
            return False
        return self.id == other.id
    
    def __hash__(self):
        return hash(self.id)

3. Value Objects (Объекты-значения)

Объекты без уникального идентификатора. Их личность определяется значением всех атрибутов.

from dataclasses import dataclass
from typing import NewType

Money = NewType('Money', float)

@dataclass(frozen=True)  # Неизменяемость
class Price:
    """Объект-значение: цена не зависит от ID, важно значение"""
    amount: Money
    currency: str
    
    def __eq__(self, other):
        # Две цены равны, если равны amount и currency
        if not isinstance(other, Price):
            return False
        return self.amount == other.amount and self.currency == other.currency
    
    def add(self, other: 'Price') -> 'Price':
        if self.currency != other.currency:
            raise ValueError("Cannot add prices in different currencies")
        return Price(self.amount + other.amount, self.currency)

4. Aggregates (Агрегаты)

Группа сущностей и объектов-значений, которые работают вместе и имеют один корень (root entity). Агрегат — это граница транзакции и консистентности.

from dataclasses import dataclass, field
from typing import List
from uuid import UUID, uuid4
from enum import Enum

class OrderStatus(Enum):
    PENDING = "pending"
    CONFIRMED = "confirmed"
    SHIPPED = "shipped"
    DELIVERED = "delivered"

@dataclass
class OrderItem:
    """Value Object — элемент заказа"""
    product_id: UUID
    quantity: int
    price: Price

@dataclass
class Order:  # Aggregate Root
    """Корень агрегата Order"""
    id: UUID
    customer_id: UUID
    items: List[OrderItem] = field(default_factory=list)
    status: OrderStatus = OrderStatus.PENDING
    total_price: Price = field(default_factory=lambda: Price(0, "USD"))
    
    def add_item(self, item: OrderItem) -> None:
        """Методы агрегата управляют консистентностью"""
        if self.status != OrderStatus.PENDING:
            raise ValueError("Cannot add items to non-pending order")
        
        self.items.append(item)
        self.total_price = self.total_price.add(item.price)
    
    def confirm(self) -> None:
        if not self.items:
            raise ValueError("Cannot confirm empty order")
        
        self.status = OrderStatus.CONFIRMED

5. Domain Services (Доменные сервисы)

Операции, которые не принадлежат ни одной сущности или объекту-значению. Например, расчёт скидки, проверка доступности товара.

from abc import ABC, abstractmethod

class PricingService:
    """Доменный сервис для расчёта цен"""
    
    def calculate_discount(self, customer_lifetime_value: Money) -> float:
        """Рассчитывает скидку на основе жизненной стоимости клиента"""
        if customer_lifetime_value > 10000:
            return 0.15  # 15% скидка
        elif customer_lifetime_value > 5000:
            return 0.10  # 10% скидка
        return 0.0

class InventoryService:
    """Доменный сервис для управления инвентарём"""
    
    def __init__(self, repository):
        self.repository = repository
    
    def is_product_available(self, product_id: UUID, quantity: int) -> bool:
        """Проверяет доступность товара"""
        product = self.repository.find(product_id)
        return product.stock >= quantity

6. Repositories (Хранилища)

Абстракции для получения и сохранения агрегатов. Скрывают детали персистентности.

from abc import ABC, abstractmethod

class OrderRepository(ABC):
    """Интерфейс хранилища заказов"""
    
    @abstractmethod
    def save(self, order: Order) -> None:
        pass
    
    @abstractmethod
    def find_by_id(self, order_id: UUID) -> Order:
        pass
    
    @abstractmethod
    def find_by_customer(self, customer_id: UUID) -> List[Order]:
        pass

class SQLOrderRepository(OrderRepository):
    """Конкретная реализация с БД"""
    
    def __init__(self, db_session):
        self.db_session = db_session
    
    def save(self, order: Order) -> None:
        # Преобразуем доменный объект в ORM модель
        db_order = OrderModel(...)
        self.db_session.add(db_order)
        self.db_session.commit()
    
    def find_by_id(self, order_id: UUID) -> Order:
        db_order = self.db_session.query(OrderModel).filter_by(id=order_id).first()
        if not db_order:
            raise OrderNotFound()
        # Преобразуем ORM модель в доменный объект
        return self._to_domain(db_order)

7. Use Cases / Application Services (Прикладные сервисы)

Реализуют бизнес-сценарии, используя доменные объекты и сервисы.

from dataclasses import dataclass

@dataclass
class CreateOrderRequest:
    customer_id: UUID
    items: List[dict]  # [{"product_id": ..., "quantity": ...}]

class CreateOrderUseCase:
    def __init__(self, order_repository: OrderRepository, 
                 pricing_service: PricingService):
        self.order_repository = order_repository
        self.pricing_service = pricing_service
    
    def execute(self, request: CreateOrderRequest) -> UUID:
        # Создаём агрегат
        order = Order(id=uuid4(), customer_id=request.customer_id)
        
        # Добавляем товары с доменной логикой
        for item_data in request.items:
            item = OrderItem(
                product_id=item_data["product_id"],
                quantity=item_data["quantity"],
                price=Price(item_data["price"], "USD")
            )
            order.add_item(item)
        
        # Применяем скидку через доменный сервис
        discount = self.pricing_service.calculate_discount(customer_lifetime_value=5000)
        if discount > 0:
            discounted_price = Price(order.total_price.amount * (1 - discount), "USD")
            order.total_price = discounted_price
        
        # Сохраняем агрегат
        self.order_repository.save(order)
        return order.id

Преимущества DDD

  1. Гибкость — изменения в бизнес-логике легко отражаются в коде
  2. Понятность — код читается как документация бизнес-процессов
  3. Масштабируемость — легко расширять новыми функциями
  4. Тестируемость — доменная логика не зависит от фреймворков
  5. Команда на одной волне — нет путаницы между разработчиками и бизнесом

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

  • Сложная бизнес-логика
  • Долгоживущие проекты
  • Команда из 5+ разработчиков
  • Часто меняющиеся требования

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

  • CRUD приложения (блог, простой чат)
  • Однодневные проекты
  • Очень простая логика

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

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