Scott Hanselman

Exploring CQRS within the Brighter .NET open source project

June 25, '17 Comments [15] Posted in DotNetCore | Open Source
Sponsored By

The logo for the "Brighter" Open Source project is a little cannon. Fire and Forget?There's a ton of cool new .NET Core open source projects lately, and I've very much enjoyed exploring this rapidly growing space. Today at lunch I was checking out a project called "Brighter." It's actually been around in the .NET space for many years and is in the process of moving to .NET Core for greater portability and performance.

Brighter is a ".NET Command Dispatcher, with Command Processor features for QoS (like Timeout, Retry, and Circuit Breaker), and support for Task Queues"

Whoa, that's a lot of cool and fancy words. What's it mean? The Brighter project is up on GitHub incudes a bunch of libraries and examples that you can pull in to support CQRS architectural styles in .NET. CQRS stands for Command Query Responsibility Segregation. As Martin Fowler says, "At its heart is the notion that you can use a different model to update information than the model you use to read information." The Query Model reads and the Command Model updates/validates. Greg Young gives the first example of CQRS here. If you are a visual learner, there's a video from late 2015 where Ian Cooper explains a lot of this a the London .NET User Group or an interview with Ian Cooper on Channel 9.

Brighter also supports "Distributed Task Queues" which you can use to improve performance when you're using a query or integrating with microservices.

When building distributed systems, Hello World is NOT the use case. BUT, it is a valid example in that it strips aside any business logic and shows you the basic structure and concepts.

Let's say there's a command you want to send. The GreetingCommand. A command can be any write or "do this" type command.

internal class GreetingCommand : Command
{
public GreetingCommand(string name)
:base(new Guid())
{
Name = name;
}

public string Name { get; private set; }
}

Now let's say that something else will "handle" these commands. This is the DoIt() method. No where do we call Handle() ourselves. Similar to dependency injection, we won't be in the business of calling Handle() ourselves; the underlying framework will abstract that away.

internal class GreetingCommandHandler : RequestHandler<GreetingCommand>
{
[RequestLogging(step: 1, timing: HandlerTiming.Before)]
public override GreetingCommand Handle(GreetingCommand command)
{
Console.WriteLine("Hello {0}", command.Name);
return base.Handle(command);
}
}

We then register a factory that takes types and returns handlers. In a real system you'd use IoC (Inversion of Control) dependency injection for this mapping as well.

Our Main() has a registry that we pass into a larger pipeline where we can set policy for processing commands. This pattern may feel familiar with "Builders" and "Handlers."

private static void Main(string[] args)
{
var registry = new SubscriberRegistry();
registry.Register<GreetingCommand, GreetingCommandHandler>();


var builder = CommandProcessorBuilder.With()
.Handlers(new HandlerConfiguration(
subscriberRegistry: registry,
handlerFactory: new SimpleHandlerFactory()
))
.DefaultPolicy()
.NoTaskQueues()
.RequestContextFactory(new InMemoryRequestContextFactory());

var commandProcessor = builder.Build();

...
}

Once we have a commandProcessor, we can Send commands to it easier and the work will get done. Again, how you ultimately make the commands is up to you.

commandProcessor.Send(new GreetingCommand("HanselCQRS"));

Methods within RequestHandlers can also have other behaviors associated with them, as in the case of "[RequestLogging] on the Handle() method above. You can add other stuff like Validation, Retries, or Circuit Breakers. The idea is that Brighter offers a pipeline of handlers that can all operate on a Command. The Celery Project is a similar project except written in Python. The Brighter project has stated they have lofty goals, intending to one day handle fault tolerance like Netflix's Hystrix project.

One of the nicest aspects to Brighter is that it's prescriptive but not heavy-handed. They say:

Brighter is intended to be a library not a framework, so it is consciously lightweight and divided into packages that allow you to consume only those facilities that you need in your project.

Moving beyond Hello World, there are more fleshed out examples like a TaskList with a UI, back end Http API, a Mailer service, and core library.

Be sure to explore Brighter's excellent documentation and examples, but be aware, this is a project under active development. Perhaps if you're new to OSS, if you find a broken link or two or a misspelling, you can do Your First Pull Request with a small fix?

Do be aware, again, that CQRS is not for every project. It's non-trivial and it's a "mental leap" as Martin Fowler puts it. If you buy in, you're adding complexity...for a reason. Keep your eyes open and do your research. It's a great pattern if you have a high performance/volume application that struggles with write concurrency or a flaky backend.

In fact there are quite a few mature CQRS libraries in the .NET open source space. I'll explore a few - which are your favorites?


Sponsor: Seq is simple centralized logging, on your infrastructure, with great support for ASP.NET Core and Serilog. Version 4 adds integrated dashboards and alerts - check it out!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Tuesday, 27 June 2017 06:46:52 UTC
Hi,

you might want to check Mediatr which is maintained by Jimmy Bogard, the guy who's also doing Automapper. Don't know how it compares to Brighter, but after your article I will certainly investigate Brighter.

Also, using a GUID as a unique ID is often not desirable in this context and a more sequential ID is advised (like a custom (Twitter) snowflake ID for example). After all, CQRS is used a lot in combination with event sourcing, and, when storing things in a SQL DB, GUIDs are painfull. A more sequential ID has less fragmentation and speeds up the consolidation of events (e.g. to build up aggregates or snapshots).

Regards,

Wim
Tuesday, 27 June 2017 07:16:51 UTC
Nice article Scott. I follow Dino Esposito and Andrea Saltarello way when implementing Event Sourcing and CQRS for DDD, and SingalR of course. As modern app development currently uses Microservices or Serverless, therefore DDD (ES-CQRS) are more relevant. I also request more examples and demos on this topics as I'm still learning this awesome architecture. Some articles advices this might only be use for huge or complex app that has multiple bounded context, well for me, I enjoy implementing it even for simple app.
Tuesday, 27 June 2017 07:36:57 UTC
@wim

It's basically impossible to use sequential ids, if your building a distributed system and your generating the IDs in client or application interacting with the distributed system. You don't really want to wait for a reply.

Issue with fragmentation can be improved using comb guid, HiLo generation or something like NewId.
Alastair Gould
Tuesday, 27 June 2017 08:52:41 UTC
Hi Scott!

I wrote a quite complex example a few years ago about cqrs with event sourcing using NServiceBus and EventStore. Also a few blog posts explaining it.

Hope you like it.

https://github.com/pablocastilla/CQRS-NServiceBus-EventStore-ElasticSearch

https://pablocastilla.wordpress.com/2014/09/22/cqrs-with-event-sourcing-using-nservicebus-event-store-elastic-search-angularjs-and-asp-net-mvc/


Tuesday, 27 June 2017 09:22:02 UTC
Hi Wim,

Mediatr has a similar model for command and handler seperation, along with a pipeline for orthogonal concerns to Brighter. There are some syntactic differences.

The main differences are (1) that we also support task queues i.e. running the handler asynchronously using a queue to provide availablility (both throttling and guaranteed delivery) and (2) we tend to use our sister project Darker (https://github.com/BrighterCommand/Darker) to do the query side, allowing us to seperate some of the requirements.

But, Mediatr is a great alternative (and there are other projects like Mass Transit and Rebus that you can check out in this space too).


Ian
Tuesday, 27 June 2017 10:24:27 UTC
Nice to see CQRS/ES everywhere. CQRS was coined in Vancouver with Greg in 2008 - it was distributed DDD before that. I added consensus algorithms to the write side (Paxos). I've done nothing except this approach since then. My company delivers software only in this way.
Tuesday, 27 June 2017 11:28:00 UTC
I employ a very similar command/handler pattern in my CQRS "framework" NEventLite. Much like Brighter it is very lightweight and you can only use the parts you like when implementing CQRS + ES. Have a look.
Tuesday, 27 June 2017 12:01:10 UTC
The soft skills side of this post - "up on github" brings up codeplex and its demise. Considering many aspects - will .net based projects become a department within github in the way it was with its own site or will all projects just be part of a big graph like stones on a great beach with attributes? The list of languages available on github is impressive.

Reviewing your post on Sept 10th 2009 when the codeplex foundation was created so much of an open source perspective has increasingly found resonance with Microsoft. However for whatever that means - how will advanced search in itself on github be able to bring out the sense of a community continuity - the creativity and synergy of an awareness of the Microsoft language category that you are suggesting in this post - other than by way of the threads you have realized over time.
Tuesday, 27 June 2017 12:20:38 UTC
I've successfully used Cirqus: https://github.com/d60/Cirqus
Tuesday, 27 June 2017 14:39:06 UTC
I've found this career is all about having many as many tools in the toolbox as one can have so that the right tool can be selected for the job. Thank you Scott for bringing another one to us. Keep up the good work!
Kevin Rich
Tuesday, 27 June 2017 15:15:47 UTC
Call me crazy, but isn't this for building CQS, not CQRS? The classes helps you build a event system that separates your methods between commands and queries, but it remains unopiniated over your model.
Tuesday, 27 June 2017 21:26:02 UTC
I've been the architect for a large system based on Event Sourcing and CQRS for over six years. I've tried to compile my experiences on building projections into a new OSS project called LiquidProjections. Check it out at https://github.com/liquidprojections/LiquidProjections
Wednesday, 28 June 2017 07:33:48 UTC
@Alastair Gould and everybody else

I indeed explained myself "not clear enough" or not extensive enough... (I'm not a native English speaker).

I'm aware strict sequential ID's are not feasible in this context and wasn't suggesting they should be strict sequential (which is a bottle neck in distributed systems). I just wanted to make clear that it's better to have an ID that is not "random" like a GUID because it is a disaster for fragmentation in your database. Twitter snowflake ID concept (or other similar concepts) is made in the context of distributed ID generation (without central bottleneck) with a garantuee for uniqueness, but, it still has a certain sequential structure (not strict, so the next is not +1 after the other) and grouping structure, which is a lot better to store in databases in terms of indexing and perforamance.

We had this experience in a system where we originally used GUIDs for events, and replaced the by a variant on Twitter snowflake and had a major increase in system performance, and almost no fragmentation on the DB. Just want to point out GUIDs aren't really nice for DB's when used as keys, and there are really good alternatives when you want to make these kind of distributed systems.
Wednesday, 28 June 2017 18:15:11 UTC
There's a tiny typo in the code sample. Instead of this:
  public GreetingCommand(string name)
:base(new Guid())

It should be this:
  public GreetingCommand(string name)
:base(Guid.NewGuid())


As you're no doubt aware, this:
  new Guid()
produces an empty GUID, i.e. all zeros:
00000000-0000-0000-0000-000000000000

Jim
Thursday, 06 July 2017 21:08:33 UTC
I have been using a fork of https://github.com/mhinze/ShortBus since 2013. Its a great pattern for more complex systems. It has really worked out well for us.
Eric Hexter
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.