简化DDD实体类状态转换

时间:2018-09-09 15:24:55

标签: c# asp.net-core domain-driven-design

我第一次尝试以DDD方式开发应用程序。

我有一个实体类,该实体类可以具有多个状态,并且要遵循一些规则才能从一种状态过渡到另一种状态。我有一本字典,其中键是实体可以转换为的状态,值是状态可以从其转换的状态:

protected static readonly IDictionary<State, State[]> aGoodName = new Dictionary<State, State[]>
{
    { State.Approved, new State[] { State.Requested } },
    { State.Standardized, new State[] { State.Approved } },
    { State.Queued, new State[] { State.Standardized, State.Succeeded, State.Failed } },
    { State.Running, new State[] { State.Queued } },
    { State.Succeeded, new State[] { State.Running } },
    { State.Failed, new State[] { State.Running } },
    { State.Completed, new State[] { State.Succeeded } },
    { State.Canceled, new State[] { State.Requested, State.Approved, State.Standardized, State.Succeeded, State.Failed, State.Queued } }
};

我有一个通用方法,该方法具有所需状态作为参数,并检查它是否可以从以下方法过渡:

public void Transition(State state)
{
    if (!aGoodName[state].Contains(State))
    {
        throw new ArgumentException($"Cannot change state from {State.ToString()} to {state.ToString()}.");
    }

    State = state;
}

但是,如果我理解正确的话,DDD方法就是用开发人员和业务人员都可以理解的语言来表达域代码,例如,拥有一种方法就更有意义了,让我们继续说: / p>

public void Succeed()
{
    Transition(State.Succeeded);
}

甚至:

public void Succeed()
{
    var states = new List<State> { State.Running };

    if (!states.Contains(State))
    {
        throw new ArgumentException($"Cannot change state from {State.ToString()} to {State.Succeeded.ToString()}.");
    }

    State = State.Succeeded;
}

我对这种设计模式还很陌生,想知道上面哪种方法最适合DDD。

1 个答案:

答案 0 :(得分:1)

我认为您的问题的答案首先取决于另一个问题:

  • 这些状态在您的域中如何重要?

在这里重要的是在设计中考虑领域文献(又称无处不在的语言)。但是实施细节实际上取决于问题的重要性。他们甚至可以是独立的实体!

entity.To(new ApprovedState());

如果状态转换只是一个标记数据,那么您在此处提供的第一个实现可能就足够了,但是如果它更重要并且周围有更多的业务规则,则可以使用第二个实现或状态或策略之类的模式

interface IState{...}

class Approved : IState {...}

class Requested : IState {...}

class Entity{
   public IState State {get; set;}
}

最后,您可以提供一薄层的fluent API,以更精细地表达您的域(当然,您也可以以这种方式设计它……):

TheEntity.IfItsPossible().Approve();

更新

这里还有一件事。有时我们向实体中添加字段,就像对数据库中的表一样。这就是db world中的工作方式!但是我认为在DDD中完全不同。也许这些不同的状态实际上扮演着不同实体或隐藏的业务规则的角色,需要深入研究。假设 Request 不是Reload实体的状态,但它是人们在需要Reload时自行创建的实体。就像在创建订单时需要购买产品一样。

public class Reload
{
}

public class Request
{
    public string User { get; set; }
    public DateTime Time { get; set; }
    // and other logics about requests
}

public interface IFactory
{
    Reload Create(Request request);
}

如果这确实是域中发生的事情,那么这些状态不过是其他实体和域服务内部工作的结果。例如,我们称​​ Standardized 状态为正在队列中等待处理的Reload,您可以从应用程序的模块中查询以下信息:

public interface IQueueService
{
    void Push(Reload reload);
}


public IEnumerable<Reload> GetStandardizedReloads()
{
    return _queueService.Items();  
}

public IEnumerable<Request> GetRequests()
{
    return _requestRepository.GetAll();
}