Article
CRUD, REST, and the Case for Explicit Commands
CRUD and REST interfaces are centered on manipulating resources or fields, not on expressing business actions. Collaborative systems rely on explicit commands such as submit, approve, reject, or escalate, each with rules, permissions, preconditions, and side effects. This article explores why explicit command interfaces are essential for building collaborative systems.

CRUD (Create, Read, Update, Delete) and REST have become the default patterns for building APIs. They work well for simple data manipulation: create a record, update a field, delete an item. But collaborative systems operate differently. They don't just manipulate data—they execute business actions with meaning, intent, and consequences.
When you submit a document, approve a request, or escalate a case, you're not just updating fields. You're triggering a state transition, validating permissions, checking preconditions, emitting events, and potentially affecting other parts of the system. CRUD and REST lack native representations for these actions, forcing teams into awkward workarounds that push logic to the wrong places.
This article explores why explicit command interfaces are essential for collaborative systems and how they create clearer workflows, simpler UIs, and APIs that express intent rather than just data changes.
The Problem with CRUD and REST
Resource-centric, not action-centric
CRUD and REST are built around resources and their fields. You update a document by sending a PATCH request with field changes. You delete a record by sending a DELETE request. But what if you want to "submit" a document? Or "approve" a request? Or "escalate" a case?
These actions don't map cleanly to CRUD operations. Teams often resort to:
- PATCH updates with special fields:
PATCH /documents/123 {"status": "submitted"}. This looks like a field update but is actually a state transition with rules and side effects. - Verb-like endpoints:
POST /documents/123/submit. This works but feels like a workaround, and REST purists might object. - Action fields in update payloads:
PATCH /documents/123 {"action": "submit"}. This mixes data updates with action execution.
None of these approaches feel natural because they're trying to force actions into a resource-centric model.
Logic pushed to the frontend
When actions are forced into PATCH updates or special fields, the frontend often becomes responsible for:
- Determining valid transitions: The UI must know which status values are allowed in the current state
- Enforcing permissions: The frontend decides which actions to show or hide based on user roles
- Validating preconditions: The UI checks if required fields are filled before allowing an action
- Handling workflow rules: The frontend orchestrates multi-step workflows
This creates several problems:
- Duplication: Business logic exists in both frontend and backend, and they can drift out of sync
- Security gaps: Frontend validation can be bypassed; the backend must still validate everything
- Complexity: The frontend becomes a workflow engine rather than a presentation layer
- Maintenance burden: Changes to business rules require updates in multiple places
Backend as passive repository
When the backend only accepts field updates, it behaves like a passive repository. It stores whatever the frontend sends, validates field formats, but doesn't govern behavior. The backend doesn't know that "submitting" a document is different from "updating" its status field.
This creates a fundamental mismatch: collaborative systems need the backend to be an active governor of behavior, enforcing transitions, validating permissions, and managing side effects. But CRUD/REST encourages a passive model where the backend just stores data.
The result is that critical business logic—what actions are allowed, who can perform them, what preconditions must be met—ends up scattered across frontend code, API middleware, and database constraints, making it hard to reason about and maintain.
Explicit Command Interfaces
Commands express intent
Explicit command interfaces treat actions as first-class citizens. Instead of updating fields, you send commands:
POST /commands/submit-documentPOST /commands/approve-requestPOST /commands/escalate-caseEach command has:
- Explicit name: The command name clearly expresses the business action
- Parameters: The data needed to execute the command (document ID, reason, etc.)
- Rules: Validation, permissions, and preconditions are part of the command definition
- Side effects: Commands can trigger events, notifications, and other system changes
Benefits of explicit commands
Server validates state
The server can validate that the current state allows the command. It knows that a document can only be submitted if it's in "Draft" state, not just because a field was updated.
Server enforces transitions
The server controls which state transitions are allowed. It enforces the state machine rules, preventing invalid transitions regardless of what the frontend sends.
Server emits events
Commands can emit events that other parts of the system react to. When a document is submitted, an event is emitted that triggers notifications, updates projections, or starts workflows.
Centralized rules
All business rules—permissions, validations, preconditions—are centralized in the command handlers. The frontend doesn't need to know these rules; it just presents available commands and sends them when the user acts.
Simpler, clearer UIs
When the backend exposes explicit commands, the frontend becomes simpler:
- Query available commands: The UI can ask the server "what commands are available for this document?" and display buttons accordingly
- Send commands directly: When the user clicks "Submit," the UI sends the submit command—no need to figure out which fields to update
- Handle results: The command returns success or a clear error message, making error handling straightforward
- No workflow logic: The UI doesn't need to know state machines, transitions, or business rules
The UI becomes a presentation layer that queries available commands and sends them when users act. All the complexity stays on the server where it belongs.
APIs that express intent
Command interfaces make APIs self-documenting. When you see POST /commands/submit-document, you immediately understand what it does. Compare that to PATCH /documents/123 {"status": "submitted"}, which requires reading documentation to understand the implications.
Commands also make it easier to:
- Audit actions: Command logs clearly show what actions were taken, not just what fields changed
- Debug issues: When something goes wrong, you can see exactly which command was sent and why it failed
- Test behavior: You can test commands in isolation, verifying that they enforce rules and produce correct side effects
- Version APIs: Commands can be versioned independently, allowing gradual migration
Commands in the State-Based Collaboration Framework
Actions as explicit commands
In the State-Based Collaboration Framework, actions are explicit commands. Each action has:
- A name: Clearly expresses the business intent (SubmitDocument, ApproveRequest, EscalateCase)
- Parameters: The data needed to execute the action
- State reducer: A pure function that validates the action, checks permissions, evaluates guards, and computes the new state
- Event emission: Actions generate events that are recorded and can trigger side effects
The framework ensures that actions are evaluated in the context of the current state, with all rules and permissions checked before any state change occurs. This makes the system deterministic, testable, and auditable.
No CRUD confusion
By using explicit actions, the framework avoids the CRUD/REST problems:
- No field updates masquerading as actions: You don't update a status field; you execute a Submit action
- No logic in the frontend: The frontend queries available actions and sends them; all business logic is on the server
- No passive repository: The backend actively governs behavior, enforcing state machines, validating permissions, and managing transitions
- Clear intent: Every API call expresses a business action, making the system easier to understand and maintain
Related content
This article connects to:
- State Machine Design Pattern - Commands trigger state transitions defined by state machines
- State Reducer Pattern - Commands are processed by reducers that validate and compute new state
- Framework overview - Learn about actions, transitions, and events
Explore more articles
This article is part of a series exploring collaboration patterns and practices. Check out other articles or dive into the framework components.