Monday, January 25, 2010

Let's get rid of middle management

Let's get rid of middle management.

No, not you, boss.

I'm talking about middle management in Java code. At one presentation in the New England Software Symposium in Boston last year, some Java-bashing guy (Stuart Halloway) described Java as the language that invented middle management. You have probably seen them: these classes often end with "Manager.java". I'm wondering whether we really need them.



In the movie Office Space, there is a character named Tom Smykowski whose job was to get paperwork moved between people. In a funny interview, he demonstrated that he did not do anything useful nor produce anything of value. Smykowski's position was merely a layer through which real work had to pass. The Java code I have in mind are our Smykowskis: they don't actually do anything except add clutter. They are not an intrinsic part of the Java, but a result of best practices we've adopted in enterprise Java development. In other words, this code is our choice. Office Space was a comedy, but if we inflict "Smykowski code" on ourselves perhaps we are the ones to be laughed at. (On the other hand, if you don't have to deal with such code feel free to have a chuckle over the rest of this article.)

Here's an example. We are writing an application on top of a typical "lightweight" enterprise Java stack: Spring, Hibernate, Tomcat. In a Spring MVC controller class, we want to retrieve an instance of Thing from the database. Following the common patterns and best practices, the code might look like:


// controller
Thing thing = thingManager.findThingById(id); 

interface ThingManager { // service layer
    Thing getThingById(long id);
}

class ThingManagerImpl implements ThingManager {
    public Thing getThingById(long id) {
        return thingDao.findThingById(id);
    }

    private ThingDao thingDao;
}

interface ThingDao { // persistence layer
    Thing findThingById(long id);
}

class ThingDaoImpl implements ThingDao {
    public Thing findThingById(long id) {
        return sessionFactory.getCurrentSession().get(Thing.class, id);
    }

    private SessionFactory sessionFactory;
}

This code follows a layered architecture with service and persistence layers. Following Spring best practices, each POJO bean implementation has an interface. Each persistent class has a DAO to isolate DB operations. That all sounds reasonable, but here it results in 4 layers of almost entirely useless code, and the above doesn't even include dependency injection code (XML or annotations) nor boilerplate getters/setters. With Groovy on Grails, I would merely write:


def thing = Thing.get(id)

But in this case the excess code has nothing to do with the Java language nor the framework. It's all self-inflicted. Grails, after all, is also built on Spring and Hibernate. The Java equivalent could also be a one-liner:


Thing thing = sessionFactory.getCurrentSession().get(Thing.class, id);

That would be the straightforward way. The enterprise Java style on the other hand required that I fight my way through 4 layers of classes and interfaces to add a trivial operation. In the movie, Tom Smykowski was laid off once his uselessness was established. Can we dump our own Smykowskis? Here are some thoughts.

Do we need layers?

In theory, I think architectural layers or modules is a good thing. As I said when I reviewed XDepend, code complexity is a dragon that needs to be tamed. Layers help control complexity by limiting dependencies, so I'm not prepared to jettison the idea. On the other hand, we might take defensive layering to an extreme. Do we need so many layers?

Do we need interfaces?

Interfaces are the ultimate caricature of middle management. They do absolutely nothing, but they take all the credit. Their underlings -- the real implementations -- do all the work. What are they for? I can think of two main justifications for slapping interfaces on everything that moves:

  • It's easy to substitute different implementations.
  • It's easy to stub out collaborators for unit testing.

I don't think these are strong reasons. Most beans I work with do not have multiple implementations: why add all this cruft to handle the rare case? And for unit tests, it's easy to stub methods of collaborators by either subclassing the original classes or generating proxies (creating such a proxy in Groovy is trivial: just coerce a map of closures to that class).

I think the real culprit is excessive defensiveness. To guard against all hypothetical future changes, we build in these annoying abstractions and firewalls that we think we might need. We can be more bold. After all, the Java platform has some of the finest refactoring tools on the planet. If we discover we need an interface there one day, we can stick one there. But only when we need it. Eliminate the interfaces and those 4 layers of middle management go down to 2.

Do we need DAOs?

I think the DAO pattern is a poor fit for POJO-based persistence frameworks like Hibernate. Why create a separate DAO class for each persistent object when Hibernate's Session object is basically your universal DAO? Moreover, manipulating Hibernate-managed objects with DAOs is basically a lie: you can effect DB operations even outside of a DAO operation. If you modify a live object, it will typically be updated to the database even if you don't call the DAO's update method on it. Depending on how you mapped them, objects can also be added or deleted from the database without any explicit DAO call: you just manipulate the POJO object graph.

Not that a persistence layer is a bad thing. There's a place for the code that makes the more hairy Hibernate queries. You can still call them repository or DAO classes, but with the understanding that they are not the traditional DAOs through which all DB changes must be funnelled. In fact, if we more frequently persisted changes by manipulating the objects directly, then we need not push such logic into the persistence layer. This would reduce the amount of middle management code that we need to write.

Your turn

So you managed to read this far. Congratulations. Personally, I am not completely settled in how to balance the ideals of doing it "right" by enterprise Java standards and getting things done in a relatively straightforward way. I would like to hear how you, my patient reader, do this balancing act in light of what you have read.

8 comments:

  1. I think the observation of the middle management design pattern in Java server side follows directly from flaws in the language itself. Java makes it very difficult for one class to be used differently in different contexts (model classes in form vs. persisted objects, api classes in unit tests vs. integration). Dynamic, object oriented languages such as Ruby and Python have taken big strides towards addressing these shortcomings.

    The controller-service-persistence model does have its advantages, though. Controllers pop up in all sorts of circumstances, and in many ways are as volatile as page layouts, as new data requirements are incorporated. A stable API tier helps to mitigate that complexity.

    ReplyDelete
  2. I too have seen this emerge many times in my career. While Spring is great, the mindset that they push has only made things complicate unnecessarily. I remember IBM pushed a framewok called pandoora on the USDA. What a disaster. Something like 7 or 8 layers. I'm glad I personally got over the notion that things had to be complicated and lots of indirection just to do something simple.

    I recently worked for a large online retailer. We had a simple codebase that was fast and easy to follow. It got switched for this multilayer monstrosity that caused no end of trouble for them. performance went through the floor, object counts were so high the garbage collector was always running and trying to follow the code was an excerise in frustration.

    Einstein was right, make it as simple as possible but no simpler.

    ReplyDelete
  3. I think those middle management give you some flexibility to build automated test cases. Without them, you probably have to setup a fake datasource

    or, I am wrong?

    ReplyDelete
  4. Excellent post!!!
    As for this statement:
    "Personally, I am not completely settled in how to balance the ideals of doing it "right" by enterprise Java standards and getting things done in a relatively straightforward way."

    It's VERY IMPORTANT that you don't even think too much about the "Java" way when it comes to getting things done as a professional. It's really all about what works best for you *not* the Java community. Don't fall for the group think it can only lead to mediocrity. Whatever code that you complete and sign off on is a direct reflection of your own competence and credibility as a developer not the Java community.

    ReplyDelete
  5. Excellent post, and I agree with most everything said.

    When I'm working on a personal project, I've completely stopped using interfaces for a service/persistence layer.

    I'm not here to bash ORM, but I'm not really a fan of Hibernate/JDO/JPA, etc. So I personally wouldn't do away with a DAO layer. What if you needed multiple DB operations within a single service call, with some being transactional?

    I have actually had a case, in my professional career, where we decided to change an implementation of our persistence layer from a Hibernate/JPA based one to a JDBC one, using Spring's JdbcTemplate.

    ReplyDelete
  6. My experience is that in non-trivial cases it's better to have levels: UI class -> Service -> DAO.

    Alas, I need to have also Service and DAO interfaces if I want Spring proxies without cglib.
    And usually I really need those proxies because I use Spring aspects for logging bean calls performance.

    But in some 50% cases the code in layers is a trivial forward downstairs and this really bothers me.

    The compromise solution so far is: on early stages of development I keep all the code in the UI class (something like the one-liner example in this blog).

    This gives me additional advantage when I use Tapestry: changes in the UI class are reloaded on-the-fly without restart.

    ReplyDelete
  7. I was reviewing this blog and wondered if you can elaborate on your elimination of interfaces, layers and adapters as it relates to the maintainability of an application as the following occurs: 1) New people join the team and need to learn the structure without having to know all the different components and what each does? 2) New front end consumers of a service are developed, such as mobility devices, sensors and actuators etc? 3) Different backend dsta sources are introduced with the need to have transactional control across many different parts of the business model?

    Also, help me understand how performance changes when I implement an interface...interfaces require no additional consumption of run time processes or memory, or do I have this wrong? I also assume you don't have any issues with enforcing MVC to allow for a less coupled solution?

    Thanks for your insight, I like the thread.

    ReplyDelete