代码之家  ›  专栏  ›  技术社区  ›  Brandon Yarbrough

Spring与贫血域模型

  •  63
  • Brandon Yarbrough  · 技术社区  · 16 年前

    因此,我注意到我确实有一种倾向,我的Spring/Hibernate堆栈对象的模式如下:

    • Foo控制器调用“FooService”
    • FooService调用FooRepository.getById()方法来获取一些Foo。
    • FooService与Foos进行一些交互。它可以使用相关的TransactionalFooService来处理需要在事务中一起完成的事情。
    • FooService要求FooRepository保存Foos。

    这里的问题是Foos没有任何真正的逻辑。例如,如果每次Foo过期时都需要发送电子邮件,则不会调用Foo.expire()。有一个对FooService.expireFoo(fooId)的调用。原因有很多:

    • 从Foo获取其他服务和对象很烦人。它不是SpringBean,它是由Hibernate加载的。
    • 让一个Foo做一些事务性的事情是很烦人的。
    • 很难决定Foo是否应该负责选择何时拯救自己。如果调用foo.setName(),foo是否应该保持更改?它是否应该等到调用foo.save()时再运行?foo.save()是否应该调用FooRepository.save(这个)?

    因此,出于这些原因,我的Spring域对象基本上是带有一些验证逻辑的美化结构。也许这没关系。也许web服务可以作为过程代码。也许随着新特性的编写,可以接受创建以新方式处理相同旧对象的新服务。

    但我想避开这种设计,我想知道其他Spring的用途是什么?你有没有用一些花哨的技巧来对付它,比如在加载时编织(我不太习惯)?你还有别的把戏吗?你认为程序性的好吗?

    4 回复  |  直到 16 年前
        1
  •  12
  •   ptomli    16 年前

    您可以让Spring使用AOP将您的服务注入到Hibernate实例化的实例中。您还可以使用拦截器让Hibernate执行同样的操作。

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

    关于“让一个Foo以事务方式做一些事情很烦人”,我希望您的服务实现会知道/关心事务,如果您现在正在域模型中使用服务接口,那么现在应该不会那么烦人了。

    我怀疑,决定何时保存域模型取决于它是什么以及您正在使用它做什么。

    FWIW我有一种倾向,就是产生同样类型的贫血结构,但我正在达到这一点,现在我知道有可能用一种更明智的方式来做。

        2
  •  10
  •   Kevin Swiber    16 年前

    听起来您的应用程序是围绕过程编码原则设计的。这将阻碍您尝试进行的任何面向对象编程。

    Foo可能没有它控制的行为。这也是可以接受的 使用 Domain Model Transaction Script 这种模式有时很有意义。

    当这种逻辑开始增长时,问题就出现了。将事务脚本重构为域模型不是最容易的事情,但肯定不是最困难的。如果您有大量关于Foo的逻辑,我建议您转到域模型模式。封装的好处使得理解发生了什么以及谁参与了什么变得非常容易。

    如果你想 Foo.Expire() OnExpiration . 把你的电话接通 foo.OnExpiration += FooService.ExpireFoo(foo.Id) 在对象创建时,可能通过 FooRepository .

    先好好想想。它是 非常 可能一切都已经在正确的位置上了。。。现在。

    祝你好运

        3
  •  5
  •   Sled bayer    14 年前

    1. 将服务注入到存储库中。
    2. 现在在你的Foo上调用你想要的方法。如果它不能自己实现它们,让它调用FooService上的适当方法。
    3. 现在,通过我喜欢在Foo上调用的“bucket bridge”方法删除对FooService的所有调用(它只是将参数传递给服务)。
    4. 从现在起,无论何时您想要添加一个方法,都要将它添加到Foo。
    5. 仅当您需要时才向服务添加内容 真正地 出于性能方面的原因,需要更改。与往常一样,应该通过模型对象调用这些方法。

    ^我假设FooService执行DB调用,这在ORM中执行起来太慢了,比如选择与给定Foo共享属性X的最新Foo。这就是我所见过的大多数工作方式。


    实例

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

    你会朝着这样的方向发展:

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

    我希望你会同意这一点,它似乎已经变得更富有了。现在,您正在进行另一个数据库调用,但如果您将学校缓存在会话中,则惩罚是可以忽略的。我担心任何真正的OOP模型的效率都会低于您正在使用的贫血模型,但是通过代码清晰性减少bug应该是值得的。一如既往,YMMV。

        4
  •  0
  •   Elom Atsou Agboka    6 年前

    我向你推荐这本书 用例驱动的UML对象建模 道格·罗森博格和马特·斯蒂芬斯合著。它讨论了ICONIX过程——一种软件开发方法,也讨论了贫血领域模型。这也是Martin Fowler在其网站上开发的一个主题 https://www.martinfowler.com/bliki/AnemicDomainModel.html . 但是,当使用SpringFramework和/或SpringBoot时,我们应该如何实现呢?这也是我试图弄清楚的。