Границы и связанность — Примеры

Пример 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/

Разделение по существительным даёт аккуратную диаграмму, совпадающую с моделью данных. Однако оно группирует вещи, у которых причины меняться не связаны, поэтому правка цен тянет за собой код доставки и счетов, и каждое будущее изменение пересекает тот же шов. Полезный вопрос не в том, чем вещь является, а в том, что меняется вместе с ней: границы, проведённые вокруг скоростей изменения, дают каждому правилу один дом. А связанность становится видна как число файлов, которых касается типичное изменение, а не как число слоёв на диаграмме.