Этот вопрос проверяет, понимаете ли вы, как провести границы по доменам так, чтобы сервисы не превратились в “паутинообразную” систему взаимных вызовов.
Границы определяют через домены: у каждого домена — свои правила, данные и терминология. Сервис должен иметь понятного владельца данных и минимальные обязательные зависимости от соседей. Если между доменами есть пересечения, их оформляют как явные контракты (API/события), а не общие таблицы и “общие модели”. Также полезно искать “агрегаты”: какие сущности должны изменяться вместе в одной транзакции. Чем меньше совместных релизов — тем лучше границы.
Границы ответственности микросервисов — это решение о том, какие бизнес-правила, данные и операции принадлежат одному сервису так, чтобы изменения и эксплуатация были максимально автономными.
Разделение по доменам и поддоменам
Для каждого домена определить:
ключевые сущности,
инварианты (что “всегда должно быть верно”),
жизненный цикл данных.
Выделение агрегатов (что меняется вместе)
Если несколько сущностей должны быть согласованы в одной транзакции — это кандидат на единый сервис или единый агрегат внутри сервиса.
Если согласованность может быть “в итоге” (eventual consistency) — границы можно разнести.
Владение источником истины
Один сервис отвечает за запись и правила валидности “своих” данных.
Другие сервисы получают данные через:
запросы (sync),
события и проекции (async).
Контракты на пересечениях доменов
На стыке доменов формулируют:
какие события публикуются,
какие команды принимаются,
какие SLA и ошибки допустимы.
Антикоррупционный слой (ACL)
Если домены сильно отличаются по модели данных/терминам, делают преобразование на границе.
Это защищает домен от “протекания” чужих понятий.
В монолите на Django часто есть соблазн сделать “общие модели” и импортировать их везде. При выделении сервисов лучше рассматривать модель как внутреннюю деталь сервиса, а наружу отдавать DTO/контракт.
# Внутри сервиса "Billing" Django-модели могут быть любыми,
# но наружу лучше отдавать стабильный контракт.
def get_invoice_view(invoice_id: str) -> dict:
invoice = Invoice.objects.get(id=invoice_id)
return {
"invoice_id": str(invoice.id),
"status": invoice.status,
"total": str(invoice.total_amount),
}
Много “сквозных” сущностей, которые правятся в разных сервисах.
Частые циклические зависимости: A → B → C → A.
Массовые синхронные вызовы на один пользовательский запрос без таймаутов/деградации.
Границы ответственности лучше всего определяются через домен, агрегаты и владение данными, а на пересечениях фиксируются через явные контракты (API/ивенты) с защитным слоем.