Header menu logo FCQRS

2. Wiring and running it

Chapter 1 produced a document aggregate as pure functions. This chapter registers those functions with FCQRS, stores their events in SQLite, projects the events into query data, and sends a command through the complete path. Continue in the same Program.fs.

Course position: chapter 1 defined a pure aggregate. This chapter runs it and follows one stored event into a query model. By the end you will understand the journal, projection offset, correlation id, and subscribe-before-send ordering from one working request.

This chapter adds three pieces around chapter 1's aggregate. Each failwith message names the section that implements it.

let buildApi () : IActor =
    failwith "create the actor system"

let handle (offset: int64) (message: obj) : unit =
    failwith "build the query side"

let run () : Async<unit> =
    failwith "send a command and read your own write"
// "Create the actor system": the host-builder replaces buildApi.
var builder = WebApplication.CreateBuilder(args);

// "Build the query side": the projection handler.
void Handle(long offset, object message) =>
    throw new NotImplementedException("build the query side");

// "Send a command and read your own write": top-level statements after Build().

Two paths, on purpose: writing and reading

The application has two paths:

                decide / fold
  command  ---------------------->  JOURNAL   (append-only events; the truth)
                                       |
                                       v
                                   projection
                                       |
                                       v
                                  read model   (a derived view; disposable)

Commands flow through the aggregate and append events to the journal. Projections consume those events and update read models. Application queries use the read model, not the aggregate state or journal.

Motivation: Two paths let query shapes evolve without widening the aggregate, and let business rules evolve without redesigning every query. The stored events are the stable handoff between them.

Create the actor system

Fcqrs.actor builds the Akka.NET system. The connection configures the journal, query journal, and snapshot store. The embedded defaults configure serialization, sharding, and a one-node cluster.

let buildApi () : IActor =
    let config = ConfigurationBuilder().Build()
    let loggerFactory = LoggerFactory.Create(fun _ -> ())
    let connection =
        Fcqrs.connect FCQRS.Actor.DBType.Sqlite "Data Source=tutorial.db;"
    Fcqrs.actor config loggerFactory (Some connection) "tutorial"
// C#: the DI host-builder creates the actor system and registers the aggregate;
// config and logger come from the container, so there's no explicit buildApi.
var builder = WebApplication.CreateBuilder(args);
builder.Services
    .AddFcqrs("Data Source=tutorial.db;", "tutorial")
    .AddAggregate<DocumentAggregate, DocumentState, DocumentCommand, DocumentEvent>();

The empty IConfiguration accepts every embedded default. User configuration is merged over those defaults when an application needs different Akka.NET settings. tutorial.db outlives the process, so the next run can recover the aggregate and replay the projection. See Configure the database for other providers.

Build the query side

This tutorial uses an in-memory dictionary for query data. The projection starts at offset zero and replays all stored Updated events on every run. A production projection instead persists its read model and, in the same database transaction, the offset of the last event it applied. The shared transaction is what makes a crash safe: with separate writes, an offset saved first skips an event forever, and data saved first applies an event twice on restart. The in-memory version here keeps the event-to-query transformation visible.

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

let handle (_offset: int64) (message: obj) =
    match message with
    | :? Event<Document.Event> as event ->
        match event.EventDetails with
        | Document.Updated document -> readModel[document.Id.ToString()] <- document
    | _ -> ()
// C#: the same in-memory read model and projection.
var readModel = new ConcurrentDictionary<string, Document>();

void Handle(long offset, object message)
{
    if (message is Event<DocumentEvent> { EventDetails: DocumentEvent.Updated updated })
        readModel[updated.Document.Id.ToString()] = updated.Document;
}

builder.Services.AddProjection((offset, ev) => Handle(offset, ev));

Projection.single publishes an aggregate event to subscribers after handle returns. A caller can therefore wait until this projection has applied a specific command's event. See Add a projection for transactional offset storage.

Send a command and read your own write

The aggregate acknowledges its stored event before the projection necessarily updates the read model. A query sent immediately after the command can therefore return the previous view. FCQRS carries a correlation id (CID) through the command, event, and projection notification:

  mint a CID
      |
      v
  SUBSCRIBE to that CID    <-- before sending, so the answer can't slip past
      |
      v
  send command  -->  aggregate  -->  journal  -->  projection re-publishes  -->  your wait wakes

Subscribe before sending. Subscribing afterward creates a race in which the projection can publish the notification before the subscription exists. .Send accepts the CID, aggregate id, command, and a predicate selecting the aggregate 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 subs = Fcqrs.projection api (Projection.single 0 handle)

        let cid = Fcqrs.newCid ()
        let id = Fcqrs.aggregateId "11111111-1111-1111-1111-111111111111"

        // Subscribe to this CID *before* sending so the confirmation can't be missed.
        use awaiter = subs.Subscribe(cid, 1)

        match Document.Root.TryCreate(Guid "11111111-1111-1111-1111-111111111111", "Welcome", "draft") with
        | Error e -> printfn "rejected: %s" e
        | Ok doc ->
            let! event =
                documents.Send cid id (Document.CreateOrUpdate doc)
                    (fun e ->
                        match e with
                        | Document.Updated _ -> true)

            do! awaiter.Task |> Async.AwaitTask
            let projected = readModel[doc.Id.ToString()]
            printfn "saved version %A; query returned '%s'" event.Version projected.Title.Value
    }
// C#: resolve the command handler + subscription from DI, then the same
// subscribe-before-send, read-your-writes flow.
using var app = builder.Build();
await app.StartAsync();
var documents = app.Services.GetRequiredService<Handler<DocumentCommand, DocumentEvent>>();
var subs = app.Services.GetRequiredService<ISubscribe>();

var cid = Values.NewCID();
var id = Values.CreateAggregateId("11111111-1111-1111-1111-111111111111");

using var awaiter = subs.SubscribeForFirst(cid);   // subscribe BEFORE sending
if (Document.TryCreate(Guid.Parse("11111111-1111-1111-1111-111111111111"), "Welcome", "draft", out var doc, out var err))
{
    var ev = await documents(
        e => e is DocumentEvent.Updated,
        cid, id, new DocumentCommand.CreateOrUpdate(doc));
    await awaiter.Task;
    Console.WriteLine($"saved version {ev.Version}; query returned '{readModel[doc.Id.ToString()].Title}'");
}

await app.StopAsync();

Wire it to your entry point:

[<EntryPoint>]
let main _ =
    run () |> Async.RunSynchronously
    0
// Nothing to wire: the top-level statements are the entry point, and the
// StartAsync and StopAsync calls shown above form its lifecycle.

Run it, then run it again

Run the program twice without deleting tutorial.db:

dotnet run
# saved version 1; query returned 'Welcome'

dotnet run
# saved version 2; query returned 'Welcome'

The second process starts with empty memory. FCQRS replays the first Updated event through fold, then handles the new command and stores version 2. The projection also starts with an empty dictionary and replays from offset zero before applying the new event.

Delete tutorial.db* and run again to start a new event history at version 1. In production, deleting a read model and resetting its offset is a rebuild operation; deleting the journal discards the source of truth.

What you now understand

A command produced a stored event. The aggregate recovered its state by replaying stored events, and a separate projection rebuilt query data from the same history. Subscribing to the CID before sending coordinated the query with that projection.

Common mistakes

Continue the learning path

Next, add a saga for a publication rule that spans a document and its URL slug. The next chapter assumes you understand the two paths and the projection timing shown here.

For optional depth after chapter 3, The read side explains transactional offsets and rebuilds, while Add a projection is the short production recipe.

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 DocumentId = | DocumentId of Guid override ToString: unit -> string static member OfGuid: g: Guid -> DocumentId member Value: Guid
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
val g: Guid
Multiple items
union case DocumentId.DocumentId: Guid -> DocumentId

--------------------
type DocumentId = | DocumentId of Guid override ToString: unit -> string static member OfGuid: g: Guid -> DocumentId member Value: Guid
val this: DocumentId
Guid.ToString() : string
Guid.ToString(format: string) : string
Guid.ToString(format: string, provider: IFormatProvider) : string
type Title = | Title of ShortString static member TryCreate: s: string -> Result<Title,string> member Value: string
type ShortString = private | ShortString of string member Equals: ShortString * IEqualityComparer -> bool override ToString: unit -> string member IsValid: bool static member Value_: (ShortString -> string) * (string -> ShortString -> Result<ShortString,ModelError list>)
<summary> Validated non-blank string up to 255 chars inclusive. </summary>
val s: string
type ValueLens = static member Create: innerValue: 'Inner -> 'Wrapped (requires member Value_) static member CreateAsResult: v: 'a -> Result<'b,'d> (requires member Value_ and member Value_) static member IsValidValue: this: 'Wrapped -> bool (requires member Value_) static member ToString: this: 'Wrapped -> string (requires member Value_) static member TryCreate: innerValue: 'Inner -> Result<'Wrapped,'Error> (requires member Value_) static member Value: this: 'Wrapped -> 'Inner (requires member Value_) + 1 overload
static member ValueLens.TryCreate: innerValue: 'Inner -> Result<'Wrapped,'Error> (requires member Value_)
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
val ss: ShortString
Multiple items
union case Title.Title: ShortString -> Title

--------------------
type Title = | Title of ShortString static member TryCreate: s: string -> Result<Title,string> member Value: string
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val this: Title
val s: ShortString
static member ValueLens.Value: this: 'Wrapped -> 'Inner (requires member Value_)
static member ValueLens.Value: this: 'Wrapped -> 'Inner (requires member Value_)
type Content = | Content of LongString static member TryCreate: s: string -> Result<Content,string> member Value: string
type LongString = private | LongString of string member Equals: LongString * IEqualityComparer -> bool override ToString: unit -> string member IsValid: bool static member Value_: (LongString -> string) * (string -> LongString -> Result<LongString,ModelError list>)
<summary> Represents any string at least 1 chars </summary>
val ss: LongString
Multiple items
union case Content.Content: LongString -> Content

--------------------
type Content = | Content of LongString static member TryCreate: s: string -> Result<Content,string> member Value: string
val this: Content
val s: LongString
module Values from 2-running-it
val guid: Guid
val title: string
val content: string
static member Title.TryCreate: s: string -> Result<Title,string>
static member Content.TryCreate: s: string -> Result<Content,string>
val t: Title
val c: Content
static member DocumentId.OfGuid: g: Guid -> DocumentId
val e: string
Multiple items
type State = { Document: Root option }

--------------------
type State<'Command,'Event> = { CommandDetails: CommandDetails<'Command,'Event> Sender: IActorRef }
type Root = { Id: DocumentId Title: Title Content: Content } static member TryCreate: guid: Guid * title: string * content: string -> Result<Root,string>
type 'T option = Option<'T>
val initial: State
union case Option.None: Option<'T>
Multiple items
type Command = | CreateOrUpdate of Root

--------------------
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>
Multiple items
module Event from Microsoft.FSharp.Control

--------------------
type Event = | Updated of Root

--------------------
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 decide: cmd: Command<Command> -> state: 'a -> EventAction<Event>
val cmd: Command<Command>
val state: 'a
Command.CommandDetails: Command
<summary> The specific details or payload of the command. </summary>
union case Command.CreateOrUpdate: Root -> Command
val doc: Root
union case Event.Updated: 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>
val fold: event: Event<Event> -> state: 'a -> State
val event: Event<Event>
Event.EventDetails: Event
<summary> The specific details or payload of the event. </summary>
union case Option.Some: Value: 'T -> Option<'T>
val buildApi: unit -> 'a
val failwith: message: string -> 'T
val handle: offset: int64 -> message: obj -> unit
val offset: int64
Multiple items
val int64: value: 'T -> int64 (requires member op_Explicit)

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

--------------------
type int64<'Measure> = int64
val message: obj
type obj = System.Object
type unit = Unit
val run: unit -> Async<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>
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 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>
Multiple items
val string: value: 'T -> string

--------------------
type string = String
module Document from 2-running-it
val handle: _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
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 = | Updated of Root
val event: Event<Document.Event>
Event.EventDetails: Document.Event
<summary> The specific details or payload of the event. </summary>
union case Document.Event.Updated: Document.Root -> Document.Event
val document: Document.Root
Document.Root.Id: Values.DocumentId
override Values.DocumentId.ToString: unit -> string
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: cmd: Command<Document.Command> -> state: 'a -> EventAction<Document.Event>
val fold: event: Event<Document.Event> -> state: 'a -> 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 subs: 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 cid: CID
val newCid: unit -> CID
<summary> A fresh correlation id (UUID v7). </summary>
val id: 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 awaiter: 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
static member Document.Root.TryCreate: guid: Guid * title: string * content: string -> Result<Document.Root,string>
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
val doc: Document.Root
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.CreateOrUpdate: Document.Root -> Document.Command
val e: Document.Event
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
Event.Version: Version
<summary> The version number of the aggregate after this event was applied. </summary>
Document.Root.Title: Values.Title
property Values.Title.Value: string with get
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.