November 28, 2007
One thing I don't like when starting on a new application is having to go through the process of setting up and configuring the database before I can get anything even running at all. So, for a couple of projects I'm working on I thought about reading and writing data to plain and simple XML files and then later replace that section of the code with a proper database access layer.
However the idea of just throwing away a large chunk of the application didn't sound very appealing, so after some thinking, I started playing with the idea of abstracting the actual storage medium from the application logic. Something that would let me change from XML files to database tables without having to change (or throw away) any application code. This, of course would involve some sort Data Access Objects, and also somewhere along the way would incorporate some sort of shape shifting object that would know how to either read/write files or do insert/select/deletes.
Well, after some more playing, thinking and tinkering I came up with a way to address the issue at hand; and in the eventual case that there is someone else out there with a mind as twisted as mine that would actually consider making something like this, I share my solution here.
Before getting into the full discussion, here is a simplified UML diagram for the approach I'm using.
The idea is to have the application talk to a special class of objects for all its data related needs. Let us call this the "DAO" class. Each entity on the application's universe will have its own DAO, for example we would have an AddressDAO, an AccountsDAO, a CountryDAO, etc. And the application would only deal with these objects when it comes to persistent data. So far, this is just standard OO thinking. The interesting bits come after this.
The difference with these DAOs and traditional data access objects, is that the former know nothing about how to actually talk to a specific storage medium. Each DAO maintains a reference to another class of object named the DataProvider. Whenever a DAO needs to actually interact with the data store, it does so through its DataProvider. For example, if we ask the AddressDAO to update a record, the DAO will relay this request to the DataProvider, who will be the one that will talk to the actual data store.
It is the role of the DataProvider to gets its hands dirty into how the data is actually stored; and here is where we get a little help from a concept called polymorphism, which is basically the idea of working with multiple objects through the same interface. The secret sauce relies on having multiple DataProvider objects all of them sharing the same interface but doing things on a different way. Basically this interface tells all of the DataProvider objects that they must implement the same collection of basic methods that will be used by the DAO to interact with the data. These methods are: get(), getAll(), save(), delete(), and search(). Then it is up to each individual dataProvider to implement how these operations are carried on.
This separation means that for example I can have an XMLDataProvider class that only knows how to read and write XML files, an also a DBDataProvider that only knows how to do SQL statements; and even I can have other dataProvider objects that could do more specialized things like doing database failovers or other funky things. All this while my application logic is blissfully ignorant as to whether its data is coming from a file or a database or whatever. From the application point of view, it only knows about DAO objects that receive and return data. Everything depends on which type of dataProvider object the DAO is using. Therefore I can have some factory method somewhere in my application configured to produce DAOs already populated with a dataProvider for whatever backend data storage I want to use.
Now, when dealing with different types of storage mechanisms, a very important issue is the configuration and settings that will be used. Obviously, different data storage mechanisms require different configurations: For example, databases require a datasource, a username and a password; while XML files require at least a path to where to store the files. To deal with this, each dataProvider object takes an instance of a dataProviderConfigBean object as the argument of its constructor. As its name implies the dataProviderConfigBean is just a simple bean to hold properties. The only difference is that each dataProvider type should have a corresponding bean type. Thus, if I have an XMLDataProvider class, then I would also have an XMLDataProviderConfigBean class with the appropriate setter and getters for the specific requirements for using XML files as the backend data storage of my application.
Having everything structured on this way, makes it very trivial to switch the storage mechanism of an application. I could move back and forth between different storage mediums just by initializing my DAOs with different dataProvider objects. Let's say I can work with an XML data storage on the beginning stages of development and later on switch to a full blown database storage. I can even have a mix of storage mediums and from the point of view of the application they are all just DAOs.
Now, I know the actual DAO terminology implies certain definitions that can be argued over an over, but for the purposes of this discussion I only use it to refer to an object that encapsulates calls to a data storage medium; and in fact, this approach is completely oblivious as to whether the application is built around beans, queries or other kind of DAO objects. This only deals with the specific point in which the application code touches the data store.
I am enclosing the implementation I made using this idea. The zip contains a very simple proof-of-concept app that uses an XML file as its storage medium, and runs through a couple of use cases. The DAO code also contains a basic configurable factory that can be used to make the production of the DAO objects even easier.
Please leave a comment if you try it out and let me know how it goes.