Cache Retrieval Pattern

June 13, 2010

Cache Retrieval Pattern


In my previous post I wrote about cache layer and its position
in every application. In this post I’m going to explain what is
the cache retrieval pattern and show an example of how to
implement it.


Cache Retrieval Pattern


When we implement a cache layer we need a strategy in order to
retrieve cached items. The cache retrieval pattern is very
simple and can be imposed into any application very fast.
So how does it work?
The business logic component will use the cache API in order to
check whether some data exists in the cache. If the data doesn’t exists
in the cache, the business logic component responsibility will be to
insert the data into the cache. Since cache is volatile you can’t depend
on it to hold the data you need. This is why you always need to check
whether the data exists in the first place. The business logic component
will use a data access component to retrieve the relevant data from a
data storage. After it has the data in hand it will place the data in
the cache and return it to its consumer. The following diagram
shows how this pattern work:
Cache Retrieval Pattern


Cache Retrieval Pattern Example


After we understood the what lets take a look at the how.
The following BL class implement the data retrieval pattern:



public class BLComponent
{
  #region Properties
 
  private ICacheManager CacheManager { get; private set; }    
 
  #endregion
 
  #region Ctor
 
  public BLComponent(ICacheManager cacheManager)
  {
    CacheManager = cacheManager;
  }
 
  #endregion
 
  #region Methods
 
  public Department GetDepartmentById(int departmentId)
  {
    if (!CacheManager.Contains(departmentId.ToString()))
    {
      DALComponent dataAccessor = new DALComponent();
      Department department = dataAccessor.GetDepartmentById(departmentId);
      CacheManager.Insert(departmentId.ToString(), department);
      return department;
    }
    return CacheManager.Get<Department>(departmentId.ToString());
  }
 
  #endregion
}

As you can see I use the ICacheManager interface in order to
create an abstraction against the cache layer. This will enable
me to create a class which uses the cache API of my chosen
cache implementation (in memory, distributed and etc).
The ICacheManager can look like:



public interface ICacheManager
{
  /// <summary>     
  /// Add a new object into the cache     
  /// </summary>     
  /// <param name=”key”>The key of the object to add</param>     
  /// <param name=”value”>The value of the object to add</param>     
  void Add(string key, object value);
 
  /// <summary>     
  /// Check whether the key is contained by the cache     
  /// </summary>     
  /// <param name=”key”>The key to check</param>     
  /// <returns>Returns true if the key is contained by the cache</returns>     
  bool Contains(string key);
 
  /// <summary>     
  /// Returns the number of items in the cache     
  /// </summary>     
  /// <returns></returns>     
  int Count();
 
  /// <summary>     
  /// Insert a new object into the cache      
  /// </summary>     
  /// <param name=”key”>The key of the object to insert</param>     
  /// <param name=”value”>The value of the object to insert</param>     
  void Insert(string key, object value);
 
  /// <summary>     
  /// Get the object that its key is given    
  /// </summary>     
  /// <typeparam name=”T”>The object</typeparam>     
  /// <param name=”key”>The given key to check</param>     
  /// <returns>returns the object or null if it doesn’t exists</returns>     
  T Get<T>(string key);
 
  /// <summary>     
  /// Removes the object that is referenced by the given key     
  /// </summary>     
  /// <param name=”key”>The given key</param>     
  void Remove(string key);
 
  /// <summary>     
  /// Get/Set the the given key and object     
  /// </summary>     
  /// <param name=”key”>The given key to the indexer</param>     
  /// <returns>Returns the object that the given key reference</returns>     
  object this[string key]
  {
    get;
    set;
  }
}

The data access component isn’t relevant to our discussion because
underneath it can be implemented by IRepository, some service or
whatever method you pick to retrieve your relevant data. Also,
pay attention that this example isn’t thread safe.


Summary


The cache retrieval pattern is simple to understand and implement
in any application. As I wrote in my previous post using caching in
applications is a must and the pattern for retrieving data will help
you to insure you have the relevant data close to your application.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

4 comments

  1. PhilJune 13, 2010 ב 21:00

    Hey Gil

    Thanks for the concise explanation.  I have always abstracted the cache into the DAL because I think it makes the BL logic cleaner.

    //Inject the BLObject with appropriate

    //repositories.

    BLObject(IRepository<T> someRepository)

    ConcreteRepository::IRepository<T>

    {

     ICacheMgr _cacheMgr;

     ConcreteRepository(ICacheMgr cacheMgr)

     { _cacheMgr =cacheMgr;}

     T Get<T>(string id)

     {

       If(_cacheMgr.Contains(id)

    Return _cacheMgr.Get(id)

       Else

      Var theObject =  GetFromDB(id);

    _cacheMgr.Insert(theObject);

    Return  theObject;

     }

    }

    What do you think the benefits are of having the BLObject manage the caching are?

    Thanks

    Reply
  2. Gil FinkJune 14, 2010 ב 8:34

    Hi Phil,

    Both methods are acceptable.
    I agree that abstracting the cache in the DAL makes the BL more cleaner.
    Even so, cache layer’s place is between the BL and the DAL which makes it a seperate layer in the application (its like a layer that you want to use instead of the DAL if you can).
    There are places that you still want to use caching but you consume other data storages like services which you not necessary want to put behind Repositories.

    Gil

    Reply
  3. John TurnerJune 16, 2010 ב 12:12

    I have to agree with Phil in that I prefer the service layer not to interact directly with the cache. I guess this preference is a side effect of using Hibernate over many years as Hibernate delegates to the cache provider. Hibernate also does other things like collection caching and query caching which is best buried in the DAL.

    The method above relies on the service implementor being consistent in how the cache mechanism is applied. For instance, you demonstrated caching a department but what if another service caches department with a different key or not at all?

    Reply
  4. Gil FinkJune 17, 2010 ב 9:12

    Hi John Turner,
    Thanks for your comment.
    As you wrote, some of Hibernate features delegates to the cache provider and therefore you get the caching buried in the DAL. This is not contradicting the answer I wrote to Phil which both methods are an acceptable solution (BL or DAL). In the DAL solution you’ll reley on DAL which is being consistent in how the cache mechanism is applied. In the BL solution you’ll reley on service implementor which is being consistent in how the cache mechanism is applied.
    About the problem you indicated, it depends on how you implement your solution. Since the example I’ve provided is very simple it doesn’t answer the problem you raised.

    Reply