Границы и связанность — Примеры
Пример 1 — Фреймворк, импортированный правилами
Доменная функция вычисляет сумму заказа из объекта запроса. Теперь обновление фреймворка означало бы правку бизнес-правил. Скажите, куда должна указывать зависимость, затем перенесите границу.
// domain/order.ts
import type { Request } from "express";
export function total(order: Order, req: Request): number {
return order.items.length ? order.sum * req.user.tierRate : 0;
}
Решение
// domain/order.ts — простые значения, без фреймворка
export function total(order: Order, tierRate: number): number {
return order.items.length ? order.sum * tierRate : 0;
}
// http/handler.ts — единственный файл, называющий фреймворк
router.get("/orders/:id", (req, res) => res.json(total(order, req.user.tierRate)));
Импорты должны указывать от изменчивого к стабильному. Веб-фреймворк меняется чаще самих правил и по причинам, с ними связанным меньше. Приняв req, домен получил фреймворк в качестве собственного типа, поэтому каждый выпуск фреймворка становился возможной правкой логики ценообразования, а передача простого значения tierRate уезжает в обработчик, где фреймворк и так живёт. Теперь зависимость указывает в одну сторону, а домен можно тестировать, вовсе не конструируя запрос.
Пример 2 — Изменение, доходящее до двенадцати модулей
Команда делит код по существительным: всё про заказ лежит в orders/. Квартальное изменение цен снова и снова заставляет править модуль целиком. Сосчитайте усиление, затем перечертите границу вокруг того, что меняется вместе.
orders/
order.ts форма заказа
pricing.ts оптовые и розничные ставки (меняется каждый квартал)
delivery.ts окна доставки (меняется раз в год)
invoice.ts макет счёта
изменение «поднять оптовую ставку» -> pricing.ts плюс 12 импортирующих файлов
Решение
pricing/ правила ставок (меняется каждый квартал)
delivery/ правила доставки (меняется раз в год)
orders/ импортирует оба; владеет только собственной формой заказа
изменение «поднять оптовую ставку» -> только pricing/
Разделение по существительным даёт аккуратную диаграмму, совпадающую с моделью данных. Однако оно группирует вещи, у которых причины меняться не связаны, поэтому правка цен тянет за собой код доставки и счетов, и каждое будущее изменение пересекает тот же шов. Полезный вопрос не в том, чем вещь является, а в том, что меняется вместе с ней: границы, проведённые вокруг скоростей изменения, дают каждому правилу один дом. А связанность становится видна как число файлов, которых касается типичное изменение, а не как число слоёв на диаграмме.