The easiest way I've found to get started implementing a
CQRS system is to download the community edition of NServiceBus and run through some of the
samples. NServiceBus is a mature open-source application designed to minimize
the amount of work needed to get started with using messaging within your
applications. There is a significant learning curve involved with NServiceBus,
but it's one of the easiest ways to get started.
As I write this, NServiceBus' latest version is 2.5, and
there are both community and commercial versions available.
The following diagram, from NServiceBus'
architectural principles, demonstrates how Commands and Queries may be
separated within the application's architecture into two separate pipelines.
When implementing CQRS using NServiceBus, consider creating
a local data store on your web server (for instance, using SQL Server Express)
which only includes the data needed to be displayed currently. This data may
be in a completely different format than your production database, and will
often be denormalized and pre-computed so that reports and summaries can be
displayed extremely quickly.
Once this data store is in place, the next step is to ensure
it is periodically updated. You can do this on some kind of a scheduled basis,
and depending on your application's needs this might be as simple as removing
the node from the load balancer, wiping out the database, and running a fresh
population script from the canonical data store. More typically, updates to
the local read-only stores can be done by using a Publisher-Subscriber
messaging model, in which the main data store publishes updates that are of
interest to the read-only stores, and each read-only store subscribes to these
messages and responds to them by updating the data in question. Typically the
messages sent in this scenario do not contain the data updates themselves, but
rather act as a notification to the subscriber that it needs to update its
data.
The web application can continue to use SQL (assuming that's
what it used before) to access its read-only data, with the only difference
being the location of the data store and perhaps the schema of the tables
involved (which should more closely map to the individual pages in the
application).
With a local data store in place and a Pub-Sub mechanism in
place to ensure these are kept up-to-date, the system will already be much more
scalable, as the read load on the primary data store will be greatly reduced.
The second part of the equation (which of course can be implemented first or
instead of the local data store) is to remove direct writes between the
application and the canonical data store.
Rather than issuing INSERT or UPDATE or DELETE commands to
the data store, these commands are encapsulated into messages which are written
to a local, transactional queue. These queued messages are picked up by a
handler that is responsible for applying them to the data store, as well as
handling any issues that may occur (including retrying the operation or
handling exceptions). From the web application's point of view, rather than
calling a method to perform the command, which in turn executes some SQL or
calls a stored procedure, a message is created and sent. This might look
something like this:
var message = new OrderMessage(
customerId,
orderItems,
shippingDetails,
paymentDetails);
var bus = IoC.Resolve<IBus>();
bus.Send(message);
The configuration required to get started with NServiceBus
is pretty minimal as well. For message processing, the Generic Host
application provides an good starting point and can be installed as a service,
with built-in profiles for dev/integration/production settings in terms of
logging and persistence. For the web application, something like the following
needs to run prior to the first message being sent:
Configure.WithWeb()
.StructureMapBuilder((IContainer) container)
.Log4Net()
.XmlSerializer()
.MsmqTransport().IsTransactional(true)
.UnicastBus()
.LoadMessageHandlers()
.CreateBus()
.Start();
And a little bit of configuration needs to be added to
web.config:
<configuration>
<configSections>
<section name="MsmqTransportConfig"
type="NServiceBus.Config.MsmqTransportConfig, NServiceBus.Core" />
<section name="UnicastBusConfig"
type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core" />
<section name="Logging" type="NServiceBus.Config.Logging, NServiceBus.Core"/>
</configSections>
...
<MsmqTransportConfig InputQueue="webinputqueue" ErrorQueue="error"
NumberOfWorkerThreads="1"
MaxRetries="5"
/>
<UnicastBusConfig
DistributorControlAddress=""
DistributorDataAddress="">
<MessageEndpointMappings>
<add Messages="Application.Infrastructure" Endpoint="mainqueue" />
</MessageEndpointMappings>
</UnicastBusConfig>
<Logging Threshold="WARN" />
This is just an example, and is meant to show how little
code is required to get started with sending messages from your web application
and handling them from an NServiceBus Host application/service.