Thursday, June 4, 2009

Service granularity and re-use

Architects in the IT organisation where I work display an interesting tendency to equate re-usabilty with granularity.  The received wisdom seems to be that the more granular a service is, the more re-useable it is.  To a certain extent it is useful to have the ability to mix and match just the bits of functionality you require.  This becomes detrimental at the point where it starts to push behaviour to toward the consuming systems, of which there are usually more than one.

As a case in point, a new system is being written, which (among other things) exposes the ability to lookup items in a cache.  It employs a side-caching strategy to do this.  It's API looks something like this:
MyItemCache
    findItem(itemId : String) : Item
    createItem(item : Item)
Consumers of this service will call findItem() to check for the item in the cache, using the Item if it is there.  If the Item is not there, they are expected to fetch the Item from the source system - which is a different system entirely - and then add it to the cache, using createItem().

There are a number of issues with this approach.
* The fact that the cache is a side cache (and therefore does not front the source system) means that consumers of this cache must also have knowledge of the source system - making the overall architecture more complicated and brittle.
* Each consuming system is expected to implement over again the logic required to check in the cache, then fetch and add the Item if it is not already cached.

While this last point may seem like a small amount of code to write, it should be remembered that forcing each client system to re-implement this logic means that the difficultly of changing this logic is multiplied by the number of client systems, with all the associated co-ordination of teams that this involves.  Add to that the impact that differing or buggy implementations might add to the mix and you have a problem far more damaging that the cost of a few lines of code.

An alternative approach, which would eliminate both of these problems, would be to make the cache a through-cache, with the cache itself handling the "if not cached: fetch from source" behaviour. Having eliminated the need to manually add things to the cache, the service interface is simplified, as follows:
MyService
    findItem(itemId : String) : Item
Client systems are then relieved of responsibility for implementing this logic over and over, and need not have knowledge of yet another system.  Re-use is enhanced, while complexity is reduced.  Everyone is happy!  :-)