Фокус на предметной области (Domain
) в DDD означает, что разработка сосредотачивается на моделировании бизнес-логики и правил реального мира, а не на технических деталях (БД
, фреймворки
).
Если коротко, то основной смысл в следующем:
- Предметная область (
Domain
) — это сфера бизнеса, которую решает приложение (финансы, e-commerce, медицина). - Код отражает реальные процессы и термины этой области, а не инфраструктуру.
- Программисты и эксперты бизнеса совместно создают общий язык (
Ubiquitous Language
).
Реализация Domain с примерами на Python
1. Сущности (Entities)
Сущности - это объекты, которые имеют идентичность и жизненный цикл. Они определяются не своими атрибутами, а своей идентичностью.
class Product:
def __init__(self, product_id: str, name: str, price: float):
self.product_id = product_id # Идентификатор - ключевое свойство сущности
self.name = name
self.price = price
def change_price(self, new_price: float):
if new_price <= 0:
raise ValueError("Price must be positive")
self.price = new_price
def __eq__(self, other):
if not isinstance(other, Product):
return False
return self.product_id == other.product_id
2. Объекты-значения (Value Objects)
Объекты-значения определяются только своими атрибутами и не имеют идентичности.
class Address:
def __init__(self, street: str, city: str, zip_code: str):
self.street = street
self.city = city
self.zip_code = zip_code
def __eq__(self, other):
if not isinstance(other, Address):
return False
return (self.street == other.street and
self.city == other.city and
self.zip_code == other.zip_code)
3. Агрегаты (Aggregates)
Агрегат - это кластер связанных объектов, которые рассматриваются как единое целое.
class OrderItem:
def __init__(self, product: Product, quantity: int):
self.product = product
self.quantity = quantity
self.validate()
def validate(self):
if self.quantity <= 0:
raise ValueError("Quantity must be positive")
def total_price(self):
return self.product.price * self.quantity
class Order:
def __init__(self, order_id: str, customer_id: str):
self.order_id = order_id
self.customer_id = customer_id
self.items = []
self.status = "created"
def add_item(self, product: Product, quantity: int):
# Проверяем, нет ли уже этого продукта в заказе
for item in self.items:
if item.product == product:
item.quantity += quantity
item.validate()
return
# Если продукта еще нет, добавляем новый элемент
new_item = OrderItem(product, quantity)
self.items.append(new_item)
def total_amount(self):
return sum(item.total_price() for item in self.items)
def confirm(self):
if not self.items:
raise ValueError("Cannot confirm empty order")
self.status = "confirmed"
def cancel(self):
self.status = "cancelled"
4. Репозитории (Repositories)
Репозитории предоставляют интерфейс для доступа к агрегатам.
from abc import ABC, abstractmethod
class OrderRepository(ABC):
@abstractmethod
def save(self, order: Order):
pass
@abstractmethod
def get_by_id(self, order_id: str) -> Order:
pass
@abstractmethod
def get_all(self) -> list[Order]:
pass
class InMemoryOrderRepository(OrderRepository):
def __init__(self):
self.orders = {}
def save(self, order: Order):
self.orders[order.order_id] = order
def get_by_id(self, order_id: str) -> Order:
return self.orders.get(order_id)
def get_all(self) -> list[Order]:
return list(self.orders.values())
5. Сервисы предметной области (Domain Services)
Сервисы содержат логику, которая не принадлежит ни одной сущности или объекту-значению.
class OrderService:
def __init__(self, order_repository: OrderRepository):
self.order_repository = order_repository
def create_order(self, customer_id: str) -> Order:
order = Order(str(uuid.uuid4()), customer_id)
self.order_repository.save(order)
return order
def add_product_to_order(self, order_id: str, product: Product, quantity: int):
order = self.order_repository.get_by_id(order_id)
if not order:
raise ValueError("Order not found")
order.add_item(product, quantity)
self.order_repository.save(order)
def confirm_order(self, order_id: str):
order = self.order_repository.get_by_id(order_id)
if not order:
raise ValueError("Order not found")
order.confirm()
self.order_repository.save(order)
6. Фабрики (Factories)
Фабрики отвечают за создание сложных объектов и агрегатов.
class OrderFactory:
@staticmethod
def create_order(customer_id: str, items: list[tuple[Product, int]]) -> Order:
order = Order(str(uuid.uuid4()), customer_id)
for product, quantity in items:
order.add_item(product, quantity)
return order
7. События предметной области (Domain Events)
События представляют факты, которые произошли в предметной области.
class DomainEvent(ABC):
@property
@abstractmethod
def occurred_on(self):
pass
class OrderConfirmedEvent(DomainEvent):
def __init__(self, order_id: str, customer_id: str, total_amount: float):
self.order_id = order_id
self.customer_id = customer_id
self.total_amount = total_amount
self._occurred_on = datetime.now()
@property
def occurred_on(self):
return self._occurred_on
class Order:
# ... предыдущий код ...
def confirm(self):
if not self.items:
raise ValueError("Cannot confirm empty order")
self.status = "confirmed"
self.events.append(
OrderConfirmedEvent(self.order_id, self.customer_id, self.total_amount())
)
def collect_events(self):
events = self.events.copy()
self.events.clear()
return events
P.S.
Фокус на предметной области в DDD позволяет создавать гибкие и поддерживаемые системы, которые точно отражают бизнес-требования. Python с его гибкостью и выразительностью отлично подходит для реализации DDD-подхода.
- Центрируйте разработку вокруг доменной модели.
- Четко разделяйте ответственности между компонентами.
- Инкапсулируйте бизнес-правила в доменных объектах.
- Управляйте сложностью через ограниченные контексты.
- Избегайте анемичных моделей.