---
name: cratis-command
description: Step-by-step guidance for creating a Cratis Arc command — [Command] record, Handle() method, CommandValidator, proxy generation, and React .use() hook with CommandDialog. Use when adding or creating a command, wiring up a form or button to the backend, working with IEventLog, CommandResult, CommandValidator, CommandDialog, or [Command] attribute.
---
# Creating a Cratis Command
A command represents a user action that changes state. In Cratis Arc the path is:
```
[Command] record + Handle() → validator → dotnet build → TypeScript proxy → React .use()
```
The command record **owns its own handler** — no separate controller class required. Follow the steps in order. Jump to the reference files for deeper detail on any step.
---
## Step 1 — Define the C# command record
A command is a **record decorated with `[Command]`** that contains its own `Handle()` method. No separate controller is needed.
```csharp
// Accounts/OpenDebitAccount/OpenDebitAccount.cs — the slice file
namespace MyApp.Accounts.OpenDebitAccount;
using Cratis.Arc.Commands.ModelBound;
using Cratis.Chronicle.Events;
[Command]
public record OpenDebitAccount(AccountId AccountId, AccountName Name, OwnerId OwnerId)
{
public DebitAccountOpened Handle() =>
new(Name, OwnerId); // Arc appends the returned event; AccountId is the event source
}
/// Emitted when a debit account is opened.
[EventType] // NO arguments — never [EventType("some-guid")]
public record DebitAccountOpened(AccountName Name, OwnerId OwnerId);
```
**Rules:**
- `[Command]` attribute is **required** — it makes the type discoverable and the analyzer will warn without it
- `Handle()` returns the event (or events) to append — Arc's Chronicle integration automatically appends them; **never inject `IEventLog` to append the primary event**
- `[EventType]` takes **no arguments** — the identifier is generated from the type name
- Name the command as an imperative action — `OpenDebitAccount`, not `OpenDebitAccountCommand`
- All backend artifacts for the slice live in this one file; place it in the slice folder, not an `API/` or `Commands/` folder (see [vertical-slices.md](https://github.com/Cratis/AI/blob/main/.ai/rules/vertical-slices.md))
- Use concept wrappers for every domain value — **identity** concepts derive from `EventSourceId`, **value** concepts from `ConceptAs`; never raw `Guid`/`string`
```csharp
// Accounts/AccountId.cs — identity concept derives from EventSourceId
public record AccountId(Guid Value) : EventSourceId(Value)
{
public static AccountId New() => new(Guid.NewGuid());
public static implicit operator AccountId(Guid value) => new(value);
}
```
### Generating a new ID and returning it
```csharp
[Command]
public record RegisterEmployee(string FirstName, string LastName, string Department)
{
// Return (eventSourceId, event) — Arc uses the first element as the event source ID
// and sends it back to the client as CommandResult.response
public (EmployeeId, EmployeeRegistered) Handle()
{
var employeeId = new EmployeeId(Guid.NewGuid());
return (employeeId, new(FirstName, LastName, Department));
}
}
```
### Appending multiple events
Return `IEnumerable