Published 21 Apr 2015
I have been struggling for some time to find an application architecture for ASP.NET MVC that both respects SOLID principles and is also quick and simple to use. Is the command-query separation (CQS) pattern the answer?
The goal is to achieve clean boundaries between aspects of an application, to keep business logic isolated from the presentational concerns within an ASP.NET MVC application.
The simplest model for achieving this is the repository pattern, you create a repository for each entity type and you expose these to controllers as constructor dependencies.
publc class MyController : System.Web.Mvc.Controller
{
protected readonly IRepository _repository;
public MyController(IRepository repository)
{
_repository = repository;
}
}
However, this approach soon starts to get unwieldy when you get into real world projects as you quickly build up dependencies on several repositories. Even if we are being disciplined about separating responsibilities across different controllers, it is likely that you will need to touch at least two or three repositories. Does this really matter? Yes, because when it comes to writing unit tests, adding extra dependencies makes it more time consuming to write each test, and this deters you from doing good unit testing.
The approach also tends to lead to business domain concerns leaking into the presentational layer - some of the orchestration between repositories is really a core feature of the business domain.
One route to solving these problems is to roll these dependencies up into a service. This keeps things simple at the controller level, but really only pushes the problem up a layer. The services get more and more bloated over time, and rack up the dependencies themselves. We are still breaking the single responsibility principle and making it hard to unit test.
I first came across CQS in the CodeCampServer example application from Jeffrey Palermo, but I have also seen it in S#arp Architecture. Both projects appear to have run out of steam a few years ago, but demonstrated some interesting ideas.
The CQS principle (often referred to as CQRS where the "R" stands for responsibility) states that every method should either be a command that performs an action or a query that returns data, not both. In some architectures this separation is driven by the desire to separate the reading and writing of data into different systems using messaging and events but I don't want to focus on these more complex architectural systems. Eventing is not an essential aspect of this pattern, something that is commonly mistaken.
Whilst the essence of CQS is the separation of read and write, and you could just split writes into one service and reads into another, for me the benefit is the way you can make your business logic more "atomic" and therefore simpler to manage and test. I like the approach where each command is a separate entity. A typical implementation of the command side pattern is captured via an interface like this:
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command)
}
So each command handler is a separate class instantiating this interface, and there is a one-to-one relationship with a unique command message class. This approach immediately provides several benefits.
TCommand
in essence provides a route to locate the appropriate command handler when the system is wired up using IoCThe bit that people start to baulk at is the fact that we have no return type. In strict implementations this is for scale out reasons and wanting to separate reads from writes. Personally this is a good approach to take if it works, and I don't think it is too difficult to develop systems that work with this asynchronous approach, but I'm not averse to using a return type if it works for a given application. We can tweak as follows:
public interface ICommandHandler<TCommand, TResult>
{
TResult Handle(TCommand command)
}
I think the command side of things is pretty strong, and you can take away some of the pain of having to create so many classes by using the Mediatr library, created by Jimmy Bogard, to wire things up automatically. Mediatr provides interfaces that allow you to implement the pattern with minimal setup, you use IRequest<out TResponse>
for the command message and IRequestHandler<in TRequest, TResponse>
for the handler. In .NET Core the setup using the baked in DI container is a siple one liner, but Jimmy provides examples for how to wire it up with many of the popular DI containers.
There is nothing to stop you simply switching back to using repositories/services in the controller, strictly speaking this is separate from commands but would lead back to the limitations outlined earlier. It also feels wrong to me as it gives the controllers too many responsibilities. So if we reject this we need some form of query objects. In S#arp Architecture they suggest that such objects should live at the presentation layer. I can see the motivation for this, queries are about viewing information, and many concerns around the format and nature of these queries will be driven by presentational concerns such as pagination. It definitely feels wrong to be putting pagination logic into domain level libraries. However, this then requires them to "infect" the presentation layer with infrastructure concerns:
public class ProductListQuery : NHibernateQuery, IProductsListQuery
{
public IPagination<ProductViewModel> GetPagedList(int page, int size)
{
...
}
}
Now there are some intelligent programmers who have argued that there is nothing wrong with making direct use of your persistence framework. Personally I prefer to keep my options open by not being quite so explicit. I suggest the following approach, again using Mediatr:
public class ArticleQuery IRequest<Article>
{
public string ArticleUrlSlug { get; }
public ArticleQuery(string articleUrlSlug)
{
ArticleUrlSlug = articleUrlSlug;
}
}
public class ArticleQueryHandler : IRequestHandler<ArticleQuery, Article>
{
private readonly ISomeDependency _someDependency
public ArticleQueryHandler(ISomeDependency someDependency)
{
_someDependency = someDependency;
}
public async Task<Article> Handle(ArticleQuery query, CancellationToken cancellationToken)
{
var article = await _someDependency.GetArticleBySlug(query.ArticleUrlSlug);
return article;
}
}
I appreciate that it does involve some overhead over a more aggregate approach using fewer read/write services but on balance I think that is worth it for the improved unit testability. Each request and handler forms its own isolated domain, only needing the exact dependencies to fulfil the command and so can be implemented to perfectly suit that requirement.