Value of architecture

If I have to tell what is the main property of a good architecture, I surely will answer that a good architecture centralize common operations and make simple to extend the application.

Here is a typical example. I have a WCF services called from external clients, but I use the same service inside my organization. I created a little WPF project that needs to use services functions, but since this program is run internally, I do not want it to pass in the WCF stack, but I simply want to use concrete services classes that access the database.

The solution is really simple, here it is:

1
2
[AutoscanComponent(LifestyleType.Transient, IsDefault = true, Id = "KeywordService", ServiceType = typeof(IKeywordService))]
public class KeywordService : IKeywordService

AutoscanComponent is used by a custom Castle facility, and is used to automatically register a component. For services I’m sure that there is always only an instance registered for each interface, so I can simply configure castle to use the autoscan facility, and when the code declare a dependency to IKeywordService, Castle resolve it with the concrete class instead of the WCF Proxy.

But this is not enough, I begin to notice that the program is slow, and after a simple inspection I see that there are too many calls to the database. The problem is that the user interact with a WPF UI and there are too many call to the database because the service interfaces are thought for a different usage pattern. This problem could be easily resolved with caching, so I create a simple interceptor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class CacheInterceptor : global::Castle.Core.Interceptor.IInterceptor
{
    private static String BuildCacheToken(IInvocation invocation)
    {
        StringBuilder sb = new StringBuilder(100);
        sb.Append(invocation.TargetType.Name)
           .Append("|")
           .Append(invocation.Method.Name)
           .Append("|");
        foreach (object argument in invocation.Arguments)
        {
            sb.Append(argument.ToString())
               .Append("|");
        }
        return sb.ToString();
    }

    public Int32 CacheDurationInMinutes { get; set; }

    #region IInterceptor Members
    ICacheManager invocationCache = CacheFactory.GetCacheManager();

    public void Intercept(IInvocation invocation)
    {
        String cacheToken = BuildCacheToken(invocation);
        Object cacheObject;
        if ((cacheObject = invocationCache.GetData(cacheToken)) != null)
        {
            invocation.ReturnValue = cacheObject;
        }
        else
        {
            invocation.Proceed();
            invocationCache.Add(cacheToken, invocation.ReturnValue, CacheItemPriority.Normal, 
                null, 
                new AbsoluteTime(DateTimeService.Now.AddMinutes(CacheDurationInMinutes)));
        }
    }

    #endregion
}

This component is really simple, It builds a string key that represent the cache composing service name + method name + parameterlist, so I’m sure that I cache only calls with the same parameters call. Then I simply use entlib caching application block to store data in cache. Now I simply need to configure this interceptor in config file.

1
2
3
4
5
6
7
<component id="CacheInterceptor"
     service="Castle.Core.Interceptor.IInterceptor, Castle.Core"
     type="BaseServices.Castle.CacheInterceptor, BaseServices"
     lifestyle="singleton">
  <parameters>
      <CacheDurationInMinutes>5</CacheDurationInMinutes>
  </parameters>

Next I need to instruct my facility to add this interceptor to all autoregistered components

1
2
<facility id="AutoScan" type="BaseServices.Castle.AutoscanFacility, BaseServices" ><assembly name="DataService" /><assembly name="Analyzer.Service" /><interceptor name="CacheInterceptor" /><interceptor name="SessionPerCallInterceptor" />
</facility>

With this configuration I’m sure that each service is intercepted by the cache component, cache duration is 5 minutes, and the application gains a tremendous speed increase.

This is possible because the architecture extensively use IoC to resolve services and components, and is simple to add features with AOP.

alk.