Spring and the anemic domain model

前端 未结 4 694
后悔当初
后悔当初 2020-12-04 09:05

So, I\'ve noticed that I definitely have a tendency to pattern my Spring/Hibernate stack objects like this:

  • Foo controller makes a call to \"FooService\"
4条回答
  •  自闭症患者
    2020-12-04 09:40

    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.

提交回复
热议问题