Header menu logo FCQRS

2. Wiring and running it

Right now our Document is a pair of pure functions with nowhere to live. They can't remember anything between calls, and nobody can send them a command. This chapter fixes both, and ends with a tiny demo that makes event sourcing visible: you'll run the program, run it again, and watch a number go up because the past was waiting on disk. Keep adding to Program.fs.

Two paths, on purpose: writing and reading

Here's the shape of the thing we're about to build:

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

The letters CQRS just mean: the path that writes and the path that reads are separate on purpose. Writes flow through the aggregate and land in the journal, an append-only log of events that is the single source of truth. Reads come from a read model derived from that log.

πŸ’‘ Mental model. Treat the journal like a bank's transaction ledger and the read model like the balance shown in your banking app. The ledger is authoritative and never edited; the balance is just a convenient running total computed from it. Lose the displayed balance and you recompute it from the ledger β€” you've lost nothing. Lose the ledger and you've lost everything. That asymmetry is the whole design: the read model is disposable; the journal is not.

Create the actor system

Fcqrs.actor builds the entire Akka.NET system from plain values. The only thing you're required to hand it is a database connection (via Fcqrs.connect). Everything else β€” serializers, sharding, snapshots, a one-node cluster that joins itself β€” comes from defaults baked into the package.

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>();

Notice the config is an empty ConfigurationBuilder().Build(). If you've used Akka.NET before, this is the part to do a double-take at:

πŸ€” Did you know? Akka.NET is normally configured with HOCON β€” often pages of it for persistence, serialization, and sharding. FCQRS ships those defaults embedded and merges anything you supply over them, so the minimum is no HOCON file at all. (When you eventually need to override a knob, you can β€” see Configuration. The default is "nothing," not "you can't.")

That tutorial.db file is the journal, and the fact that it's a real file on disk is exactly what makes the demo at the end work β€” it outlives the process. (Swapping DBType.Sqlite for Postgres or SQL Server is a one-line change; the rest of your code doesn't notice. See Configure the database.)

A read side to listen on

A projection is a function run once per event, in order. A real one folds each event into a SQL table you can query; ours does the smallest thing that still demonstrates the principle β€” it recognises Document events and hands each one back:

let handle (_offset: int64) (event: obj) : IMessageWithCID list =
    match event with
    | :? Event<Document.Event> as e -> [ e :> IMessageWithCID ]
    | _ -> []
// C#: the same projection, returned as a method and registered via .AddProjection.
public static IList<IMessageWithCID> Handle(long offset, object ev) =>
    ev is Event<DocumentEvent> e
        ? new List<IMessageWithCID> { e }
        : new List<IMessageWithCID>();

builder.Services.AddProjection(handler: _ => Handle, lastOffset: _ => 0);

Why hand the event back instead of just doing the write? Because the list you return is re-published to subscribers, and that re-publish is what lets a caller know its write has reached the read side. That's the mechanism behind the next step. (This projection is deliberately minimal β€” a production one also writes to a table and tracks its offset, which is its own how-to: Add a projection.)

Send a command β€” and read your own write

Now the subtle part, and the reason well-built CQRS systems feel solid instead of flaky. Writes are asynchronous: the command is handled, the event is journaled, and then it propagates to the read side a beat later. So the naΓ―ve "save, then immediately query" can read stale data β€” the classic bug where you create something, redirect to its page, and it's not there yet.

You might reach for a Thread.Sleep or a retry loop. Don't β€” there's an exact signal. FCQRS threads a correlation id (CID) through the whole round trip, and the move is:

  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

🎯 Key principle. Subscribe before you send. If you subscribed afterward, the event could be processed in the gap and you'd wait forever for a notification that already fired. Subscribe-then-send closes that window by construction β€” the same reason you start recording before the rocket launches, not after.

Fcqrs.aggregate registers the aggregate and returns a handle whose .Send ties it together: you give it the CID, the aggregate id, the command, and a predicate for the event you're waiting on.

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

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

        // No sagas yet β€” the saga-starter still has to be wired, with none. (Ch. 3 adds one.)
        Fcqrs.wireSagaStarters api []

        let subs = Fcqrs.projection api { LastOffset = 0; Handle = handle }

        let cid = Fcqrs.newCid ()
        // A FIXED id, so every run addresses the SAME document β€” that's what lets the version climb.
        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 // read side is now up to date
            printfn "saved %A at version %A" event.EventDetails event.Version
    }
// C#: resolve the command handler + subscription from DI, then the same
// subscribe-before-send, read-your-writes flow.
var app = builder.Build();
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;                             // read side is now up to date
    Console.WriteLine($"saved {ev.EventDetails} at version {ev.Version}");
}

Wire it to your entry point:

[<EntryPoint>]
let main _ =
    run () |> Async.RunSynchronously
    0

Run it, then run it again

This is the moment the whole tutorial has been building toward, so do it for real:

dotnet run
# saved Updated ... at version 1

dotnet run
# saved Updated ... at version 2

Sit with what just happened. Between the two runs the process exited completely β€” every byte of in-memory state was thrown away. Yet the second run reports version 2. Nobody wrote "load the document" code, because there isn't any to write. On startup FCQRS found the document's one stored event, replayed it through your fold (0 β†’ 1), and then applied the new write (1 β†’ 2). The number climbs because the journal persisted across the restart and fold is pure enough to reconstruct the exact same state every time.

πŸ’‘ Try this. Delete tutorial.db* and run again β€” you're back to version 1. That's the asymmetry from the start of the chapter made tangible: erase the derived state and it rebuilds from the log; there's nothing else to lose because the log was the truth.

⚠️ And the honest counterweight: for one document with one field, this is a lot of moving parts to print a number. You wouldn't reach for it here. What you just watched scales, though β€” to thousands of entities that must stay consistent under concurrent edits, each with a perfect audit trail and free rebuildable views. You got all three without writing the persistence, the loading, or the concurrency control. That's the trade you're actually evaluating, not the line count of a hello-world.

What you now understand

A command doesn't change anything by itself β€” it produces an event, the event lands in a durable log, and everything else (current state, read models, the answer to your caller) is derived from that log. You also met the one piece of discipline that makes async writes feel synchronous to a caller: subscribe to a correlation id, then send, then wait.

Common mistakes

Further study

Next, a rule a single aggregate structurally cannot enforce β€” a per-user quota β€” and the saga that coordinates the two aggregates it takes.

namespace System
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* β€” it 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 users = Fcqrs.aggregate api { Name="User"; ... } let quota = Fcqrs.saga api (quotaDef documents.Factory users.Factory) Fcqrs.wireSagaStarters api [ quota ] let subs = Fcqrs.projection api { LastOffset = 0; Handle = handle } // send a command and await the resulting event (read-your-writes): 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 Isvalid: this: 'a -> bool (requires member Value_ and 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 Version: int64 }

--------------------
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>
type Version = private | Version of int64 member Equals: Version * IEqualityComparer -> bool override ToString: unit -> string static member Value_: (Version -> int64) * (int64 -> Version -> Result<Version,ModelError>) static member Zero: Version
<summary> Aggregate Version </summary>
Multiple items
val int64: value: 'T -> int64 (requires member op_Explicit)

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

--------------------
type int64<'Measure> = int64
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 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 IMessage interface ISerializable member Equals: Event<'EventDetails> * IEqualityComparer -> bool override ToString: unit -> string
<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: State -> State
val event: Event<Event>
val state: State
Event.EventDetails: Event
<summary> The specific details or payload of the event. </summary>
union case Option.Some: Value: 'T -> Option<'T>
State.Version: int64
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) -> 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 -> 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 abstract Mediator: IActorRef ...
<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 handle: _offset: int64 -> event: obj -> IMessageWithCID list
val _offset: int64
val event: obj
type obj = Object
type IMessageWithCID = abstract CID: CID
<summary> Interface for messages that carry a Correlation ID (CID). </summary>
type 'T list = List<'T>
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 IMessage interface ISerializable member Equals: Event<'EventDetails> * IEqualityComparer -> bool override ToString: unit -> string
<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>
module Document from 2-running-it
type Event = | Updated of Root
val e: Event<Document.Event>
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: cmd: Command<Document.Command> -> state: 'a -> EventAction<Document.Event>
val fold: event: Event<Document.Event> -> state: Document.State -> Document.State
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>
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). </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
val event: 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 event (read-your-writes). </summary>
union case Document.Command.CreateOrUpdate: Document.Root -> Document.Command
val e: Document.Event
union case Document.Event.Updated: Document.Root -> Document.Event
property FCQRS.Query.IAwaitable.Task: Threading.Tasks.Task with get
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>
static member Async.AwaitTask: task: Threading.Tasks.Task -> Async<unit>
static member Async.AwaitTask: task: Threading.Tasks.Task<'T> -> Async<'T>
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.