Допустимо ли перекрещивание ссылок между модулями в классическом монолите
Что проверяют
Понимаете ли вы, что монолит не значит «всё связано со всем», и умеете ли держать границы модулей внутри одного приложения. Следом обычно просят показать, как разорвать уже существующий цикл и как не дать ему появиться снова.
Как отвечать
Короткий ответ: зависимости в одну сторону допустимы, циклические нет. Дальше разберите по уровням:
- проекты: если модули разнесены по отдельным
.csproj, цикл ссылок сборка не допустит, Visual Studio просто откажется добавлять ссылку. Это самый дешёвый способ держать границы; - папки и неймспейсы: внутри одной сборки компилятор циклы не видит,
Ordersспокойно зовётBilling, аBillingзовётOrders. Так монолит и превращается в «большой ком грязи»: модуль уже нельзя поменять, протестировать или вынести отдельно; - база: скрытый цикл часто живёт в данных. Если
DbContextодного модуля читает таблицы другого, связность есть, даже когда ссылок между классами нет.
Способы разорвать цикл:
- инверсия зависимостей: модуль публикует контракт в отдельной сборке вида
Billing.Contracts, остальные зависят только от неё, реализация остаётсяinternal; - события: вместо прямого вызова модуль публикует доменное событие, а заинтересованный модуль подписывается на него через внутреннюю шину или outbox;
- вынос общего: общий кусок уходит в третий модуль. Иногда честнее признать, что два модуля на деле один, и объединить их;
- контроль: архитектурные тесты на NetArchTest или ArchUnitNET падают в CI, если кто-то добавил запрещённую зависимость.
Хорошо упомянуть модульный монолит как целевую форму: один деплой, но у модулей явный публичный API и свои схемы в базе.
Где спотыкаются
Отвечают «в монолите можно, это же одна сборка». Интервьюер ждёт обратного: понимания, во что цикл превратится через год.
Кладут интерфейс рядом с реализацией в том же модуле. Зависимость от модуля при этом никуда не девается, контракт должен лежать отдельно или на стороне потребителя.
Разрывают циклы через общий проект Common, куда постепенно переезжает половина логики. Получается новый центр связности, только с другим названием.