Skip to content

Commands

Opening an account or adding a cart line needs input, a handler, and a result the caller can check. An Arc command keeps the input with the behavior that handles it. Arc supplies the HTTP endpoint, command result, and generated TypeScript proxy, so you do not repeat that plumbing for each operation.

Start with a handler that calls your application service. Arc uses CQRS (Command Query Responsibility Segregation): commands change state, while queries read it. Your service can use any suitable storage; a command does not have to return an event or use Chronicle.

POST

Client

Arc endpoint

Authorization then validation

Provide and Handle

Response processing

CommandResult

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. The service owns the cart write; declaring the command does not supply storage. Follow the linked example to implement and register that service, then execute the command before adding pipeline extensions.

Choose the return shape by what the caller needs. Returning a value reports an outcome; it does not by itself make a service write transactional:

  • void or Task — 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.

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.

Add validation when input needs rules, and authorization when the operation needs access checks. Pre-flight validation can give a form feedback before submission, but the caller must still check the execution result.

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

If your application uses event sourcing, Chronicle command integration adds returned-event handling. Its transactional command behavior coordinates that integration’s work; it does not turn arbitrary service writes into a transaction. You do not need Chronicle for service-backed commands or Guid responses.

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.