3

我见过很多这样的问题,但它们似乎总是与我的想法不符。我认为这是由于我对聚合-聚合根-实体-值对象之间的关系的理解草率。

完成的解决方案对我来说就像 DTO,因为一切(所有逻辑)似乎最终都在存储库中。也许我一直在看 EF 教程太多。

假设我们有一个非常简单的类,带有第一个版本的 repo(让我们忽略它只处理一个人):

class Person 
{
    int Age;
    void MakeOlder() { Age++; }
}
interface IPersonRepository
{
    Person GetAPerson();
}

现在,我想从 UI 中(例如,按下按钮)做的不是

person.MakeOlder();
_repo.Save(person);

但也许只是:

person.MakeOlder();

对我来说,“MakeOlder”的动作应该触发保存。但这当然需要在 Person 中引用 repo。我能想到的唯一选择是:

_repo.MakePersonOlder(person);

(看起来很糟糕。)

person.MakeOlder(_repo);

(UI pov 没有任何好处,这就是我现在正在查看的内容)

class Person : IMyEntityBaseType { ...
    void MakeOlder() { 
        Age++; 
        EntityDataWasChangedNowIWantToBeSaved(); 
    }
}

或者这个的一些变体;事件、AOP 等。以某种方式发出或捕获应该完成保存的信号。

我想我也可能将我对 DDD 的看法与事件溯源和类似概念混为一谈。

我认为SavePersonUI 代码中的调用感觉很脏,这是完全错误的吗?

什么是“正确”的做法?

4

2 回答 2

4

您不应该将存储库传递给 Person。使用该模型,您必须将存储库传递给每个方法。通常,如果使用 EF 或其他一些 ORM,则有一个内置的工作单元概念。一个工作单元跟踪在定义的交互中发生变化的所有对象。当您提交一个工作单元时,它会提交其中的所有更改。代码看起来更像:

person.MakeOlder();
_unitOfWork.Commit();

当多个实体发生更改时,可以使用此方法。由于 ORM 会跟踪更改,因此您不必显式保存作为工作单元一部分的每个对象。

一个更好的选择,也解决了您的问题,是用应用程序服务封装用例。应用程序服务将有一个方法,例如:

public void MakeOlder(int personId)
{
   var person = this.personRepository.Get(personId);
   person.MakeOlder();
   this.unitOfWork.Commit();
}

然后 UI 将直接调用应用程序服务而不是域对象。

于 2013-05-21T17:23:43.427 回答
0

不能同意更多。您缺少 ApplicationService 概念。应用层非常重要。

我只是不同意你的观点:

完成的解决方案对我来说就像 DTO,因为一切(所有逻辑)似乎最终都在存储库中。

这是因为您需要更好地理解图层。表示层 (UI) 将调用 na ApplicationService (Façade)。建议的 MakePersonOlder(int personId)。

应用层负责协调动作:)

其中一项操作可能是保存到存储库、登录到文本文件、发送电子邮件等。

明白了吗?

布鲁诺

于 2013-05-21T22:00:54.250 回答