Commands
Opening an account or adding a cart line should not require a handwritten HTTP client and repeated endpoint boilerplate. In Arc, a command carries the input for an operation and can handle itself. Arc supplies the endpoint, command result, and generated TypeScript proxy.
Arc is a standalone CQRS framework. Your handler can call application services backed by any suitable storage; it does not have to return an event or use Chronicle.
Your first command
Section titled “Your first command”This is a declaration fragment using the shared CartLineId, Sku, and Quantity concepts and fully implemented ICartService from the model-bound command example:
using Cratis.Arc.Commands.ModelBound;
[Command]public record AddItemToCart(Sku Sku, Quantity Quantity){ public CartLineId Handle(ICartService carts) => carts.AddItem(Sku, Quantity);}Arc discovers the public instance Handle() on the [Command] record. It resolves ICartService from the command scope, waits for the operation, and places the CartLineId in the response. Follow the linked example for the service implementation, registration, and an execution checkpoint.
Choose the return shape that fits the operation:
voidorTask— no response value. A task is awaited; this is not fire-and-forget execution.- A value or
Task<T>— an unhandled value becomes the typed response. - An operation or
CommandOperations— declare one or many server-side actions, with optional compensation. - A tuple — combine one response with command operations and other values consumed by response value handlers.
Cratis.Monads.Result<TSuccess, TError>— process the active alternative. A recognized validation value becomes a rejection; arbitrary error types are not automatically failures.
Use provided data when fetching handler input separately makes the operation clearer.
Two ways to define a command
Section titled “Two ways to define a command”| Style | Choose it when |
|---|---|
| Model-bound | You want input and behavior together with minimal endpoint boilerplate. |
| Controller-based | You need MVC action control or are integrating existing controllers. |
The model-bound pipeline and MVC action filters are different execution paths. Do not assume extensions or authorization attributes for one automatically govern the other.
Build on the basics
Section titled “Build on the basics”| Next concern | Read |
|---|---|
| Validate input | Validation |
| Protect commands | Model-bound authorization |
| Check without invoking the handler | Pre-flight validation |
| Execute from application code | Command pipeline |
| Keep inline side effects out of the decision | Command operations · Testing operations |
| Understand scalar and tuple responses | Response value handlers · Examples |
| Carry cross-cutting data | Command context |
| Extend execution | Filters · Execution scopes |
Optional Chronicle integration
Section titled “Optional Chronicle integration”If your application uses event sourcing, Chronicle command integration adds returned-event handling. Its transactional command behavior belongs to that integration, not standalone Arc. You do not need it for service-backed commands or Guid responses.
The frontend follows the backend
Section titled “The frontend follows the backend”The proxy generator generates the TypeScript command contract from the backend. After building the backend, use commands in React to submit it and inspect the result. Continue to queries to read the data your commands change.