从域对象调用 Grails 服务是否很糟糕? [关闭]
Posted
技术标签:
【中文标题】从域对象调用 Grails 服务是否很糟糕? [关闭]【英文标题】:Is it bad design to call Grails services from domain objects? [closed]从域对象调用 Grails 服务是不是很糟糕? [关闭] 【发布时间】:2011-04-08 21:39:22 【问题描述】:随着我越来越多地使用 Grails,我发现自己在多个控制器中编写代码,看起来它确实应该是域类的一部分。有时,此域代码包含对服务类的调用。例如,我最近写了一个类似这样的域方法:
class Purchase
// Injected
def paymentService
String captureTransactionId
Boolean captured
// ...
def capture()
captureTransactionId = paymentService.capturePurchase( this )
captured = captureTransactionId != null
我不觉得写这段代码很脏,但我还没有研究过 Grails 中的最佳设计实践,所以我想听听一些意见。
【问题讨论】:
迟来的评论:什么时候注入服务?例如,如果您从数据库加载 10,000 个购买,paymentService 会被注入其中吗?性能将受到严重影响。 @User277434 - Hibernate 并不是真的要进行这种类型的批量加载。如果我要一次加载这么多记录,我可能会跳过使用休眠,或者使用带有投影的查询来避免创建对象。 【参考方案1】:我来回处理这样的事情。在使用 Grails 之前,我对贫血的领域类和将所有内容都放在帮助程序中没有任何问题。我经常以贫乏的课程结束的一个重要原因是验证。在类中验证可空性、长度等很简单,但唯一性需要数据库检查,这与域类(在非 Grails 应用程序中)无关,因此我将其移至帮助程序。现在我已经在两个地方进行了验证,因此我将合并到帮助程序中,并留下一个仅数据类。
但是 Grails 通过将 GORM 方法连接到域类中来取代对 DAO 的需求,并且通过将验证放入域类中来取代对验证器的需求。因此,在决定哪些业务逻辑应该放在域类中以及哪些应该放在服务或其他帮助程序中时,这会产生问题 - 服务是一个很好的地方,可以将可能需要的业务逻辑放在域类方法或验证器中。
是的,它不是纯面向对象的,是的,您创建了一个循环(服务调用域类,域类调用服务),不,这不是“Spring 方式”,但很多 Grails 不是“Spring 方式” ”。
像这样的耦合确实使将应用程序分离成组件或插件以供重用变得更加困难,但使用“def paymentService”声明服务有很大帮助,因为您没有与包名称或实现耦合。
【讨论】:
是的,我一直使用的服务主要是小型且集中的组件,它们与外部系统通信,然后从任何有意义的地方调用它们。我认为由于您提到的注入模型,它们已经足够解耦。我比@duffymo 和@ammoQ 似乎建议的事务脚本类型的模型更喜欢这个。 你说这不是“Spring 方式”,但这是 Spring 创始人的演讲,他主张这样做:infoq.com/presentations/rod-johnson-are-we-there-yet 我主张以更快、更简单的方式来实现它,在这种情况下是使用域类内部的服务。正确的做法在书上看起来不错,但老实说,您打算多久将域类用于使用相同数据库的不同应用程序?即使以后成为需求,也要在当时解决问题,而不是现在。【参考方案2】:我认为域/模型类不应该调用服务。应该是反过来的。
一个服务可以协调其他服务来完成一个用例。我认为这是正确的方法。
【讨论】:
您能解释一下您的观点背后的理由吗?是什么让一种方式比另一种方式更好或更坏? 我同意达菲莫;原因是最小意外原则。一个领域类调用一个服务类是令人惊讶的,并且是一个强烈的迹象,表明职责没有被正确地分配给类。 这就是 Spring 的方式,而 Spring 是 Grails 的基础。 感谢@ammoQ 和@duffymo 的回答,但我仍然不太相信(不是我不能)。我敢肯定,不是每个人都同意“春天的方式”是创建一个贫血的域模型(参见:***.com/questions/1304245/…)。 如果你这样做 /right/,你可以将你的领域类重用于另一个需要对同一个数据库做不同事情的项目。 /Wrong/ 依赖关系使这变得困难和丑陋。【参考方案3】:我只发表我的个人意见。由于 grails 支持自动将服务注入到域类中(不像将服务注入到标准的 groovy 类中,您必须自己配置),我猜它是打算以这种方式使用的,因此不是一个坏习惯。
此外,使用“myDomainInstance.someUsefulMethod()”之类的代码比“someService.someUsefulMethod(myDomainInstance)”之类的代码更具可读性(希望你明白我的意思)。
【讨论】:
是的,我知道你的意思,我也有同感。我发现这类似于在 Java 中使用隔离接口并让您的域代码调用 DAO 类。这种模式似乎启发了诸如 GORM 之类的东西,您只需告诉域类保存自己即可。我认为这是同一件事。我告诉支付类捕获自己,并与外部系统对话以处理它。通过这种方式,耦合度很低,并且我避免了编写一个非常程序化的 Transaction-Script 样式 服务类。【参考方案4】:我不确定是对还是错。
在类似示例的情况下,如何选择一种方式:或者将 paymentService.capturePurchase() 代码放在 Purchase 类中,或者您可以将所有逻辑放在服务中?
【讨论】:
因为我想分离关注点并为我的组件保持单一责任。我希望域代码更新域逻辑,我希望服务代码与外部支付系统交互。以上是关于从域对象调用 Grails 服务是否很糟糕? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章