【问题标题】:Strategies for abstracting db id from domain class从域类中提取 db id 的策略
【发布时间】:2016-10-09 19:12:22
【问题描述】:

我正在使用 spring-data 在 Java 和 Spring4 中编写一个简单的 CRUD 应用程序,我想看看我是否可以尽可能地保持域的纯净/干净,并且我正在考虑我的一些想法/想法可以实现这一目标。
我用父 pom 和一些子模块构建了项目,如下所示:

parent pom
    |- domain
    |
    |- persistence
    |   |- api
    |   |- impl
    |
    |- service
    |   |- api
    |   |- impl
    |
    |- rest

在持久化 api 模块中,我有与用于持久化/检索域类的方法的接口。例如:

public interface GiftPersistence() {

    Gift saveOrUpdateGift(Gift gift);
    /* other methods ... */
}

重要的是,这些方法不是存储库方法。此模块中的方法是由服务模块实现调用的方法。

persistence impl 模块具有特定于我正在使用的数据库的接口实现(今天是 mongo,但可以是 oracle 或 ms-sql 或其他任何东西)。这是我拥有数据库供应商特定的存储库接口的地方。

在域模块中,我有我的域类,但我不想在任何以数据库为中心的东西中建模。例如:

public final class Gift {

    private String description;

    private boolean claimed;

    private String claimedBy;

    /* getters & setters etc ... */
}

我不想为特定于数据库的事物建模的原因是:

  • 持久性与域无关
  • 不同的数据库供应商可能使用不同的 id 策略/数据类型,我不想将我的域代码耦合到任何数据库供应商
  • 我可能想将域模块导入到另一个项目的 pom 中,而该项目可能不会对数据库做任何事情

我过去参与过一个类似的项目(老实说,这就是我的灵感来源!),但我无法再访问该项目,所以不能不要用它来表达想法。
话虽如此,我隐约记得它对 jackson mixins 做了一些事情(尽管这可能是一个类似的想法/概念,但更接近于 web 关注而不是 db 关注)。
我考虑过的另一种方法是使用方面,但不确定我将如何/在哪里进行。

因此,非常感谢任何关于我如何实现保持我的域不受数据库问题影响的目标的想法或想法。

根据我刚刚发现的新想法进行了编辑
这是一个有趣的想法 - https://github.com/CK35/example-ddd-with-spring-data-jpa
这个想法是域模块将域类建模为接口,具体实现(在我的例子中)在持久性实现模块中。通过这种方式,它们可以使用域关注点进行注释,包括 id 等附加属性。
这感觉像是一个不错的选择,因为它意味着域是干净的(尽管不是具体的实现),并且我们可以有不同的持久化 API 模块实现,其中域类的实现具有通用 jpa 注释或 mongo 注释,或甲骨文...

还有其他想法吗?

【问题讨论】:

  • 考虑使用JPA/Spring-Data-JPA?它可以为您处理大多数常见的数据库供应商,如果 JPA 未设置为扫描它们,注释将被安全地忽略(我相信)。
  • 感谢@CollinD,但是(如果我理解正确的话),这正是我想要避免的。如果我使用注释,则意味着我需要将 id 的属性添加到域类(这与域无关),并且我还需要通过导入 jpa 类来污染域模块。跨度>

标签: java database spring spring-data


【解决方案1】:

如果你愿意,你可以使用一个接口,尽管你可能会发现你只有这个接口的一个实现,违背了接口的全部目的。注释本身几乎就是接口。所以带注释的类将大体相同。

我不确定您为什么认为拥有 ID 字段意味着该类将依赖于数据库。 spring 的整个设计意图是不让它与你想要的任何持久化技术接口,因此你可以使用 hibernate 或其他任何东西来实现持久化。如果你确实使用休眠,你可以在 xml 中配置你的持久性类,所以你不会在你的域对象中看到任何持久性的东西......当然除了 ID,虽然我不认为这是一个问题,如果它是一个大问题,您可以从具有私有 id 字段的基类扩展,但您可能不喜欢这样。

当您对具有持久性问题的域对象进行注释时,不会对其他不想了解持久性的客户端代码产生任何问题。他们只有在使用反射或一些疯狂的东西来阅读注释时才能看到它。

恕我直言,将所有服务、daos、实体和所有这些东西都变成接口,然后只有一个实现是矫枉过正的,尽管我知道有些人会不同意。但我相信这仍然是一种代码味道,因为在一天结束时,你有一个只有 1 个实现的接口。假设您稍后将拥有多个实现,实际上需要 10 秒才能将其更改回具有 1 个实现并在 Eclipse 中进行重构的接口。只需将您的实现代码复制到某个位置,提取接口,将当前类更改为提取的接口,然后将您的复制代码放回您的项目中,顶部带有InterfaceImp extends Foo 之类的东西。完毕。一开始不需要过度维护一些幽灵界面。

【讨论】:

  • 我同意带有 1 个 impl 的接口是一种代码味道,但在这种情况下,它是一种学习练习,而不是您在实际项目中可能会做的事情(如果这有意义的话!)我认为我想做的是DDD。回复:一个 ID 列依赖于数据库,我认为它们是。 Oracle 可能会使用很长的 mongodb 使用 ObjectId(可以建模为字符串)。或者可能需要 id 具有给定的模式(尽管我认为这是业务密钥而不是 db id)。无论如何,Oracle 与 mongo 是 id 不同的一个明显例子。在域中对它们进行建模会将您与数据库联系起来
  • 现在我明白你的 id 是什么意思了。但是你不能只使用泛型吗?此外,您可能仍然有需要 id 的域对象。经典示例是同名员工。如果没有像 id/username 这样的标识符,您将无法区分它们。但听起来你一心想摆脱 ids,你讨厌 ids 吗?哈哈
猜你喜欢
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 2011-02-20
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
相关资源
最近更新 更多