> Насколько жестко проект был разделен на ограниченные контексты и агрегаты в DDD (JavaScript)

Уровень: senior · Роль: frontend · Язык: JavaScript · Категория: Технические вопросы

Компании: Mosline

Стек: Node.js, JavaScript

> Пример ответа

Короткий ответ

Проект был разделён на ограниченные контексты по бизнес-доменам: каталог, корзина, заказы, платежи, пользователи. Каждый контекст имел чёткие границы через API и изолированное хранилище. Агрегаты внутри контекстов были минимальными - например, Order как корень с OrderItem как value object. Жёсткость проявлялась в запрете прямого доступа к данным соседнего контекста и строгой валидации инвариантов внутри агрегата.

Подробное объяснение

Ограниченные контексты (bounded contexts) в этом проекте определялись на основе границ бизнес-логики, а не технических слоёв. Каждый контекст имел:

  • собственное хранилище (отдельная таблица или коллекция в БД)
  • явный API для взаимодействия (REST или events)
  • изолированную модель данных, не зависящую от других контекстов

Агрегаты проектировались с учётом консистентности: один агрегат - одна транзакция. Например, Cart содержал только CartItem как value objects, без ссылок на Product напрямую - только productId. Order включал OrderItem и ShippingAddress как value objects, а статус заказа менялся через методы агрегата с проверкой инвариантов (нельзя перейти из "delivered" в "pending").

Границы контекстов были жёсткими: cross-context запросы шли только через API или event bus, никогда через прямые SQL-запросы. Это давало слабую связанность, но требовало больше boilerplate для интеграции.

На практике

В реальной работе это выглядело так:

  • каждый контекст - отдельный модуль с чётким public API (например, CatalogService, OrderService)
  • внутри модуля - агрегаты с приватными полями и методами для изменения состояния
  • для cross-context данных использовались проекции (read models) - например, OrderSummaryView собиралась из событий OrderPlaced и ProductUpdated
  • транзакции не выходили за границы одного агрегата: если нужно обновить и заказ, и склад - использовалась сага (Saga pattern) через event bus

Типичный пример: при оформлении заказа агрегат Order проверял только свои инварианты (корректность суммы, статус). Проверка наличия товара делегировалась внешнему сервису через API, а результат приходил как событие.

Пример кода

JAVASCRIPT
// Агрегат Order в контексте заказов
class Order {
constructor(id, items, customerId) {
this.id = id;
this.items = items.map(item => new OrderItem(item.productId, item.quantity, item.price));
this.customerId = customerId;
this.status = 'pending';
this.total = this.calculateTotal();
}
calculateTotal() {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
confirm() {
if (this.status !== 'pending') {
throw new Error('Order can only be confirmed from pending status');
}
this.status = 'confirmed';
// событие для внешнего мира
this.events.push(new OrderConfirmedEvent(this.id, this.total));
}
addItem(productId, quantity, price) {
if (this.status !== 'pending') {
throw new Error('Cannot modify confirmed order');
}
this.items.push(new OrderItem(productId, quantity, price));
this.total = this.calculateTotal();
}
}
// Value object
class OrderItem {
constructor(productId, quantity, price) {
this.productId = productId;
this.quantity = quantity;
this.price = price;
}
}

Как отвечать на собеседовании

Начни с чёткого разделения контекстов и объясни, почему выбраны именно такие границы. Упомяни, что агрегаты проектировались с учётом консистентности и инвариантов. Приведи конкретный пример из практики, где жёсткое разделение помогло избежать проблем (например, race condition при параллельных заказах). Покажи понимание trade-off: жёсткие границы усложняют интеграцию, но дают изоляцию и масштабируемость. Не углубляйся в детали реализации БД или фреймворков - фокус на бизнес-логике.

Что проверяет интервьюер

  • понимание DDD-терминологии (bounded context, aggregate, value object, invariant)
  • умение применять DDD на практике, а не только в теории
  • способность объяснить, как жёсткость границ влияет на архитектуру и разработку
  • знание trade-off между изоляцией и сложностью интеграции
  • опыт работы с event-driven подходами (саги, проекции)

Типичные ошибки

  • путать агрегат с простой сущностью (например, делать Product агрегатом без чётких границ)
  • нарушать границы контекста: прямой вызов методов другого контекста или общая БД
  • игнорировать инварианты агрегата: позволять менять состояние извне без проверок
  • делать агрегаты слишком большими (god aggregate), что ломает консистентность
  • забывать про eventual consistency между контекстами и пытаться делать distributed transactions

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью