Friday, August 26, 2016

CQRS Application Building Blocks

From time to time I try to rethink the architecture of an application I work on, do some sanity check, find hidden things which were not realized before, make them explicit.

During last such procedure, I have identified the following building blocks for CQRS app.

CQRS application consists of at least several independently running processes, 
There are 
- a process for making action on business domain
- a reaction to that action through backend workflow process (hosting sagas or process managers)
- a separate process for building a read model

You want to be able to easily host those independent functionalities in one or more processes, configure them to work cooperatively, and do this in response to business needs - there are different clients, many bounded contexts, many environments, many infrastructure variations

So the pain I was feeling is how to effectively bootstrap a process and explain it how to cooperate with other processes in a big picture.

At this stage I identified four groups for functional units I call features or modules.

Those groups are:
- kernel
- context specific 
- infrastructure
- host

In kernel there are the following features (building blocks)
- event
- domain (depends on event)
- event provider (depends on event)
- query
- projection (depends on event, event provider and query)
- api
- scheduler (depends on event)
- saga (depends on event, event provider, domain and scheduler)

kernel is not bound to any infrastructure
every feature is implemented with some class inheriting abstract module, and module instance is responsible for doing feature specific task relying on abstractions
(for example domain feature provides Engine, which can execute commands)

context specific group has different number of blocks from context to context, but they can be
- context domain (defines aggregates, and commands/events)
- context query (defines query schema)
- context projection (defines what events from context domain to project to what schema from query)
- context api (exposes UI-specific api, relying on context domain and context query)
- context saga (implements business processes relying on context domain, references context query as well)

infrastructure layer implements abstractions used by features in kernel, and may be (this is an example):
- sql server event store
- katana self hosted web api
- sql server query
- sql server scheduler
- sql server event provider

there may be more than one infrastructure for each feature (kernel module)

and last big group - hosts, hosts are separately hostable services bootstrapping set of functions they host, they may bootstrap a  lot of endpoints in a single process or in many separate processes. The typical hosts can be:
- api host (uses katana web api,  sql server event store and sql server query for context api which relies on context domain and context query)
- projection worker host (uses sql server event provider, sql server query, context domain events, context query, context projection)
- saga worker host (uses sql server event store, event provider, scheduler and query, with context saga etc)

So there are small features in kernel, they will be reused between many contexts.
Context features and kernel features have no knowledge of infrastructure, the difference between context features and kernel features is - context features implement context specific program logic, while kernel features provide base classes for doing that.

Infrastructure modules also does not need to be coded from context to context, they can be reused, but the infrastructure implements abstractions for kernel, so one context can use one infrastructure while another can use another.

Hosts are the central bootstrappers where you can decide (in one simple class), 
- which context modules you host in this process
- which kernel feature abstractions they rely on, and provide infrastructure implementations for those
- provide configuration for this (ports, connection strings, other context connection details etc)