3

I'm slowly trying to migrate our web application to use CQRS principals. In particular, I have separated "write" and "read" interfaces into commands and queries.

I have implemented a "reader" class that can read some data from the DB. I have also implemented a decorator for this reader that caches the data to avoid any unnecessary roundtrips to the DB. At the moment this decorator is caching per-request. I'd like to cache this data for a longer duration - eg for a sliding 5 minute window.

However, the problem is that a command that updates the data should invalidate the cache once the write occurs, and I'd like to capture that notion explicitly in our domain model.

Specifically, here is the "read" interface (c#):

public interface IRoleLister
{
    IList<DomainRole> Fetch(OtuIdentity otuIdentity);
    IList<DomainRole> FetchForAuthenticatedId(int userId);
}

And here is the write interface:

public interface ICanChangeDomainRoles
{
    void AddRoleToUser(int userId, DomainRole domainRole);
    void RemoveRoleFromUser(int userId, DomainRole domainRole);
}

I realize that some variant of the observer pattern would probably be applicable, but I'm not sure how to capture that in the domain model.

I also have identified another problem - namely cache key naming. In the IRoleLister I want to cache IList<DomainRole> by user (eg typeof(IList<DomainRole>) + ":userid" as the key) but in ICanChangeDomainRoles I don't know the key name. My thoughts are that the Observer should in someway be responsible for this. (Or perhaps some other interface? How do I capture that concept in my domain).

Finally, I realize that some form of distributed cache would help, but that seems like an implementation detail - I want to capture the invalidation/notification/observer concept in my domain model.

Any pointers would be appreciated

6
  • Do you need the data to be consistent?
    – Miau
    Mar 14, 2012 at 16:12
  • I understand that CQRS does not promise instant consistency (delayed writes). But I want the data to be consisted AFTER the write has occurred. That's why I want to invalidate the cache when writes have completed. Mar 14, 2012 at 16:53
  • well instead of using a cache, why not create views of the data that change after the data has changed?
    – Miau
    Mar 14, 2012 at 17:03
  • @Miau: Not sure I understand. I think you are referring to a concept around event-sourcing, which I did not mention. In my app, the cache is the view, so my question becomes how can I update (invalidate) the view (cache) when a write occurs? Mar 15, 2012 at 9:26
  • It's not really event sourcing (in short that means that you store data as events and its eventually consistent) I m offering you an alternative (that is normally used with ES) this is why I didnt propose it as a solution
    – Miau
    Mar 15, 2012 at 11:55

1 Answer 1

1

General case, I would expect caching to be a policy applied to the data and not an attribute of the data itself.

I this is a perfect candidate for aspect-oriented-programming (eg PostSharp) and/or inversion of control frameworks (eg Spring.Net).

5
  • Well, the caching is handled by a read-decorator, so in that sence it is an aspect. I think I need an observer that is notified when a write occurs, and that object is responsible for invalidating cache items. I would just like to figure out how to make that a first class concept of the ICanChangeDomainRoles interface... Mar 15, 2012 at 9:30
  • To be more clear - I would not want caching to be a first class concept of your functional interfaces. Don't think of caching as a part of your ICanChangeDomainRoles interface, think of caching as a part of a specific configuration, deployment, etc. That said, you could add a method to the interface to invalidate the cache. Maybe have your ICanChangeDomainRoles interface implement another interface ICanBeCacheInvalidated and that interface exposes a method to invalidate the cache. But, I think this is the wrong answer to your problem from a forward-looking perspective.
    – jasonk
    Mar 15, 2012 at 11:49
  • 1
    To map that to your language of patterns - the cache should be a decorator(s) across both your command and query channels. Neither "core" implementation should even need to know there is a cache. It's a decorator, it should be transparent to the decorated object and anything dependent on the decorated object. Decorator of your query provides a cache capability transparently, decorator of your command evict/flushes the cache. Have your decorator(s) solve the problem - but don't change your core interfaces to provide the capability. AOP is not required but makes all this natural and easy.
    – jasonk
    Mar 15, 2012 at 11:58
  • So I do think that I am in agreement with you: Caching is a decorator on a concrete core impl that reads the data. and I do agree that concept should not pollute the interface. However, I think I need to have a "data changed" event/notification concept modelled in the ICanChange interface. Perhaps it;s just as simple as ICanChange must "inherit" from IObservable<T> (where T is some kind of key class that can identify user/role pairs) Mar 15, 2012 at 14:42
  • Now I just have to figure out how that maps to dependency injection! (Namely: ICanChange implmentations are per request, but the IObserve<T> impl (the cache invalidator) is long lived) Mar 15, 2012 at 14:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.