小编Ben*_*n J的帖子

C#/ DDD:在使用洋葱架构时,如何使用域层不可实例化的内部状态对象的实体建模?

我正在将一个"大球泥"(BBOM)式系统迁移到基于域驱动设计思想的系统.

在重构的各种迭代之后,域聚合/实体当前使用内部状态对象建模,如本文中Vaughn Vernon所描述的,例如:https://vaughnvernon.co/?p = 889#comment-1896

所以基本上,实体可能看起来像这样:

public class Customer
{
    private readonly CustomerState state;

    public Customer(CustomerState state)
    {
        this.state = state;
    }

    public Customer()
    {
        this.state = new CustomerState();
    }

    public string CustomerName => this.state.CustomerName;

    [...]
}
Run Code Online (Sandbox Code Playgroud)

截至今天,该系统中的状态对象始终是来自当前使用的应用程序的专有数据访问框架的数据库表包装器,其类似于活动记录模式.因此,所有状态对象都从数据访问框架的基类部分继承.目前,无法将POCO用作状态对象,实体框架或其中任何一种.

该应用程序目前使用经典的层架构,其中基础结构(包括提到的表包装器/状态对象)位于底部,然后是域.域知道基础结构,并且使用基础结构在域中实现存储库.正如您在上面所看到的,大多数实体都包含一个公共构造函数,用于在域内方便地创建新实例,内部只是创建一个新的状态对象(因为域知道它).

现在,我们希望进一步发展这个并逐渐转变架构,从而产生更多的"洋葱"式架构.在该体系结构中,域只包含存储库接口,实际的实现将由位于其上的基础结构层提供.在这种情况下,域无法再知道实际的状态对象/数据库表包装器.

解决这个问题的一个想法是让状态对象实现域定义的接口,这实际上似乎是一个很好的解决方案.它在技术上也是可行的,因为即使状态对象必须从特殊的数据访问基类继承,它们也可以自由地实现接口.所以上面的例子将改为:

public class Customer
{
    private readonly ICustomerState state;

    public Customer(ICustomerState state)
    {
        this.state = state;
    }

    public Customer()
    {
        this.state= <<<-- what to do here??;
    }

    [...]
}
Run Code Online (Sandbox Code Playgroud)

因此,当存储库(现在在基础结构中实现)实例化一个新的Customer时,它可以轻松地传入实现ICustomerState的数据库包装器对象.到现在为止还挺好

但是,在域中创建新实体时,不再可能创建内部状态对象,因为我们不再知道它的实际实现.

有几种可能的解决方案,但它们似乎都没有吸引力:

  • 我们总是可以使用抽象工厂来创建新实体,然后这些工厂将由基础设施实施.虽然在某些情况下,由于实体的复杂性,域工厂是合适的,但我不希望在每种情况下都使用一个,因为它们会导致域中的大量混乱,并且还会传递另一个依赖项. .
  • 我们可以使用另一个类(POCO)来保存值,然后通过基础结构从/向数据库包装器进行转换,而不是直接使用数据库表包装器作为状态对象.这可能会起作用,但最终会产生大量额外的映射代码,并导致每个数据库表(DB包装器,状态对象,域实体)有3个或更多类,这使得维护变得复杂.如果可能的话,我们希望避免这种情况.
  • 为了避免在工厂周围传递,实体内部的构造函数可以调用一些类似于单一的魔术StateFactory.Instance.Create<TState>() …

c# architecture oop domain-driven-design onion-architecture

5
推荐指数
1
解决办法
845
查看次数