Skip to content
/patternsdocs
Using patterns

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 foldersOrganize a React App by Feature
Business logic is coupled to HTTP, databases, queues, or vendorsBuild a Framework-Independent TypeScript Service
One backend has tangled capabilities but does not need microservicesStructure One Backend with Strong Module Boundaries
Several monorepo apps duplicate or drift on data contractsShare Type-Safe Contracts Across Monorepo Apps
Replacing storage would rewrite application policyReplace Database Adapters Safely
Each backend feature changes across controller, service, and repository layersOrganize 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.

On this page