As already mentioned the I use a factory within an application architecture as a means for layer separation. The „Layer“ in “layer separation” refers to the omnipresent 3-tier, 3+-tier, multi-layer, what-ever-you-call-it-layer approach that is used in one or the other incarnation for todays web applications and services. Let’s assume the most basic incarnation for a web application, i.e. 3 classical layers.
I described the understanding of the term “factory” in my last post, now let’s see how to employ it.
The architecture
I‘ll spare you (and me) the boring picture of three blocks sitting on top of each other. Curiously no application is built purely this way, anyway. But let’s go ahead and structure the core layers and add the necessary vertical layers.
Still very simple. Just for the bookkeeping: “Presentation” stands for the Web Application (the bunch of config files, .aspx files, .css files, etc.) and I assumed some kind of MVC (or related) pattern for the UI layer, implemented in the “Controller” block.
“Data Structures” contains the [please fill in your favourite choice of Data Sets, Value Objects, Entity Objects, or in whatever shape your data flows across the data layer, business layer, and presentation layer].
“Utils & Factory” contains orthogonal services, e.g. helper classes, tracing, error handling, and – hey! – our factory would reside here.
The factory
Let’s stress the fact that the business layer and the data access layer have a small interface block. This block is meant to be the only visible part of the respective layer. No bypassing allowed! Thinking of code, this part would be made out of C# interfaces and layer specific exceptions (VB.NET? No, sorry, you can’t do that in VB 😉 ). The implementation block of the layer contains the actual implementation classes and whatever else is needed.
The interface blocks are also the place where the factory enters the stage: The calling layer will never access these implementation classes directly, in fact it should not even know them. Rather it will ask the factory for an object that supports one particular interface. The factory will create the respective object and return it. E.g. the presentation layer would access some kind of customer information like this:
1. The business layer defines a customer service interface and a correponding implementation (within the respective blocks.
2. The business service is registered to be used by the factory with the type name of the interface as key:
<typeNames>
<add
key=“SDX.MyApp.Business.Contract.ICustomerService”
value=“SDX.MyApp.Business.Components.CustomerService, SDX.MyApp.Business.Components” />
…
</typeNames>
3. The presentation code uses the factory to gain access to the business service:
ICustomerService o= Factory<ICustomerService>.Create();
In case you didn’t notice: I just repeated the sample from my last post. It’s that simple — and yet, it still introduces additional complexity and error conditions.
The bennefits
So, what would be gained by this approach (other than complicating object instantiation and satisfying the play instinct of a certain architect 😉 )? Well, there are very different aspects and they vary depending on the project life cycle:
Think of the early project stages…
The “Best Case” starting a development project would probably be to have a business specification with detailed descriptions. Descriptions that you can analyze to determine the business data and the operations upon this data … . In other words, you design the business entities and the (business) interface. In other words — since this also depends on the demands of the presentation layer — you do some kind of negotiation between those two layers — a process called contracting in the SOA world. Yes, we just introduced some SOA concepts into application design. The interfaces are being designed prior to the implementation and in close negotiation between the responsible developers of the respective layers. That should yield better results than ad-hoc implementations (and you may take that as understatement).
Once those interfaces are in place the different layers can be developed independently from each other. The presentation layer team can start to develop the frontend using some simulation classes as long as the business layer team is still working on the implementation. The business layer team can use unit tests as frontend simulation. Thus we also reduced team dependencies.
Think of development …
During development you will encounter additional orthogonal (cross cutting) behaviour, e.g. caching, routing, or security. Take caching as example. If every read or write access of customers is done via (exactly) one business service interface, this interface defines when to cache and when to invalidate cached data. Rather than putting the caching code in the business service (and spoiling the business logic) or in the presentation layer (sprinkling it all over the layer), we can inject it between the two layers, using a pattern called interception.
For example:
<typeNames>
<add
key=“SDX.MyApp.Business.Contract.ICustomerService”
value=“SDX.MyApp.Business.Components.CachedCustomerService, SDX.MyApp.Business.Components” />
<add
key=“SDX.MyApp.Business.Contract.ICustomerService-CACHED”
value=“SDX.MyApp.Business.Components.CustomerService, SDX.MyApp.Business.Components” />
…
</typeNames>
And the CachedCustomerService looks somewhat like this:
public ICustomerService Service
{
get
{
if (_service==null)
{
// create the real service
string key= typeof(ICustomerService).FullName+“-CACHED”;
_service = Factory<ICustomerService>.CreateAlias(key);
}
return _service;
}
}public IList<Customer> GetCustomerList(…)
{
string key = “CustomerList-“ + …;
IList<Customer> ret = HttpContext.Current.Cache[key] as IList<Customer>;
if (ret == null)
{
// call the real service
ret = Service.GetCustomerList(…);
HttpContext.Current.Cache[key] = ret;
}
return ret;
}
This way we have introduced caching without the need to change either the presentation layer or the business logic. In fact, neither layer is even aware that there is a man-in-the-middle.
As a side note: The idea of „interceptors“ is nothing new. MTS/COM+ used call interception to add runtime behaviour (e.g. transactions and security) to existing interfaces. The Policy Injection Application Block does something similar, using remoting proxies.
Other features that could be added this way include tracing, data tracking, performance counting, error handling, call routing and other infrastructure services. Implementing these things as interceptors rather than within the layers provides flexibility and also promotes separation of concerns.
Think of testing …
The two patterns we have used so far — simulators and interceptors — can be used for testing as well. Simulators (or more appropriate mock objects) can easily replace real implementations for component tests. Interceptors can be used to induce simulated error conditions that are hard to create i.r.l.. (You wouldn’t crash you disk drive on purpose, would you?).
The limits
Of course this approach has weak spots and does not fit in any situation. I found it particullarly valuable on the presentation layer/business layer boundary when working with entity objects of some kind. Realizing the same bennefit between business layer and data access layer may be harder, though. An object mapper for example may be reluctant to fit into a clean layer structure, DataSets also carry data access logic enough to question the need for a layer with a service like interface. Real simple applications (i.e. nothing more than a simple database frontent) with little to no business logik will probably contain the same interfaces as business interfaces as well as data access interfaces, and the business layer will simply route any call to the data access layer. Why go through that trouble anyway? (Other than my experience that no applications stays that simple.)
This approach also profits from project teams of a certain size that have their responsibilities clearly separated horizontaly, aligned to layers, and assigned to different people (although I still like the contracting part even in a one-man show). It may not work so well if you have a vertical separation (aligned to business features).
Anyway, I’ve used this approach in small and mid-sized projects and it definitely payed off. Big projects need some additional thoughts but the principle still holds.
The next post will talk about using a factory for framework extensions points.
Leave a Reply