4

存储库应该作为聚合根的边界,即IRepository<TAggreagte>提供以事务方式将数据保存到数据库的 CRUD 功能。到目前为止,一切都很好。

但是如果聚合有一个复合主键呢?在我的问题中,它是一个 Identity INT列加上一个SMALLINT序列号。(这是数据库设计,不是我的想法!)

我见过的存储库示例有 egvoid Add(TAggregate aggregate)bool Add(TAggregate aggregate).

使用“最终一致性”的示例:

我想添加一个聚合 A并且我需要调用存储库 A然后使用存储库 B插入一个依赖 聚合 B,我必须知道聚合 A插入后的 ID。

那是我迷路的地方。如果您插入 A,您将如何获得它的 ID,特别是如果它是一个复合键?我看到的唯一解决方案是再次返回整个对象,所以:

TAggregate Add(TAggregate a);

有什么建议吗?

4

3 回答 3

2

身份是 DDD 中一个非常棘手的话题。

关于身份创建的“时机”,有两种思想流派:

  • 标识是在创建该实体类的实例时生成的,或者
  • 身份是在持久化时创建的(例如,当它插入关系数据库时)。

第二种方法会导致很多问题。你已经提出的一个。当仅在持久性时建立身份时,还会出现其他一些实际问题。考虑域实体的这两个核心属性:

  • 实体通过它们的身份来区分。因此,实体不能没有身份而存在。
  • 当它们的身份相同时,实体被认为是平等的。

当使用“持久性身份”方法创建实体类的新实例时,您最初没有身份,因此违反了上述所有原则。身份现在在您的实体中建模为多少可选?在我看来,这种方法会带你走上一条非常黑暗的道路。

有效解决这些问题的唯一方法是在实例化时生成标识。这也将解决您的问题。身份将立即提供给您。

如果您的数据库技术自动生成 ID,这对您来说可能会很棘手。

有几种方法可以在实体实例化时生成身份。

在实体内生成身份:

简单的例子:

public Person : DomainEntity<Guid>
{
    //..
    public Person(string name)
      : base(Guid.New()) // Providing a unique GUID
    {
        Name = name;
    }
}

客户端代码:

// A new person with identity!
var person = new Person("Eric Evans"); 

这是首选方法,但并非总是可行。

您向实体提供身份:

简单的例子:

public Person : DomainEntity<int>
{
    //..
    public Person(int identity, string name)
      : base(identity) // Providing a unique GUID
    {
        Name = name;
    }
}

客户端代码:

// A new person with identity!
var person = new Person(IdentityGenerator.Create(), "Eric Evans");

生成器IdentityGenerator可能会与您的数据库交互以获取和保留“下一个”标识(不幸的是,SQL Server 不支持此标识)。

你站在哪里?

关于您是否有复合键,您需要问的问题是“我是否能够在实例化时生成或提供实体身份?”

于 2015-05-08T09:37:24.563 回答
2

Key 是一个值对象——重要的是它的值,格式只是一个技术细节。您可以创建一个TAggregateKey类 (PersonKey, OrderItemKey...) 来保存键值

class TAggregateKey
{
    int id {get; set;}
    byte sequence {get; set;}
}

然后按如下方式使用它:

TAggregate a = new Agreggate(value, value, value);
TAggregateKey id = repositoryA.Add(a);    //TAggregateKey Add(TAggregate a)
repositoryB.DoSomething(id, value);      //void DoSomething(TAggregateKey id, int value)
于 2015-05-08T10:02:35.210 回答
1

但是如果聚合有一个复合主键呢?在我的问题中,它是一个 Identity INT 列加上一个 SMALLINT 序列号。(这是数据库设计,不是我的想法!)

聚合永远不会有复合主键,因为

  1. 聚合是一组对象。一个实体,即聚合根,有一个Id,它可以是一个值对象,其格式确保 Id 是全局的(提示:Guid)。Id 可以是对域有意义的自然 id,例如一些生成的数字或字母和数字的组合(通常由域服务生成)。
  2. 主键是一个 RDBMS 概念,一个持久性细节。您的 Db 设计应考虑实体的 Id 格式。如果您的数据库架构是首先设计的或与域需求无关,那么您做错了,即您实际上并没有进行域驱动设计。

我想添加一个聚合A,我需要调用存储库A,然后使用存储库B插入一个依赖聚合B,我必须知道聚合A插入后的ID。

尽管工作单元概念是一个持久性细节,但如果在更高级别上使用它是一种反模式,事务性 CRUD 思维方式在这里不起作用。解决方案非常简单:服务将创建并持久化聚合 A,然后发布包含 id 的领域事件(A 已创建)。另一个服务将充当事件处理程序,并将创建/保留聚合 B。取决于它在技术上的实现方式,最终的一致性将非常短。

为获得可靠的结果,应使用耐用的服务总线。

于 2015-05-08T15:09:36.080 回答