将领域模型与持久化模型分离时如何在实体中引用外键

问题描述 投票:0回答:2

我正在尝试一个六边形架构项目,并将域模型与持久性模型分开。但是当我必须引用持久性模型中另一个实体的外键时,我会遇到困难。

我有一个看起来像这样的客户实体及其对应的持久化模型

class Customer(val customerId: String, val name: String) {
}

@Entity(name = "customer")
data class CustomerEntity(
    var customerId: String,
    var name: String,
    @Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long = -1
)

customerId
应该是业务 ID,而对应的实体还有自己的数据库 ID。

现在我有另一个引用客户的实体Report

class Report(val reportId: String, val date: LocalDate, val customerId: String) { } @Entity data class ReportEntity(val reportId: String, val date: LocalDate, val customerId: String, // what should be saved here? the persistence Id (Long) of the customer or its business ID (String)? @Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long = -1)
如果我要保存客户的持久性 ID,那么我必须首先从数据库中获取它,这意味着我需要从报表组件中查询客户表。但这两个是两个独立的有界上下文,我不确定这是否是正确的解决方案或者这里存在建模问题。

kotlin foreign-keys domain-driven-design entity hexagonal-architecture
2个回答
0
投票
如果我要保存客户的持久性 ID,那么我必须首先从数据库中获取它,这意味着我需要从报表组件中查询客户表。但这两个是两个独立的有界上下文,我不确定这是否是正确的解决方案或者这里存在建模问题。

如果您的两个有界上下文共享相同的持久层,那么您的持久层可以查询 Customer 表以从自然键解析主键。

如果您的两个有界上下文具有单独的持久层,那么您需要在报表上下文的持久层中维护客户信息的副本。我们称它们为

Customer.Customer

(客户上下文中的事实来源)和 
Report.Customer
(报告上下文中的复制)。这些模型可以具有不同的结构,这称为
多义域模型

客户希望在两个持久层中拥有不同的主键。持久化报表时,持久层可以查询

Report.Customer

 表以从自然键解析主键。您还需要某种机制将数据从真实来源同步到副本。不要在持久性级别(数据库到数据库)进行复制,否则您将在基础结构级别创建一些依赖关系。更喜欢从客户服务的界面读取数据,因为它更稳定并且通常是版本化的。


0
投票
我认为如果你没有性能问题,你可以摆脱生成的ID并仅使用CustomerId。

但是如果你坚持有一个单独的ID,你会希望使用生成的ID作为参考键,因为它再次对性能更好。另一方面,您应该考虑查询报告的用例。他们是否按 CustomerId 过滤报告?

如果答案是“否”,您不会遇到任何问题,因为用例不需要 CustomerId。

但是,如果答案是“是”,那么您有三个选择:

  1. 您可以将两个 ID 都带入报告模型。这样,您就可以获得有关报表模型所需的所有信息。但请注意,信息必须是不可修改的(就像这些 ID,因为它们不会更新)。因为此信息的任何更新都会破坏您的数据。

  2. 您可以复制报告需要了解的客户信息。您必须通过集成事件(CRUD 事件)复制和管理它。

  3. 从客户上下文中提取数据。这样,您不需要重复数据,但您将依赖于客户上下文。

在前两个选项中,您不会依赖于客户上下文来提取数据,但它们更难应用,特别是对于第二个选项。

第三种选择更容易应用,但以耦合为代价。

© www.soinside.com 2019 - 2024. All rights reserved.