存储库应该作为聚合根的边界,即IRepository<TAggreagte>
将提供CRUD功能,以事务方式将数据保存到数据库。到目前为止一切都很好。
但是如果聚合具有复合主键怎么办? 在我的问题中,它是一个Identity INT 列加上一个 SMALLINT 序列号。 (这是数据库设计,不是我的想法!)
我见过的存储库示例有例如void Add(TAggregate aggregate)
或bool Add(TAggregate aggregate)
。
使用“最终一致性”的示例:
我想添加聚合A ,我需要调用存储库A ,然后插入依赖 聚合B 使用存储库B ,插入后我必须知道聚合A 的ID。
这就是我迷失的地方。如果你插入A,你会如何得到它的ID,特别是如果它是一个复合键? 我看到的唯一解决方案是再次返回整个对象,所以:
TAggregate Add(TAggregate a);
有什么建议吗?
答案 0 :(得分: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不支持这种标识)。
关于您是否有复合密钥,您需要提出的问题是 “我是否能够在实例化时生成或提供实体身份?” 强>
答案 1 :(得分:1)
但是如果聚合有一个复合主键怎么办?在我的问题中,它是一个Identity INT列加上一个SMALLINT序列号。 (这是数据库设计,不是我的想法!)
聚合永远不会有复合主键,因为
我想添加一个聚合A,我需要调用存储库A,然后使用存储库B插入一个依赖聚合B,我必须知道聚合A插入后的ID。
交易型CRUD思维模式在这里并不起作用,尽管工作单元概念是持久性细节但如果在更高级别使用anti-pattern。解决方案非常简单:服务将创建并持久化Aggregate A然后发布将包含id(创建了A)的域事件。另一个服务将充当事件处理程序,并将创建/持久聚合B.根据技术实现的方式,最终的一致性将非常短。
为获得可靠的结果,应使用耐用的服务总线。
答案 2 :(得分:0)
Key is a value object - it's its value that matters, the format is just a technical detail. You could create a <Team id="team1">
<Players>
<Player id="player1"/>
<Player id="player2"/>
<Player id="player3"/>
<Player id="player4"/>
</Players>
<Captain ref="player3"/>
</Team>
class (PersonKey, OrderItemKey...) to hold the key value
class Team
{
public List<Player> Players { get; set; }
[XmlReference]
public Player Captain { get; set; }
}
and then use it as follows:
TAggregateKey