使用数据正确填充域对象?

Populating your domain objects with data correctly?

我无法理解如何正确设计我的域对象。我一直在努力解决的问题是如何用数据填充我的域对象。我发现的例子对于真正帮助我来说是微不足道的。我尝试了多种方法,但我都不喜欢其中任何一种。假设您有大量数据需要传递到 class 中,因此您将其捆绑在 POCO 中。

我的第一个方向(将数据传入构造函数):

public class MyClass
{
    private readonly ICalculator _calculator;
    private readonly MyClassDataPOCO _data;

    public MyClass(ICalculator _calculator, MyClassDataPOCO data)
    {
        this._calculator = _calculator;
        _data = data

这不是很好,因为你的 IOC 容器不能自动初始化你的 classes。

第二个方向(将数据传递到操作中):

public class MyClass
{
    private readonly ICalculator _calculator;

    public MyClass(ICalculator _calculator)
    {
        this._calculator = _calculator;
    }

    public decimal CalculateComplicatedValue1(MyClassDataPOCO data)
    {

    }

    public decimal CalculateComplicatedValue2(MyClassDataPOCO data)
    {

    }

出于各种原因我不喜欢这个

  1. 您的 class 只不过是实例函数(不是真正的 classes)。他们只有行为,没有数据。
  2. 您将数据委托给您的客户。似乎不是一个聪明的主意。我相信你最终会 运行 陷入变异状态问题。

Third Direction(只允许你class通过静态工厂方法创建):

public class MyClass
{
    private readonly ICalculator _calculator;
    private MyClassDataPOCO _data;

    private MyClass(ICalculator _calculator)
    {
        this._calculator = _calculator;
    }

    public static MyClass Create(MyClassDataPOCO data)
    {
        return Create(_container.GetInstance<ICalculator>(), data);
    }


    public static MyClass Create(ICalculator calculator, MyClassDataPOCO data)
    {
        //do some input validation here

        var myReturn = new MyClass(calculator);
        myReturn._data = data;
        return myReturn;
    }

我想在所有选项中我最喜欢这个。我唯一不喜欢的是必须有两个创建函数,所以它可以进行单元测试(所以我可以注入 ICalculator)。

我没有尝试的唯一选择是 属性 注入,因为 id 认为通过属性注入数据不是一个好主意。

您根据业务概念和用例设计域对象 (DO)。根据我的经验,这意味着您的对象应该非常纤细。 DO是基于概念定义来实现的。业务用例在服务中实现(可以是应用程序服务,可以是域服务,这取决于上下文),它将使用 DO 来更新内容。

当我设计一个对象时,我只是想我需要什么样的输入来实现什么样的行为。所有对象都应具有初始状态,因此您可以传递具有初始值的 DTO(一切都是 POCO,我们不关心这里的持久性)。其实每个方法都是一样的

关于持久性,因为我使用的是 CQRS,所以我只关心 save/get 一个对象。我个人更喜欢 json 对象(如果我不使用事件源),所以 save=serialize,get=deserialize。关于封装,您可以将 json 序列化程序配置为使用私有属性,基本上拥有私有属性是您做出的唯一妥协。

正如我之前所说,用例是作为服务实现的,因此在您的场景中,MyClass 实际上是服务,而不是 DO。作为经验法则,DO 包含 有助于定义对象的数据和行为。 CalculateComplicatedValue 看起来不像概念的一部分,但它看起来确实像一个用例,因此是一项服务。

这里不需要工厂,实例化 DO 通常很简单,但是服务通常由 DI 容器实例化,因为在大多数情况下服务确实使用其他服务(如存储库或验证器) .