Implementing CQRS Using MVC

by scott.rudy on 09-07-2010 08:49 PM - last edited on 09-07-2010 09:24 PM

Greg was one of the first bloggers to bring the idea of CQRS (Command Query Responsibility Segregation) into the .Net world, so I think it is safe to say he knows what CQRS is. He recently posted a blog on a how CQRS is essentially the same as the MVC UI pattern. He makes this comparison based on the fact that one would use a "command model" and a "query model" in an application. 

 

However, I completely disagree with his relation between a code-based implementation pattern and a data store location. The thing I don’t like about Greg's comparison is that I don't have to use CQRS in MVC and I don't have to implement CQRS using MVC, so I think his statement is going to cause confusion. Therefore saying they are sort of the same doesn’t work for me. Furthermore, I still hold the CQRS is nothing more but a new term to talk about caching.

 

The controller is responsible for separating the view and the model. To that end, one "could" use the controller in an MVC implementation to know which model to read from and which model to write to, although there is no reason you couldn’t have more than two models. The controller would more then likely expose a CQS-style interface and it would probably be a great way to implement CQRS.

 

If I did implement CQRS using MVC then I would probably use a cached abstraction over my model that would look something like this:

 

public class DataStruct {
    public int key { get; set; }
    public string stuff { get; set; }
}

public interface IModel {
    DataStruct GetData(int key);
    void UpdateData(DataStruct data);
    void InsertData(DataStruct data);
}

public class CachedModel : IModel
{
    private readonly Cache _cache = HttpContext.Current.Cache;
    private readonly IModel _model;

    public CachedModel(IModel model) {
        this._model = model;
    }
    public DataStruct GetData(int key) {
        return this._cache.Get(GetKey(key)) as DataStruct ?? GetDataFromStore(key);
    }
    private DataStruct GetDataFromStore(int key) {
        DataStruct data = this._model.GetData(key);
        this._cache.Add(GetKey(key), data, null, DateTime.Now.AddSeconds(30),
            TimeSpan.Zero, CacheItemPriority.Default, null);
        return data;
    }
    public void UpdateData(DataStruct data) {
        this._model.UpdateData(data);
        this._cache.Remove(GetKey(data.key));
    }
    public void InsertData(DataStruct data) {
        this._model.InsertData(data);
    }
    private string GetKey(int key) {
        return string.Format("dataKey:{0}", key);
    }
}

public class Model : IModel {
    private readonly ModelEntities _context;

    public Model(ModelEntities context) {
        this._context = context;
    }
    public DataStruct GetData(int key) {
        return this._context.DataStructs.FirstOrDefault(d => d.key == key);
    }
    public void InsertData(DataStruct data) {
        this._context.AddToDataStructs(data);

 

   

We encourage you to share your comments on this post. Comments are moderated and will be reviewed and posted as promptly as possible during regular business hours.

To ensure your comment is published, please follow our community guidelines.

Comments
by Greg Young(anon) on 09-08-2010 12:46 AM

Where is the CQRS in this? It seems to me that you have a read/write model which is explicitly what is not recommended.

by scott.rudy on 09-08-2010 03:40 AM - last edited on 09-08-2010 03:41 AM

Greg, thanks for the response. The implementation I used here was the most simplistic way I could think of showing code. The controller would only use the CachedModel class. The CachedModel class would in turn know how to deal with the Model class and abstract it from use. I could have separated these models into two distinct classes and allowed the controller to use each of them, but that seemed more complicated than it needed to be.

 

Inserts and updates are not written to the in-memory model (or whatever query store I would choose to use), but would be persisted to what ever command model data store was behind the context in this case. Reads would enable the value from the datastore to be synchronized back with the cache. The 30 second expiration window would provide eventual consistency that would not exceed 30 seconds plus the time it takes the transaction to execute.

 

Admittedly, I probably could have left out the remove from cache call from the UpdateData method to be more inline with not updating the readonly store. However, this update is part of the atomic command transaction and it would still allow for a timely response to the user.

by Greg Young(anon) on 09-08-2010 10:48 AM

I guess I am failing to see where the CQRS in this is. This looks more like a model with caching on top of it to me.

 

At the heart of CQRS is the understanding that there are multiple mental models for the application and the read model is always read only for the client. I am not seeing this in the example placed here.

by scott.rudy on 09-08-2010 01:01 PM

Perhaps the disconnect between us then. I see CQRS as being the exact same as caching. You write to the datastore and you read from the cache. There is the segregation of command and query, its that simple. I used the same data shape (mental model) in my example, but there is nothing in the implemention that prevents me from using two different shapes on the data. It would just require a mapping between data types where it makes sense.

 

 

Post a Comment
Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.

Find HP in Social Media

Facebook Twitter YouTube SlideShare Flickr
About the Author
Labels