First, a minor confession, I dislike loathe databases. The only thing I may loathe more are the self-appointed high-priests of databases, DBA's. However, They are a
necessary evil. Invariably, most software solutions need one, so I begrudgingly
accept its role. My major complaint is that the industrial solutions that exist
today are all some form of Relational Database Management System (RDMS).
Working with them always is a hassle. The software I write has nice little
object abstractions for the entities I deal with it – it's great. I don’t worry
much about how they do what they do – they just do it. That is until I must
bridge the great impedance mismatch to get them in and out of the RDMS.
There are many solutions to this problem – most of which I
am continually disappointed with. There are several Object-To-Relational
Mapping (ORM) solutions out there; most of which I despise. Why? Well, they ask
me to know too much about the database (remember I loathe them???). I don’t
want to design stored procedures, I don’t want to design tables, and I don’t
want to map the two. I want to build objects!
Why must I:
- Maintain data layout information in two places?
- Know some less than conformant version of SQL syntax?
- Deal with a mess of SQL scripts as I adjust the definition of objects over time?
In my previous commercial existence, I and some very
talented folks have waded right into this problem. The first time we tackled
the problem, it was to provide an RDMS like view of network data. As things
went forward, we realized we wanted to address this problem in such away as the
backing store did not matter. In other words, the objects we requested
information from could be backed up by a database, network calls, excel
spreadsheets, etc. You get the idea!
Before you all start posting comments, yes I have heard of
ODBC, ADO.NET, WMI, CSLA, and Hibernate. Hold your horses. Each of these
doesn’t really help solve the problem I want solved. I DON”T WANT TO DEAL WITH
THE DATABASE.
Further, when querying I am a huge fan of Query by Example
(QBE). Let me show you an object and you give me back a list of them that look
like it.
Here is how this works (lights, camera, pseudo-code):
Formula exemplar = new Formula();
IStore store =
ServiceFactory.Instance.Retrieve<IStore,Formula>();
exemplar.Email = ‘mwebb@tribalpizza.com’;
Console.WriteLine (“My formulas”);
foreach (Formula f in store.Retrieve (exemplar))
{
Console.WriteLine (f.Display);
}
Yes, this is how our current system works. Short, sweet, and
to the point.
In dealing with this problem, I want to work with three real
abstractions:
- An entity
- An entity’s definition
- A store
An entity is just that -- well let’s say something like a
Pizza Formula. I want to describe its properties, their constraints, relevant
data-types, and be done. We’ll call this description an entity’s definition.
A store is a place to store and retrieve entities from.
Here is how I want life to work:
- I describe an entity declaratively
- A code generator creates me a magic wrapper for ease of dealing with the entity. This is not entirely necessary but damn handy. The system needs to be able to retrieve and operate on entities for which these wrappers don’t exist though!
- A store is selected based on an implementation of the service factory and the type of identity. This gives me great flexibility in changing out backing store as well as potentially selecting the type (and characteristics) of the backing store based on entity.
- A store must be able to read an entities’ definition and create the necessary backing store for the entity. Period! I don’t want to help it. Read the definition and create the store. I’m not creating your SQL scripts, got it.
- A store must be able to read an entity’s definition and realize that a newer description exists. The store must be able to take reasonable actions to upgrade the backing store to realize these changes. When I say reasonable, I say the store should be smart enough to handle the additions of columns. Changing the data-type and/or deleting of columns may be beyond the capability of the store. I generally try to avoid these operations anyways.
- A store must be able to perform relevant read, delete, and update operations.
- I want to be able to access existing data without having the original data definitions. How? Well, easy, the backing store is also responsible for keeping track of the data definitions used to create objects. Wow! This has huge implications. More to come on this in the future.
Now, I know I loose some things:
- I may not be able to eeck out every micro-second of performance that a perfectly tuned SQL script could offer. However, I intend to deal with this problem another way. The selection of which store to use is done by requesting a store from a service factory based on entity. This allows me to *tune* up specialized stores for performance critical entities that have special characteristics. I shouldn’t need to do this much, but, I have a back door if needed.
- I am accepting some amount of lowest common denominator in backing store capabilities. Again, if this is a challenge see my solution to the problem above.
I’ve made some pretty good progress on a solution that works
for us. This is the first part in my series of explaining the design philosophy and its inner workings. Eventually,
we will be sharing an open source solution to this problem.
What are your thoughts on the great impedance mismatch and my approach to the
problem?