Boundaries and coupling — Examples
Example 1 — The framework imported by the rules
A domain function computes an order total from the request object. A framework upgrade would now mean editing business rules. Say which direction the dependency should point, then move the boundary.
// 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;
}
Solution
// domain/order.ts — plain values, no framework
export function total(order: Order, tierRate: number): number {
return order.items.length ? order.sum * tierRate : 0;
}
// http/handler.ts — the only file that names the framework
router.get("/orders/:id", (req, res) => res.json(total(order, req.user.tierRate)));
Imports should point from the volatile toward the stable, and a web framework changes more often, and for reasons less related to the rules, than the rules do. Taking req made the framework a type inside the domain, so every framework release became a possible change to pricing logic. Passing a plain tierRate moves the adaptation into the handler, where the framework already lives. The dependency now points one way, and the domain can be tested without constructing a request at all.
Example 2 — A change that reaches twelve modules
A team splits its code by noun: everything about an order sits under orders/. A quarterly pricing change keeps forcing edits across the module. Count the amplification, then redraw the boundary around what changes together.
orders/
order.ts order shape
pricing.ts wholesale and retail rates (changes quarterly)
delivery.ts delivery windows (changes yearly)
invoice.ts invoice layout
change "raise the wholesale rate" -> pricing.ts plus 12 importing files
Solution
pricing/ the rate rules (changes quarterly)
delivery/ the delivery rules (changes yearly)
orders/ imports both; owns only the order's own shape
change "raise the wholesale rate" -> pricing/ only
Splitting by noun produces a tidy diagram that matches the data model, but it groups things whose reasons to change are unrelated, so a pricing edit drags delivery and invoice code along and every future change spans the same seam. The useful question is not what a thing is but what changes with it. Boundaries drawn around rates of change give each rule one home, and the coupling becomes visible as the number of files a typical change touches rather than as a layer count on a diagram.