Spring and the anemic domain model

It sounds like your application is designed around procedural coding principles. This alone will hinder any object-oriented programming you're trying to do.

It's possible that a Foo has no behavior it controls. It's also acceptable to not use a Domain Model pattern if your business logic is minimal. A Transaction Script pattern sometimes just makes sense.

The problem comes in when that logic starts growing. Refactoring a Transaction Script into a Domain Model isn't the easiest thing, but it's certainly not the most difficult. If you have tons of logic surrounding Foo, I'd recommend moving to the Domain Model pattern. The encapsulation benefits make it very easy to understand what's going on and who is involved with what.

If you want to have Foo.Expire(), create an event in your Foo class such as OnExpiration. Wire up your foo.OnExpiration += FooService.ExpireFoo(foo.Id) on object creation, possibly through a factory used by the FooRepository.

Really think about first. It's very possible that everything's already in its right place... for now.

Good luck!


You can get Spring to inject your services into your Hibernate instantiated instances, using AOP. You can also get Hibernate to do the same, using Interceptors.

See http://www.jblewitt.com/blog/?p=129

Regarding "It's annoying to get a Foo to do several somethings transactionally", I would expect your service implementations would know/care about the transactions, and if you're now using the service interfaces within your domain model, that should now be not quite so annoying.

I suspect that deciding when a domain model should be saved is dependent upon what it is and what you're doing with it.

FWIW I have a tendency to produce just the same sort of anemic structures, but I'm getting there, now I know it's possible to do it a more sensible way.


I think that there is a simple refactoring pattern that will solve your problem.

  1. Inject your service into your repository.
  2. Before returning your Foo set its' FooService
  3. Now have your your FooController ask for the appropriate Foo from the FooRepository
  4. Now call the methods you want on you Foo. If it cannot implement them itself, have it call the appropriate method on the FooService.
  5. Now remove all the calls to the FooService through what I like to call "bucket bridge" methods on Foo (it just passes the parameters along to the service).
  6. From now on, whenever you want to add a method add it to Foo.
  7. Only add stuff to the service when you really need to for performance reasons. As always, these methods should be called through the model object.

This will help evolve you towards a richer domain model. It also preserves the Single Responsibility Principle since all your DB-dependent code remains in the FooService implmentations and helps you migrate the business logic from FooService to Foo. In you want to switch your back-end to another DB or in-memory or mock (for testing) you don't need to change anything but the FooService layer.

^ I am presuming that FooService does DB calls that would be too slow to do from an ORM like selecting the most recent Foo that shares property X with a given Foo. That is how most I've seen work.


Example

Instead of:

class Controller{
    public Response getBestStudentForSchool( Request req ){
        Student bestStudent = StudentService.findBestPupilForSchool( req.getParam( "schlId" ).asInt() );
        ...
    }
}

You'll move towards something like this:

class Controller{
    public Response getBestStudentForSchool( Request req ){
        School school = repo.get( School.class, req.getParam( "schlId" ).asInt() ); 
        Student bestStudent = school.getBestStudent();
        ...
    }
}

Which I will hope you will agree already seems richer. Now you are making another database call, but if you keep the School cached in session the penalty is neglible. I'm afraid that any truly OOP model will be less efficient than the anemic model you are using, but the reduction of bugs through code clarity should be worth it. As always, YMMV.

Tags:

Oop

Spring