Last week I kicked off a contest challenging entrants to integrate the Castle Project Automatic Transaction Management facility into my NHibernate Best Practices sample code. I felt that integrating this framework into the sample code would make the code more flexible, simpler to use and more explicit with respect to transaction management. That sounds like an improvement, right? But is adding this higher level of abstraction actually a detriment to our goals of software development? A concerned, anonymous reader, going by the name "My Word," left the following comment (you'll have to ignore the syntax in favor of the semantics of the comments):
"Has anyone one stopped to think of all types_of *overhead* with this approach?
I would like to present a contest write a Script (choose VB or Java ) app (on any end, including components from scripts) that will perform worse, slower or with more management (and/or 'wu ha star trek practices') involved.
You can not, show me, you can not, not that easy :-) Test it, and see it for yourself, how it scale especially.
Porting all that bloat from Java, including log4j (my word), hibernate (gulp!), or spring is, always was and will be nonsense.
These things leak like hell, bringsmore overhead on what should be short, fast and snappy and most of all:
All techniques and Java-trash was made obselete with Windows OS integrated features, with WWF, with Linq, with SQL server and much more.
I swear they shouldn't have left Fowler do anything with his conceptual theories too.
But I guess that is what all the followers deserve when fashion of 'enterprise theorists' gets working, and produces bloat on top of bloat squared and plus ducks on top of misunderstanding that data is not objects (not since 1998 DOH!)
Java mistakes repeated all the time."
I'm not posting this to start a flame war against this reader. On the contrary, I believe some interesting points were brought up; albeit, somewhat unintelligibly. Consider what would happen if the Castle Project Automatic Transaction Management facility was integrated with the sample code. The following constitutes the basic flow for how it would be employed during an HTTP request:
- A user makes an HTTP request after filling out an update form.
- The server, after receiving the request, registers the page controller classes with the Castle Windsor container.
- The container watches for any [Transaction] attributes to be encountered while the code is processed.
- When encountered, another class handles ensuring that a transaction exists.
- The code interacts with the NHibernate layer, via XML files, to download data which is then converted into domain objects.
- Updates are made to the domain objects.
- The controller/presentation layer converts any necessary domain object properties into a readable form for the users.
- An HTTP module, or other mechanism, watches for open transactions at the end of the HTTP request and commit them, accordingly.
- When the transaction is committed, NHibernate checks to see if any domain objects are dirty and commits them to the database.
- And throughout it all, a logger hooked up via Aspect Oriented Programming may be ready and waiting to spit out a bunch of info for the benefit of the developer.
And this is over simplifying the process! Furthermore, a couple of the steps are a little out of order and are slightly misconstruing the responsibilities. The point is, there is A LOT going on between steps 1 and 10 to process an update request. Wouldn't it be much more efficient to write some ADO code directly into the code behind page to start a transaction, send the raw form data to a stored proc, and commit the transaction? Don't all these layers just add a lot of unnecessary overhead? Perhaps.
Let's look at the flipside...go ahead and abandon the domain layer, the stand-alone data-access layer, the dependency injection, the logging, the attribute-driven transaction management, and the AOP logging. What do you have when you throw all this away and focus on minimizing the abstractions between the user and the data storage. You have a mess formally known, in the Microsoft realm, as Active Server Pages. You have duplicated, verbose and unmaintainable code which degrades further into incomprehensible dribble with each additional line added during maintenance. I'm quite sure that all the overhead associated with the additional layers adds an appreciable amount of lag to each web request made within my applications. Is it worth it? Perhaps.
I'm sure it would measurably increase the performance of my applications if I abandoned all of these layers of abstraction in favor of an ultra-thin layer between the presentation and database. Would that be worth it? Definitely not. It would lead to fragile code, ticked off developers, unhappy clients (due to being terribly behind schedule and overbudget) and an inability to quickly adapt to change. My primary goal is not to make the fastest application I can conceive, it's to keep the client happy. And I can only do this if I focus on a clean domain layer, testable controllers, and dispense with time consuming plumbing issues such as ADO.NET. And when I find performance bottlenecks, I have the time to address them properly.
Coding for maintainability and testability is far more important than making your code as efficient as possible. Performance tuning should be left to code profilers which will let you know exactly where you need to spend more time in speeding up your application. So does all this mean that we have to use all these intermediary layers. Perhaps not.
I've been a Microsoft web developer for 10 years. During this time, I've never been completely satisfied with the options I have had for writing software. To write maintainable, domain-driven software, I feel I have little choice but to use a number of layers which cause performance overhead and added indirection. There's a learning curve to be had if you're a developer on my team, but once you learn how it all works, it's very easy to extend. Down the road, in 20 years or so, I believe that people will look back upon our toolset just as we do upon the days of punch cards; software engineering still has a very long way to go. In the meantime, we do what we need to do to write clean, maintainable software. But exciting alternatives are arising...look at Ruby-on-Rails. It's clean, simple to develop with, very maintainable, and does not have a terrible amount of technical overhead running behind the scenes. But the layers of abstraction are still there and still hide a lot of magic that's happening behind the code you write. The primary difference between Ruby-on-Rails and ASP.NET, in my opinion, is that the layers of abstraction are all but invisible in RoR. ASP.NET, with its code-behind pages and page life-cycle still has a ways to go to match the simplicity of RoR...but again, if you write ASP.NET applications, you have to work with what's available.
Higher layers of abstraction have always been, and always will be, a driving force in emerging technologies. The primary benefit is that more and more details do not have to managed explicitly. The cost of this is added performance overhead and giving up some low-level control. (I recall talking to an ex-Assembly programmer who dreaded giving up control when he had to move to C++.) As software becomes more sophisticated, further abstractions will be needed to manage its development. Software factories and the intention behind Microsoft Robotics Studio are two such examples of this. (On a related note, Gödel, Escher, Bach by Douglas Hofstadter does a brilliant job of discussing abstractions and should be a required read for all software developers.)
When used appropriately, layers of abstraction add immense value to software development with the obligatory cost of performance overhead and added indirection. Fortunately, a well designed, open-sourced abstraction always leaves room for performance driven circumvention and a means to see exactly what's going on. But if you don't think it's worth it, you can always program in binary.
05-10-2007 2:33 PM