Don’t castrate your architecture
Note: I thought about trying to find a more PC title for this post, but I don’t think that I can really find a good one that express the same emotional content and the punch that this title have.
A few weeks ago I got my first interesting cold call (someone calling me from the number in the blog without some previous acquaintance), cold calling doesn’t happen very often (maybe 5 – 7 times in the 5 years I had the number there), but that was the first time that I actually got to talk to someone about an interesting and relevant problem.
Anyway, they were building a multi tier system using NHibernate, and they were running into problems with lazy loading over WCF. I quickly pointed out that I really don’t like this approach, it has more than technological faults, it has serous architectural issues.
But let us try to get some idea about how they structured they application. It looked something like this:
I think that you can figure out how a normal request would work, but let me spell it out for you.
Please note that this diagram shows the communication between tiers, that is, each of those is a separate machine.
The idea, as far as I could understand, was to use NHibernate in the Data tier, to get entities from the database and then send them to the business tier where it would do business logic processing, after which it may return it back to the Data tier, which would write them to the database.
I was… at a loss for some time, trying to find a way to explain how screwy this architecture was. It quickly became evident that while the guy on the other side of the line wasn’t aware of my reservations, he certainly felt the pain of this type of architecture:
- Slow response times
- Lazy loading over WCF
- Need to handle change tracking on the Business tier
Those are the problem that they already experienced directly. I can add a few more:
- Anemic domain model (by design!)
- Required manual caching
- Required distributed transactions
- Cascading failure scenarios
I think that I’ll stop here, but I am pretty sure that I can come up with a bigger list if I put my mind to it.
My questions about how did they come up with this architecture were mostly answered with: “that is how we do things” and “security”.
To add insult to injury, naturally the developers are running it all on a single machine, so they aren’t actually seeing what it going on there.
Here is how I would build such a system:
Note that I don’t have a Business tier or a Data tier, I have an Application tier. From my experience, even under fairly strict regulatory compliance rules, application servers can call the database, so the security aspect is covered. What we are actually doing, however, is a far more significant change.
We don’t need to worry about:
- lazy loading (NHibernate does it for us)
- change tracking (NHibernate does it for us)
- caching (NHibernate does it for us, mostly)
- reduced number of hops
- no need for distributed transactions
- reduced number of failure points
- have a chance to build a true domain model
As I mentioned before, I don’t even like the distinction between BAL and DAL, even when they are layers, instead of tiers. Trying to make them into tiers is going to cause quite a lot of pain. In essence, and the main thing that is being missed here, is that you are going to have to build some infrastructure to deal with the data at the Business tier. That may be just simple change tracking and DTC support, but it is likely that you’ll need more than that for real world applications. Caching and lazy loading are both topics that you’ll need to deal with, and neither is going to be an easy task.
This is what NHibernate is meant to do. People keep looking at NHibernate and seeing the Row <- -> Entity conversion, but that is just the very tip of a very big iceberg.
Most of the complexity within NHibernate is with things like caches, lazy loading and change tracking. That is where you are going to see the really significant time and complexity saving.
When you are forcing an architecture into that mode, you are basically removing a lot of the functionality already in the box, and forcing yourself to create it from scratch.
Instead of castrating your abilities, make sure that your architecture matches them, don’t play to your weaknesses, play to your strengths.