简体   繁体   English

从域类抽象数据库ID的策略

[英]Strategies for abstracting db id from domain class

I am writing a simple CRUD application in Java and Spring4 with spring-data, and I want to see if I can keep the domain as pure/clean as possible, and I'm after some ideas/thoughts as to how I can go about achieving this. 我正在用Spring数据在Java和Spring4中编写一个简单的CRUD应用程序,我想看看我是否可以将域保持为纯净/干净,并且我对如何进行操作有一些想法/想法。实现这一目标。
I've structured the project with a parent pom and some child modules as follows: 我使用父pom和一些子模块构造了该项目,如下所示:

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

In the persistence api module I have interfaces with methods for persisting/retrieving my domain classes. 在持久性api模块中,我具有用于持久化/检索域类的方法的接口。 eg: 例如:

public interface GiftPersistence() {

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

Importantly, these methods are not repository methods. 重要的是,这些方法不是存储库方法。 The methods in this module are those that are called by the service module implementations. 该模块中的方法是服务模块实现所调用的方法。

The persistence impl module has an implementation of the interface that is specific to the database that I'm using (it's mongo today, but could be oracle or ms-sql, or anything else). 持久性impl模块具有接口的实现,该接口特定于我正在使用的数据库(今天是mongo,但可以是oracle或ms-sql或其他任何东西)。 This is where I have my repository interface that is specific to the database vendor. 这是我具有特定于数据库供应商的存储库接口的地方。

In the domain module I have my domain classes, but I don't want to model in anything that is database centric. 在域模块中,我有域类,但是我不想在任何以数据库为中心的模型中进行建模。 eg: 例如:

public final class Gift {

    private String description;

    private boolean claimed;

    private String claimedBy;

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

The reasons I don't want to model database specific things are: 我不想为数据库特定的事物建模的原因是:

  • Persistence is not a concern of the domain 持久性不是该领域的关注点
  • Different database vendors might use different id strategies/datatypes, and I don't want to couple my domain code to any database vendor 不同的数据库供应商可能使用不同的id策略/数据类型,并且我不想将域代码耦合到任何数据库供应商
  • I might want to import the domain module into another project's pom, and that project might not do anything with a database 我可能想将域模块导入另一个项目的pom中,而该项目可能对数据库没有任何作用

I've been involved in a project in the past that did something similar (to be honest, that's where I've got the idea from!), but I don't have access to that project any more so can't use it to crib ideas. 我过去参与过一个类似项目的项目(说实话,这就是我的想法!),但是我再也无法访问该项目,因此无法使用婴儿床的想法。
Having said that I vaguely remember it did something with jackson mixins (though that might have been a similar idea/concept, but closer to the web concern than the db concern). 话虽这么说,我依稀记得它对杰克逊混入做了些什么(尽管可能是类似的想法/概念,但更接近于网络而不是数据库)。
The other approach I've thought about is using aspects, but not really sure how/where I'd go about that. 我考虑过的另一种方法是使用方面,但不确定如何/在何处进行。

So, any ideas or thoughts as to how I might achieve the goal of keeping my domain clean of database concerns would be very much appreciated. 因此,对于我如何实现使我的域保持数据库关注的目标的任何想法或想法,将不胜感激。

Edited with new idea I just found 用我刚刚发现的新主意进行了编辑
This is an interesting idea - https://github.com/CK35/example-ddd-with-spring-data-jpa 这是一个有趣的想法-https: //github.com/CK35/example-ddd-with-spring-data-jpa
The idea is that the domain module models the domain classes as interfaces, and the concrete implementations are (in my case) in the persistence-impl module. 这个想法是域模块将域类建模为接口,而具体的实现在我的案例中是persistence-impl模块。 This way they can be annotated with domain concerns, including additional properties such as id. 这样,就可以在域问题上为它们添加注释,包括其他属性,例如id。
This feels like a nice option as it means the domain is clean (albeit not a concrete implementation), and we can have different implementations of the persistence-api module, where the implementations of the domain classes have generic jpa annotations, or mongo annotations, or oracle ... 感觉这是一个不错的选择,因为它意味着域是干净的(尽管不是具体的实现),而且我们可以对persistence-api模块进行不同的实现,其中域类的实现具有通用的jpa批注或mongo批注,或甲骨文...

Any other ideas? 还有其他想法吗?

If you want you can use an interface, although you'll probably find that you only ever have one implementation of this interface, defeating the whole purpose of the interface. 如果您愿意,可以使用一个接口,尽管您可能会发现只有该接口的一种实现,这违背了该接口的全部目的。 Annotations themselves are pretty much interfaces. 注释本身就是很多接口。 So an annotated class will be much the same. 因此,带注释的类将大体相同。

I'm not sure why you think having an ID field will mean that the class will be database dependent. 我不确定为什么您认为拥有ID字段意味着该类将依赖于数据库。 The whole design intent of spring is to not make it interface with any persistence technology you want, and so you can use hibernate or whatever for persistence. spring的整个设计意图是不使其与所需的任何持久性技术交互,因此您可以使用休眠或任何持久性技术。 If you do use hibernate you could configure your persistence classes in xml, so you won't see any persistence stuff in your domain object... except the ID of course, though I don't see this as a problem, if it is a massive problem well you could extend from a base class which has a private id field but you might not like that. 如果您确实使用了hibernate,则可以在xml中配置持久性类,因此您不会在域对象中看到任何持久性东西……当然除了ID,尽管我认为这不是问题,一个巨大的问题很好,您可以从具有私有id字段的基类扩展,但是您可能不喜欢这样。

When you do annotate a domain object with persistence concerns, won't have any problems with other client code which doesn't want to know about persistence. 当您对具有持久性问题的域对象进行注释时,其他不想知道持久性的客户端代码将不会有任何问题。 They will only be able to see it if they are using reflection or something crazy to read annotations. 如果他们使用反射或疯狂的方式来读取注释,他们将只能看到它。

IMHO making all services, daos, entites, and all this stuff into interfaces, and then have only one implementation is overkill although I know some people will disagree. 恕我直言,将所有服务,链接,实体和所有这些东西都放到接口中,然后只有一种实现是过大的,尽管我知道有些人会不同意。 But I believe this is still a code smell none-the-less, because at then end of the day you have an interface with only 1 implementation. 但是我仍然相信这仍然是代码的味道,因为在一天结束时您只有一个实现的接口。 Say you are going to have more than one implementation later it will literally take you 10 seconds to change this back into an interface with 1 implementation with refactoring in eclipse. 假设您稍后将有多个实现,那么将其实际上转换为具有1个实现并在Eclipse中进行重构的接口将花费您10秒。 Simply copy your implementation code some where, extract the interface, change the current class to be the extracted interface, and put your copy code back in your project with something like InterfaceImp extends Foo on the top. 只需将实现代码复制到某个位置,提取接口,将当前类更改为提取的接口,然后将复制代码放回项目中即可,例如, InterfaceImp extends Foo到顶部。 Done. 做完了 No need to overdo it at the start with maintaining some ghost interface. 无需在维护某些鬼界面的时候就过度使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM