Services vs Shared
Где проходит граница между продуктовым сценарием и переносимой инфраструктурой.
Короткий ответ
Shared — код, который переехал бы в другой продукт без изменений. Services — код, который реализует сценарий этого продукта и от которого зависят другие модули. Это единственная граница в DMA, которую машина не решает полностью — остаётся эвристика и здравый смысл.
Эвристика одной фразой
Может ли этот код жить в другом продукте без изменений?
- Да →
shared/ - Нет →
services/
Кнопка, formatCurrency, базовый HTTP-клиент — shared. Корзина, checkout-flow, права на заказы продукта — services.
Почему оба слоя с входящими рёбрами
И services/, и shared/ имеют входящие рёбра от модулей. Различие не в графе, а в смысле кода:
| Services | Shared | |
|---|---|---|
| Знает продукт | Да | Нет |
| Пример | cart, session, permissions | Button, format-date, api-client |
public/ | Да, плоский | Не нужен — весь слой публичен |
| Группы внутри | Сегменты модуля (ui, model…) | ui/, lib/, api/, model/, domain/ |
Типичные ошибки
Слишком рано в shared. Вынесли хелпер «на будущее» — через месяц shared/lib превращается в свалку. Правило второго использования: сначала колокация, потом перенос при втором потребителе.
Продуктовый код в shared. shared/ui/cart-badge.tsx знает про корзину — это feature или service, не shared UI primitive.
Service без потребителей. Модуль в services/, от которого не зависит ни один другой модуль — нарушает предикат (service-no-inbound). Демотируйте обратно в feature или удалите.
Feature, от которого все зависят. Checkout импортируют catalog и profile — это service, не feature (feature-has-inbound).
Как принять решение на практике
- Появился второй потребитель? (
dma doctor→shared-candidate) - Код про этот продукт? →
services/, promotion из feature - Код переносимый? → нужная группа в
shared/(ui,lib,api,model,domain) - Сомневаетесь? Оставьте в feature до появления второго потребителя — колокация дешевле ошибки
Группы shared
shared/
├── ui/ # кнопки, инпуты — без знания домена
├── lib/ # даты, форматирование, storage
├── api/ # базовый HTTP-клиент, interceptors
├── model/ # query client, env, feature flags
└── domain/ # общие бизнес-типы (User, Money)Подробнее — Shared.
Services и домены
Когда services/ разрастается, режьте горизонтально (domains/billing/, отдельные пакеты) — не добавляйте новые вертикальные слои. Сигнал dense-services в doctor подскажет, что пора делить.
Связанные темы
- Слои — предикаты слоёв
- Эволюция кода — второе использование и promotion
- Почему два слоя модулей — зачем вообще деление feature/service