When developing a new application that could benefit from the Domain Drive Design (DDD), Command Query Responsibility Segregation (CQRS), and Event Sourcing (ES) I like to start out with a "quick and dirty" stack, if you will. It should meet the needs of the application in the beginning with extensibility points baked in so that I can scale out in the areas that make sense. One of the hardest parts about this is worrying about how to serialize commands to command handlers in a way that can be scaled.
One of the interesting pieces of F# (which is not F# specific, of course) is the built-in use of agent-based programming. Using the built-in MailboxProcessor we can easily write a miniature command and event bus. Not having to worry about how to build this specific piece of infrastructure (to start) allows us to focus on building the application and then plug in new pieces as we need. For many applications this approach will probably be "good enough" and might solve our needs.
So, let's take a look at what we want from our buses. Let's ignore, for now, the fact that a command should only every be handled by one command handler. Our application is just starting out, it's small, and we can keep the pieces in our head. We aren't loading up assemblies in some dynamic nature so we can visibly look at a module and see what handlers are registered. (If you can't do this, it might be time to move up a level.) Since we're not going to explicitly determine that a command must only be handled by one handler, we can assume that the two buses (Event and Command, that is) can behave the same way. A "message" comes in and it gets handed off, in a serialized fashion, to the "listeners".
This post will cover the command bus.
Command Router Interface
Let's define a little interface for our command router (the bus).
This simple little thing allows us to create different buses as we need. Of course, to start, we'll just need something to allow us to send commands into the domain. Let's now define the look of our command. We probably want to know who sent in the command, the id of the entity we're targeting, and the command itself.
So here we have a discriminated union that describes who sent the command, a record that describes some "header data" that we might need, and then the "command." The "body" portion of the "Command" record is where we'll tuck away the actual command. (We're using obj instead of a generic to make our life a little easier later. Cheating, yeah.)
Create the darn thing
Let's put our IRouter interface to work. We're only going to have one instance of the bus but we might have multiple buses. We can put together a helper function that takes in our "handlers" and returns us an instance of the bus. A handler is just a function that takes a command and returns unit (void).
Now we can wire it up to our application. Since I am partial to Simple.Web and StructureMap, I'm going to show that here, but it should be fairly obvious how to do this in your preferred framework.
I'm punting for now and putting in some fake "createHandlerN" functions into a list and then passing it into the command router function. In my next post I'll cover what those look like and how to make this play nicely with a simple event bus using NEventStore. Until then, I'm always open for feedback on what I have presented and am interested in how you might have solved this sort of problem yourself! Overall, though, nothing difficult about this and it will help us break apart our applications very nicely.
No comments:
Post a Comment