Header menu logo FCQRS

0. Quickstart: follow one request end to end

The application in this quickstart is DocStore, a small document store. It keeps titled text documents: you can create a document, edit its content, and look a document up by its id.

This page builds just enough of DocStore to save one new document and read it back. In FCQRS the save and the read take different paths. The save is recorded as an event, a small stored fact describing what happened. The read is served from a lookup view that is updated from those events shortly after they are stored. Because the view lags behind by a moment, the program waits for its confirmation before reading. Each section below introduces one part of that path as the code needs it.

Course position: this is the first practical stage. It assumes only basic F# or C# and introduces the runtime vocabulary used by chapters 1 through 5. Motivation: Build one complete write-and-read path first. An aggregate alone hides the asynchronous handoff to projections, while a full command-to-query loop exposes the architecture you will use in a real application.

The read model is kept in memory so the example needs only the FCQRS package. It is rebuilt by replaying the journal from offset zero whenever the program starts. Later stages explain why this works and replace the teaching shortcuts with production decisions.

Run a complete sample

The repository contains two small projects that perform the complete flow on stable .NET 10:

Language

Project

F#

samples/getting-started-fsharp

C#

samples/getting-started-csharp

After cloning FCQRS, run either project from the repository root:

dotnet run --project samples/getting-started-fsharp
dotnet run --project samples/getting-started-csharp

Both send one command, persist one event to SQLite, wait for an in-memory projection, and query the result. The C# sample begins with one concrete command and event type, so it does not require preview union syntax. The C# examples later on this page show how the domain expands to several cases.

Create the project

dotnet new console -lang F# -n DocStore
cd DocStore
dotnet add package FCQRS
dotnet new console -n DocStore
cd DocStore
dotnet add package FCQRS

Replace Program.fs (Program.cs in C#) with the code from the following sections. The C# tabs are direct counterparts; because the expanded command and event examples use C# discriminated unions, they require the compiler setup described in C# interop and serialization. Use the linked stable .NET 10 C# sample when you want a runnable project without preview union syntax.

The shape of the program

Program.fs will hold four pieces. The wireframe below shows every signature with an unimplemented body; sections 1 to 4 replace each failwith with a working implementation.

module Document =
    // 1. The aggregate: Root, State, Command, and Event, then two pure functions.
    let decide (command: Command<Command>) (state: State) : EventAction<Event> =
        failwith "section 1"

    let fold (event: Event<Event>) (state: State) : State =
        failwith "section 1"

// 2. The read model: apply each stored event to an in-memory view.
let handleProjection (offset: int64) (message: obj) : unit =
    failwith "section 2"

// 3. The actor system: configuration, logging, and SQLite storage.
let buildApi () : IActor =
    failwith "section 3"

// 4. One request end to end: send Create, wait for the projection, query.
let run () : Async<unit> =
    failwith "section 4"
// Program.cs as a wireframe; sections 1 to 4 replace each throw with a
// working implementation.

// 1. The aggregate: the document types, then two pure functions.
public sealed class DocumentAggregate
    : Aggregate<DocumentState, DocumentCommand, DocumentEvent>
{
    public override DocumentState InitialState => DocumentState.Initial;
    public override string EntityName => "Document";

    public override EventAction<DocumentEvent> HandleCommand(
        Command<DocumentCommand> command, DocumentState state) =>
        throw new NotImplementedException("section 1");

    public override DocumentState ApplyEvent(
        Event<DocumentEvent> eventEnvelope, DocumentState state) =>
        throw new NotImplementedException("section 1");
}

// 2. The read model: apply each stored event to an in-memory view.
void HandleProjection(long offset, object message) =>
    throw new NotImplementedException("section 2");

// 3. The host: register FCQRS, the aggregate, and the projection.
// 4. One request end to end: send Create, await the projection, query.
// Sections 3 and 4 are top-level statements, shown in full below.

1. Define the aggregate

An aggregate receives commands and produces events. Its current state is rebuilt by folding its stored events. This document aggregate accepts Create and Edit commands. A command that cannot change the document produces a deferred reply instead of a stored event.

module Document =
    type Root =
        { Id: string
          Title: string
          Content: string }

    type State = { Document: Root option }

    type Command =
        | Create of Root
        | Edit of id: string * content: string

    type Event =
        | Created of Root
        | Edited of id: string * content: string
        | AlreadyExists
        | NoSuchDocument

    let initial = { Document = None }

    // Decide: a pure function from command and current state to one event action.
    let decide (command: Command<Command>) state =
        match command.CommandDetails, state with
        | Create document, { Document = None } -> Created document |> PersistEvent // stored and published
        | Create _, { Document = Some _ } -> AlreadyExists |> DeferEvent // reply only, nothing stored
        | Edit(id, content), { Document = Some document } when document.Id = id ->
            Edited(id, content) |> PersistEvent
        | Edit _, _ -> NoSuchDocument |> DeferEvent

    // Fold: rebuilds state one event at a time; it also runs during replay after a restart.
    let fold (event: Event<Event>) state =
        match event.EventDetails with
        | Created document -> { Document = Some document }
        | Edited(id, content) ->
            match state.Document with
            | Some document when document.Id = id ->
                { Document = Some { document with Content = content } }
            | _ -> state
        | AlreadyExists
        | NoSuchDocument -> state // deferred replies never change persisted state

PersistEvent appends the event to the journal, folds it into state, and publishes it. DeferEvent publishes and folds a reply without storing it or changing the persisted aggregate version. The deferred AlreadyExists and NoSuchDocument cases above deliberately leave state unchanged in fold; otherwise their state change would disappear on recovery. The decision and fold are pure functions, so they can be tested without Akka.NET or a database.

using System.Collections.Concurrent;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using FCQRS;
using static FCQRS.Common;
using static FCQRS.CSharp;

public record Document(string Id, string Title, string Content);

public union DocumentCommand(DocumentCommand.Create, DocumentCommand.Edit)
{
    public record Create(Document Document);
    public record Edit(string Id, string Content);
}

public union DocumentEvent(
    DocumentEvent.Created, DocumentEvent.Edited,
    DocumentEvent.AlreadyExists, DocumentEvent.NoSuchDocument)
{
    public record Created(Document Document);
    public record Edited(string Id, string Content);
    public record AlreadyExists;
    public record NoSuchDocument;
}

public record DocumentState(Document? Document = null)
{
    public static readonly DocumentState Initial = new();
}

public sealed class DocumentAggregate
    : Aggregate<DocumentState, DocumentCommand, DocumentEvent>
{
    public override DocumentState InitialState => DocumentState.Initial;
    public override string EntityName => "Document";

    // Decide: a pure function from command and current state to one event action.
    public override EventAction<DocumentEvent> HandleCommand(
        Command<DocumentCommand> command, DocumentState state) =>
        (command.CommandDetails, state.Document) switch
        {
            (DocumentCommand.Create create, null) =>
                // Stored and published.
                EventActions.Persist<DocumentEvent>(new DocumentEvent.Created(create.Document)),
            (DocumentCommand.Create, _) =>
                // Reply only, nothing stored.
                EventActions.Defer<DocumentEvent>(new DocumentEvent.AlreadyExists()),
            (DocumentCommand.Edit edit, { } document) when document.Id == edit.Id =>
                EventActions.Persist<DocumentEvent>(new DocumentEvent.Edited(edit.Id, edit.Content)),
            _ => EventActions.Defer<DocumentEvent>(new DocumentEvent.NoSuchDocument())
        };

    // Fold: rebuilds state one event at a time; it also runs during replay after a restart.
    public override DocumentState ApplyEvent(
        Event<DocumentEvent> eventEnvelope, DocumentState state) =>
        eventEnvelope.EventDetails switch
        {
            DocumentEvent.Created created => state with { Document = created.Document },
            DocumentEvent.Edited edited when state.Document is { } document && document.Id == edited.Id =>
                state with { Document = document with { Content = edited.Content } },
            // Deferred replies never change persisted state.
            _ => state
        };
}

See Aggregates and the write side for the model behind these functions.

2. Build a read model

The query side below is a concurrent dictionary indexed by document id. The projection receives each stored event in order and updates the dictionary. Throwing on Edited without a preceding Created makes a broken event history visible instead of silently producing an incorrect view.

let readModel = ConcurrentDictionary<string, Document.Root>()

let handleProjection (_offset: int64) (message: obj) =
    match message with
    | :? Event<Document.Event> as event ->
        match event.EventDetails with
        | Document.Created document -> readModel[document.Id] <- document
        | Document.Edited(id, content) ->
            match readModel.TryGetValue id with
            | true, document -> readModel[id] <- { document with Content = content }
            | false, _ -> failwith $"Projection received Edited before Created for {id}"
        | Document.AlreadyExists
        | Document.NoSuchDocument -> () // deferred replies carry nothing to project
    | _ -> ()
var readModel = new ConcurrentDictionary<string, Document>();

void HandleProjection(long _offset, object message)
{
    if (message is not Event<DocumentEvent> eventEnvelope)
        return;

    switch (eventEnvelope.EventDetails)
    {
        case DocumentEvent.Created created:
            readModel[created.Document.Id] = created.Document;
            break;
        case DocumentEvent.Edited edited
            when readModel.TryGetValue(edited.Id, out var document):
            readModel[edited.Id] = document with { Content = edited.Content };
            break;
        case DocumentEvent.Edited edited:
            throw new InvalidOperationException(
                $"Projection received Edited before Created for {edited.Id}");
        // AlreadyExists and NoSuchDocument fall through: deferred replies carry nothing to project.
    }
}

This projection starts at offset zero, so it rebuilds the dictionary from all stored events on every run. A durable read model stores its last offset together with each update in one transaction and resumes from there; the shared transaction prevents a crash from skipping an event or applying one twice.

3. Create the actor system

Fcqrs.actor combines the embedded Akka.NET defaults with the supplied configuration and database connection. SQLite stores the journal and snapshots in getstarted.db.

let buildApi () : IActor =
    let config = ConfigurationBuilder().Build()
    let loggerFactory = LoggerFactory.Create(fun _ -> ())
    let connection =
        Fcqrs.connect FCQRS.Actor.DBType.Sqlite "Data Source=getstarted.db;"

    Fcqrs.actor config loggerFactory (Some connection) "getstarted"
var builder = Host.CreateApplicationBuilder(args);

builder.Services
    .AddFcqrs("Data Source=getstarted.db;", "getstarted")
    .AddAggregate<DocumentAggregate>()
    .AddProjection(HandleProjection, lastOffset: 0);

4. Send, wait, and query

The aggregate and projection are registered before commands are sent. Subscribe to the correlation id before sending, then wait for the projection after the aggregate returns its stored event. The new aggregate id in this example guarantees that Create stores an event rather than returning a deferred AlreadyExists reply.

let run () =
    async {
        let api = buildApi ()

        let documents =
            Fcqrs.aggregate api
                { Name = "Document"
                  Initial = Document.initial
                  Decide = Document.decide
                  Fold = Document.fold
                  Snapshots = Default }

        Fcqrs.wireSagaStarters api []
        let subscriptions = Fcqrs.projection api (Projection.single 0 handleProjection)

        let documentId = Guid.NewGuid().ToString("N")
        let aggregateId = Fcqrs.aggregateId documentId
        let correlationId = Fcqrs.newCid ()
        let document: Document.Root =
            { Id = documentId
              Title = "FCQRS notes"
              Content = "first event" }

        // Subscribe before sending so the projection cannot publish first.
        use projectedEvent = subscriptions.Subscribe(correlationId, 1)

        let! stored =
            documents.Send
                correlationId
                aggregateId
                (Document.Create document)
                // The filter picks which aggregate reply completes this send.
                (function
                    | Document.Created _ -> true
                    | _ -> false)

        // Read-your-writes: wait until the projection has handled this correlation id.
        do! projectedEvent.Task |> Async.AwaitTask
        let projected = readModel[documentId]
        printfn "stored version %A; query returned '%s'" stored.Version projected.Content
    }
using var host = builder.Build();
await host.StartAsync();

var documents = host.Services
    .GetRequiredService<Handler<DocumentCommand, DocumentEvent>>();
var subscriptions = host.Services
    .GetRequiredService<FCQRS.Query.ISubscribe>();

var documentId = Guid.NewGuid().ToString("N");
var aggregateId = Values.CreateAggregateId(documentId);
var correlationId = Values.NewCID();
var document = new Document(documentId, "FCQRS notes", "first event");

// Subscribe before sending so the projection cannot publish first.
using var projectedEvent = subscriptions.SubscribeForFirst(correlationId);

var stored = await documents(
    // The filter picks which aggregate reply completes this send.
    outcome => outcome is DocumentEvent.Created,
    correlationId,
    aggregateId,
    new DocumentCommand.Create(document));

// Read-your-writes: wait until the projection has handled this correlation id.
await projectedEvent.Task;
var projected = readModel[documentId];
Console.WriteLine(
    $"stored version {stored.Version}; query returned '{projected.Content}'");

await host.StopAsync();

Add the entry point and run the program:

[<EntryPoint>]
let main _ =
    run () |> Async.RunSynchronously
    0
// Nothing to add: the top-level statements above are the entry point,
// and await host.StartAsync() already runs the program.
dotnet run
stored version 1; query returned 'first event'

The aggregate id is new on each run, so each command creates a different document. The projection still replays previous documents before handling the new event.

Continue the learning path

You have seen the entire route once: command, aggregate decision, stored event, projection, then query. The next chapter slows down at the first step and explains how to design that decision correctly.

Continue to 1. The aggregate.

After chapter 1 introduces the domain model, its optional deep dives point to the relevant Understand and Apply pages. You do not need those pages before continuing.

Multiple items
module Event from Microsoft.FSharp.Control

--------------------
type Event<'T> = new: unit -> Event<'T> member Trigger: arg: 'T -> unit member Publish: IEvent<'T>

--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate and reference type)> = new: unit -> Event<'Delegate,'Args> member Trigger: sender: obj * args: 'Args -> unit member Publish: IEvent<'Delegate,'Args>

--------------------
new: unit -> Event<'T>

--------------------
new: unit -> Event<'Delegate,'Args>
val failwith: message: string -> 'T
Multiple items
val int64: value: 'T -> int64 (requires member op_Explicit)

--------------------
type int64 = System.Int64

--------------------
type int64<'Measure> = int64
type obj = System.Object
type unit = Unit
Multiple items
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent: event: IEvent<'Del,'T> * ?cancelAction: (unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult: iar: IAsyncResult * ?millisecondsTimeout: int -> Async<bool> static member AwaitTask: task: Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle: waitHandle: WaitHandle * ?millisecondsTimeout: int -> Async<bool> static member CancelDefaultToken: unit -> unit static member Catch: computation: Async<'T> -> Async<Choice<'T,exn>> static member Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * obj -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...

--------------------
type Async<'T>
namespace System
namespace System.Collections
namespace System.Collections.Concurrent
namespace Microsoft
namespace Microsoft.Extensions
namespace Microsoft.Extensions.Configuration
namespace Microsoft.Extensions.Logging
namespace FCQRS
module Common from FCQRS
<summary> Contains common types like Events and Commands </summary>
<namespacedoc><summary>Functionality for Write Side.</summary></namespacedoc>
namespace FCQRS.Model
module Data from FCQRS.Model
module FSharp from FCQRS
<summary> Idiomatic-F# functional facade for FCQRS. Gives F# consumers the same one-call ergonomics the C# host-builder (HostExtensions.fs) gives C#, but with F# idioms: records-of-functions for the definitions, typed handles for the results, an explicit wiring pipeline, and plain helpers for saga side effects. It is a *pure addition* that wraps only the existing primitives (IActor.InitializeActor / SagaBuilder.initSimple / Query.init / InitializeSagaStarter / CreateCommandSubscription / Actor.api) and changes nothing in the C# interop layer or the core. open FCQRS.FSharp let api = Fcqrs.actor config loggerFactory (Some (Fcqrs.connect DBType.Sqlite conn)) "Cluster" let documents = Fcqrs.aggregate api { Name="Document"; Initial=...; Decide=...; Fold=... } let slugs = Fcqrs.aggregate api { Name="Slug"; Initial=...; Decide=...; Fold=... } let publication = Fcqrs.saga api (publicationDef documents.Factory slugs.Factory) Fcqrs.wireSagaStarters api [ publication ] let subs = Fcqrs.projection api (Projection.single 0 updateReadModel) // (Projection.multi when you must control which notifications publish) // send a command and await the matching aggregate reply: let! ev = documents.Send (Fcqrs.newCid()) (Fcqrs.aggregateId id) cmd (fun e -&gt; ...) </summary>
type Root = { Id: string Title: string Content: string }
Multiple items
val string: value: 'T -> string

--------------------
type string = String
Multiple items
type State = { Document: Root option }

--------------------
type State<'Command,'Event> = { CommandDetails: CommandDetails<'Command,'Event> Sender: IActorRef }
type 'T option = Option<'T>
Multiple items
type Command = | Create of Root | Edit of id: string * content: string

--------------------
type Command<'CommandDetails> = { CommandDetails: 'CommandDetails CreationDate: DateTime Id: MessageId Sender: AggregateId option CorrelationId: CID Metadata: Map<string,string> } interface IEnvelope interface IMessage interface ISerializable member Equals: Command<'CommandDetails> * IEqualityComparer -> bool override ToString: unit -> string
<summary> Represents a command to be processed by an aggregate actor. &lt;typeparam name="'CommandDetails"&gt;The specific type of the command payload.&lt;/typeparam&gt; </summary>

--------------------
type Command<'Command,'Event> = | Execute of CommandDetails<'Command,'Event>
<summary> Represents the message sent to the internal subscription mechanism. &lt;typeparam name="'Command"&gt;The type of the command payload.&lt;/typeparam&gt; &lt;typeparam name="'Event"&gt;The type of the expected event payload.&lt;/typeparam&gt; </summary>
val id: x: 'T -> 'T
Multiple items
module Event from Microsoft.FSharp.Control

--------------------
type Event = | Created of Root | Edited of id: string * content: string | AlreadyExists | NoSuchDocument

--------------------
type Event<'EventDetails> = { EventDetails: 'EventDetails CreationDate: DateTime Id: MessageId Sender: AggregateId option CorrelationId: CID Version: Version Metadata: Map<string,string> } interface IEnvelope interface IMessage interface ISerializable member Equals: Event<'EventDetails> * IEqualityComparer -> bool override ToString: unit -> string member Journaled: bool option
<summary> Represents an event generated by an aggregate actor as a result of processing a command. &lt;typeparam name="'EventDetails"&gt;The specific type of the event payload.&lt;/typeparam&gt; </summary>

--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate and reference type)> = new: unit -> Event<'Delegate,'Args> member Trigger: sender: obj * args: 'Args -> unit member Publish: IEvent<'Delegate,'Args>

--------------------
new: unit -> Event<'Delegate,'Args>
val initial: State
union case Option.None: Option<'T>
val decide: command: Command<Command> -> state: State -> EventAction<Event>
val command: Command<Command>
val state: State
Command.CommandDetails: Command
<summary> The specific details or payload of the command. </summary>
union case Command.Create: Root -> Command
val document: Root
union case Event.Created: Root -> Event
union case EventAction.PersistEvent: 'T -> EventAction<'T>
<summary> Persist the event to the journal. The actor's state will be updated using the event handler *after* persistence succeeds. </summary>
union case Option.Some: Value: 'T -> Option<'T>
union case Event.AlreadyExists: Event
union case EventAction.DeferEvent: 'T -> EventAction<'T>
<summary> Publish and fold the event in the live actor without storing it or incrementing the persisted version. </summary>
union case Command.Edit: id: string * content: string -> Command
val id: string
val content: string
Root.Id: string
union case Event.Edited: id: string * content: string -> Event
union case Event.NoSuchDocument: Event
val fold: event: Event<Event> -> state: State -> State
val event: Event<Event>
Event.EventDetails: Event
<summary> The specific details or payload of the event. </summary>
State.Document: Root option
val readModel: ConcurrentDictionary<string,Document.Root>
Multiple items
type ConcurrentDictionary<'TKey,'TValue> = interface ICollection<KeyValuePair<'TKey,'TValue>> interface IEnumerable<KeyValuePair<'TKey,'TValue>> interface IEnumerable interface IDictionary<'TKey,'TValue> interface IReadOnlyCollection<KeyValuePair<'TKey,'TValue>> interface IReadOnlyDictionary<'TKey,'TValue> interface ICollection interface IDictionary new: unit -> unit + 6 overloads member AddOrUpdate: key: 'TKey * addValueFactory: Func<'TKey,'TValue> * updateValueFactory: Func<'TKey,'TValue,'TValue> -> 'TValue + 2 overloads ...
<summary>Represents a thread-safe collection of key/value pairs that can be accessed by multiple threads concurrently.</summary>
<typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
<typeparam name="TValue">The type of the values in the dictionary.</typeparam>


--------------------
ConcurrentDictionary() : ConcurrentDictionary<'TKey,'TValue>
ConcurrentDictionary(collection: Collections.Generic.IEnumerable<Collections.Generic.KeyValuePair<'TKey,'TValue>>) : ConcurrentDictionary<'TKey,'TValue>
ConcurrentDictionary(comparer: Collections.Generic.IEqualityComparer<'TKey>) : ConcurrentDictionary<'TKey,'TValue>
ConcurrentDictionary(collection: Collections.Generic.IEnumerable<Collections.Generic.KeyValuePair<'TKey,'TValue>>, comparer: Collections.Generic.IEqualityComparer<'TKey>) : ConcurrentDictionary<'TKey,'TValue>
ConcurrentDictionary(concurrencyLevel: int, capacity: int) : ConcurrentDictionary<'TKey,'TValue>
ConcurrentDictionary(concurrencyLevel: int, collection: Collections.Generic.IEnumerable<Collections.Generic.KeyValuePair<'TKey,'TValue>>, comparer: Collections.Generic.IEqualityComparer<'TKey>) : ConcurrentDictionary<'TKey,'TValue>
ConcurrentDictionary(concurrencyLevel: int, capacity: int, comparer: Collections.Generic.IEqualityComparer<'TKey>) : ConcurrentDictionary<'TKey,'TValue>
module Document from Get-started
val handleProjection: _offset: int64 -> message: obj -> unit
val _offset: int64
Multiple items
val int64: value: 'T -> int64 (requires member op_Explicit)

--------------------
type int64 = Int64

--------------------
type int64<'Measure> = int64
val message: obj
type obj = Object
Multiple items
module Event from Microsoft.FSharp.Control

--------------------
type Event<'EventDetails> = { EventDetails: 'EventDetails CreationDate: DateTime Id: MessageId Sender: AggregateId option CorrelationId: CID Version: Version Metadata: Map<string,string> } interface IEnvelope interface IMessage interface ISerializable member Equals: Event<'EventDetails> * IEqualityComparer -> bool override ToString: unit -> string member Journaled: bool option
<summary> Represents an event generated by an aggregate actor as a result of processing a command. &lt;typeparam name="'EventDetails"&gt;The specific type of the event payload.&lt;/typeparam&gt; </summary>

--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate and reference type)> = new: unit -> Event<'Delegate,'Args> member Trigger: sender: obj * args: 'Args -> unit member Publish: IEvent<'Delegate,'Args>

--------------------
new: unit -> Event<'Delegate,'Args>
type Event = | Created of Root | Edited of id: string * content: string | AlreadyExists | NoSuchDocument
val event: Event<Document.Event>
Event.EventDetails: Document.Event
<summary> The specific details or payload of the event. </summary>
union case Document.Event.Created: Document.Root -> Document.Event
val document: Document.Root
Document.Root.Id: string
union case Document.Event.Edited: id: string * content: string -> Document.Event
ConcurrentDictionary.TryGetValue(key: string, value: byref<Document.Root>) : bool
union case Document.Event.AlreadyExists: Document.Event
union case Document.Event.NoSuchDocument: Document.Event
val buildApi: unit -> IActor
type IActor = abstract CreateCommandSubscription: (string -> IEntityRef<obj>) -> CID -> AggregateId -> 'b -> ('c -> bool) -> Map<string,string> option -> Async<Event<'c>> abstract InitializeActor: 'a -> string -> (Command<'c> -> 'a -> EventAction<'b>) -> (Event<'b> -> 'a -> 'a) -> SnapshotPolicy -> EntityFac<obj> abstract InitializeActorWithRunner: 'a -> string -> (Command<'c> -> 'a -> EventAction<'b>) -> (Event<'b> -> 'a -> 'a) -> SnapshotPolicy -> (obj -> Async<obj>) option -> EntityFac<obj> abstract InitializeSaga: SagaState<'SagaState,'State> -> (obj -> SagaState<'SagaState,'State> -> EventAction<'State>) -> (SagaState<'SagaState,'State> -> SagaStartingEvent<Event<'c>> option -> bool -> SagaTransition<'State> * ExecuteCommand list) -> (SagaState<'SagaState,'State> -> SagaState<'SagaState,'State>) -> string -> SnapshotPolicy -> EntityFac<obj> abstract InitializeSagaStarter: (obj -> ((string -> IEntityRef<obj>) * PrefixConversion * obj) list) -> unit + 1 overload abstract Stop: unit -> Task abstract SubscribeForCommand: Command<'a,'b> -> Async<Event<'b>> abstract Configuration: IConfiguration abstract LoggerFactory: ILoggerFactory abstract Materializer: ActorMaterializer ...
<summary> Defines the core functionalities and context provided by the FCQRS environment to actors. This interface provides access to essential Akka.NET services and FCQRS initialization methods. </summary>
val config: IConfigurationRoot
Multiple items
type ConfigurationBuilder = interface IConfigurationBuilder new: unit -> unit member Add: source: IConfigurationSource -> IConfigurationBuilder member Build: unit -> IConfigurationRoot member Properties: IDictionary<string,obj> member Sources: IList<IConfigurationSource>
<summary> Builds key/value-based configuration settings for use in an application. </summary>

--------------------
ConfigurationBuilder() : ConfigurationBuilder
val loggerFactory: ILoggerFactory
Multiple items
type LoggerFactory = interface ILoggerFactory interface IDisposable new: unit -> unit + 4 overloads member AddProvider: provider: ILoggerProvider -> unit member CreateLogger: categoryName: string -> ILogger member Dispose: unit -> unit static member Create: configure: Action<ILoggingBuilder> -> ILoggerFactory
<summary>Produces instances of <see cref="T:Microsoft.Extensions.Logging.ILogger" /> classes based on the given providers.</summary>

--------------------
LoggerFactory() : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>) : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>, filterOptions: LoggerFilterOptions) : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>, filterOption: Extensions.Options.IOptionsMonitor<LoggerFilterOptions>) : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>, filterOption: Extensions.Options.IOptionsMonitor<LoggerFilterOptions>, ?options: Extensions.Options.IOptions<LoggerFactoryOptions>) : LoggerFactory
LoggerFactory.Create(configure: Action<ILoggingBuilder>) : ILoggerFactory
val connection: FCQRS.Actor.Connection
module Fcqrs from FCQRS.FSharp
val connect: dbType: FCQRS.Actor.DBType -> connectionString: string -> FCQRS.Actor.Connection
<summary> Build a SQLite/etc. Connection from a raw connection string (ShortString hidden). </summary>
module Actor from FCQRS
type DBType = | Sqlite | SqlServer2012 | SqlServer2014 | SqlServer2016 | SqlServer2017 | SqlServer2019 | SqlServer2022 | PostgreSQL | PostgreSQL15 | MySql ... member Equals: DBType * IEqualityComparer -> bool member IsDB2: bool member IsFirebird: bool member IsMySql: bool member IsOracle: bool member IsPostgreSQL: bool member IsPostgreSQL15: bool member IsSqlServer2012: bool member IsSqlServer2014: bool member IsSqlServer2016: bool ...
<summary> Represents the type of database connection </summary>
union case FCQRS.Actor.DBType.Sqlite: FCQRS.Actor.DBType
<summary> SQLite using Microsoft.Data.Sqlite provider </summary>
val actor: config: IConfiguration -> loggerFactory: ILoggerFactory -> connection: FCQRS.Actor.Connection option -> clusterName: string -> IActor
<summary> Create the actor system from plain values (cluster name as a string). </summary>
val run: unit -> Async<unit>
val async: AsyncBuilder
val api: IActor
val documents: AggregateHandle<Document.Command,Document.Event>
val aggregate: api: IActor -> def: Aggregate<'State,'Command,'Event> -> AggregateHandle<'Command,'Event>
<summary> Register an aggregate and return its typed handle. Calling this IS the registration (it initializes the sharding region). </summary>
union case TargetName.Name: string -> TargetName
<summary> Identify the target by its string name (entity ID). </summary>
val initial: Document.State
val decide: command: Command<Document.Command> -> state: Document.State -> EventAction<Document.Event>
val fold: event: Event<Document.Event> -> state: Document.State -> Document.State
union case SnapshotPolicy.Default: SnapshotPolicy
<summary> Use the global config (config:akka:persistence:snapshot-version-count), or 30. </summary>
val wireSagaStarters: api: IActor -> sagas: SagaHandle list -> unit
<summary> Wire every registered saga into one saga-starter (or the empty starter if none). Call after the aggregates + sagas are registered. </summary>
val subscriptions: FCQRS.Query.ISubscribe
val projection: api: IActor -> p: Projection -> FCQRS.Query.ISubscribe
<summary> Register the read-model projection and return the subscription stream. </summary>
Multiple items
module Projection from FCQRS.FSharp
<summary> Constructors for the two projection-handler shapes. </summary>

--------------------
type Projection = { LastOffset: int64 Handle: (int64 -> obj -> IMessageWithCID list) }
<summary> A read-model projection definition. </summary>
val single: lastOffset: int64 -> handle: (int64 -> obj -> unit) -> Projection
<summary> Single-event handler: just update the read model (returns unit); each aggregate event is then published to subscribers as-is. The common case when every event is worth notifying. </summary>
val documentId: string
Multiple items
[<Struct>] type Guid = new: b: byte array -> unit + 6 overloads member CompareTo: value: Guid -> int + 1 overload member Equals: g: Guid -> bool + 1 overload member GetHashCode: unit -> int member ToByteArray: unit -> byte array + 1 overload member ToString: unit -> string + 2 overloads member TryFormat: utf8Destination: Span<byte> * bytesWritten: byref<int> * ?format: ReadOnlySpan<char> -> bool + 1 overload member TryWriteBytes: destination: Span<byte> -> bool + 1 overload static member (<) : left: Guid * right: Guid -> bool static member (<=) : left: Guid * right: Guid -> bool ...
<summary>Represents a globally unique identifier (GUID).</summary>

--------------------
Guid ()
Guid(b: byte array) : Guid
Guid(b: ReadOnlySpan<byte>) : Guid
Guid(g: string) : Guid
Guid(b: ReadOnlySpan<byte>, bigEndian: bool) : Guid
Guid(a: int, b: int16, c: int16, d: byte array) : Guid
Guid(a: int, b: int16, c: int16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : Guid
Guid(a: uint32, b: uint16, c: uint16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : Guid
Guid.NewGuid() : Guid
val aggregateId: AggregateId
val aggregateId: s: string -> AggregateId
<summary> An aggregate id from a string (e.g. a document/user key). Any non-blank id works: the shard names entity actors Uri.EscapeDataString(entityId), so characters Akka actor names would reject directly (spaces, %) are escaped before they reach an actor path. </summary>
val correlationId: CID
val newCid: unit -> CID
<summary> A fresh correlation id (UUID v7). </summary>
val projectedEvent: FCQRS.Query.IAwaitableDisposable
abstract FCQRS.Query.ISubscribe.Subscribe: callback: ('TDataEvent -> unit) * ?cancellationToken: Threading.CancellationToken -> IDisposable
abstract FCQRS.Query.ISubscribe.Subscribe: cid: CID * take: int * ?callback: ('TDataEvent -> unit) * ?cancellationToken: Threading.CancellationToken -> FCQRS.Query.IAwaitableDisposable
abstract FCQRS.Query.ISubscribe.Subscribe: filter: ('TDataEvent -> bool) * take: int * ?callback: ('TDataEvent -> unit) * ?cancellationToken: Threading.CancellationToken -> FCQRS.Query.IAwaitableDisposable
abstract FCQRS.Query.ISubscribe.Subscribe: cid: CID * filter: ('TDataEvent -> bool) * take: int * ?callback: ('TDataEvent -> unit) * ?cancellationToken: Threading.CancellationToken -> FCQRS.Query.IAwaitableDisposable
val stored: Event<Document.Event>
AggregateHandle.Send: CID -> AggregateId -> Document.Command -> (Document.Event -> bool) -> Async<Event<Document.Event>>
<summary> Send a command and await the first matching aggregate event. </summary>
union case Document.Command.Create: Document.Root -> Document.Command
property FCQRS.Query.IAwaitable.Task: Threading.Tasks.Task with get
static member Async.AwaitTask: task: Threading.Tasks.Task -> Async<unit>
static member Async.AwaitTask: task: Threading.Tasks.Task<'T> -> Async<'T>
val projected: Document.Root
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
Event.Version: Version
<summary> The version number of the aggregate after this event was applied. </summary>
Document.Root.Content: string
Multiple items
type EntryPointAttribute = inherit Attribute new: unit -> EntryPointAttribute

--------------------
new: unit -> EntryPointAttribute
static member Async.RunSynchronously: computation: Async<'T> * ?timeout: int * ?cancellationToken: System.Threading.CancellationToken -> 'T

Type something to start searching.