Software Architect · Модуль 05
Декомпозиция — это не раскладывание файлов по папкам. Это выбор границ, внутри которых изменения остаются локальными.
Cohesion · coupling · bounded context · ownership
Хорошая граница проходит там, где у системы естественно меняется ответственность, язык или скорость изменений.
Декомпозиция начинается с причин изменения
Если одну дверь постоянно двигают из-за склада, а другую из-за входа для гостей, это разные зоны здания. Их не нужно проектировать как одну стену.
Компонент с high cohesion содержит вещи, которые меняются по одной причине. Компонент с low coupling мало знает о соседях и общается через понятный контракт. Эти два свойства важнее названий паттернов.
Плохая декомпозиция часто выглядит аккуратно в IDE: controllers, services, repositories, utils. Но если маленькая продуктовая правка требует менять пять слоёв и три домена, система всё равно плохо разрезана. Хорошая декомпозиция позволяет изменить checkout, billing или identity без мысленной загрузки всего продукта.
Граница — это ещё и ownership
Карта города бесполезна, если никто не знает, кто чинит мосты, кто отвечает за светофоры и кому звонить при аварии.
Архитектурная граница должна иметь владельца. Если модуль «общий», но меняют его все, он быстро становится зоной риска. Если bounded context принадлежит команде, у неё появляется право менять внутреннюю модель и обязанность держать внешний контракт стабильным.
Поэтому decompositon и team topology связаны. Иногда правильный вопрос звучит не «какой сервис выделить», а «какая команда должна владеть этим потоком end-to-end».
Один и тот же домен можно разрезать по таблицам, слоям или бизнес-потокам. Работает обычно третий вариант.
Пример: checkout как вертикальный slice
Касса в магазине отвечает за путь покупки: корзина, цена, оплата, чек. Она не является «слоем кнопок» или «слоем SQL».
В e-commerce checkout может владеть pricing snapshot, payment intent, order draft и failure handling. Catalog остаётся владельцем товаров, identity — пользователей, warehouse — остатков. Checkout не копирует чужую логику, а запрашивает данные через контракты и публикует событие OrderPlaced.
Такой vertical slice удобен: команда понимает пользовательский поток целиком, а изменения в оплате не расползаются по всем подсистемам.
Антипример: декомпозиция по техническим слоям как единственная граница
Если все повара режут овощи в одной комнате, жарят в другой, а солят в третьей, простой заказ начинает требовать координации всего здания.
Система разделена на user-service, order-service, payment-service, но внутри каждого сервиса есть общий common-domain, общая база и shared validators. Формально сервисы есть, фактически границ нет.
Ещё хуже, когда команды владеют слоями: frontend team, backend team, database team. Любая фича становится проектом координации. Это увеличивает lead time и cognitive load.
- Что является причиной изменения этого модуля? - Может ли команда изменить его без согласования с пятью соседями? - Есть ли у границы владелец? - Какие данные и инварианты действительно принадлежат этому контексту?