7 lessons

Resource & Capability Modeling

From domain to resources, from resources to operations. Resource vs action, state machines with explicit transitions, backend-for-frontend, and composition — without REST dogma.

RequirementConsumersResource ModelStyleContractValidationAuthorizationErrorsIdempotencyPaginationVersioningObservabilityEvolutionTrade-offs

Every lesson below names the consumers, the design question and the guarantee before recommending anything. Recommendations come with what they cost, when not to use them, and how they evolve.

From Domain to Resources

Resources are the nouns your consumers need to point at — not your tables, not your classes. Deriving /users, /projects, /memberships and /invitations from one requirement shows the reasoning; the paths are just the residue.

Q · Which concepts in this domain deserve a stable, addressable identity in the contract — and which are just attributes of something else?
Resource or Action?

POST /cancelOrder, POST /orders/{id}/cancellations, PATCH {status: "cancelled"} — three shapes for one operation, each promising something different. Actions with their own data and lifecycle are domain concepts worth modeling; the rest can stay verbs or field updates.

Q · Is this operation a state edit, a command, or a domain concept with its own data — and which shape tells consumers the truth about it?
Resources Have State Machines

An order moves created → paid → processing → shipped → delivered, and not one step in any other order. If the contract does not say which transitions exist, every consumer invents its own machine — and the server enforces a third one.

Q · Which states can this resource be in, which transitions are legal, who may trigger each — and does the contract say so, or do consumers guess?
Designing State Transitions

PATCH {status: "shipped"} makes the client the owner of the machine; POST /orders/{id}/ship makes the server own it. Command-style transitions carry data, enforce guards, and answer retries — at the cost of one endpoint per transition.

Q · Should clients edit the state field directly, or request transitions through operations the server owns?
The "Everything Is CRUD" Trap

CRUD describes storage, not behavior. Payments, approvals, workflows and agent runs have states, guards and side effects that create/read/update/delete cannot say — flattening them into updates hides exactly the semantics consumers must know.

Q · Does create/read/update/delete actually describe what this domain does — or is UPDATE about to become a trapdoor for every behavior the contract refuses to name?
Backend for Frontend

A BFF is an API whose consumer is one client experience: the web app or the mobile app, not "clients in general". It buys screen-shaped responses and per-client iteration speed, and costs an extra service per client type — a price not every team should pay.

Q · Should each client experience get its own API layer, or should all clients share one general-purpose surface?
Composed APIs: Aggregating Other Services

An endpoint that answers by calling four other services inherits four latencies, four failure modes and four teams' release schedules. Composition buys consumers one call instead of N — and the contract must say what happens when one of the N fails.

Q · When one endpoint aggregates several internal services, what does it promise about latency, freshness and behavior when a dependency is down?