The model
A module groups decisions that are likely to change together and hides details other modules do not need. A useful boundary reduces the number of places that must change for one product decision. The public interface should express stable intent rather than expose internal storage or control flow.
A concrete walk-through
If billing and email share a database table but change for different reasons, each may be better represented by its own module even when both run in one process. A narrow interface can let the implementation change from a local adapter to a remote service without rewriting every caller.
Costs and failure cases
More modules do not automatically mean less complexity: excessive fragmentation adds indirection and coordination cost. A boundary drawn around technical layers may still couple unrelated product rules. Strong boundaries usually emerge from repeated change patterns and explicit ownership.
Check your understanding
Choose a boundary for a feature that sends a receipt after a purchase. Decide which module owns payment state, receipt formatting, and delivery retries, and explain how dependencies flow.