Header menu logo FCQRS

FCQRS

A functional CQRS and event-sourcing framework built on Akka.NET, with first-class F# and C# APIs.

The same order domain shown in two ways: one shared model on the left, and separate decision-making and query models on the right.
The same domain represented by one shared model and by separate write and read models.

Why two models?

The information you need to make a decision is usually not the same information you want to show on a screen.

Take an order cancellation. To decide whether an order can be cancelled, the system may only need to know whether it has been paid, shipped, or cancelled already.

The order page needs something different. It may show the customer's name, the products they bought, prices, delivery details, payment status, and a timeline of everything that happened.

These are two different jobs. One model helps the system make correct decisions. The other helps people find and understand information.

You can force both jobs into one model. Many systems begin that way because it looks simpler. Over time, every new screen and every new rule pulls that model in a different direction. Queries become tangled with business rules, and changing one side risks breaking the other.

CQRS separates them.

Motivation: Separating the models is extra work, but it is worthwhile when decision rules and query needs change for different reasons. Each model can then stay small instead of making every screen and rule negotiate one shared shape.

The write model handles decisions and protects the rules of the system. The read model presents the information people and applications need. Each can then stay simple in its own way.

How FCQRS works

FCQRS is a functional framework for building the two models with Akka.NET actors and event sourcing, usable from F# and C# alike.

Every command for an order goes to one actor identified by that order's id. This decision-making actor represents an aggregate: the state and rules for one order. It handles one command at a time. If two cancellation requests arrive together, one is decided first and changes the order's state before the second is considered. The cancellation rule therefore has one place to run and one current state to inspect. Other order aggregates can proceed at the same time, on the same server or elsewhere in the cluster.

The decision is an ordinary function. It receives the command and the current state, then returns an event such as OrderCancelled or a rejection such as OrderAlreadyShipped. A rejection is returned to the caller but is not stored, so it must not change recoverable state. A second function folds a stored event into the state. These functions contain the business rules but no database or actor code, so they can be tested on their own.

Stored events form the order's history. If the actor has been dropped from memory while idle, or the process restarts, FCQRS replays those events to recover the current state. The state held by the actor is derived from the events; it is not a row that the actor overwrites.

The same events feed projections on the read side. One projection can maintain the order page while another maintains a customer history or a reporting table. A projection chooses the data structure that fits its query. It does not have to copy the shape of the write model.

A saga coordinates work that crosses aggregate boundaries. For example, an OrderPlaced event can start a process that reserves stock with the inventory aggregate, takes payment with the payment aggregate, and reports the outcome to the order. The saga stores its progress so it can resume the next unfinished step after a restart.

Every command and event carries a correlation id. A client can subscribe to that id before sending a command and wait until the projection publishes the matching event. The signal tells the client when the new data is available to query, without polling or an arbitrary delay.

FCQRS supplies the actor lifecycle, cluster sharding, event storage and replay, saga coordination, projection stream, and correlation subscriptions around the functions that define the domain.

How FCQRS fits together

What FCQRS handles at a glance

Find your path

Choose one path based on what you need now:

Your situation

Start here

Stay there until

You are learning FCQRS

Learn FCQRS

you finish the numbered course

You finished the course and want deeper reasoning

Understand

the guarantee or failure boundary is clear

You know the model and need to implement one task

Apply

the task is complete

You need an exact setting or API member

Configuration or API reference

you have the required value

The beginner path includes the quickstart. It does not require you to alternate between tutorial, concept, and how-to pages. Those other sections become useful after the course has introduced their topic.

When FCQRS is a good fit, and when it isn't

FCQRS is useful when the system must protect business rules, preserve the order of changes, or keep a history that may be queried again later. It also fits when several users or processes can issue commands for the same entity, or when one action starts a workflow that must survive a restart.

The cost is an event journal, separate read models, asynchronous projections, and an actor system to operate. That cost has little return for data with no behaviour beyond create, read, update, and delete. A small internal directory or a table maintained by one user may be clearer as a conventional database application.

The choice depends on the rules and failure modes in the application, not its current number of users. The concepts section explains the guarantees and the responsibilities that remain with the application.

Type something to start searching.