Actors and commands
Troolio uses Microsoft Orleans actors as the command boundary. An actor interface extends IActor; a concrete actor handles typed messages and keeps the current state derived from its event history. CqrsActor, EventSourcedActor, StatefulActor, ProjectionActor, and OrchestrationActor serve different roles rather than interchangeable base classes.
On this page
Command path
A Command<TActor> addresses a specific actor contract. A handler named Handle returns zero or more domain events. The actor dispatches those events to On handlers to update its in-memory state. Queries use Query<TActor,TResult> and a typed handler to read that current state. The first actor guide has a compiled example.
InternalCommand<TActor> is used for framework and orchestration work inside the actor system. A serving host still has to decide which external commands it permits, authenticate the caller, and authorize the target. Message Metadata carries correlation and identity fields; it is trace context, not proof of current authorization.
Actor roles
| Role | Typical responsibility |
|---|---|
| Event sourced actor | Replays a stream, handles commands, emits events, and answers queries. |
| Stateful actor | Maintains state from subscribed events for internal orchestration. |
| Projection actor | Consumes events to update a read model outside the command transaction. |
| Orchestration actor | Reacts to events and may issue internal commands. |
| Dispatch actor | Provides typed message dispatch without selecting a persistence policy. |
Ordinary and selected command paths
The ordinary actor path appends to its configured store and dispatches events according to host configuration. A separately selected atomic path plans a closed participant set and requires a capable transaction store, route authority, and completion receipt. Do not infer selected-route guarantees from an ordinary Command call. Read selected command route before using it.
Design checks
- Keep command handlers focused on deciding events. Historical event replay must be free of external side effects.
- Treat an empty event result as a meaningful no-change outcome only when the selected route's completion store supports it.
- Use stable actor IDs and version expectations. A store rejects stale expected versions rather than silently overwriting history.
Example: Send a typed command
Using the counter actor from the first actor guide, a client addresses one actor ID and sends its command. The host still authorizes the caller and target.
public static Task Send(ITroolioClient client, Metadata headers) =>
client.Tell<ICounterActor>("42", new Increment(headers));
Choosing a real application boundary
Use actor boundaries and identity to place invariants and stable keys, then follow CQRS in practice to choose between actor queries and semantic SQL views.