Bounded Context Перший крок — варто уникати створення єдиної, | Beer::PHP 🍺
Bounded Context
Перший крок — варто уникати створення єдиної, монолітної доменної моделі для всієї системи. Намагайтесь розділити модель і ваші обʼєкти на менші частини, кожна з яких відповідає за конкретний сценарій або контекст. Наприклад, якщо у нас є система для обробки замовлень інтернет магазину, то є обʼєкт (Order), але в різних частинах системи це слово означає різне:
Для створення замовлення важливі товари й ціна
Для оплати — номер картки й статус транзакції
Для доставки — адреса й дата
Обмежений контекст — це коли ми ділимо модель на маленькі шматочки, кожен із яких живе окремо і відповідає за конкретну задачу.
Як це працює?
Cтворюємо окремі класи для кожного контексту. Наприклад:
OrderCreation — для створення замовлення
OrderPayment — для оплати
OrderDelivery — для доставки
Кожен із них має тільки те, що йому потрібно, і не знає про існування інших.
[golang] [php] [python] [nodejs]
final readonly class OrderCreation {
/**
* @param OrderItem[] $items
*/
public function __construct(
private OrderId $orderId,
private UserId $userId,
private array $items
) {
}
}
// Контекст оплати замовлення
final readonly class OrderPayment {
public function __construct(
private OrderId $orderId,
private Money $sum
)
{
}
}
// Контекст доставки
final readonly class OrderDelivery {
public function __construct(
private OrderId $orderId,
private Address $address
)
{
}
}
// Використання
$orderCreation = new OrderCreation(
new OrderId($this->orderIdGenerator->generate()),
new UserId($this->userId),
[
new OrderItem(new ProductId('notebook'), $quantity),
new OrderItem(new ProductId('gamepad'), $quantity),
]
);
$orderPayment = new OrderPayment(
$orderCreation->orderId(),
new Money($this->total)
);
$orderDelivery = new OrderDelivery(
$orderCreation->orderId(),
new Address(
$this->city,
$this->street,
$this->house
)
);
Моделі (обʼєкти, сервіси, ентіті, хендлери і т.д.) в одному обмеженому контексті мають бути тісно пов’язані (cohesion) й працювати разом для однієї мети. Наприклад, у контексті OrderCreation це може бути додавання, видалення й оновлення товарів в замолені, бо це одна бізнес-здібність.
В той же час контекст не має сильно залежати від інших (loose coupling). Наприклад, OrderDelivery оперує саме OrderId а не цілим Order і зовсім нічого не знає про деталі оплати.
Переваги такого підходу:
1. Код стає простішим: кожен клас маленький і зрозумілий.
2. Легше тестувати: можна окремо перевіряти кожну частинку, не чіпаючи іншу.
3. Передаємо менше даних. Наприклад, OrderPayment випадково не потягне за собою адресу доставки, бо вона йому не потрібна.
Розділені контексти - це не раз і назавжди. З часом, коли проект росте, ви можете і маєте переглядати їх. В моноліті кордони між контекстами можуть бути малопомітні, адже виклик сервіса із сусідньої папки не викликає ніяких проблем. Проте варто за цим слідкувати. Як мінімум впровадити архітектурні тести (deptrac) з описаними правилами, котрий буде запускатись разом з тестами і статичним аналізатором.
#backend #architecture #middle #source