Из чего состоит DDD
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# DDD: архитектурный подход к разработке ПО
Domain-Driven Design (DDD) — это методология проектирования архитектуры приложений, которая ставит в центр бизнес-логику и предметную область. Она состоит из нескольких ключевых компонентов.
Стратегические паттерны DDD
1. Bounded Context
Bounded Context — это логическая граница, в пределах которой существует единое понимание того или иного концепта. Это отделение ответственности между различными частями системы.
# Пример: Bounded Context для системы заказов и доставки
class OrderContext:
"""Контекст управления заказами"""
def create_order(self, items: List[Item]) -> Order:
pass
class DeliveryContext:
"""Контекст управления доставкой"""
def schedule_delivery(self, order_id: str, address: str) -> Delivery:
pass
2. Ubiquitous Language
Это общий язык, который понимают как разработчики, так и бизнес-аналитики. Все термины в коде должны соответствовать словарю предметной области.
# Правильно: используем терминологию предметной области
class Customer:
def place_order(self, items: List[Product]) -> Order:
pass
# Неправильно: абстрактные имена
class User:
def create(self, data: dict) -> Entity:
pass
Тактические паттерны DDD
3. Entities (Сущности)
Объекты, которые имеют уникальный идентификатор и изменяемое состояние. Их идентичность сохраняется на протяжении всего жизненного цикла.
class Order:
def __init__(self, order_id: str, customer_id: str):
self.id = order_id
self.customer_id = customer_id
self.status = OrderStatus.PENDING
self.created_at = datetime.now(UTC)
def confirm(self) -> None:
if self.status != OrderStatus.PENDING:
raise InvalidOrderStateError()
self.status = OrderStatus.CONFIRMED
4. Value Objects
Объекты без собственной идентичности, определяемые своими атрибутами. Они неизменяемы и сравниваются по значению.
from dataclasses import dataclass
@dataclass(frozen=True)
class Money:
amount: Decimal
currency: str
def add(self, other: 'Money') -> 'Money':
if self.currency != other.currency:
raise CurrencyMismatchError()
return Money(self.amount + other.amount, self.currency)
@dataclass(frozen=True)
class EmailAddress:
value: str
def __post_init__(self):
if not self._is_valid_email(self.value):
raise InvalidEmailError()
5. Aggregates
Группировки Entity и Value Objects вокруг корневого Aggregate Root. Aggregate Root отвечает за обеспечение консистентности данных внутри Aggregate.
class OrderAggregate:
"""Root Aggregate для заказов"""
def __init__(self, order_id: str, customer_id: str):
self.id = order_id
self.customer_id = customer_id
self.items: List[OrderItem] = []
self.status = OrderStatus.PENDING
def add_item(self, product: Product, quantity: int) -> None:
if self.status != OrderStatus.PENDING:
raise CannotModifyConfirmedOrder()
item = OrderItem(product, quantity)
self.items.append(item)
def get_total_price(self) -> Money:
return sum(
(item.price * item.quantity for item in self.items),
Money(Decimal(0), 'USD')
)
6. Repositories
Абстракции для доступа к Aggregates. Они скрывают детали персистентности и работают на уровне бизнес-объектов.
from abc import ABC, abstractmethod
class OrderRepository(ABC):
@abstractmethod
def save(self, order: OrderAggregate) -> None:
pass
@abstractmethod
def find_by_id(self, order_id: str) -> Optional[OrderAggregate]:
pass
@abstractmethod
def find_by_customer(self, customer_id: str) -> List[OrderAggregate]:
pass
class SQLOrderRepository(OrderRepository):
def __init__(self, db_session):
self.db = db_session
def save(self, order: OrderAggregate) -> None:
orm_order = OrderORM(
id=order.id,
customer_id=order.customer_id,
status=order.status.value
)
self.db.add(orm_order)
self.db.commit()
7. Domain Services
Сервисы для бизнес-логики, которая не принадлежит ни одному конкретному Entity или Aggregate.
class PricingService:
"""Domain Service для расчёта цен"""
def calculate_discount(self, order: OrderAggregate, customer: Customer) -> Money:
if customer.loyalty_level >= LoyaltyLevel.GOLD:
return order.get_total_price() * Decimal('0.1')
return Money(Decimal(0), order.get_total_price().currency)
8. Domain Events
События, которые сигнализируют об изменениях в domain.
@dataclass
class OrderConfirmedEvent:
order_id: str
customer_id: str
timestamp: datetime
total_price: Money
class OrderAggregate:
def confirm(self) -> None:
if self.status == OrderStatus.PENDING:
self.status = OrderStatus.CONFIRMED
event = OrderConfirmedEvent(
order_id=self.id,
customer_id=self.customer_id,
timestamp=datetime.now(UTC),
total_price=self.get_total_price()
)
self.events.append(event)
Взаимосвязь компонентов
DDD создаёт архитектуру, где каждый компонент имеет чёткую ответственность. Entities и Value Objects — основа, Aggregates обеспечивают целостность, Repositories скрывают детали хранения, Domain Services реализуют сложную логику, а Domain Events связывают независимые контексты.