This is Part 3 of a four-part series describing how to build an application in .NET using the Command-Query Responsibility Segregation (CQRS) and Event Sourcing (ES) patterns, as well as the SimpleCQRS NuGet package. Click here for Part 1.
UPDATE (Sep 6th 2016): Also check out my follow-up series, Real-World CQRS/ES with ASP.NET and Redis.
Building from the work done in Part 1 and Part 2, we are now ready to implement a front-end to our CQRS/ES system.
For this demo, the front end will be an ASP.NET Web API application, and we'll be using Microsoft's Unity container to provide Dependency Injection and use the Service Locator pattern.
Defining and Using the Runtime
The first thing we need to do is create a runtime in which our CQRS/ES app will execute. That runtime, further, needs to override a method so that it returns a SQL Server Event Store rather than the default LocalEventStore. Finally, the runtime must be hooked into SimpleCQRS's own Unity implementation. The runtime looks like this:
public class SampleCQRSRuntime : SimpleCqrs.SimpleCqrsRuntime<UnityServiceLocator>
{
protected override IEventStore GetEventStore(SimpleCqrs.IServiceLocator serviceLocator)
{
var configuration = new SqlServerConfiguration(Constants.SampleCQRSConnectionString);
return new SqlServerEventStore(configuration, new JsonDomainEventSerializer());
}
}
This runtime inherits from the SimpleCqrsRuntime and specifies that we're using Unity as the Service Locator pattern. Some people will argue that Service Locator is an anti-pattern, and it may well be, but for simplicity's sake I'm going to be using it here.
Once we have the runtime defined, we can create one in our app's Global.asax file:
public class Global : System.Web.HttpApplication
{
public static SampleCQRSRuntime Runtime { get; set; }
protected void Application_Start()
{
Runtime = new SampleCQRSRuntime();
Runtime.Start();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
}
protected void Application_End()
{
Runtime.Shutdown();
}
}
With these two pieces in place, we are ready to start defining our Controllers.
Defining the Controllers
Because we are using Dependency Injection, we need to define our Web API Controller classes in a specific way so that SimpleCQRS and Unity will properly inject the desired dependencies. Here's a skeleton for MovieController:
[RoutePrefix("movie")]
public class MovieController : ApiController
{
}
Now, we must think about what kinds of dependencies MovieController has. In a "normal" application, MovieController might well have a dependency on a Repository or Service class, but in our implementation neither of those exist at this level. Instead, MovieController has a dependency on the Command Bus we talked about in Part 3, and we need to set up the controller such that this dependency is resolved when the controller instance is created (aka "constructor injection"):
[RoutePrefix("movie")]
public class MovieController : ApiController
{
public ICommandBus CommandBus;
public MovieController() : this(ServiceLocator.Current.Resolve<ICommandBus>())
{
}
public MovieController(ICommandBus commandBus)
{
CommandBus = commandBus;
}
}
With our dependencies in place and ready for injection, we can build the action for creating a movie. Nice thing is, the action is all of one line long!
[HttpPost]
[Route("add")]
public CreateMovieStatus Create(CreateMovieCommand command)
{
return (CreateMovieStatus)CommandBus.Execute(command);
}
Note the Execute() call. There is also a Send() call. The difference appears to be that Execute() can return an integer value, whereas Send() only returns void.
Guess what? With that controller action in place, we're done! We have a fully-functional CQRS system!
Order of Execution
Of course, no system is complete without testing. Let's see a sample Postman call to our new action:
Here's the order of execution when we make this call:
- An instance of MovieController is created, and a LocalCommandBus is injected into it.
- The action Create() fires.
- The CreateMovieCommand is placed on the Command Bus.
- The CreateMovieCommandHandler picks up the CreateMovieCommand and processes it.
- As part of the CommandHandler, a new Domain object is created and has a MovieCreatedEvent applied to it.
- The CommandHandler saves the Events in the Domain Object to the IDomainRepository.
- The IDomainRepository serializes the event to the SQL Server Event Store and places the event on the Local Event Bus.
- All Event Handlers which are listening for a MovieCreatedEvent pick up the event and process it.
- In our specific case, the MovieCreatedEventHandler processes the new event to the MovieReadStore.
- The Action returns an Enum value to the caller.
You can try this for yourself! The sample project is over on GitHub.
There you have it! We've got a fully-functional CQRS/ES application, with a Web API front end, and a full event store. In the next and final part of this series we'll add some more events and commands, specifically ones that implement changes being made to an already-existing Domain Object, as well as list some drawbacks of this particular solution.
If you'd like to see the code I built for this series, check out the GitHub repository.
Happy Coding!