In a previous post I showed the general architecture of Bunian. I’d like, in this post, to touch on the Data Access part and how it interacts with the Business Objects.
In traditional architectures there are 3 known layers: Data Access, Business, and Presentation. DTO’s (Data Transfer Objects) are used to carry the data back and forth between the layers. Look to the following diagram:
Bunian contributors seemed to have the same experience developing against such architecture, so we have decided to go with something different; smart Business Objects with more toward OOP.
The way I’d present the current architecture is like the following:
As you can see from the diagram, the architecture revolves around one core unit, the Business Objects; because it’s the essence of the application, it’s where all the real things happen. Being the core, the interaction will happen in different ways depending on the nature of the service the component provides (or requires). Those can be divided into three categories:
- Interacting with components that provide services consumable by many other applications; like logging for example. Here our core can reference the component directly without any worries because the component is absolutely independent of the core (represented in the previous graph as solid arrow). Of course it would be better if there is simple abstraction layer so we can manage any change of components.
- Interacting with components that consume services from our core directly; like the User Interface. The interface will merely represent the business behavior in a way the end user will understand, no dependency what so ever from the core on the UI, it should be even representable through console application if needed (also represented in a solid arrow)
- Interacting with components that are providing a special need to the business, like the Data Access. This should be done via interfaces and Dependency Injection; that is because it is the business who determines the needs to be fulfilled, it is the one who states it needs to be able to Save, Delete, Update..etc.., and the component willing to fulfill this need should adhere to the contract the core dictates. (represented by the dotted line meaning indirect dependency)
We are concentrating on the 3rd interaction, specifically the data access component.
The main thing to note here is that by using Interfaces and Dependency Injection, we will be eliminating any circular dependencies; even if the candidate component will require parts of the core (like our example: the data access will want to return strong types that reside in the business objects, or take types as parameters), even if that so, we will make sure that the core is independent of the component (references wise).
The best way to understand is by an example, I will try to make it as simple as possible first, and in future post I might include more real scenarios (like the one we are using in Bunian) so lets get our sleeves folded:
we have a simple class MyBusinessObject with a simple property DisplayName:
public class MyBusinessObject
{
private string _displayName;
public string DisplayName
{
get
{ return _displayName; }
set
{ _displayName = value; }
}
}
This class needs to have a way to access the database and bring an instance, but on the other hand, it doesn’t want to do anything with the implementation. So it declares that it will need a component that should adhere to the IRepository interface which will have the Get method. It will have this component as a static member:
public static IRepository _repository;
IRepository definition is:
public interface IRepository
{
MyBusinessObject Get();
}
So a component volunteers, MyConcreteRepository:
class MyConcreteRepository : IRepository
{
public MyBusinessObject Get()
{
MyBusinessObject myObject = new MyBusinessObject();
myObject.DisplayName = “ConcreteRepository”;
return myObject;
}
}
Ok great, now all what we need is to assign an instance of this concrete class to the _repository static member in MyBusinessObject static constructor. but if we do so the following it will be wrong:
static MyBusinessObject()
{
_repository = new MyConcreteRepository(); //wrong!
}
simply because if MyConcreteRepository resides in different project/dll (and mostly it will), then you will have circular dependency between the Business Objects and the Data Access. So the answer is to use reflection and get an instance of class without referring to dll. Using Windsor we will do the following:
static MyBusinessObject()
{
IWindsorContainer container = new WindsorContainer(new XmlInterpreter(new ConfigResource(“castle”)));
_repository = container.Resolve<IRepository>(“anotherConcrete.repository”);
}
The lines above, in the simplest explanation, will check in a configuration file, and see which class we will use to create instance of to assign a property of type IRepository. supplying the key “anotherConcrete.repository” tells Windsor which class to use. The config file is like the following:
<castle>
<components>
<component
id=“concrete.repository“
service=“BusinessObjects.IRepository, BusinessObjects“
type=“ConcreteRepository.MyConcreteRepository, ConcreteRepository“ />
<component
id=“anotherConcrete.repository“
service=“BusinessObjects.IRepository, BusinessObjects“
type=“AnotherConcreteRepository.MyOtherConcreteRepository, AnotherConcreteRepository“ />
</components>
</castle>
By that we will have achieved our data access within our business objects without circular dependencies and in a way that will make it easy to change data access component with another in the future.
Of course in real life you would use inheritance for example to manage similar code, this will be postponed in another post by god willing, hopefully soon.
Note that we didn’t use essence of “dependency injection”, since it means more than the Resolve<T>(key) part.
For more information about dependency injection read this excellent series of articles here.
Source code of the example above is available here.
[digg=http://digg.com/programming/Data_Acces_within_Business_Objects]