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.
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.
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.
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.
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.
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.
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.
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.
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.