AppHarbor-style Lokad.CQRS Host
Tuesday, June 12, 2012 at 16:27 Tweet in
CQRS,
Cloud Computing,
DDD,
Event Sourcing,
Lokad,
Story,
xLim Just sharing a weird idea that surfaced earlier.
As you probably know, in addition to numerous improvements, Windows Azure has announced support for git deployments of web sites recently (nice move!). This can reduce development friction noticeably, but is still far from my dream low-friction scenario on AppHarbor, which works like this:
- Developer adds new feature, which affects both server-side and UI client. UI changes require completely different read model representations (say, for querying across multiple bounded contexts in a new way) and so new projections are defined as well.
- Changes are tested locally using file system abstractions and then are pushed into the master.
- Deployment server picks up the changes and begins deployment.
- New instance of application server is built and deployed side-by-side with the old one. When server starts up, it detects that projection code has changed and automatically rebuilds persistent read models (in essence, precomputes views). After initialization process is complete - server comes back online and swaps new persistent models in.
- New version of web UI is deployed and swapped with the old one as well (via the load balancer managed by deployment server).
This scenario works on AppHarbor, but is not a good fit for Windows Azure, since step 4 is not supported.
While trying to come up with various work-arounds for this limitation, an old idea surfaced under the new look. What if we rewrote deployment server in a way that is hard-wired specifically to Lokad.CQRS projects?
In essence such deployment would be somewhat similar to Lokad.Cloud.AppHost, but with a different twist. Instead of having a generic process as deployment cell unit, we would have bounded context as defined within Lokad.CQRS. Such bounded context can be described via:
- Application Services (sets of command handlers);
- View projections (sets of event handlers that update same persistent read model);
- Tasks (long-running processes);
- Ports (subscriptions to external events which trigger certain actions in response).

In my projects each bounded context is explicitly described in the code like this:
public static class SomeBoundedContext
{
public static IEnumerable<object> Projections(IDocumentStore docs)
{
yield return new UserIndexProjection(docs.GetWriter<byte, UserIndexLookup>());
// all other projections
}
public static IEnumerable<object> ApplicationServices(
IDocumentStore docs, IEventStore store)
{
// set up some dependencies
var storage = new NuclearStorage(docs);
var id = new DomainIdentityGenerator(storage);
var unique = new UserIndexService(storage);
var passwords = new PasswordGenerator();
yield return new UserApplicationService(store);
yield return new SecurityApplicationService(store, id, passwords, unique);
yield return new RegistrationApplicationService(store, id, unique, passwords);
yield return id;
// all other application services (groups of command handlers)
}
// etc for tasks and ports
In essence, each new CQRS/DDD project at Lokad starts by copying latest source code of Lokad.CQRS Sample Project and then starting iterative domain modeling process. Models are always expressed in form of Bounded Context (like the one posted above), optional specification tests and some optional client UI (all this stuff - runnable on a local development machine or server using file system for persistence and messaging). After all is ready for the first spin, code are wired to proper deployment adapters in Lokad.CQRS and pushed to the cloud.
The most boring and tedious part in this routine is actually setting up the infrastructure and Lokad.CQRS building blocks to host the bounded contexts and UI.
So all this got me thinking - what if it would be worth another try to build a .NET DDD Model Host from all that code and experience, which would:
- provide same Lokad.CQRS infrastructure (including the new version of embedded event-store I'm building for Lokad projects) but as a server host, which is either started locally or in the cloud;
- Load bounded context definitions and wire them to the message handlers, persistence adapters (i.e. files for local environment and Azure for cloud), message quarantine routines etc
- Track changes in the code and invoke rebuilds of persistent read models, when necessary.
- Manage swapping of application services between versions (along with exception handling, timeouts of individual command handlers etc).

Theoretically, this would allow following scenario.
- Developer starts a DDD Model Host somewhere.
- His TeamCity build server is configured to push bounded contexts (as dlls or nuget packages) to this Host. Essentially, whenever a code is pushed to certain branch, it will be started up in a separate app domain, just like AppHarbor does.
- Developer registers some web UIs in this Host. Host then polls them regularly for uptime (like here) and also checks some well-known endpoint for projection code. If new projection code is detected, it will immediately be pulled and rebuilt automatically, by rerunning event streams against this new code. Host will then be responsible for keeping these read models up-to-date.
In essence, among other things, this allows to have multiple thin web clients for various bounded context running against one application server, which would host multiple bounded contexts. Theoretically scaling out rules can also be defined.
There is nothing really new in this (yet another) attempt to make a framework out of Lokad.CQRS. Here are the primary differences from other existing approaches:
- AppHarbor scenario for Lokad.CQRS deployments: is that we have a server that is strictly tailored to the Lokad.CQRS building blocks (application services, tasks, projections and ports) and relevant cross-cutting concerns. We can have multiple bounded contexts within the same worker, plus keep the ease of deploying same BC to various environments (on-premises and in the cloud) using the same codebase. Plus, we get web console for viewing all the stuff.
- The only real difference from Lokad AppHost: strong tailoring towards Lokad.CQRS blocks and providing infrastructure (proper persistence and messaging abstractions are injected by the environment).
- Difference from Lokad.CQRS deployments: bounded contexts are loaded not statically but rather dynamically; it becomes easier to deploy new BCs on an existing host.
Theoretically all this is doable and should reduce development and maintenance friction in my projects (both behavioral and big data deployments). However, from practical perspective it still might be simpler to keep on copying code from Lokad.CQRS and managing deployments manually on individual basis (less configuration code to write and hence less places for bugs to hide).
What do you think?
PS: it seems that Squarespace comments system went crazy recently and might prevent you from posting completely (as opposed to simply accepting comments to moderation queue, which is needed to stop all that spam from coming). If your comments don't show up within a day, please feel free to kick me in the twitter: @abdullin
Reader Comments (10)
You can actually fork the Azure git deployment code and run this modified version *on Azure*. Maybe that already helps?
Roy, the problem with Azure git deployment code is that it targets only IIS Web sites. Application servers require different handling.
Ah yes, that's certainly a good point. By the way, the Web UIs that you register, are they composed of different UI modules as well (i.e. one module per bounded context) or how would they know about the changes in the read model?
I tend to have small web apps and define dedicated bounded context for such an app. In current production scenarios such web BCs are statically hosted within the application server (Lokad.CQRS host). Hence whenever there is a change in specific web/client BC, then app server has to be redeployed as well.
In this imaginary scenario with DDD Host, only web UI will need to be deployed (e.g. via git push). This web app will push it's BC dll to the DDD Host per startup. The latter will be responsible for rebuilding views and maintaining them as necessary.
Right, thanks for the explanation. I'm currently looking at projects like Nancy and Simple.Web to also update the Web UI in such scenarios. Nothing concrete yet, but the way those projects group your site into modules or even, like in Simple.Web, a loosely coupled collection of classes maps quite well to how you handle bounded contexts.
Yes, AFAIK, same can be achieved in ASP.NET MVC with areas. However, either way web deployments are relatively simple in all scenarios: git push to app harbor and Azure, FTP push to any other web server. This also applies to scaled out environments (with multiple instances).
App server deployments require a bit more ceremony, though.
On the subject of AppHarbor, is it possible to employ your CQRS framework on AppHarbor? If not, what would need to be implemented to do so (a transport layer interface to IronMQ or Redis for example?).
Darran, I used to run Lokad.CQRS on Appharbor for demo purposes (in highly inefficient scenario of using Azure storage and queues, though).
Yes, you are correct. In order to run Lokad.CQRS natively on appharbor, at least two adapters have to be implemented: messaging layer (IQueueWriter and IPartitionInbox) and document persistence (IDocumentStore)
Please, keep in mind that Lokad.CQRS is just a sample app and a collection of blocks. For example, you might want to put aside storage abstractions of Lokad.CQRS and use your own persistence interfaces, if that fits your scenario better.
Thanks Rinat,
That all makes sense, but it's good to know the scope of the effort involved to get up and running on AppHarbor using some of the add-ins from their catalogue.
Cheers,
D
@Darran, my guess is that it would take a day to have an adapter, say, for AMQP messaging and some document persistence. After all, Lokad.CQRS interfaces are made extremely simple on a purpose.
FYI, I might be exploring this direction further in the upcoming months (depending on how things work out)