【问题标题】:Persistance ID's and Domain Model Entities持久 ID 和域模型实体
【发布时间】:2014-06-18 14:52:14
【问题描述】:

我很好奇人们对于将 DAL 实体的 Id 保留为域实体的属性(至多是只读属性)的想法。

我的第一个想法是这样做是可以的,但我越想越不喜欢这个想法。毕竟域模型应该完全不知道数据是如何持久化的,并且在每个域模型上保留和 Id 属性是一个不太微妙的指示。持久层可能是不需要主键的东西,或者域模型中暴露的另一个属性可能是识别的合适候选者,模型编号。也许吧。

但这让我开始思考,对于没有可靠方法来唯一标识数据库持久层中的条目的域模型,它们在更新或删除时如何识别条目?

基于弱引用键的字典可以解决问题; WeakDictionary<DomainEntity, PrimaryKeyType>。该字典将成为存储库实现的一部分,每当存储库的客户端获取DomainEntity 的集合时,对实体的弱引用及其持久层 ID 存储在此内部字典中,例如当需要将修改后的实体返回到存储库以更新持久层时,可以执行以下操作来取回 Id

PrimaryKeyType id = default(PrimaryKeyType);
if (!weakDictionary.TryGetValue(someDomainEntity, out id))
     // id not found, throw exception? custom or otherwise..

// id found, continue happily mapping domain model back to data model.

在我看来,这种方法的好处是域实体不需要维护其持久层特定的 ID,并且存储库会强制您通过调用 Fetch... 方法或 @ 来获得合法的域实体987654325@ 方法,否则如果您尝试更新/删除实体,它将引发异常。

我知道这可能是过度设计,我应该冷静下来,务实一点,我只是好奇其他人对此有何看法。


我不想仅仅为这个小问题开始另一个线程,因为它有些相关。但由于是最近我才开始研究 DDD(尽管在这种情况下我的数据库首先出现)我想知道我是否可以确认我对域实体有正确的心态,这是我的 Employee 域实体的一个简化示例。

public class Employee : DomainEntity
{
    public string FirstName { get; }
    public string LastName { get; }
    public UserGroup Group { get; }
    // etc..


    // only construct valid employees
    public Employee(string firstName, string lastName, SecureString password, UserGroup group);

    // validate, update. (not sure about this one.. pulled it 
    // from an open source project, I think that names should be able to be set individually).
    AssignName(string firstName, string lastName);

    // validate, update.
    ResetPassword(SecureString oldPassword, SecureString newPassword);

    // etc..
}

谢谢!

【问题讨论】:

  • 如果您希望这个问题得到回答(并保持开放),您应该减少措辞并提出一个真正的问题(不是讨论)
  • @ShoeMay:我不同意。这是一个相关且结构良好的问题。那些实现 DDD 的概念通常会被这样的设计问题所困扰。

标签: c# persistence domain-driven-design repository-pattern


【解决方案1】:

您使用弱引用的提议有一个重大缺陷。

您可能知道,域实体的重要特征是它们必须具有标识。出于比较的原因,这很重要。如果两个实体具有相同的身份,无论它们的属性值如何,那么它们被认为是相等的:

Entity1 == Entity2 ⇔ Entity1.Identity == Entity2.Identity

典型的“设计模式”是从 DomainEntity<T> 抽象类继承所有实体,这会覆盖这些对象的比较并按身份进行比较。

现在,考虑一下您使用弱引用查找的方法。举个例子:

您从存储库中获取Entity1,例如“Reegan Layzell”用户。然后,您再次从存储库中获取与 Entity2 完全相同的“Reegan Layzell”实体。您现在在域中的两个对象中拥有相同的实体。但他们有不同的参考(当然)。

比较时,这些实体在您的域中不会被视为相同。

我很佩服您害怕将数据库关注点引入到您的域模型中,但是将数据库 ID 传播到您的实体中几乎不会影响模型的质量,并且会为您省去很多麻烦.就像你说的,我们需要务实。

关于您的Employee 示例:AssignName 真的有意义吗?在现实中,员工的名字真的可以在创建后改变吗?除此之外,看起来你有正确的想法。我强烈建议您观看:Crafting Wicked Domain Models Jimmy Bogard。

【讨论】:

  • 其实我已经意识到了这个问题,并通过在我的WeakDictionary 实现中实现了一系列私有类来解决它。其中之一是通过弱引用实体的Identity 覆盖 Equals/GetHashCode 方法来比较弱引用。我现在遇到的麻烦是当引用丢失时自动从字典中删除实体。我没有钩子做这样的事情..至少很容易。它仍在进行中,但在这里,看看pastebin.com/Vt73HJzk
  • 我已经测试了你提到的场景,一切正常,课程本身是一个 WIP,我使用计时器来进行自动清理(临时解决方案),顺便说一下很好的链接,我以前看过,但应该有更多这样的视频,涵盖 DDD
  • 我也意识到这些技术 ID 对于分解只需要引用特定实体而不是对其负责的大型聚合是必要的。例如,在我的一个项目中,我有一个 Appointment 聚合根,它需要引用 ClientEmployeeService,每个都是它们自己的聚合,而不是通过 Appointment it 加载和持久化它们简单地通过他们的 ID 存储对他们的引用是有意义的,因为这是域唯一需要的东西
  • 这是一个很好的观点 - 身份在域层中如此重要的另一个原因。没有身份,您无法引用聚合之外的其他实体!
猜你喜欢
  • 2020-05-18
  • 2012-12-11
  • 1970-01-01
  • 2020-02-21
  • 2018-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多