【问题标题】:Optimum Way To Restore Domain Object恢复域对象的最佳方法
【发布时间】:2012-04-19 22:07:33
【问题描述】:

这是一个如此简单和常见的场景,我想知道我是怎么做到的,为什么我现在遇到问题。

我有这个对象(基础设施程序集的一部分)

public class Queue {}

public class QueueItem
{
    public QueueItem(int blogId,string name,Type command,object data)
    {
        if (name == null) throw new ArgumentNullException("name");
        if (command == null) throw new ArgumentNullException("command");
        BlogId = blogId;
        CommandType = command;
        ParamValue = data;
        CommandName = name;
        AddedOn = DateTime.UtcNow;
    }


    public Guid Id { get; internal set; }
    public int BlogId { get; private set; }
    public string CommandName { get; set; }
    public Type CommandType { get; private set; }
    public object ParamValue { get; private set; }
    public DateTime AddedOn { get; private set; }
    public DateTime? ExecutedOn { get; private set; }
    public void ExecuteIn(ILifetimeScope ioc)
    {
        throw new NotImplementedException();
    }
}

这将在另一个这样的程序集中创建

 var qi = new QueueItem(1,"myname",typeof(MyCommand),null);

这里没有什么不寻常的。但是,该对象将被发送到将被持久化的存储库。Queue 对象将向存储库询问项目。存储库应重新创建 QueueItem 对象。

但是,如您所见,QueueItem 属性是不变的,AddedOn 属性应该只在创建项目时设置一次。 Id 属性将由 Queue 对象设置(这并不重要)。

问题是我应该如何在存储库中重新创建 QueueItem?我可以有另一个构造函数,它需要所有属性的每个值,但我不希望该构造函数可用于最初将创建队列项的程序集。存储库是不同程序集的一部分,因此内部无法工作。

我想过提供一个工厂方法 类队列项 { /* ..其余定义.. */

     public static QueueItem Restore(/* list of params*/){}
   }

这至少清除了意图,但我不知道为什么我不喜欢这种方法。我也可以仅通过 Queue 强制创建项目,但这意味着将 Queue 作为依赖项传递给 repo,这又不是我想要的。为此拥有一个特定的工厂对象,似乎也有点矫枉过正。

基本上我的问题是:在存储库中重新创建对象的最佳方式是什么,不将特定的创建功能公开给另一个消费者对象

更新

重要的是要注意,存储库是指模式本身作为抽象,而不是 ORM 的包装器。域对象的持久化方式或位置无关紧要。重要的是如何由存储库重新创建。另一个重要的事情是我的域模型 持久性模型不同。我确实使用了 RDBMS,但我认为这只是一个不应该具有任何重要性的实现细节,因为我正在寻找不依赖于特定存储访问的方法。

虽然这是一个特定场景,但它基本上可以应用于将由 repo 恢复的每个对象。

更新2

好吧,我不知道我怎么会忘记 AutoMapper。我的错误印象是它不能映射私有字段/setter,但它可以,我认为这是最好的解决方案。

事实上,我可以说最佳解决方案 (IMO) 是有序的:

  1. 如果可用,直接反序列化。
  2. 自动映射。
  3. 域对象本身的工厂方法。

前两个不要求对象特别做任何事情,而第三个要求对象为这种情况提供功能(一种输入有效状态数据的方法)。它有明确的意图,但它几乎可以完成映射工作。

回答 更新

回答我自己,在这种情况下,最佳方法是使用工厂方法。最初我选择了 Automapper,但我发现自己更频繁地使用工厂方法。 Automapper 有时很有用,但在很多情况下还不够。

【问题讨论】:

  • 有点离题,但如果这是您“域”的一部分,您可能需要考虑使用 NServiceBus 或 MassTransit 之类的工具。

标签: oop domain-driven-design factory-pattern


【解决方案1】:

您谈到了对象本身的工厂方法。但是 DDD 声明实体应该由工厂创建。所以你应该有一个 QueueItemFactory 可以创建新的 QueueItems 并恢复现有的 QueueItems。

好吧,我不知道我怎么会忘记 AutoMapper。

我希望我能忘记 AutoMapper。光是看着丑陋的 API 就让我脊背发凉。

【讨论】:

  • 在这种情况下,工厂是创建实体的好选择。但在这里我想恢复,这是存储库的责任。我不认为工厂应该恢复实体。有几种方法可以进行恢复,目前我更喜欢实体本身的静态工厂方法。
  • 实际上,存储库将工作委托给工厂(如果要恢复对象,您也需要一个实例)。如果您要进行 CQRS,存储库可能会将工作委托给在该对象上重放域事件的东西。存储库只是一个用于保存聚合根列表的集合。在您的情况下,存储库将有几个不同的职责(即更改的原因)。话虽如此,大多数人已经在使用 NHibernate。这使得不需要存储库
  • 我不同意。回购可能会将工作委托给工厂,但我不明白为什么工厂应该处理恢复,这是一个持久性概念。在给定状态或事件流的情况下,最多是对象(或构造函数中的事件)上的工厂方法,它将重新创建对象。存储库被视为一个集合,它们是一个外观,它们不仅仅是一个简单的列表。 ORM 是 DAL 的实现细节,可由存储库使用,仅此而已。 NH 处理持久性模型,存储库返回域对象。不同的职责。
  • 工厂的作用是代替构造函数调用。它与它是否已经持久化无关。我不同意你关于 ORM 是一个实现细节的说法。它不是一个轻量级库,而是一个非常重的库,会对您的应用程序产生很多影响。你应该做任何对你和你的团队最有意义的事情。就我个人而言,我认为无论如何 DDD 被高估了。
【解决方案2】:

ORM 框架会为您解决这个问题。您只需要告诉它重新水化一个对象,然后域类的常规实例就会提供给您(有时您只需将属性声明为虚拟或受保护,例如在 NHibernate 中)。原因是因为在底层,它们通常对派生自您的基类的代理对象进行操作,从而使您可以保持这些基类的完整性。

如果您想实现自己的持久层,那就另当别论了。在不破坏对象中最初定义的范围约束的情况下从数据库中重新水化对象可能涉及反射。您还必须考虑很多附带问题:如果您的对象引用了另一个对象,则必须先重新水化该对象,等等。

你可以看看那个教程:Build Your Own dataAccess Layer 虽然我不建议在大多数情况下重新发明轮子。

【讨论】:

  • 该问题与数据库无关。当我说存储库时,我的意思是它完全是一种抽象。即使使用 ORM,我仍然必须将 orm 实体映射到域实体。我已经用这个澄清更新了我最初的问题
  • 那我猜你“只有”有这些选项: internal 关键字 + [InternalsVisibleTo(YourReposAssembly)] or Reflection or Restore() method 或 YourReposAssembly 中的 QueueItem 子类...
  • 感谢您的建议,但我想我找到了最适合我的方法。我更新了问题。
猜你喜欢
  • 1970-01-01
  • 2014-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
相关资源
最近更新 更多