从域对象调用Grails服务是一种糟糕的设计吗?

从域对象调用Grails服务是一种糟糕的设计吗?,grails,Grails,随着我越来越多地使用Grails,我发现自己在多个控制器中编写的代码看起来应该是域类的一部分。有时,此域代码包含对服务类的调用。例如,我最近编写了一个类似以下内容的域方法: class Purchase { // Injected def paymentService String captureTransactionId Boolean captured // ... def capture() { captureTran

随着我越来越多地使用Grails,我发现自己在多个控制器中编写的代码看起来应该是域类的一部分。有时,此域代码包含对服务类的调用。例如,我最近编写了一个类似以下内容的域方法:

class Purchase {

    // Injected
    def paymentService

    String captureTransactionId
    Boolean captured

    // ...

    def capture() {
        captureTransactionId = paymentService.capturePurchase( this )
        captured = captureTransactionId != null
    }

写这段代码我并不觉得很肮脏,但我还没有研究Grails中的最佳设计实践,所以我想征求一些意见。

我认为域/模型类不应该调用服务。应该是相反的方向


服务可以协调其他服务以实现用例。我认为这是正确的方法。

我反复提到这样的东西。在Grails之前,我对贫乏的域类和将所有内容放在helpers中没有任何问题。我经常以贫血课程告终的一个重要原因是验证。在类内验证可空性、长度等很简单,但唯一性需要数据库检查,这与非Grails应用程序中的域类无关,因此我将其移动到帮助器。现在我在两个地方进行了验证,所以我将合并到helper中,剩下的将是一个仅数据类

但是Grails通过将GORM方法连接到域类中来代替dao,并且通过将验证放在域类中来代替对验证器的需要。因此,在决定哪些业务逻辑应该放在域类中,哪些应该放在服务或其他帮助程序中时,这就产生了问题——服务是将可能需要的业务逻辑放在域类方法或验证器中的绝佳场所

是的,这不是纯面向对象的,是的,你创建了一个循环,服务调用域类,域类调用服务,不,这不是Spring方式,但很多Grails不是Spring方式


这样的耦合确实会使将应用程序分离为组件或插件以供重用变得更加困难,但使用“def paymentService”声明服务会有很大帮助,因为您没有耦合到包名称或实现。

我只给出我个人的意见。由于grails支持自动将服务注入域类,这与将服务注入标准groovy类(您必须自己配置)不同,因此我猜它是打算以这种方式使用的,因此这不是一种坏做法


此外,它还使代码更易于阅读,比如myDomainInstance.someUsefulMethod,而不是someService.someUsefulMethodmyDomainInstance。希望您知道我的意思。

我不确定它是对还是错


在像本例这样的情况下,采用这种或那种方式如何:要么将paymentService.capturePurchase代码放在Purchase类中,要么将所有逻辑放在该服务中?

您能解释一下您的观点背后的基本原理吗?是什么让一种方式比另一种方式更好或更糟呢?我同意达菲莫的观点;原因是最小意外的原则。一个域类调用一个服务类是令人惊讶的,这是一个强烈的信号,表明职责没有正确地分配给类。这是Spring的方式,Spring是Grails的基础。感谢@ammoQ和@duffymo的回答,但我仍然不太相信我不能。我相信不是每个人都会同意Spring的方法是创建一个贫血的域模型。请参见:。如果您这样做/right/,您就可以将域类重新用于另一个项目,该项目需要对同一数据库执行不同的操作/错误/依赖性使这变得困难和丑陋。因为我想分离关注点,并对我的组件保持单一责任。我希望域代码更新域逻辑,我希望服务代码与外部支付系统交互。是的,我知道你的意思,我也有同感。我发现这类似于在Java中使用隔离接口并让域代码调用DAO类。这种模式似乎启发了GORM这样的东西,在GORM中,您只是告诉域类保存自己。我认为这是同样的事情。我告诉payment类捕获自己,然后它与外部系统进行对话以处理它。通过这种方式,耦合性很低,我避免了编写一个看起来非常程序化的事务脚本样式的服务类。是的,我一直将服务主要用作小型和集中的组件,与外部系统通信,然后在任何有意义的地方调用它们。我认为它们已经足够解耦了,因为您提到的注入模型。与@duffymo和@ammoQ似乎建议的事务脚本类型相比,我更喜欢这种类型的模型。你说这不是Spring的方式,但这里有一个Spring创始人的演示,他主张做到这一点:我主张以更快、更简单的方式实现,在本例中,它使用域类内部的服务。做对了看起来很好
这是一本书,但老实说,对于使用同一数据库的不同应用程序,您打算多久使用一次域类?即使以后成为需求,也要在那时解决问题,而不是现在。最新评论:何时注入服务?例如,如果您从一个数据库加载10000次购买,paymentService会被注入其中吗?性能将受到严重影响。@User277434-Hibernate并不是真正用来进行这种批量加载的。如果一次加载这么多记录,我可能会跳过使用hibernate,或者使用带有投影的查询来避免对象创建。