[Entity и Value Object в DDD]
Привет! В DDD сразу встречаешь два ключевых понятия — Entity и Value Object. Оба вроде как про данные, но ведут себя по-разному.
Что такое `Entity`?
Что такое `Value Object`?
Когда что использовать?
- Если объект уникален сам по себе — это
- Если важны только его значения — это
Фишка DDD:
Хорошая практика — делать
#DDD #Python #Entity #ValueObject #Программирование
Привет! В DDD сразу встречаешь два ключевых понятия — Entity и Value Object. Оба вроде как про данные, но ведут себя по-разному.
Что такое `Entity`?
Entity
— это объект, который имеет уникальный идентификатор и жизненный цикл. Его идентичность важна, и она сохраняется на протяжении всего времени существования объекта. Например, пользователь в системе (User) — это Entity
, потому что у него есть уникальный ID, и даже если его имя или email изменятся, это все равно будет тот же самый пользователь.from dataclasses import dataclass, field
import uuid
@dataclass
class User:
name: str
email: str
id: uuid.UUID = field(default_factory=uuid.uuid4, init=False)
def __eq__(self, other):
if isinstance(other, User):
return self.id == other.id
return False
# Пример использования:
user1 = User("Иван", "ivan@example.com")
user2 = User("Иван", "ivan@example.com")
print(user1) # User(name='Иван', email='ivan@example.com', id=...)
print(user2) # User(name='Иван', email='ivan@example.com', id=...)
print(user1 == user2) # False, потому что ID разные
Что такое `Value Object`?
Value Object
, в отличие от Entity
, не имеет идентичности. Он определяется своими атрибутами, и если все атрибуты одинаковы, то два Value Object
считаются равными. Value Object
обычно неизменяемы (immutable), что делает их более предсказуемыми и безопасными в использовании.from dataclasses import dataclass
import re
@dataclass(frozen=True)
class EmailAddress:
value: str
def __post_init__(self):
if not self._is_valid_email(self.value):
raise ValueError(f"Некорректный email: {self.value}")
@staticmethod
def _is_valid_email(email: str) -> bool:
pattern = r"^[\w\.-]+@[\w\.-]+\.\w+$"
return re.match(pattern, email) is not None
# Пример использования:
try:
email1 = EmailAddress("valid.email@example.com")
print(email1) # EmailAddress(value='valid.email@example.com')
email2 = EmailAddress("invalid-email") # Ошибка валидации!
except ValueError as e:
print(e)
Когда что использовать?
- Если объект уникален сам по себе — это
Entity
.- Если важны только его значения — это
Value Object
.Фишка DDD:
Хорошая практика — делать
Value Objects
иммутабельными и использовать их для инкапсуляции логики. Например, не просто string
для email
, а EmailAddress
, который сразу валидирует значение.Entity
и Value Object
— это мощные концепции, которые помогают структурировать код и делать его более предсказуемым. В Python их реализация может быть элегантной с использованием dataclasses
.#DDD #Python #Entity #ValueObject #Программирование
[Агрегаты в DDD: Организация сложности через границы]
В Domain-Driven Design (DDD) работа с сложными доменными моделями требует четкой структуры, чтобы избежать хаоса.
Агрегат (Aggregate) — один из ключевых паттернов DDD, который помогает управлять целостностью данных и упрощает взаимодействие с моделью.
Что такое Aggregate?
Агрегат — это кластер связанных объектов, рассматриваемых как единое целое.
Он объединяет:
1) Корень агрегата (Aggregate Root) — единственная точка входа для внешних взаимодействий.
2) Внутренние сущности (Entity) и value-объекты (ValueObject) — элементы, которые могут изменяться только через корень.
По своей сути агрегат - это тоже Entity.
Зачем нужны агрегаты?
- Консистентность данных
Агрегат гарантирует, что изменения внутри него соответствуют бизнес-правилам. Например, нельзя добавить OrderItem с отрицательной ценой — корень проверяет это.
- Границы транзакций
Изменения в рамках одного агрегата обычно выполняются в одной транзакции. Это упрощает управление конкурентным доступом.
- Сокрытие сложности
Внешние системы не знают о внутренней структуре агрегата — они работают только с корнем.
Ключевые принципы проектирования:
- Единый корень
Все внешние запросы идут через корень. Если нужно изменить дочерний объект — делайте это через методы корня.
- Инварианты внутри границ
Правила целостности (например, «максимум 10 товаров в заказе») должны соблюдаться внутри агрегата.
- Ссылки только на корни других агрегатов
Агрегаты не должны хранить ссылки на внутренние объекты чужих агрегатов — только на их корни (через идентификаторы).
Пример:
Агрегат Заказ (корень) включает элементы заказа (OrderItem), адрес доставки и методы для добавления/удаления товаров. Внешние системы обращаются только к Order, а не к OrderItem напрямую.
Ошибки при работе с агрегатами:
- Слишком большие агрегаты
Если агрегат включает десятки сущностей, это усложняет транзакции и повышает риск конфликтов.
Решение: Дробите агрегаты, ориентируясь на бизнес-контекст.
- Нарушение инкапсуляции
Прямое изменение дочерних объектов в обход корня ломает целостность.
Решение: Скрывайте внутренние структуры (private поля, методы только для корня).
Как определить границы агрегата?
- Ищите транзакционные границы — что должно изменяться атомарно?
- Анализируйте бизнес-инварианты — какие правила связывают объекты?
- Избегайте «анаемичных» агрегатов — они должны содержать логику, а не быть просто набором данных.
———
Итого
Агрегаты в DDD — это не просто группировка классов, а способ защитить целостность домена и управлять сложностью.
Правильно выбранные границы агрегатов делают код:
- Более понятным,
- Устойчивым к ошибкам,
- Легким для масштабирования.
Главное правило:
Если сомневаетесь — делайте агрегаты меньше. Четкие границы спасут в долгосрочной перспективе!
P.s.
Полезная ссылка про агрегаты: https://dddinpython.hashnode.dev/mastering-aggregates-in-domain-driven-design
#DDD #Python #Aggregate #Entity #ValueObject #Программирование
В Domain-Driven Design (DDD) работа с сложными доменными моделями требует четкой структуры, чтобы избежать хаоса.
Агрегат (Aggregate) — один из ключевых паттернов DDD, который помогает управлять целостностью данных и упрощает взаимодействие с моделью.
Что такое Aggregate?
Агрегат — это кластер связанных объектов, рассматриваемых как единое целое.
Он объединяет:
1) Корень агрегата (Aggregate Root) — единственная точка входа для внешних взаимодействий.
2) Внутренние сущности (Entity) и value-объекты (ValueObject) — элементы, которые могут изменяться только через корень.
По своей сути агрегат - это тоже Entity.
Зачем нужны агрегаты?
- Консистентность данных
Агрегат гарантирует, что изменения внутри него соответствуют бизнес-правилам. Например, нельзя добавить OrderItem с отрицательной ценой — корень проверяет это.
- Границы транзакций
Изменения в рамках одного агрегата обычно выполняются в одной транзакции. Это упрощает управление конкурентным доступом.
- Сокрытие сложности
Внешние системы не знают о внутренней структуре агрегата — они работают только с корнем.
Ключевые принципы проектирования:
- Единый корень
Все внешние запросы идут через корень. Если нужно изменить дочерний объект — делайте это через методы корня.
- Инварианты внутри границ
Правила целостности (например, «максимум 10 товаров в заказе») должны соблюдаться внутри агрегата.
- Ссылки только на корни других агрегатов
Агрегаты не должны хранить ссылки на внутренние объекты чужих агрегатов — только на их корни (через идентификаторы).
Пример:
Агрегат Заказ (корень) включает элементы заказа (OrderItem), адрес доставки и методы для добавления/удаления товаров. Внешние системы обращаются только к Order, а не к OrderItem напрямую.
Ошибки при работе с агрегатами:
- Слишком большие агрегаты
Если агрегат включает десятки сущностей, это усложняет транзакции и повышает риск конфликтов.
Решение: Дробите агрегаты, ориентируясь на бизнес-контекст.
- Нарушение инкапсуляции
Прямое изменение дочерних объектов в обход корня ломает целостность.
Решение: Скрывайте внутренние структуры (private поля, методы только для корня).
Как определить границы агрегата?
- Ищите транзакционные границы — что должно изменяться атомарно?
- Анализируйте бизнес-инварианты — какие правила связывают объекты?
- Избегайте «анаемичных» агрегатов — они должны содержать логику, а не быть просто набором данных.
———
Итого
Агрегаты в DDD — это не просто группировка классов, а способ защитить целостность домена и управлять сложностью.
Правильно выбранные границы агрегатов делают код:
- Более понятным,
- Устойчивым к ошибкам,
- Легким для масштабирования.
Главное правило:
Если сомневаетесь — делайте агрегаты меньше. Четкие границы спасут в долгосрочной перспективе!
P.s.
Полезная ссылка про агрегаты: https://dddinpython.hashnode.dev/mastering-aggregates-in-domain-driven-design
#DDD #Python #Aggregate #Entity #ValueObject #Программирование
[Domain Services в DDD: Логика, которая не принадлежит агрегатам]
(Когда и зачем использовать доменные сервисы?)
Продолжаю знакомиться с DDD. Следующий паттерн Domain Services.
Domain Service - это класс, который реализует бизнес-правила, выходящие за рамки одного агрегата.
В Domain-Driven Design (DDD) не вся бизнес-логика уместна внутри агрегатов или сущностей.
Иногда операции:
- Затрагивают несколько агрегатов,
- Зависят от внешних систем (например, проверка кредитного рейтинга),
- Не имеют естественного места в какой-то одной сущности.
Для такой логики создают Domain Services (доменные сервисы).
Отличия от других сервисов в DDD
Когда использовать Domain Service?
✅ Логика требует нескольких агрегатов.
✅ Зависит от доменных правил, но не принадлежит ни одной сущности (например, проверка сложных условий).
✅ Чистая бизнес-логика без инфраструктурных деталей.
❌ Не используйте, если:
- Логика относится к одному агрегату (лучше поместить в агрегат).
- Нужен доступ к БД, API и т.д. (это Application/Infrastructure Service).
✔️ Плюсы:
- Четкое разделение ответственности.
- Удобство тестирования (чистая логика без побочных эффектов).
❌ Минусы:
- Риск превращения в "God Object" (если сервис делает слишком много).
Domain Services — это мост между агрегатами для сложной доменной логики.
P.s.
Полезная ссылка по теме:
https://enterprisecraftsmanship.com/posts/domain-vs-application-services/
#DDD #DomainServices #CleanArchitecture
(Когда и зачем использовать доменные сервисы?)
Продолжаю знакомиться с DDD. Следующий паттерн Domain Services.
Domain Service - это класс, который реализует бизнес-правила, выходящие за рамки одного агрегата.
В Domain-Driven Design (DDD) не вся бизнес-логика уместна внутри агрегатов или сущностей.
Иногда операции:
- Затрагивают несколько агрегатов,
- Зависят от внешних систем (например, проверка кредитного рейтинга),
- Не имеют естественного места в какой-то одной сущности.
Для такой логики создают Domain Services (доменные сервисы).
Отличия от других сервисов в DDD
Domain Service
— содержит только бизнес-логику, не зависит от инфраструктуры.Application Service
— оркестрирует вызовы Domain-сервисов и инфраструктуры (например, «создать заказ → сохранить в БД → отправить уведомление»).Infrastructure Service
— технические детали (отправка почты, запросы к API).Когда использовать Domain Service?
✅ Логика требует нескольких агрегатов.
✅ Зависит от доменных правил, но не принадлежит ни одной сущности (например, проверка сложных условий).
✅ Чистая бизнес-логика без инфраструктурных деталей.
❌ Не используйте, если:
- Логика относится к одному агрегату (лучше поместить в агрегат).
- Нужен доступ к БД, API и т.д. (это Application/Infrastructure Service).
✔️ Плюсы:
- Четкое разделение ответственности.
- Удобство тестирования (чистая логика без побочных эффектов).
❌ Минусы:
- Риск превращения в "God Object" (если сервис делает слишком много).
Domain Services — это мост между агрегатами для сложной доменной логики.
P.s.
Полезная ссылка по теме:
https://enterprisecraftsmanship.com/posts/domain-vs-application-services/
#DDD #DomainServices #CleanArchitecture
[Репозитории в DDD: как правильно работать с данными в доменном слое]
Привет! Сегодня разберём ещё одну важную концепцию Domain-Driven Design — Репозитории (Repositories). Когда я только начал изучать DDD, репозитории казались мне просто "прослойкой над базой данных". Давайте разберёмся, зачем они нужны и как их правильно использовать!
Репозиторий (Repository) — это абстракция, которая:
✔️ Инкапсулирует доступ к данным (БД, API, файлы и т. д.).
✔️ Предоставляет "коллекцию" доменных объектов (как если бы они хранились в памяти).
✔️ Работает только с Aggregate Roots (не с отдельными Entity или Value Object!).
Пример из жизни:
Если
- Сохранять заказ (save).
- Загружать заказ по ID (find_by_id).
- Искать заказы по критериям (find_all_by_customer).
Но он НЕ даёт прямого доступа к
Ниже пример кода.
Почему репозитории — это важно?
1) Отделение домена от инфраструктуры
Домен не знает, как хранятся данные (БД, API, CSV и т. д.).
2) Гибкость
Можно легко поменять базу данных, не трогая доменную логику.
3) Соблюдение DDD-принципов
Работа только с агрегатами, а не с отдельными таблицами БД.
Где должны находиться репозитории в структуре проекта?
Репозитории относятся к инфраструктурному слою, но их интерфейсы (абстракции) определяются в доменном слое.
Ниже пример классической структуры проекта по слоям DDD.
```bash
src/
├── domain/
│ ├── models/
│ ├── repositories/
│ └── services/
│
├── infrastructure/
│ ├── db/
│ ├── repositories/
│ └── caching/
│
├── application/
│ └── services/
│
└── presentation/
```
Типичные ошибки
❌ Репозиторий для каждой Entity
Репозиторий должен быть только для Aggregate Root!
❌ Возвращение DTO вместо доменных объектов
Репозиторий должен возвращать
❌ Нарушение CQRS
Для запросов (Read) иногда лучше использовать отдельные запросы в cлое приложения, а не репозитории.
P.s.
Полезные ссылки по теме:
1) https://dddinpython.hashnode.dev/implementing-the-repository-pattern
2) https://dddinpython.hashnode.dev/implementing-the-repository-pattern-using-sqlalchemy
#DDD #Repository #DomainDrivenDesign #Программирование #Python
Привет! Сегодня разберём ещё одну важную концепцию Domain-Driven Design — Репозитории (Repositories). Когда я только начал изучать DDD, репозитории казались мне просто "прослойкой над базой данных". Давайте разберёмся, зачем они нужны и как их правильно использовать!
Репозиторий (Repository) — это абстракция, которая:
✔️ Инкапсулирует доступ к данным (БД, API, файлы и т. д.).
✔️ Предоставляет "коллекцию" доменных объектов (как если бы они хранились в памяти).
✔️ Работает только с Aggregate Roots (не с отдельными Entity или Value Object!).
Пример из жизни:
Если
Order
— это агрегат, то OrderRepository позволяет:- Сохранять заказ (save).
- Загружать заказ по ID (find_by_id).
- Искать заказы по критериям (find_all_by_customer).
Но он НЕ даёт прямого доступа к
OrderItem
— только через корень (Order
).Ниже пример кода.
from abc import ABC, abstractmethod
from uuid import UUID
from typing import List, Optional
from .order import Order # Агрегат
class OrderRepository(ABC):
"""Абстракция репозитория"""
@abstractmethod
def save(self, order: Order) -> None:
pass
@abstractmethod
def find_by_id(self, order_id: UUID) -> Optional[Order]:
pass
@abstractmethod
def find_all_by_customer(self, customer_id: UUID) -> List[Order]:
pass
from sqlalchemy.orm import Session
class SQLOrderRepository(OrderRepository):
"""SQL реализация (через SQLAlchemy)"""
def __init__(self, session: Session):
self.session = session
def save(self, order: Order) -> None:
self.session.add(order)
self.session.commit()
def find_by_id(self, order_id: UUID) -> Optional[Order]:
return self.session.query(Order).filter_by(order_id=order_id).first()
def find_all_by_customer(self, customer_id: UUID) -> List[Order]:
return self.session.query(Order).filter_by(customer_id=customer_id).all()
Почему репозитории — это важно?
1) Отделение домена от инфраструктуры
Домен не знает, как хранятся данные (БД, API, CSV и т. д.).
2) Гибкость
Можно легко поменять базу данных, не трогая доменную логику.
3) Соблюдение DDD-принципов
Работа только с агрегатами, а не с отдельными таблицами БД.
Где должны находиться репозитории в структуре проекта?
Репозитории относятся к инфраструктурному слою, но их интерфейсы (абстракции) определяются в доменном слое.
Ниже пример классической структуры проекта по слоям DDD.
```bash
src/
├── domain/
│ ├── models/
│ ├── repositories/
│ └── services/
│
├── infrastructure/
│ ├── db/
│ ├── repositories/
│ └── caching/
│
├── application/
│ └── services/
│
└── presentation/
```
Типичные ошибки
❌ Репозиторий для каждой Entity
Репозиторий должен быть только для Aggregate Root!
❌ Возвращение DTO вместо доменных объектов
Репозиторий должен возвращать
Order
, а не OrderData
.❌ Нарушение CQRS
Для запросов (Read) иногда лучше использовать отдельные запросы в cлое приложения, а не репозитории.
P.s.
Полезные ссылки по теме:
1) https://dddinpython.hashnode.dev/implementing-the-repository-pattern
2) https://dddinpython.hashnode.dev/implementing-the-repository-pattern-using-sqlalchemy
#DDD #Repository #DomainDrivenDesign #Программирование #Python
[Use Case в DDD: как проектировать сценарии взаимодействия с системой]
Привет! Сегодня поговорим про Use Case (Сценарии использования) — один из ключевых элементов DDD и чистой архитектуры.
1. Что такое Use Case?
Use Case (Сценарий использования) — это:
✔️ Описание конкретного бизнес-действия (например, "Оформление заказа", "Отмена бронирования").
✔️ Изолированная логика, которая координирует работу домена и инфраструктуры.
✔️ Мост между presentation (API/UI) и domain (бизнес-правила).
Примеры:
2. Где находятся Use Case в структуре проекта?
Они относятся к application-слою (слое приложения):
3. Пример use case "Создание заказа"
4. Почему Use Case — это важно?
- Чёткое разделение ответственности
Presentation-слой (API/CLI) не должен содержать бизнес-логику.
Domain-слой не должен знать о внешних сервисах (API, БД).
- Упрощение тестирования
Use Case можно тестировать изолированно, подменяя репозитории на заглушки.
- Гибкость
Один Use Case может быть вызван из разных мест: API, CLI, фоновой задачи.
- Документирование системы
Названия Use Cases (CreateOrder, CancelBooking) явно описывают, что делает система.
5. Типичные ошибки
❌ Use Case = "God Object"
Не нужно пихать всю логику в один Use Case. Разбивайте на мелкие сценарии.
❌ Логика в контроллерах
Код вида
#DDD #UseCase #CleanArchitecture #Python
Привет! Сегодня поговорим про Use Case (Сценарии использования) — один из ключевых элементов DDD и чистой архитектуры.
1. Что такое Use Case?
Use Case (Сценарий использования) — это:
✔️ Описание конкретного бизнес-действия (например, "Оформление заказа", "Отмена бронирования").
✔️ Изолированная логика, которая координирует работу домена и инфраструктуры.
✔️ Мост между presentation (API/UI) и domain (бизнес-правила).
Примеры:
CreateOrderUseCase
→ создание заказа.CancelBookingUseCase
→ отмена бронирования.ProcessPaymentUseCase
→ обработка платежа.2. Где находятся Use Case в структуре проекта?
Они относятся к application-слою (слое приложения):
src/
├── domain/
├── application/ # Use Cases + Application Services/CQS
│ ├── use_cases/
│ │ ├── commands/
│ │ └── queries/
│ └── services/
├── infrastructure/
└── api/
3. Пример use case "Создание заказа"
# /delivery/core/application/use_cases/commands/create_order/create_order_handler.py
from dataclasses import dataclass
from core.domain.model.order_aggregate.order import Order
from core.domain.model.shared_kernel.location import Location
from core.application.use_cases.commands.create_order.create_order_command import CreateOrderCommand
from infrastructure.adapters.postgres.unit_of_work import UnitOfWork
@dataclass
class CreateOrderCommandHandler:
unit_of_work: UnitOfWork
def handle(self, massege: CreateOrderCommand) -> None:
# Получаем геопозицию из Geo (пока ставим рандомное значение)
location = Location.create_random_location()
# Создаем заказ
order = Order(
order_id=massege.basket_id, # ID заказа совпадает с ID корзины
location=location
)
# Сохраняем
with self.unit_of_work as uow:
uow.orders.add(order)
4. Почему Use Case — это важно?
- Чёткое разделение ответственности
Presentation-слой (API/CLI) не должен содержать бизнес-логику.
Domain-слой не должен знать о внешних сервисах (API, БД).
- Упрощение тестирования
Use Case можно тестировать изолированно, подменяя репозитории на заглушки.
- Гибкость
Один Use Case может быть вызван из разных мест: API, CLI, фоновой задачи.
- Документирование системы
Названия Use Cases (CreateOrder, CancelBooking) явно описывают, что делает система.
5. Типичные ошибки
❌ Use Case = "God Object"
Не нужно пихать всю логику в один Use Case. Разбивайте на мелкие сценарии.
❌ Логика в контроллерах
Код вида
if user.is_admin: ...
должен быть в Use Case или домене, не в API.#DDD #UseCase #CleanArchitecture #Python
[Input Adapters в DDD: как внешний мир общается с приложением]
Привет! Сегодня про Input Adapters — элемент гексагональной архитектуры (и DDD), который отвечает за преобразование внешних запросов (HTTP, CLI, события) в команды приложения.
1. Что это такое?
Input Adapter — это не только про REST-эндпоинты, а это единая точка входа для всех внешних взаимодействий.
Он:
- Парсит входящие данные (JSON, protobuf, CLI-аргументы).
- Валидирует их (например, через Pydantic).
- Вызывает Use Case (Command или Query).
- Не содержит бизнес-логики (это задача домена).
Примеры Input Adapters:
- REST API (FastAPI, Django View).
- CLI-команды (Click, Typer).
- Обработчики событий (Kafka Consumer).
- WebSocket-хэндлеры.
Ниже пример REST API
2. Где находятся Input Adapters в структуре проекта?
Они относятся к presentation-слою, но могут выноситься в отдельный модуль для сложных сценариев:
3. Почему Input Adapters — это важно?
- Изоляция домена
Домен не знает, откуда пришёл запрос (HTTP, Kafka или CLI).
- Гибкость
Можно добавить новый адаптер (например, gRPC), не меняя Use Case.
- Тестируемость
Адаптеры легко тестировать в изоляции (например, мокая Use Case).
- Согласованность
Все входные данные проходят одинаковую валидацию перед передачей в Use Case.
4. Типичные ошибки
❌ Бизнес-логика в адаптерах
Код вида if user.role == "admin" должен быть в домене, а не в REST-хэндлере.
❌ Прямая работа с репозиториями
Адаптер вызывает только Use Case, не лезет в БД напрямую.
5. Вывод
Input Adapters — это "входные ворота" приложения, которые:
🔹 Преобразуют внешние запросы в команды/запросы.
🔹 Не содержат логики (только парсинг и валидацию).
🔹 Делают систему гибкой к изменениям протоколов.
#DDD #CleanArchitecture #InputAdapters #Python
Привет! Сегодня про Input Adapters — элемент гексагональной архитектуры (и DDD), который отвечает за преобразование внешних запросов (HTTP, CLI, события) в команды приложения.
1. Что это такое?
Input Adapter — это не только про REST-эндпоинты, а это единая точка входа для всех внешних взаимодействий.
Он:
- Парсит входящие данные (JSON, protobuf, CLI-аргументы).
- Валидирует их (например, через Pydantic).
- Вызывает Use Case (Command или Query).
- Не содержит бизнес-логики (это задача домена).
Примеры Input Adapters:
- REST API (FastAPI, Django View).
- CLI-команды (Click, Typer).
- Обработчики событий (Kafka Consumer).
- WebSocket-хэндлеры.
Ниже пример REST API
# presentation/api/rest/order_adapters.py
from fastapi import APIRouter, Depends
from application.commands import CreateOrderCommand
from presentation.schemas import OrderCreateRequest
router = APIRouter()
@router.post("/orders")
async def create_order(
request: OrderCreateRequest, # Pydantic-модель для валидации
command: CreateOrderCommand = Depends(), # Use Case
):
# Преобразуем запрос в DTO (без бизнес-логики!)
order_id = command.execute(
user_id=request.user_id,
items=[item.to_domain() for item in request.items]
)
return {"order_id": order_id}
2. Где находятся Input Adapters в структуре проекта?
Они относятся к presentation-слою, но могут выноситься в отдельный модуль для сложных сценариев:
src/
├── domain/
├── application/
├── infrastructure/
└── presentation/ # Input Adapters
├── api/ # HTTP-адаптеры
│ ├── rest/ # REST (FastAPI)
│ └── graphql/ # GraphQL
├── cli/ # CLI-адаптеры (Click)
└── events/ # Обработчики событий (Kafka, RabbitMQ)
3. Почему Input Adapters — это важно?
- Изоляция домена
Домен не знает, откуда пришёл запрос (HTTP, Kafka или CLI).
- Гибкость
Можно добавить новый адаптер (например, gRPC), не меняя Use Case.
- Тестируемость
Адаптеры легко тестировать в изоляции (например, мокая Use Case).
- Согласованность
Все входные данные проходят одинаковую валидацию перед передачей в Use Case.
4. Типичные ошибки
❌ Бизнес-логика в адаптерах
Код вида if user.role == "admin" должен быть в домене, а не в REST-хэндлере.
❌ Прямая работа с репозиториями
Адаптер вызывает только Use Case, не лезет в БД напрямую.
5. Вывод
Input Adapters — это "входные ворота" приложения, которые:
🔹 Преобразуют внешние запросы в команды/запросы.
🔹 Не содержат логики (только парсинг и валидацию).
🔹 Делают систему гибкой к изменениям протоколов.
#DDD #CleanArchitecture #InputAdapters #Python