Choose a pattern by problem
Use the smallest pattern that addresses the change pressure or boundary you actually have.
Architecture labels are useful search terms, but poor starting requirements. Begin with the repeated cost you need to remove.
| If this is happening… | Start with… |
|---|---|
| A React change crosses global component, state, API, and test folders | Organize a React App by Feature |
| Business logic is coupled to HTTP, databases, queues, or vendors | Build a Framework-Independent TypeScript Service |
| One backend has tangled capabilities but does not need microservices | Structure One Backend with Strong Module Boundaries |
| Several monorepo apps duplicate or drift on data contracts | Share Type-Safe Contracts Across Monorepo Apps |
| Replacing storage would rewrite application policy | Replace Database Adapters Safely |
| Each backend feature changes across controller, service, and repository layers | Organize Backend Features by Use Case |
Compose deliberately
Patterns can fit at different boundaries. A modular monolith can contain use-case-organized operations. A vertical slice can use a hexagonal port when one external dependency deserves isolation. A replaceable repository adapter can implement that persistence port.
This does not mean every project should install all three. Each abstraction adds navigation, contracts, tests, and maintenance. Install a pattern only when its explicit benefit exceeds that cost.
Check “avoid when” first
The fastest way to reject a pattern is often its “Avoid it when” section. Small CRUD services, short-lived prototypes, or cohesive algorithms may be made worse by boundaries designed for larger change surfaces.