【问题标题】:In DDD can an entity and repository pull from multiple tables?在 DDD 中,一个实体和存储库可以从多个表中提取吗?
【发布时间】:2021-03-11 17:44:02
【问题描述】:

我对 DDD 很陌生,我正在尝试正确实现我的用例。

我有多个绑定在一起的实体,很像通常的 Order 聚合示例,它封装了 LineItem。

根据我对基本 DDD 的了解,我倾向于创建 2 个实体,一个用于订单,另一个用于订单项,但从那里似乎有 2 个选项:

  • 让两个存储库都返回Tx,将它们全部实例化到另一个接口/结构中作为“聚合”(可能不是正确的名称)以进行交易以创建新订单

  • 创建一个新的存储库,然后它将在整个订单范围内自行执行交易(创建订单+行项目)

对我来说,第二个选项似乎比第一个更容易实现。我可以在此存储库中进行连接、tx 或任何所需的操作,以从两个表(订单和行项目)中检索数据,还可以创建事务并确保它们的数据完整性。但我不确定这是否是好的做法。

“伪代码”看起来像这样:

type Order struct {
   ID int
   lineItems []LineItem
   ...
}

type LineItem struct {
   ID int
   ...
}

type OrderRepoistory interface {
   GetOrders()([]*Order,err)
   GetOrder(id int)(*Order,err)
   Create(order *Order) err
}

在OrderRepository 实现中,create 函数看起来像这样:

func Create(order *Order) err {
  tx := BeginTX
   insert order into the orders table
   insert the lineitems into the line_items table
  tx.commit or rollback
  return nil or err
}

func GetOrder(orderId int)*Order {
   var order Order
   var items []*LineItem
   row,_ := db.Query("select * from orders where id = $1",orderId)
   row.Scan(&order)
   rows ,_ := := db.Query("select * from line_items where order_id = $1",orderId)
   loop and get append each rows to items slice
   // copy slice to 
   order.LineItems = items
   return &order
}

因此,存储库的 Create 和 Getorder 都包含对两个表的查询以检索一个 Order。

这种实现是否有意义,因为它对于“实体”来说似乎非常广泛?如果不是,那么一次对多个表进行事务和查询的正确方法是什么?

【问题讨论】:

  • DDD 关注的是概念,而不是实现。 DDD 没有具体说明实体如何映射到底层数据存储的表 - 事实上,DDD 可以用于根本不涉及表概念的数据存储。此外,即使它确实指定,它也只是一个指导框架,而不是硬性规定。
  • 我认为我理解一个实体必须拥有自己的存储库。我相信当您需要跨多表的原子性同时仍然保留我的理解失败的实体的“纯粹性”时,实现有点重要执行时。因为要么我总是返回 TX,然后实现泄漏服务,要么我将我的域定义为比简单实体“更大”的东西。或者我对实体的理解是错误的。
  • 实现对功能绝对重要,但对 DDD 无关。
  • 在另一个 Answer 中,我刚刚提供了与事务处理相关的链接,我链接到一系列博客文章,详细介绍了基于 DDD 的 Golang 架构的所有细节。
  • 是的,你可以。 aggregate 代表多个实体。因此,例如,当您坚持回购时,您应该在事务中进行(如果您使用的是 RDBS)。我认为 DDD 书有表 person 和 customer 的示例。聚合customer在创建时需要插入两个表中。

标签: go transactions repository domain-driven-design


【解决方案1】:

我建议更仔细地研究 聚合 的概念。 存储库或多或少代表一个聚合集合,并且一个实体始终是聚合根,您可以将其视为多个实体的父级彼此相关 - 作为一个聚合体。

订单可以是聚合根,订单行可以是实体,并且在您加载订单聚合时会从持久性中完全加载。

或者订单项可以自己聚合,在这种情况下,您通常只会在订单聚合中保留订单项的 ID 列表。

但这真的取决于业务逻辑。因为这就是 DDD 的意义所在 - 尽可能地反映业务环境。

但无论您如何使用聚合、实体和值对象设计域模型,这些类看起来都与您的数据库模型完全不同。

以一种易于实现领域逻辑的方式构建您的领域模型类,并以一种便于持久化和查询数据的方式构建您的数据模型。

所以你的问题:

...如果不是同时对多个表进行事务和查询的正确方法是什么?

如果您从存储库请求聚合(例如订单通过和订单 ID),则存储库实现应该处理所有涉及的查询,当然可以在几个不同的表上执行。因为同样,领域模型类外壳独立于数据库模型。

然后应该将聚合与存储库方法中的所有数据一起加载,其中包括子实体和使用的值对象的所有数据。但这仍然发生在一种存储库方法中,例如 findById()。

关于事务同样适用于存储聚合。在这里,存储库采用当前状态的完全加载聚合,然后将其与所有数据一起保存。这可能再次涉及对多个表的插入或更新(当谈到关系数据库时)。

【讨论】:

  • 您能给我们介绍一下如何使用聚合的示例吗?
猜你喜欢
  • 1970-01-01
  • 2012-03-10
  • 2017-05-18
  • 2021-10-23
  • 2013-12-05
  • 2012-01-27
  • 1970-01-01
  • 2011-09-13
  • 2022-11-17
相关资源
最近更新 更多