Spring, objects, and scalability

Keith Donald has picked up on the Floyd-and-Ted discussion, and weighs in on a few of the points that I raised. In particular, he goes into some of my assertions regarding Spring:

    I believe it is accurate to say that Spring leads developers back to an internal, in-process "objects-first" approach. I think it is wrong to say Spring encourages a "distributed-objects-first" approach, or even the use of distribution in the first place. I imagine anyone working with Spring for any length of time will back me up on this. Surely anyone who has read J2EE Design and Development by Rod Johnson…

I wish that were the case, Keith, but too often I run into developers who are of the "objects uber alles" mindset, and any technology that allows them to go back to their basic O-O principles is obviously a Good Thing. Will Spring developers suffer from this same mindset? Possibly not, but I’m not worried about the guys who learned Spring from Rod and/or his immediate camp directly. I’m more worried about the folks who pick up Spring second-hand and use it after reading a few tidbits about POJOs and such. THEY’re the ones who’re going to give Spring the bad name… because in many respects they’re the same ones who did it to EJB.

    First of all, the Spring container itself does not address the problem of distributing objects at all, period. The container has always been about providing a solution for the management of the internal structure of a software system. It is a sophisticated factory for local, in-process application objects (services) based on POJOs. This factory lets you develop the internal components of your app in a object-oriented, test-driven fashion, yes. You can easily have finer-grained strategies that execute polymorphically, you can develop in parallel and you can test in isolation. You use Spring to manage the assembly of these local components into a working system by providing it instructions about what implementations it should instantiate and how those implementations should be configured and wired. You do all of that before you even start thinking about which services you’ll export over remote transport and how you’ll export them…

ALL containers have been about providing that same solution. They manage a variety of resources on your behalf, including (but not limited to) threads, network resources, database resources, messaging resources, object lifecycle, and so on. The fact that Spring chooses to express its application code via POJOs is dangerous, moreover, because it leads people down the slippery slope that distributed object models can become. (Don’t believe me? Go re-read the Distribution chapter of Fowler’s PEAA book–it’s not just me saying this.) Fact is, Keith, YOU may think of Spring in that fashion, but that’s actually a fairly distribution-oriented way of approaching the problem, and wasn’t the point of Spring to get developers back to thinking in objects, because EJB clearly wasn’t an object-oriented system?

    Most good software developers I know care most about building business components that contribute to solving a domain-specific problem: that actually carry out business-differentiating work. With a POJO-based approach, you can focus on building and testing that core business logic first. You don’t need any container to do that–Spring or otherwise. Just give us JUnit and the new operator. We like that.

Agreed. So, then, what I hear you saying is that Spring doesn’t directly enable easier building and testing, because the focus here is for people to build POJOs and front it with… whatever. And thus the actual container into which the POJO is deployed is more or less irrelevant, yah? After all, if it’s socially acceptable for people do this for Servlets or Spring, why not for EJBs? But again, I’ll say we need to be careful here, because once again we’re approaching this via the "objects uber alles" approach, and I submit that in a number of scenarios, you can’t start with your internal data structures (which we both agree that your object model is, unless you’re advocating that it’s OK to expose that object model across processes, at which point we disagree).

    But that’s only one half of the puzzle and without mentioning the other half I’m being completely naive. You still need the ability to apply enterprise services to your POJOS as (if) needed. You may need to secure them, make them transactional, selectively export them to the server for remote access or management (perhaps over a variety of mechanisms), etc. Spring’s container, using the techniques of 1. Dependency Injection and 2. proxy-based AOP, combined with 3. its service abstractions, gives you a mechanism to do that in a externalized, decorative fashion without impacting your mainline business logic. In addition, you can execute first-level integration tests that exercise the interaction of much of this stuff without having to deploy to a full-blown J2EE container–essentially, you fail faster.

Dependency injection *only* saves a single function call, that of the JNDI lookup, and unfortunately you cannot control when the dependency gets injected, unlike in the JNDI case where you control when the dependency gets resolved. This means that if the system administrator changes the JNDI entry (yes, they can and should be able to do that, at runtime, without you knowing!), the next JNDI lookup pulls back the modified entry. When, exactly, do you ask for Spring to re-inject your dependencies? Plus, proxy-based AOP is essentially what CORBA, RMI, DCOM, and other remoting tools do already–Spring simply joins a handful of other technologies (unfortunately NOT EJB prior to 3.0) that expose that interceptor stack to you. Guys, I hate to say this, but .NET even builds this in, via ContextBoundAttribute and friends. (Frankly, doing interception really isn’t AOP, as I’ve said before, but people seem determined to take the term and co-opt it into whatever they want, so I’m about to give that up as a lost cause. Sure. Interception is AOP. Go ahead. And VB was object-oriented, too. Whatever you want.) As to the "integration tests that exercise the interaction of much of this stuff without having to deploy to a full-blown J2EE container", dude, that’s a failure of the open-source community to write an in-process EJB engine. If JBoss were embeddable (hint, hint, Marc Fleury) or if OpenEJB were more popular (hint, hint, Keith–there is one), then we could mock the EJB parts up around it and do exactly the same kind of in-proc integration testing that Spring can also do.

    As an aside: Ted, I agree with you to a point regarding API. If an object is a MessageListener, you better believe it should implement a MessageListener interface! The Spring team does not subscribe to this belief that everything in the world should be abstracted away. We believe an object’s non-core concerns are a good fit for externalization. So the API is important to us from that point of view: we don’t want our application code full of extraneous non-core concerns that clutter our main-line logic or introduce hardcoded dependencies on expensive resources. Also from an API perspective it needs to be easy to test application code in isolation: a heavyweight API that bakes in a first class dependency on a expensive-to-setup deployment environment is not acceptable.

Now you’ve lost me–an interface is good, but EJB is somehow a "first class dependency"? Dude, an interface is an interface is an interface! If you’re going to argue that JNDI or JTA/JTS is somehow that "first class dependency", then I’m afraid that I’m going to have to turn around and argue the same for MessageListener and the rest. If MessageListener is "a good fit for externalization", then I’m going to have to suggest that the SessionBean or MessageDrivenBean interfaces are the same–you can’t have your cake and eat it too. But more importantly, it *is* possible to do your testing without the heavyweight container. And let’s call that out more carefully, that the J2EE interfaces are NOT heavyweight APIs, because by definition they are interfaces, and interfaces are no more heavyweight in EJB or JMS or Servlets than Spring’s are.

    Bottom line: Spring enables a non-invasive, scalable up+down POJO-based design model and that is the major innovation that has brought agile J2EE into the mainstream.

Sorry, no. Spring is a reinvention of the EJB concept designed to run in-process, more or less a reinvention of the old JavaBeans model. It is no more scalable than the code that’s written to run inside of it (as is EJB, quite frankly), and the major design emphasis of Spring–that being the POJO and all of its object-orientedness–are a slippery slope that will, if Spring achieves the kind of mainstream deployment that EJB achieved, eventually come back to bite us all.

You cannot do a "good distributed object model", because if you build a good object model, you are creating a model which requires a great deal of cross-object communication and therefore spends a great deal of its time over the wire. If you build a good distribution model, however, you find yourself making serious compromises on your interface design, passing DTOs instead of making property-set calls and so on. The two are logically at diametrically opposite ends from one another from a design perspective.