Reference
At this moment this documentation is written and does not cover everything. We already made it public because it could already contain some information you are looking for. There are some parts of the documentation that are not in sync with the code release we did two months ago. It is written with the upcoming release in mind.
Contents
Introduction
This reference documentation explains what the Ncqrs framework is, how it can help and how you can use it.
What is Ncqrs
Ncqrs is a framework for .NET that helps build scalable, extensible and maintainable applications by supporting developers apply the Command Query Responsibility Segregation (CQRS) architectural pattern. It does so by providing an infrastructure and implementations for the most important building blocks for command handling and execution, domain modeling, event sourcing, domain events, denormalization and event storage. These building blocks help you to focus on the code that adds business value. They come with annotation, convention and configuration support and help you to write isolated and testable code.
When to use Ncqrs
Scotty said in Star Trek V: “How many times do I have to tell you, the right tool for the right job!“. Ncqrs isn’t the right tool for every job. Which means that not every application will benefit from Ncqrs. Simple CRUD based systems without much behavior will not benefit from it. But there is a wide variety of applications that do benefit from Ncqrs.
Applications will most likely benefit from Ncqrs when they have one or more of the following characteristics:
- Long life time – The project will exists for a longer period or time and therefore has the needs to organically grow.
- Need to be scalable – The application needs the be scalable when needed.
- Contain business logic – The application contains business logic that needs to stay maintainable.
- Demand for integration – Other applications need to be able to integrate with it.
- Having multiple views – There are multiple screens, reports or application that have there own view on the data.
- Must have a audit log – All state changes must be logged and a auditable log needs to be build.
System Requirements
You need the .NET Framework 3.5 or higher to use the Ncqrs framework.
License information
The Ncqrs framework and its documentation are licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Contributing to Ncqrs
Ncqrs is an open source project that is driven by software developers that love to make the world just a little bit better. The project is driven by the contribution of people. Whether it is that they give feedback, make a bug report, fixing some code or add some documentation. The project would not be a success without the people that are contributing. It keeps the project vibrant, alive and on the path of progress.
Please consider contributing to the project in one or more of the ways outlined below.
Feedback
- One of the key drivers for us is feedback. We take all feedback serious and it help us in the decisions we make, to choose the parts where we spent our time on and sometime just puts a smile on our faces. Whether you just want to say that we rock, that you would like to see feature X or Y, or that you are confused about something related to the project. You can drop your feedback through one of the follow ways:
- Twitter: @ncqrs
- Mail: feedback@ncqrs.org
- Discussions: Ncqrs Framework Discussions board
- Issue tracker: Ncqrs Framework Issue Tracker
Documentation
A solid framework is one thing, but a framework is almost worthless without documentation. Applications that are build using the Ncqrs framework will have it as part of there fundament. A choice for a component can have a big impact in the future. Documentation is important to guide people and preventing them from making mistakes. This documentation can come in many ways. For example, this reference documentation. But also quick starts, blog posts, how to video’s, they all have a big impact.
Did you blogged about your experience, did you find a weak spot in our documentation or do you have any other contribution, please see “Joining the project” for more details.
Code contribution
The code base of Ncqrs will never be complete. We are always looking for developers that are willing to share there valuable spare time. You can contribute to the framework itself or by writing a components implementation to extent it. The easiest way to do this is creating a fork this does not require anything else then that you have a Codeplex account and works instantly. When you are done you can request a pull to the main. You can also join us, see below for more information about joining.
Joining the project
We are always looking for people that want to keep the project vibrant, alive and on the path of progress. If you want to join us, you can let use know via the people page on Codeplex where you can hit the “Request to join project” link after you have signed in.
The architecture
Ncqrs helps developers apply the Command Query Responsibility Segregation (CQRS) architectural pattern. This pattern itself is quite simple. It states that the component that processes commands should be separated from the one that processes queries. Applying this pattern also gives room for a few other very powerful patterns. Ncqrs provides the building blocks that that implements these patterns.
The diagram below shows the common architecture of an application that applies the powerful patterns.
The first thing you should notice is that there is a distinction between getting data and data modification; the read side and the write side. The user interface component that you see on top of the diagram sends commands to command handlers. These make changes to the domain. These changes in the domain cause events and these events are stored in a event store and are published to denormalizers. The denormalizers denormalize these events to changes to the read models. Via this way we keep the read models up to date. The user interface will use these read models to query the data from.
Command handling
The user interface sends commands to make changes to the system. Commands are simple object that contain all the data needed to perform the underlying action. An example of the command could be MoveUserToNewAddress. The command should hold the new address for the user and the user id that indicates which user has moved.
Commands also tent to express intent by there name. For example, although the command MoveUserToNewAddress and CorrectAddressForUser both contain the same data – the address and the user id – the intent is definitely different.
All commands are send to a Command Service. This service receives the commands and routes them to the corresponding command executors. All command executors respond to a specific command and execute an action based on there content.
Ncqrs provides building blocks to help you implement a command service easily with extension points for cross cutting concerns like authorization or logging.
Command executors should not contain any business logic. The only thing they do is making changes to aggregate roots from the domain and makes changes to them. To stay away from plumbing code, Ncqrs supports mapping for command to map a command directly to an domain object.
The domain
Command executors make changes to aggregate roots in the domain. All business logic is captured within these objects and is not used for querying. This allows us to optimize this model for behavior.
Aggregate roots contain the actual business logic and are responsible for guarding their own invariants. State changes on aggregate roots cause domain events to occur. This sequence of domain events represents all the changes that has been made. This pattern is called event sourcing.
The domain events
All state changes in the domain are represented by domain events. They are simple object that contain all data related to the change. We gave two examples of command names. The events that are related to the state change of these commands will be UserMovedToNewAddress and AddressCorrectedForUser. Notice that the names are in the past tense.
The Repository
The repository is used to get and save aggregate roots. This is done by there events. Saving an aggregate root will result in persisting all his uncommitted events that occurred while making change to it. Getting an aggregate root is done by getting this events and replaying them to build up the aggregate root into the latest state.
The events are stored in the event store and when an aggregate root is saved, all the events will also be published to the event store.
The event store
All events that have occurred end up in the event store. It contains all the event that represents the state changes in the system. These can be used to build up the current state by replaying them all. This store can also be used to fill up new or repair existing read model.
Ncqrs currently supports the following products as event store:
- In memory – use it for testing purpose only!
- MongoDB
- Microsoft SQL Server
- Microsoft Windows Azure
The event bus
When an aggregate root is saved via the repository all the uncommitted events that represent the state changes that has been made are persisted into the event store. Beside that, the repository also publish these events via the event bus. This bus publishes it to everybody that has registered itself as one being interested in the events. An important choice to make is whether the bus publishes the events to the subscribers in a synchronous or asynchronous way.
Synchronous means that the event handling that is done by the subscribers blocks the current execution and that this waits for all subscribers to complete before returning to the user. This model is simple and is a sensible default.
Asynchronous would not wait for all subscribers to complete before returning to the user. It will drop the event on the bus and return to the user. This will keep the execution of command fast, but introduces eventual consistency. The read models will be consistent at some point of time.
The event handlers
There are different event handlers subscribed to the events bus. The most common one are denormalizers. These event handlers take events and makes changes to the read model based on them. For example, a denormalizer could update the users address in the user table based on the data in the UserMovedToNewAddress event. But it could also update the total number of users in city X based on that the same event.
Event handlers are not only interesting to keep the read model up to date. But they could also be written to make changes to an external system or send warning email to the business when certain event occur. It could also be that an event handler issues a new command. Event handlers are great components to extent the system with new functionality without making changes in it.
Read models
An important part of every application is data. Most of the screen in an user interface request it. But every screen just tents to have a different view on the data. For example, one wants all the products with there name, price and category, while another wants the products with there name and top 3 latest product review score and name of the reviewer.
Read models are models that can be optimized for data querying. And what is a optimal query? That is a query that just queries the data from one source. In other words: select * from x where y. No joining, just give me the data. We can accomplish this by creating one table per view. So that every view can just request data by a simple query.
You read model doesn’t even have to be a relational database, it could be a document based database that contains a collection for every view.
Ncqrs does only provide a base class for denormalizers that allows them be subscribed to the event bus. The denormalization itself is simple and straightforward.
The final application flow
The diagram bellow illustrates the application flow in a abstract form.
Getting started
This section explains how you can get started with the Ncqrs framework. It explain how to obtain the latest version and how you can get started with it.
Minimal requirements
The Ncqrs framework runs on the .NET Framework 3.5 or higher. There are a some extension projects that require the .NET Framework 4.0 because of the external libraries they depend on. The minimal reference you need in your project is Ncqrs.dll; all other libraries are additional.
Downoad Ncqrs
You can obtain the Ncqrs framework from the download page at the Codeplex project: http://ncqrs.codeplex.com/releases. There are three packages: the Ncqrs framework itself, the extension to the framework and the sample application. In most cases you only need the Ncqr framework and the extensions. You can use the Sample application as some kind of documentation.
Reference Ncqrs in your project
In most cases you should only reference Ncqrs.dll to your projects.
Enable logging
The Ncqrs framework uses log4net to log messages when available, otherwise it uses the Trace class of the .NET framework. We strongly advise you to enable logging when you are new to Ncqrs. This gives you information about what is happening under the hood.
Configuring log4net
For configuring the log4net logging we refer to the Configuration page of the log4net manual.
Configuring trace
If log4net is not available Ncqrs uses the Trace class of the .NET framework to log. You can adjust the level of the trace switch in “ncqrs” in the MyApp.exe.config or Web.config configuration file.
<system.diagnostics>
<switches>
<add name="ncqrs" value="Error" />
</switches>
</system.diagnostics>
For more information about configuring the trace switch see the page “How to: Configure Trace Switches” on MSDN.
Commanding
One of the root namespaces of Ncqrs is Ncqrs.Commanding. This includes building blocks to support command definition, execution and servicing.
The Ncqrs framework makes some kind of distinction between command servicing and command execution. A command service accepts commands and route them to the corresponding executors. Where executors execute the corresponding action with the data provided by the command message.
Commands
Commands are simple object that contain all the data to perform the underlying action. They also express intent by there name. For example, although the command MoveUserToNewAddress and CorrectAddressForUser both contain the same data the intent is definitely different.
Creating a command definition
All commands must implement the ICommand interface. The ICommand interface contains one readonly property named CommandIdentifier that return a Guid that identifies the command and should be difference for all commands. You can use the CommandBase class that initializes this identifier with the value generated by the IUniqueIdentifierGenerator from the NcqrsEnvironment.
We advice you to only use primitive or simple types in command messages. In other words, types like string, int, long, byte, Guid or Point. In other words: you should avoid all complex types.
Relations to domain objects
If the command is related to a domain object you should only use the id of that object in the command. For example, a AddProductToShoppingCart command probably has a relation with Product and ShoppingCart and uses the id’s of these object to refer to them as shown in the example code bellow.
Example
public class AddProductToShoppingCart : CommandBase
{
public Guid ProductId
{
get; private set;
}
public int Amount
{
get; private set;
}
public Guid ShoppingCartId
{
get; private set;
}
public AddProductToShoppingCart(Guid productId, int amount, Guid shoppingCartId)
{
ProductId = productId;
Amount = amount;
ShoppingCartId = shoppingCartId;
}
}
Command Servicing
A command service is a service that accepts command messages and routes them to the corresponding command executors.
You can use the CommandService class.
Command Execution
A command executor is a object that can execute a command. It must implement the ICommandExecutor<TCommand> interface. An executor accepts commands of a certain type specific by the TCommand and performs a corresponding action based on them. The corresponding action should not contain any business logic and should directly use the domain.
public class AddProductToShoppingCartExecutor : CommandExecutorBase<AddProductToShoppingcart>
{
protected override void ExecuteInContext(IUnitOfWorkContext context, AddProductToShoppingCart command)
{
// Get the shopping cart.
var shoppingCart = context.GetById(command.ShoppingCartId);
// Add the product to the shopping cart.
shoppingCart.AddProduct(command.ProductId, command.Amount);
// Accept all the work we just did.
context.Accept();
}
}
As you can see here, there is not business logic it here. It just gets the shopping cart, calls the AddProduct method and accepts the context.
Accepting the changes
All changes that you make to the domain within the IUnitOfWorkContext context will be persisted when the context gets accepted; that is by calling the Accept method. This means you do not have to add, update or save anything explicit.
A big advantage of this is that command executors can stay dumb and do not have to be aware of all the things that happened in the domain while making a few calls to it. For example, maybe the AddProduct method holds logic that makes the customer that adds the millionth product a winner of a nice flat screen TV and creates a new GrandPrice aggregate root with the details. The creation of this aggregate root is also registered within the context and accepting the context will not only persist the changes made to the ShoppingCart, but also the creation of the GrandPrice.
Domain
Another root namespace of Ncqrs is Ncqrs.Domain. This included building blocks to support domain modeling.
Aggregate Roots
Mapping
Since aggregate roots modify there own state by handling events, you will end up with a lot of event handlers. To prevent you from writing a lot of plumbing code to wire event handlers the Ncqrs framework offers aggregate root based types that support event handler mapping. The following base types are currently available: AggregateRootMappedByConvention and AggregateRootMappedWithAttributes.
Mapped by convention
When you use the AggregateRootMappedByConvention class as base for your aggregate roots you are able to create event handlers very easily by convention. Here is an example:
protected void OnFooEvent(FooEvent e)
{
// Will be mapped as a event handler for the FooEvent.
}
This OnFooEvent method will be mapped as an event handler for the FooEvent event.
The convention
The following characteristics has to be true to satisfy the convention:
- Method name starts with On or on. E.q: OnProductAdded or onProductAdded.
- Method is an instance method; not static.
- Method only accepts one parameter. Methods with zero or more than one parameter are ignored.
- The first parameter is a subclass of DomainEvent.
Although a method may be public, protected, internal, protected internal or private and can be virtual we do not advice you to make event handlers public.
Excluding methods
Sometimes you want to prevent methods that satisfy the convention for being mapped. The can be excluded from the mapping by annotating them with the NoEventHandlerAttribute attribute. All methods annotated with this attribute will be ignored in the mapping process.
The following code shows a method that will be excluded:
[NoEventHandler]
protected void OnFooEvent(FooEvent e)
{
// Will not be mapped.
}
Mapped by attributes
When you use the AggregateRootMappedWithAttributes class as base for you aggregate roots you are able to create event handlers very easily by annotating event handling methods with the EventHandlerAttribute attribute. Here is an example:
[EventHandler]
private void FooEventHandler(FooEvent eventToHandle)
{
// Event handler for FooEvent.
}
Constrains
Only methods that have the following characteristics can be mapped:
- The method should be an instance method (no static).
- It should accept 1 parameter.
- The parameter should be, or inherited from, the DomainEvent class.
- The method should be marked with the EventHandlerAttribute attribute.
All method that are annotated with the EventHandlerAttribute and to not satisfy these conditions will cause an InvalidEventHandlerMappingException exception to occur.
Implement your own mapping strategy
If you would like to add your own mapping strategy you should implements the IDomainEventHandlerMappingStrategy interface. After that you should create your own aggregate root base class to use for all your aggregate roots. This base class and subclass the MappedAggregateRoot and passes your own strategy through the base constructor.
public abstract class MyCustomAggregateRootBase : MappedAggregateRoot
{
public MyCustomAggregateRootBase() : base(new MyCustomMappingStrategy())
{
}
}
Snapshot support
Building an aggregate root from history can become time consuming when you have a lot of events to replay. We can minimize the replay by the introduction of snapshots. A snapshot is a object that holds the full state for an object at a certain point in time. We can load an aggregate root from this state and now we only have to apply the events that occurred after this snapshot was taken.
You can give your aggregate root object support for snapshots by implementing the ISnapshotable<TSnapshot> interface and creating a snapshot object that can hold the state of the aggregate root where this object should implement the ISnapshot interface.
Adding support to your aggregate root
Consider we have the following simple aggregate root Person that just holds the name and birthday for a person.
public class Person : AggregateRootMappedByConvention
{
private string _name;
private DateTime _birthday;
public Person(string name, DateTime birthday)
{
var e = new PersonCreated(name, birthday);
ApplyEvent(e);
}
public void ChangeName(string newName)
{
var e = new PersonNameChanged(newName);
ApplyEvent(e);
}
private void OnPersonCreated(PersonCreated e)
{
_name = e.Name;
_birthday = e.Birthday;
}
private void OnPersonNameChanged(PersonNameChanged e)
{
_name = e.NewName;
}
}
If we want to add snapshot support to this Person aggregate we should first define a snapshot object that can hold the current state of you aggregate.
public class PersonSnapshot : Snapshot
{
public string Name
{
get; set;
}
public DateTime Birthday
{
get; set;
}
}
Now we should implement the ISnapshotable<TSnapshot> interface. We need to implement two methods for this interface. The first one is CreateSnapshot that is used to create a snapshot of the current state of the aggregate root. In this method implementation we create a new snapshot object that holds the current state. There are two properties that should always be set and that are not related to the state of Person, those are EventSourceId and EventSourceVersion.
public PersonSnapshot CreateSnapshot()
{
var snapshot = new PersonSnapshot();
// Set general snapshot data.
snapshot.EventSourceId = Id;
snapshot.EventSourceVersion = Version;
// Set person state.
snapshot.Name = _name;
snapshot.Birthday = _birthday;
return snapshot;
}
EventSourceId should always be initialized with the Id of the aggregate root and the EventSourceVersion should always be the version of the aggregate when the snapshot was created.
As you can see, the code is pretty straight forward. It just sets all the properties to the state of the members. Now are be able to restore a Person from a snapshot. The next step is to restore a Person from a snapshot. We implement the second method RestoreFromSnapshot of the ISnapshotable<TSnapshot> interface.
public void RestoreFromSnapshot(PersonSnapshot snapshot)
{
// Restore aggregate details.
Id = snapshot.EventSourceId;
InitialVersion = snapshot.EventSourceVersion;
// Restore person state.
_name = snapshot.Name;
_birthday = snapshot.Birthday;
}
Also this code is pretty straight forward. We just set the state of the members with the state that is stored in the snapshot.
Final code
The final code looks like this:
public class PersonSnapshot : Snapshot
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
public class Person : AggregateRootMappedByConvention, ISnapshotable<PersonSnapshot>
{
private string _name;
private DateTime _birthday;
public Person(string name, DateTime birthday)
{
var e = new PersonCreated(name, birthday);
ApplyEvent(e);
}
public void ChangeName(string newName)
{
var e = new PersonNameChanged(newName);
ApplyEvent(e);
}
private void OnPersonCreated(PersonCreated e)
{
_name = e.Name;
_birthday = e.Birthday;
}
private void OnPersonNameChanged(PersonNameChanged e)
{
_name = e.NewName;
}
public PersonSnapshot CreateSnapshot()
{
var snapshot = new PersonSnapshot();
// Set general snapshot data.
snapshot.EventSourceId = Id;
snapshot.EventSourceVersion = Version;
// Set person state.
snapshot.Name = _name;
snapshot.Birthday = _birthday;
return snapshot;
}
public void RestoreFromSnapshot(PersonSnapshot snapshot)
{
// Restore aggregate details.
Id = snapshot.EventSourceId;
InitialVersion = snapshot.EventSourceVersion;
// Restore person state.
_name = snapshot.Name;
_birthday = snapshot.Birthday;
}
}
Adding support to the repository
Although we have added snapshot support to the Person object, we should use a repository that contains a reference to a ISnapshotStore so that this snapshot support will also be used. Most event stores that are provided by Ncqrs also implement the ISnapshotStore.
Project code repository
The project is hosted at GitHub where you can find the source control repository. The git source repository can be found here:
Clone URL: git@github.com:ncqrs/ncqrs.git
Minimal requirements
To be able to work with the code you need the following requirements:
Branches
The default should contain the latest stable state of the Ncqrs code. All other branches are feature branches that contain unstable code that is will under construction. A feature branch is pushed to the main when it is completed, or stable enough to share.
Folder structure
In the repository you will find three root folder: Extension, Framework and Sample.
Extensions - contains projects that extent Ncqrs. Framework - contains the Ncqrs framework projects. Sample - contains the sample application.
The extension folder contains all the projects that add extra functionality to the Ncqrs framework, but do require additional libraries. For example it contains the MongoDB project that adds a implementation an event store based on MongoDB. This project depends on a MongoDB driver, therefore it is an extension project and placed in the extension folder.
All the root folder contain one or more of the following folders: bin, src and doc. The bin folder holds all the binaries of external libraries, such as log4net. The src directory contains the source code, c# projects and solution. The doc directory contains the documentation.
Code Contract rewriter problems
It could be that if you try to run the code that you get a big bad nasty assertion with the message: “Must use rewriter when using Contract.Requires<TException>”. Here you will find the background of this error and how to solve it.
Background
The Ncqrs Framework uses Code Contracts to make sure things go as they have been planned to be. For precondition that are related to parameters, we use the Contract.Requires<TException> method. This method adds runtime validation of the preconditions and throws an exception when they where not satisfied. But to do this, the code has to be rewritten on compile time. That means, emitting the assertion check in the method. We have turned on runtime checking for all the projects in our solution. Unfortunately if you opened one of the projects without having Code Contracts installed it could be that these settings will be lost. To solve the problem you need to enable runtime code contract checking for every project in the Ncqrs Framework.
Solving the problem
You need to enable runtime code contract checking for every project in the Framework folder of the Ncqrs solution. You can do this by following these steps:
- Install Code Contracts.
- Restart after the installation of Code Contracts.
- Open the Ncqrs.sln file.
- Now, for all projects:
- Open the project properties by right clicking on the project item in the solution explorer and then click on the Properties menu item.
- Click on the Code Contract property tab.
- Check the Perform Runtime Code Contract Checking checkbox.
- Also make sure the combobox after this checkbox is set to FULL.
In the end the code contracts project pane should look like this for every project in the Framework solution folder:
Running the sample application
Documentation is very important, but developers can learn very quickly from a small reference application. That is the reason why we provide a small sample application that is based on Ncqrs. This sample application gives you a nice overview of a project that contains all the main elements of the Ncqrs based application. It contains a command service, domain, event store, event bus, denormalizer, and a simple read model.
Steps to get it running
To run the sample application you need Visual Studio 2010 and Code Contracts. If you have that installed, it will not take you more than 5 minutes to get the sample application running; otherwise, I’ll buy you a beer!
Get and build the source
- First download the latest release here and unpack.
- Open the solution Ncqrs.sln that is located in the /trunk/src/ directory.
- After the solution opened you should compile the solution.
- Make sure everything compiled correctly and that there where no build errors.
Get and run MongoDB
- Download MongoDB 1.4.0 for windows here.
- Unpack all the content in the bin folder to c:\mongodb\.
- Create the following folder c:\data\db
- Start a Command Shell (cmd.exe) and cd to c:\mongodb\.
- Now enter mongod.exe and hit enter.
Now run it!
- Set Sample.UI as startup project.
- Now you can just run the Sample.UI by hitting F5 in Visual Studio.
References
- Capt. Montgomery ‘Scotty’ Scott, Star Trek V: The Final Frontier
Trackbacks & Pingbacks
Comments are closed.