DDD和持久性。再次

时间:2015-06-29 05:40:07

标签: c# .net domain-driven-design persistence

我正在努力坚持领域驱动设计。据我所知,域模型永远不应该是持久感知的。让我们说我正在构建一个简单的待办事项列表应用程序。我有一个以下界面的任务:

var originalString = new Buffer("SGVsbG8gV29ybGQ=", 'base64').toString('utf-8')

通用实现应如下所示:

interface ITask
{
   bool IsCompleted {get;}
   string Description {get;}

   void Complete();
   void ChangeDescription(string description);
}

我希望描述是必要的 - 因为它是一个商业规则。所以从这一刻起,如果我想通过序列化器保存这个对象,我将失败,因为没有提供无参数构造函数。我不应该提供它,因为没有持久感知规则。如果我以DTO \ POCO的形式模拟我的任务,我将最终遇到另一个问题 - 所谓的贫血模型。此外,我不想为某些房产提供制定者。

那么所有这些的解决方案在哪里?我可以创建一个紧密耦合的保护程序,它将知道如何保存和恢复任务状态。但我只能访问公共属性和方法,如果任务的内部逻辑复杂且无法保存\还原会怎么样?我应该在任务内部标记所有字段并且有可能保存对象的内部状态吗?难道有点代码嗅觉和违反没有持久性意识的规则吗?

你是如何解决这个问题的?

5 个答案:

答案 0 :(得分:5)

根据我的理解,Entity Framework的灵活性远低于Hibernate,因此您必须在模型中做出更多的妥协。 Vaughn Vernon,实现域驱动设计(IDDD)shows a great way的作者,保留自封装实体,同时使用Entity Framework轻松保持其状态。

如果你可以使用你选择的持久性存储,你也可以使用一个不会导致阻抗不匹配的different strategy

答案 1 :(得分:1)

如果您查看DDD作者(http://dddsample.sourceforge.net/)的示例项目,您可以看到实体有一个私有空参数构造函数,作为Hibernate要求的一个变通方法,用于持久化对象。
因此,我认为,当由于语言或基础设施的技术限制而导致模型对象存在“肮脏”的程度时。 无论如何,我建议你通过一些工厂方法(可能是聚合根)公开ToDo实体的创建,这样你就可以强制执行对象创建的业务规则。这样,您就可以为客户提供一种约定,以便以正确的方式操作您的模型 您无法避免客户端创建无效的状态模型(考虑反射,字节代码检测等),因此真正重要的是设计正确的方法来使用您的模型并指导客户端。

答案 2 :(得分:1)

就个人而言,我使用洋葱/六角形/端口&适配器样式体系结构,我的域由引用域模型层的应用程序层组成。我通常最终会在应用程序层中使用大部分特定于域的持久性。我从应用程序层执行大部分持久层操作。我在应用程序层中的类和方法以业务功能,流程和工作流命名,它们处理实体的获取和保存。

要回答问题的主要部分,我发现使用DDD非常难以使用ORM(特别是代码优先)。像Entity Framework这样的域实体与DDD中的实体非常不同,它们只是共享相同的单词。有DDD和EF世界的支持者会提倡制定程序,让这两件事情协同工作。就个人而言,延迟加载和导航属性的所谓ORM好处足以让我将我的ORM隐藏在我的存储库后面。即存储库接受并返回域实体,存储库方法中发生的事情通常是与ORM生成的实体之间的一些映射。出于这个原因,我倾向于首先使用数据库生成的实体而不是代码,因为我没有从我的ORM获得所谓的良好。

答案 3 :(得分:1)

我相信已经存在的实体的状态不应该在补液时经历相同的不变执行,就好像它是一个你试图达到的新状态。否则,当您的不变代码发生变化时,数据库中的实体可能不再有效,如果您不采取补偿措施(填写默认值等),您可能会失去很多历史记录。

具体而言,这意味着:

  • 您的ORM可以完全通过无参数构造函数直接访问实体的状态(它怎么能知道传递给带有参数的构造函数?)和setter。虽然它确实会对您的实体产生影响,但您可以将其最小化,例如使构造函数受到保护,将setter设为私有possible in Entity Framework,并且我不会将其称为“#34;持久性感知&#34 ;本身。

或者,

  • 您必须从主Entity对象中分离持久状态。有关经典方法,请参阅plalx和Adrian的答案。

Event Sourcing以优雅的方式解决了这个问题。国家重组的概念在实体本身中根深蒂固,因为它知道如何处理它所发生的各种事件。仍然不认为持久性意识 - 在重新水化实体时事件是重放,但这会触发与第一次对实体进行时的逻辑。< / p>

答案 4 :(得分:1)

不幸的是,您的域中总会存在一些与持久性相关的行为。你只需要决定多少:)

我不喜欢ORM,有些人要求对域类进行过多的处理,例如标记方法虚拟或恐怖恐怖,属性。

您的域对象具有行为,并且形状。您想要检索形状部分(状态)并持久化 。在加载对象时,您希望将该状态返回到对象以“保湿”。这是偶然的模式。

事件采购(ES)也是如此。事件表示对象中的某些状态更改。加载事件会在内部更改状态以保持一致。

在ES中,当有很多事件时,快照用于提高性能。 快照表示特定时间点的状态,并且还应用该时间点之后的所有事件。 快照也遵循了时刻模式。

因此,归结为 您希望公开对象的状态。