春天和贫血领域的模型

Bra*_*ugh 64 oop spring

所以,我注意到我绝对倾向于模式化我的Spring/Hibernate堆栈对象,如下所示:

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

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

  • 从Foo获取其他服务和对象很烦人.它不是Spring bean,而是由Hibernate加载的.
  • 让Foo在事务上做几件事很烦人.
  • 很难确定Foo是否应该负责选择何时保存自己.如果你调用foo.setName(),foo应该保持更改吗?它应该等到你调用foo.save()吗?foo.save()应该调用FooRepository.save(this)吗?

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

但是我想摆脱这种设计,我想知道其他Spring使用的是什么呢?你是否用装载时编织等花哨的技巧来对抗它(我觉得不舒服)?你有其他的伎俩吗?你认为程序是好的吗?

pto*_*mli 12

您可以使用AOP将Spring注入您的Hibernate实例化实例中.你也可以使用拦截器让Hibernate做同样的事情.

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

关于"让Foo在事务上做几个事情很烦人",我希望你的服务实现能够知道/关心事务,如果你现在在你的域模型中使用服务接口,那现在应该是不完全的很烦人.

我怀疑决定何时应该保存域模型取决于它是什么以及你正在做什么.

FWIW我倾向于产生同样的贫血结构,但我已经到了那里,现在我知道可以以更明智的方式做到这一点.

  • Hibernate拦截器是一个有趣的想法.我不喜欢对我的Tomcat类加载器进行可怕的编辑. (3认同)
  • 为什么要在对象中使用服务?听起来真可怕. (3认同)
  • @ArtB - 对象中的小型自包含方法不是问题,我特意指的是大多数人会考虑服务方法(即Transaction Scripts).例如,让BankAccountObject知道如何获得平衡,更新自己等等就可以了.利用相同的对象来创建年度报表,执行对帐等似乎很荒谬.与域对象分开的一组独立的事务脚本似乎是执行这些冗长操作的更合理的方法. (3认同)
  • @Jess,因为这是OOP的想法......容纳数据的同一个对象知道如何操作实例.其他任何东西都只是来自C的奇特结构. (2认同)

Kev*_*ber 9

听起来您的应用程序是围绕过程编码原则设计的.仅这一点就会阻碍你尝试做的任何面向对象的编程.

Foo可能没有它控制的行为.如果您的业务逻辑很小,那么使用域模型模式也是可以接受的.一个事务处理脚本模式有时也很有道理.

当逻辑开始增长时,问题就出现了.将事务脚本重构为域模型并不是最简单的事情,但它肯定不是最困难的事情.如果你有很多关于Foo的逻辑,我建议你转向Domain Model模式.封装的好处使得它很容易理解发生了什么以及谁参与了什么.

如果你想拥有Foo.Expire(),请在你的Foo类中创建一个事件,例如OnExpiration.连接foo.OnExpiration += FooService.ExpireFoo(foo.Id)对象创建,可能通过工厂使用的工厂FooRepository.

真的先考虑一下.这是非常可能的,一切都已经在正确的地方...现在.

祝好运!


Sle*_*led 5

我认为有一个简单的重构模式可以解决您的问题。

  1. 将您的服务注入到您的存储库中。
  2. 在返回你的 Foo 之前设置它的' FooService
  3. 现在让你的 FooController 从 FooRepository 请求适当的 Foo
  4. 现在调用你想要的方法 Foo。如果它不能自己实现它们,让它在 FooService 上调用适当的方法。
  5. 现在通过我喜欢在 Foo 上调用“bucket bridge”方法(它只是将参数传递给服务)删除对 FooService 的所有调用。
  6. 从现在开始,每当你想添加一个方法时,将它添加到 Foo.
  7. 仅在出于性能原因确实需要时才向服务添加内容。与往常一样,应通过模型对象调用这些方法。

这将帮助您朝着更丰富的领域模型发展。它还保留了单一职责原则,因为所有依赖于数据库的代码都保留在 FooService 实现中,并帮助您将业务逻辑从 FooService 迁移到 Foo。如果您想将后端切换到另一个数据库或内存或模拟(用于测试),除了 FooService 层之外,您不需要更改任何内容。

^ 我假设 FooService 执行的 DB 调用太慢,无法从 ORM 执行,例如选择与给定 Foo 共享属性 X 的最新 Foo。这就是我见过的大多数工作的方式。


例子

代替:

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

你会朝着这样的方向前进:

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

我希望你会同意,这似乎更丰富了。现在您正在进行另一个数据库调用,但是如果您在会话中缓存 School ,则损失可以忽略不计。恐怕任何真正的 OOP 模型都比您使用的贫血模型效率低,但是通过代码清晰度减少错误应该是值得的。一如既往,YMMV。