repository - 应如何表示将聚合根添加到存储库?

标签 repository domain-driven-design entity aggregateroot

假设我们有一个类型为 Order 的聚合根实体,它关联客户和订单行。当我考虑订单实体时,更自然地将其概念化为没有 Id 就没有定义。没有 Id 的订单似乎比订单更好地表示为订单请求。

要将订单添加到存储库,我通常会看到人们在没有 Id 的情况下实例化订单,然后让存储库完成对象:

class OrderRepository
{
    void Add(Order order)
    {
        // Insert order into db and populate Id of new order
    }
}

我喜欢这种方法的一点是,您正在向 OrderRepository 添加一个 Order 实例。这很有意义。但是,订单实例没有 ID,在存储库的使用者范围内,订单没有 ID 对我来说仍然没有任何意义。我可以将 OrderRequest 定义为 order 的一个实例并将其添加到存储库中,但这感觉就像从一个橙子中获取一个苹果,然后将它添加到一个橙子列表中。

或者,我也看到了这种方法:
class OrderRepository
{
    Order AddOrder(Customer customer)
        // It might be better to call this CreateOrder
    {
        // Insert record into db and return a new instance of Order
    }
}

我喜欢这种方法的一点是,没有 Id 的订单是未定义的。在创建和返回订单实例之前,存储库可以创建数据库记录并收集所有必需的字段。这里的味道是,您实际上从未将订单实例添加到存储库中。

无论哪种方式都有效,所以我的问题是:我是否必须接受这两种解释中的一种,或者是否有最佳实践来模拟插入?

我发现这个答案很相似,但对于值对象:
how should i add an object into a collection maintained by aggregate root .当谈到值对象时,没有混淆,但我的问题涉及具有从外部源(自动生成的数据库 ID)派生的标识的实体。

最佳答案

我想首先排除第二种方法。这不仅看起来违反直觉,而且还违反了几个好的设计原则,例如 Command-Query SeparationPrinciple of Least Surprise .

其余选项取决于域逻辑。如果领域逻辑规定没有 ID 的 Order 没有意义,那么 ID 是 Order 的必需不变量,我们必须对其进行建模:

public class Order
{
    private readonly int id;

    public Order(int id)
    {
        // consider a Guard Clause here if you have constraints on the ID
        this.id = id;
    }
}

请注意,通过标记 id字段为 readonly我们使它成为一个不变量。对于给定的 Order 实例,我们无法更改它。这与 Domain-Driven Design 完美契合的 实体图案。

您可以通过将 Guard Clause 放入构造函数来进一步强制执行域逻辑,以防止 ID 为负数或零。

到现在为止,您可能想知道这如何与数据库中自动生成的 ID 一起工作。好吧,它没有。

没有好的方法可以确保提供的 ID 尚未被使用。

这让您有两个选择:
  • 将 ID 更改为 Guid。这允许任何调用者为新订单提供唯一 ID。但是,这也要求您使用 Guid 作为数据库键。
  • 更改 API 以便创建新订单不采用 Order 对象,而是采用您建议的 OrderRequest - OrderRequest 可能与 Order 类几乎相同,但减去 ID。

  • 在许多情况下,创建新订单是一项在任何情况下都需要特定建模的业务操作,因此我认为进行这种区分没有问题。尽管 Order 和 OrderRequest 在语义上可能非常相似,但它们在类型层次结构中甚至不必相关。

    我什至可以说它们不应该相关,因为 OrderRequest 是 值对象 而 Order 是 实体 .

    如果采用这种方法,AddOrder 方法必须返回一个 Order 实例(或至少是 ID),否则我们无法知道刚刚创建的订单的 ID。这让我们回到 CQS 违规,这就是为什么我倾向于 更喜欢实体 ID 的指南 .

    关于repository - 应如何表示将聚合根添加到存储库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2096705/

    相关文章:

    c# - 数据映射器、表数据网关(网关)、数据访问对象(DAO)和存储库模式之间有什么区别?

    c# - DDD : Enum like entities

    php - 在刷新实体之前计算唯一别名

    Magento - 向客户实体添加属性

    uml - 实体关系图或类图

    entity-framework - 从聚合根中删除一个 child

    git - 如何将现有的裸 git 存储库导入 Gitlab?

    nhibernate - 有关资源库及其域对象的Save方法的问题

    elasticsearch - 如何将Spring数据 Elasticsearch 与DDD原理集成在一起?

    svn - Bitbucket,Assembla .. 还是什么?