Headquarters application for developers
Headquarters web application uses CQRS with event sourcing based architecture.
At a high level a change to the state of the system is done in the following sequence:
- Client creates a command that describes what kind of change is needed
- Infrastructure restores aggregate root from its event stream
- Aggregate root checks if command execution is allowed and executes it
- Events produced by aggregate root are published to denormalizers to build reports, lists and any other representations for end user
Sections below describe major aspects of Headquarters application that developers need to know about.
Web request lifecycle
Each request in system passes a set of steps that developer should know about:
Determine target workspace
WorkspaceMiddleware
check web request URL and list of created workspaces. If rules match it wraps web request into an inner Autofac scope where IWorkspaceContextAccessor
can be resolved and client code knows workspace details that its working inside.
When workspace middleware sets asp.net HttpContext.Request.Path
and HttpContext.Request.PathBase
. This allows for all other infrastructure like routing and link builders not to think about prefix for workspace name in the URL.
In order to make all database queries to be executed in proper workspace set of NHibernate ISessionFactory
objects is stored in HqSessionFactoryFactory
class. Each has its connection string SearchPath
property configured to match workspace.
To have access to workspace information in all views global action filter (WorkspaceInfoFilter
) sets it into ViewData
dictionary.
Request transaction
UnitOfWorkActionFilter
class begins transaction and commits it if no exception occurred during request execution. This transaction wraps calls that are made to data that is stored in IPlainStorage
and IPlainKeyValueStorage
.
Command execution
Command that is sent into CommandService
to be handled by aggregate root. Each command is handled inside its own scope so that events and denormalized data is stored in one transaction and either committed fully or rolled back.
Command is executed by CommandExecutor
class and can be of two types - regular and stateless. Difference between the two is that stateless command does not restore aggregate root state from events. Example of stateless command is interview deletion that occurs when questionnaire is deleted. In such case reading each interview events and restore state would be a waste of resources.
AggregateRoot
state is restored from events by calling Apply
methods one by one in sequence recorded in eventsequence
column. Method mapped as a command handler (see usages of CommandRegistry
class to see the mapping) is called on AggregateRoot
.
Events produced by aggregate are published to denormalizers (good example of such is InterviewSummaryDenormalizer
). If non of raised an exception during event handling command transaction is committed (UnitOfWorkInScopeExecutor
is responsible for wrapping command in transaction).
Public API
Public api is the one that is intended to be used by any other software that should be integrated with survey solutions. This means that developer should not introduce breaking changes without necessity. Code that relates to public api located in WB.UI.Headquarters/Api/PublicApi
folder. It consists of 2 parts, regular api controllers that are documented with swagger and graphql.