【问题标题】:How can I split domain logic and data access in Grails如何在 Grails 中拆分域逻辑和数据访问
【发布时间】:2015-01-11 08:56:20
【问题描述】:

如何在 Grails 中拆分域逻辑和数据访问(这是个好主意)?

我们编写的许多软件应用程序都以数据(基础)为中心,并且在 Grails 中,通常会从服务类或控制器直接保存到 DataSource.groovy 中配置的数据库中。更改数据库很容易,但我们并不是真正独立于代码中的持久化实现。

我正在尝试编写一个为不同的持久性和数据源(不仅是数据库)实现打开的应用程序,并专注于业务领域而不是数据库实体。这也是测试时的一个加分项(易于编写 fake/mock 持久性) 最初我只有一个持久性实现——Grails 域类,使用 GORM。但有可能我将来希望拥有数据库以外的其他数据源,例如休息服务或其他东西。

目前,我只将数据库作为数据源,并且主要做一些杂乱无章的事情(以及一些领域逻辑)。我认为我仍然停留在“旧”思维中,专注于数据库持久性,因为我的大多数业务域类都有一个 Grails 域类等价物,它是它的副本。当要持久化域类时,我只需将属性复制到 Grails 域类。

我对这个解决方案不太满意。我能想到至少两个可能的改进/更改:

  1. 我的 Grails 域类的组织方式可能与业务域类不同,因此我不只是将属性从一个类复制到另一个类。但是,在从数据库读取或写入数据库时​​,这仍然会涉及从一个类到另一个类的大量属性映射。
  2. 也许有一种方法可以使用业务领域类,从常规的 src/main/groovy 包中使用 GORM 东西进行装饰?或者以其他方式拆分域逻辑和持久性?我已经看到可以通过在域类上使用 hibernate conf 来做到这一点。这是唯一的方法吗?

我看到了一些关于 Grails 架构的有趣讨论,包括干净的架构、六边形架构和 ddd,但我还没有找到任何示例。有吗?

在这一点上,正如我所说,大部分功能都是 CRUD 的东西,但不是全部。而且,应用程序可能有更多的业务逻辑,所以我不希望使用带有视图、控制器、服务、域的 Grails 的“默认”架构。我想要一个独立于 grails 视图/控制器和域/GORM 的“核心”应用程序

【问题讨论】:

  • 也许我可以使用基类并在 Grails 域文件夹和业务域文件夹中扩展它...
  • GORM 本身非常“可插拔”,并且您可能知道,许多支持实现已经存在:Mongo、Hibernate、Neo4j、Redis 等(看看github.com/grails/grails-data-mapping)只要当您避开 HQL 和原生 SQL(更喜欢动态查找器和 where 查询)时,可以在实现之间移动而无需大量代码更改。
  • 感谢您的回复@Andrew。是的,GORM 是可插拔的,但如果可能的话,我试图独立于数据库作为持久性。所以它应该是一个可以是任何东西的存储库(即模拟、文件系统等)。也许我的数据库模型根本不应该看起来像业务领域。至少我必须考虑到这一点。
  • 对,还有一些额外的想法:1) 看看grails.org/plugin/gorm-rest-client,这是 GORM 极端灵活性的一个例子,2) 命令对象是一个强大的概念,可能有助于您的工作,请参阅例如:skillsmatter.com/podcast/groovy-grails/…
  • 我会@Andrew。欣赏它!谢谢。

标签: grails dns domain-driven-design persistence hexagonal-architecture


【解决方案1】:

您发布问题已经有一段时间了,但这对我来说是一个非常有趣的话题......

我目前从事大型 Java8 项目,这些项目实施清洁架构、ddd、cqrs 和六边形架构等原则。我对 Grails 1.x 项目的经验也很有限,我记得问过和你现在一样的问题。

现在我有了更广阔的视野,老实说,我认为强迫 Grails 成为一个干净的架构是没有意义的。您将经历一段非常痛苦的尝试来实现它,而且您可能不会对结果感到满意。

Grails 中的所有内容都旨在以一种固执己见的、基于约定的方式使用。从 GORM 作为一个 ActiveRecord 实现开始,然后按照他们对目录结构、您需要定义的工件的语义(控制器、服务、模型......)等做出的每一个小决定。我不是说这个不好。事实上,当您开发适合这种事物架构的东西时,这非常棒。

工件之间的这种耦合和隐式行为使得除了您的数据访问(或您的 http 交互,或与第三方的任何其他交互)之外,很难对您的业务逻辑进行建模。

从 DDD 的角度来看,您应该更喜欢基于数据或集合的存储库而不是 ActiveRecord 实现。然后,您可以开始将持久性逻辑与域模型分离。在与持久层保持类似 ActiveRecord 的交互的同时尝试这样做会产生一个非常“肮脏”的适应层,并带有大量重复。

例如,当您尝试使用应该放入不同数据库表的聚合对象来调整复杂的域时,您将非常困难。

现在,针对您建议的两项改进,我可以告诉您以下内容:

  1. 我的 Grails 域类的组织方式可能与业务域类不同,因此我不只是将属性从一个类复制到另一个类。但是,在从数据库读取或写入数据库时​​,这仍然会涉及从一个类到另一个类的大量属性映射。

你确实可以按照你说的去做。只需在 src/groovy 文件夹中放置一些代码。您将在这里面临的主要问题是依赖注入。当在标准目录中定义服务和控制器时,Grails 会自动注入对您的服务和控制器的依赖项。对于其他所有内容,您需要明确地tell Grails how to take dependencies and pass them to your custom artifacts

  1. 也许有一种方法可以使用业务领域类,从常规的 src/main/groovy 包中使用 GORM 东西进行装饰?或者以其他方式拆分域逻辑和持久性?我已经看到可以通过在域类上使用 hibernate conf 来做到这一点。这是唯一的方法吗?

如果你用 GORM 装饰你在 src/groovy 中定义的域对象(如果可能的话)你会遇到同样的问题。您在这里的任务是将您的域与持久性逻辑隔离开来。通过在其中包含任何 GORM 来实现它的目的是失败的。

我在这里的所有建议都是:

  1. 切换到其他耦合度较低的库,让您设计自己的架构(即RatpackJooq)或
  2. 如果这不是一个选项,请完全接受 Grails 的行事方式。

有一个非常全面的库列表,您可以浏览以获取灵感:Awesome Java

【讨论】:

  • 感谢您的 cmets。我很欣赏你的想法,我认为你可能是对的,应该接受 Grails 方式。到目前为止,我也这样做了。但是我并没有放弃这些想法 :) 实际上,将域逻辑放在 src/main/groovy 中是我尝试过的,但在这种情况下,我最终将大部分代码放在服务中——“标准”Grails 方式.
  • 一些想法:1) 可以在 src/main/groovy 中使用域逻辑的类似 ddd 的解决方案,从 Grails 服务和/或控制器调用,并且此逻辑可以调用 GORM-stuff,它是存储库接口的实现?这将涉及复制数据,但在没有 Grails 的情况下也是如此。 2)如果域和数据库表之间存在一对一的映射,可以将域类放在标准的 Grails 域文件夹中。然后可以将域逻辑分离并放入 src/main/groovy 并且可以在运行时创建对象(数据+域逻辑)。有什么想法@ggalmazor?
猜你喜欢
  • 2018-01-30
  • 2010-09-25
  • 2010-10-24
  • 1970-01-01
  • 1970-01-01
  • 2019-04-19
  • 2011-06-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多