- Getting and displaying data from the database.
- Inserting and updating some data.
In other words I have Queries and Commands. I’m not going to talk what is the “query command pattern” - that you can read in Ayende’s “Limit your abstractions” posts. I am going to talk about a nice way to implement that pattern in your MVC application.
Let’s go
The implementation is pretty easy: if you need to query something you create a query class that does it. If you have to insert or update some data then you create a command class. The controller will execute commands and queries directly from it’s actions, no need no create a BL layer, DAL or repository of some kind.
Command
First we need an abstract command class
1: public abstract class Command
2: {
3: public abstract void Execute(IDocumentSession session);
4: }
The Command class a single method “Execute” that receives the session as a parameter. The Command class supposed to handle a business logic that creates, updates or deletes something in the application that’s why it has a void as a return type.
Command example
1: public class RegisterUserCommand :Command
2: {
3: public User User { get; set; }
4:
5: public RegisterUserCommand(User user)
6: {
7: User = user;
8: }
9:
10: public override void Execute(IDocumentSession session)
11: {
12: if (session == null)
13: throw new ArgumentNullException("session");
14:
15: if (User == null)
16: throw new InvalidOperationException("Can't register null user");
17:
18: session.Store(User);
19: }
20: }
When I create the command I pass it my model, throw in some validations and finally use the received session to save the entity. Note that the command should not return a result. It should succeed or fail.
Query
Same as the command first I’ll create an abstract query class a single method:
1: public abstract class Query<T>
2: {
3: public abstract T Execute(IDocumentSession session);
4: }
As oppose to the command class, the query has to return a result (T).
Query example
1: public class CurrentCustomerQuery : Query<Customer>
2: {
3: private string _email;
4:
5: public CurrentCustomerQuery(string email)
6: {
7: if (string.IsNullOrWhiteSpace(email))
8: throw new ArgumentNullException("email");
9:
10: _email = email;
11: }
12:
13: public override Customer Execute(IDocumentSession session)
14: {
15: if (session == null)
16: throw new ArgumentNullException("session");
17:
18: return session.Query<Customer>().SingleOrDefault(x => x.Email == _email);
19: }
20: }
How to use them
So now we know how queries and commands look like, all that’s left is to use them.
The controller is responsible for executing the commands and the queries according to the action scenario.
The easiest way will be creating the command or the query in the controller and simply execute them.
1: [HttpPost]
2: public ActionResult RegisterCustomer(Customer model)
3: {
4: if (ModelState.IsValid)
5: {
6: new RegisterCustomerCommand(model).Execute(RavenSession);
7: return RedirectToAction("Index", "Home");
8: }
9:
10: return View(model);
11: }
However that’s not really testable since we don’t really want to execute our commands or queries. Furthermore it just looks bad.
We need to wrap the commands/queries so that we could mock them easily (and make the code look better).
In order to wrap the queries and the commands we’ll create “CommandExecuter” and “QueryExecuter” with a single method “Execute”.
1: public class CommandExecuter
2: {
3: public void Execute(Command command, IDocumentSession sesseion)
4: {
5: command.Execute(sesseion);
6: }
7: }
8:
9: public class QueryExecuter
10: {
11: public T Execute<T>(Query<T> query, IDocumentSession session)
12: {
13: return query.Execute(session);
14: }
15: }
The base controller will be ctor injected with both of them and will implement two methods “ExecuteCommand” and “ExecuteQuery” which receive a command or a query and call it Execute method.
1: public class BaseController : Controller
2: {
3: #region Properties
4:
5: private QueryExecuter QueryExecuter { get; set; }
6: private CommandExecuter CommandExecuter { get; set; }
7: private IDocumentStore Store { get; set; }
8: public IDocumentSession RavenSession { get; protected set; }
9:
10: #endregion
11:
12: #region Ctor
13:
14: public BaseController(IDocumentStore store, CommandExecuter commandExecuter, QueryExecuter queryExecuter)
15: {
16: Store = store;
17: CommandExecuter = commandExecuter;
18: QueryExecuter = queryExecuter;
19: }
20:
21: #endregion
22:
23: #region Executers
24:
25: protected void ExecuteCommand(Command command)
26: {
27: CommandExecuter.Execute(command, RavenSession);
28: }
29:
30: protected T ExecuteQuery<T>(Query<T> query)
31: {
32: return QueryExecuter.Execute(query, RavenSession);
33: }
34: #endregion
35:
36: #region Action Execution
37:
38: protected override void OnActionExecuting(ActionExecutingContext filterContext)
39: {
40: RavenSession = Store.OpenSession();
41: }
42:
43: protected override void OnActionExecuted(ActionExecutedContext filterContext)
44: {
45: if (filterContext.IsChildAction)
46: return;
47:
48: using (RavenSession)
49: {
50: if (filterContext.Exception != null)
51: return;
52:
53: if (RavenSession != null)
54: RavenSession.SaveChanges();
55: }
56: }
57:
58: #endregion
59: }
Now when we want to execute a command or a query we’ll call the proper method and pass it the right parameter.
1: [HttpPost]
2: public ActionResult RegisterCustomer(Customer model)
3: {
4: if (ModelState.IsValid)
5: {
6: ExecuteCommand(new RegisterCustomerCommand(model));
7: return RedirectToAction("Index", "Home");
8: }
9: return View(model);
10: }
Summary
So what we gained?
1. A single command for every action and a single query for every … query.
2. Every command/query has its own logic but they all implement “Execute” method and the base controller just call it from a single place.
3. The deriving controllers are substitutable for their base type and use the BaseController ExecuteCommand or ExecuteQuery.
4. We have two different “Execute” methods, a void one and one which returns a result. Every command/query implements just the one they need and not forced to implement both.
5. Dependency injecting the command and query executers to the controller’s ctor.
Pretty SOLID.
Thanks bro.. Just read ayende's blog but looking for example for implementing this pattern. Yea i'm slow goddammit.
ReplyDelete@zul, I'm glad this post helped you. If you would like to discuss the implementation for your system just send me an email.
DeleteThis comment has been removed by the author.
ReplyDeleteHi, can you drop some lines of unit tests code for a Query and a Command. Do you mock the session?
ReplyDeleteHi,
DeleteThe unit tests will look exactly the same as unit tests of a class with dependencies. You'll mock every dependency with your mocking framework (I recommend NSubsitute), however you should not mock the session - just use SQLite and store the data to files or keep it in memory.
My comparison of mocking frameworks - http://dennis-nerush.blogspot.co.il/2012/10/rhino-mocks-vs-moq-vs-nsubstitute.html
Use SQLite for unit testing - http://ayende.com/blog/3983/nhibernate-unit-testing