Home > Java > Rich Domain Objects and Spring Dependency Injection are compatible

Rich Domain Objects and Spring Dependency Injection are compatible

I’m currently working in a an environment where most developers are Object-Oriented fanatics. Given that we develop in Java, I think that it is a good thing – save the fanatics part. In particular, I’ve run across a deeply-entrenched meme that states that modeling Rich Domain Objects and using Spring dependency injection at the same time is not possible. Not only is this completely false, it reveals a lack of knowledge of Spring features, one I’ll be trying to correct in this article.

However, my main point is not about Spring but about whatever paradigm one holds most dear, be it Object-Oriented Programming, Functional Programming, Aspect-Oriented Programming or whatever. Those are only meant to give desired properties to software: unit-testable, readable, whatever… So one should always focus on those properties than on the way of getting them. Remember:

When the wise man points at the moon, the idiot looks at the finger.

Back to the problem at hand. The basic idea is to have some bean having reference on some service to do some stuff (notice the real-word notions behind this idea…) so you could call:

someBean.doStuff()

The following is exactly what not to do:

public class TopCaller {

    @Autowired
    private StuffService stuffService;

    public SomeBean newSomeBean() {

        return new SomeBeanBuilder().with(stuffService).build();
    }
}

This design should be anathema to developers promoting unit-testing as the new() deeply couples the TopCaller and SomeBeanBuilder classes. There’s no way to stub this dependency in a test, it prevents testing the createSomeBean() method in isolation.

What you need is to inject SomeBean prototypes into the SomeBeanBuilder singleton. This is method injection and is possible within Spring with the help of lookup methods (I’ve already blogged about that some time ago and you should probably have a look at it).

public abstract class TopCaller {

    @Autowired
    private StuffService stuffService;

    public SomeBean newSomeBean() {

        return newSomeBeanBuilder().with(stuffService).build();
    }

    public abstract SomeBeanBuilder newSomeBeanBuilder();
}

With the right configuration, Spring will take care of providing a different SomeBeanBuilder each time the newSomeBeanBuilder() method is called. With this new design, we changed the strong coupling between TopCaller and SomeBeanBuilder to a soft coupling, one that can be stubbed in tests and allow for unit-testing.

In the current design, it seems the only reason for SomeBeanBuilder to exist is to pass the StuffService from the TopCaller to SomeBean instances. There is no need to keep it with method injection.

There are two different possible improvements:

  1. Given our “newfound” knowledge, inject:
    • method newSomeBean() instead of newSomeBeanBuilder() into TopCaller
    • StuffService directly into SomeBean

  2. Keep StuffService as a TopCaller attribute and pass it every time doStuff() is invoked

I would favor the second option since I frown upon a Domain Object keeping references to singleton services, unless there are many parameters to pass at every call. As always, there’s not a single best choice, but only contextual choices.

Also, I would also use explicit dependency management instead of some automagic stuff, but that another debate.

I hope this piece proved beyond any doubt that Spring does not prevent Rich Domain Model, far from it. As a general rule, know about tools you use and go their way instead of following some path just for the sake of it .

email
Send to Kindle
  1. Alex
    October 21st, 2013 at 10:08 | #1

    I would like to add some stuff.

    Premise no. 0 should be: the developer, at least, does not hate/try to avoid Spring. For this code (initial code), it sounds like this is not the case.

    Counter arguments for lookup-methods would be “it is to much sugar code/extra stuff” and “people don’t know what that means because they don’t use it very often” (i see a dog running around for its tail here). And the whole discussion here about Rich Domain Objects could be torn apart by “we have a service in our Rich-Domain-Objects-wannabe app, thus we can’t continue with this idea” …

    Taking for granted the premise “Yes, I want my code testable”, I would go for option 1 because of metrics, and in particular, coupling is increased between TopCalled and StuffService and it is totally unnecessary.

  2. ali akbar azizkhani
    November 30th, 2013 at 20:12 | #2

    thanks good article.
    with wich plugin you take this class diagram

  3. November 30th, 2013 at 21:26 | #3

    This is no plugin, but PlantUML.

  1. No trackbacks yet.