最佳的恢复领域对象的方法

3

这是一个非常简单和常见的场景,我想知道为什么我之前能够处理好它,现在却遇到问题。

我有这个对象(Infrastructure程序集的一部分)

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?我可以有另一个构造函数,它需要所有属性的每个值,但我不希望该构造函数对最初创建队列项的程序集可用。存储库是不同程序集的一部分,因此内部无法工作。

我考虑提供一个工厂方法: class QueueItem { /* ..其余定义.. */

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

至少这种方法清楚了意图,但我不知道为什么我不喜欢这种方法。我也可以只通过队列强制执行项的创建,但这意味着将队列作为依赖项传递给存储库,这也不是我想要的。为此专门创建一个工厂对象似乎也过于复杂。

基本上我的问题是:在不向另一个使用者对象公开特定创建功能的情况下,重新创建存储库中的对象的最佳方法是什么

更新

重要的是要注意,我所说的存储库是指模式本身作为抽象,而不是ORM的包装器。域对象保存在何处或如何保存并不重要。重要的是存储库如何重新创建它们。另一件重要的事情是,我的领域模型持久性模型是不同的。我确实使用关系数据库管理系统,但我认为这只是一个不应承担任何重要性的实现细节,因为我正在寻找不依赖于特定存储访问的方式。

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

更新2

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

事实上,我可以说最佳解决方案(在我看来)按顺序为:

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

前两个不需要对象做任何特定的事情,而第三个则需要对象提供该情况的功能(输入有效状态数据的方法)。它具有明确的意图,但它基本上只执行了映射器的工作。

答案已更新

回答自己,在这种情况下,最佳方法是使用工厂方法。最初我选择了Automapper,但我发现自己更经常使用工厂方法。Automapper有时可能很有用,但在许多情况下并不足够。


有点离题,但如果这是你的“领域”的一部分,你可能想考虑使用像NServiceBus或MassTransit这样的工具。 - Josh Kodroff
2个回答

0

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

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

我希望我能忘记AutoMapper。只是看着那可怕的API就让我毛骨悚然。


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

0

一个ORM框架会为您处理这个问题。您只需要告诉它重新生成一个对象,领域类的常规实例就会提供给您(有时您只需要将属性声明为虚拟或受保护的,在NHibernate中)。原因是因为在幕后,它们通常操作从基类派生的代理对象,使您可以保持这些基类不变。

如果您想要实现自己的持久化层,那就是完全不同的故事了。从数据库重新生成对象而不破坏最初定义在对象中的范围约束可能涉及反射。您还必须考虑许多副作用:如果您的对象引用另一个对象,则必须先重新生成该对象等。

您可以查看该教程:构建自己的数据访问层,尽管在大多数情况下我不建议重复造轮子。


这个问题与数据库无关。当我说仓储库(repository)时,我的意思是它只是一个抽象概念。即使使用ORM,我仍然需要将ORM实体映射到领域实体。我已经在我的初始问题中更新了这一澄清。 - MikeSW
那么我猜你“只有”以下这些选项:使用 internal 关键字 + [InternalsVisibleTo(YourReposAssembly)] 反射 Restore() 方法 在 YourReposAssembly 中子类化 QueueItem... - guillaume31
谢谢您的建议,但我认为我已经找到了最适合我的方法。我已经更新了问题。 - MikeSW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接