Documentation / Start

Build your first actor

An event-sourced actor turns a command into an event and rebuilds its in-memory state by applying events. This counter is deliberately small so the roles are visible. The example uses Troolio.Core 10.0.8 and Orleans 10.

On this page

A command is handled by an actor, stored as an event, and replayed into state for queries.

Define messages and the actor

using Microsoft.Extensions.Configuration;
using Orleans;
using Troolio.Core;
using Troolio.Stores;

namespace MyApp;

public interface ICounterActor : IActor { }

[GenerateSerializer]
public record Increment(Metadata Headers) : Command<ICounterActor>(Headers);

[GenerateSerializer]
public record Incremented(Metadata Headers) : Event(Headers);

[GenerateSerializer]
public record CurrentCount : Query<ICounterActor, int>;

public sealed class CounterActor(IStore store, IConfiguration configuration)
    : EventSourcedActor(store, configuration), ICounterActor
{
    private int count;

    public IEnumerable<Event> Handle(Increment command) =>
        [new Incremented(command.Headers)];

    public void On(Incremented _) => count++;

    public int Handle(CurrentCount _) => count;
}

Increment is a command. Handle emits an Incremented event. On applies the event both after an accepted append and during replay when the actor activates again. CurrentCount reads the actor's current state. The actor's event stream, not the count field, is the durable write history when a durable store is configured.

Metadata carries correlation, user, device, and message IDs. In a serving application, the host must bind the user and target to the current authenticated principal. Caller supplied headers alone are not authority.

Compile the example

Add the snippet as Counter.cs in a net10.0 class library that references Troolio.Core 10.0.8 and Microsoft.Orleans.Sdk 10.0.0, then run:

dotnet build -c Release

The snippet describes actor behavior inside a host. Host setup shows the next wiring step.

Next decisions