Domain driven design 使用DDD实现用户定义的业务规则

Domain driven design 使用DDD实现用户定义的业务规则,domain-driven-design,rule-engine,ddd-repositories,ddd-service,Domain Driven Design,Rule Engine,Ddd Repositories,Ddd Service,假设我有一个应用程序,让用户创建业务规则,应用于域实体。规则可以是一个条件和多个操作的组合,如果条件的计算结果为true,则执行相应的操作。此规则由用户以自由格式文本格式创建,然后转换为规则引擎可以理解和执行的专有格式 例如,对于员工管理系统,如果有业务规则来检查员工是否在当前职位工作超过一年,并且表现优于预期,则可以通过10%的加薪提升到下一个职位。用户可以按如下方式输入此业务规则 public class Rule : Entity { public Condition Condit

假设我有一个应用程序,让用户创建业务规则,应用于域实体。规则可以是一个条件和多个操作的组合,如果条件的计算结果为true,则执行相应的操作。此规则由用户以自由格式文本格式创建,然后转换为规则引擎可以理解和执行的专有格式

例如,对于员工管理系统,如果有业务规则来检查员工是否在当前职位工作超过一年,并且表现优于预期,则可以通过10%的加薪提升到下一个职位。用户可以按如下方式输入此业务规则

public class Rule : Entity
{
    public Condition Condition { get; private set; }
    public IList<Action> Actions { get; private set;}

    public Rule(Condition condition, IList<Action> actions)
    {
        Condition = condition;
        Actions = actions;
    }
}

public sealed class Condition : ValueObject<Condition>
{
    public string ConditionText { get; private set;}
    public ExecutableScript ExecutableCondition{ get; private set;}

    public Condition(string conditionText)
    {
        ConditionText = conditionText;            
    }     

    public Parse()
    {
        ExecutableCondition = // How to parse using external rule engine ??;            
    }

    public Execute()
    {
        // How to execute using external rule engine ??;            
    }
}      

public sealed class Action : ValueObject<Action>
{
    public string ActionText{ get; private set;}
    public ExecutableScript ExecutableAction{ get; private set;}

    public Action(string actionText)
    {
        ActionText = actionText;            
    }

    public Parse()
    {
        ExecutableAction = // How to parse using external rule engine ??;            
    }

    public Execute()
    {
        // How to execute using external rule engine ??;            
    }
}
条件:Employee.CurrentRoleLength>1和Employee.ExcepedExpections()
操作:Employee.PromoteToNextRole()|Employee.GiveSalaryIncrement(10)

请注意,多个操作用|分隔。同样为了执行此规则,应用程序使用单独的规则引擎类库将此条件和两个操作解析为专有格式,例如,ExecutableScript也在规则引擎类库中定义

现在,为了使用DDD对该需求建模;我提出了以下域对象

规则(实体)
条件(值对象)
动作(值对象)

其中,Rule是一个实体,它包含一个条件值对象和一个操作值对象列表,如下所示

public class Rule : Entity
{
    public Condition Condition { get; private set; }
    public IList<Action> Actions { get; private set;}

    public Rule(Condition condition, IList<Action> actions)
    {
        Condition = condition;
        Actions = actions;
    }
}

public sealed class Condition : ValueObject<Condition>
{
    public string ConditionText { get; private set;}
    public ExecutableScript ExecutableCondition{ get; private set;}

    public Condition(string conditionText)
    {
        ConditionText = conditionText;            
    }     

    public Parse()
    {
        ExecutableCondition = // How to parse using external rule engine ??;            
    }

    public Execute()
    {
        // How to execute using external rule engine ??;            
    }
}      

public sealed class Action : ValueObject<Action>
{
    public string ActionText{ get; private set;}
    public ExecutableScript ExecutableAction{ get; private set;}

    public Action(string actionText)
    {
        ActionText = actionText;            
    }

    public Parse()
    {
        ExecutableAction = // How to parse using external rule engine ??;            
    }

    public Execute()
    {
        // How to execute using external rule engine ??;            
    }
}
公共类规则:实体
{
公共条件条件{get;private set;}
公共IList操作{get;private set;}
公共规则(条件、IList操作)
{
条件=条件;
行动=行动;
}
}
公共密封类条件:ValueObject
{
公共字符串条件文本{get;private set;}
public ExecutableScript ExecutableCondition{get;private set;}
公共条件(字符串条件文本)
{
条件文本=条件文本;
}     
公共解析()
{
ExecutableCondition=//如何使用外部规则引擎进行解析??;
}
公共执行()
{
//如何使用外部规则引擎执行??;
}
}      
公共密封集体诉讼:ValueObject
{
公共字符串ActionText{get;private set;}
public ExecutableScript ExecutableAction{get;private set;}
公共操作(字符串actionText)
{
ActionText=ActionText;
}
公共解析()
{
ExecutableAction=//如何使用外部规则引擎进行解析??;
}
公共执行()
{
//如何使用外部规则引擎执行??;
}
}
基于上述领域模型,我有以下问题

  • 如何在不依赖外部规则引擎的情况下解析和执行条件和操作。我理解域层不应该依赖于外层,而应该局限于它自己

  • 即使我在它们的域对象之外解析条件和操作,它们解析的ExcutableScript值仍然需要存在于它们内部,这仍然需要依赖于外部规则引擎

  • 只是DDD不是这种情况下的正确方法,我走错了方向

  • 抱歉发了这么长的邮件。任何帮助都将不胜感激

    谢谢

    如何在不依赖外部规则引擎的情况下解析和执行条件和操作。我理解域层不应该依赖于外层,而应该局限于它自己

    这一部分很简单:依赖项反转。域定义了一个服务提供者接口,该接口描述了它希望如何与某个外部服务进行通信。通常,域会将其某些内部状态的副本传递给服务,并返回一个可以应用于自身的答案

    因此,您可能会在模型中看到类似的内容

    Supervisor.reviewSubordinates(EvaluationService es) {
        for ( Employee e : this.subbordinates ) {
            // Note: state is an immutable value type; you can't
            // change the employee entity by mutating the state.
            Employee.State currentState = e.currentState;
    
    
            Actions<Employee.State> actions = es.evaluate(currentState);            
            for (Action<Employee.State> a : actions ) {
                currentState = a.apply(currentState);
            }
    
            // replacing the state of the entity does change the
            // entity, but notice that the model didn't delegate that.
            e.currentState = currentState;
        }
    }
    
    Supervisor.reviews子坐标(评估服务){
    适用于(员工e:此为下级){
    //注意:state是不可变的值类型;不能
    //通过更改状态来更改员工实体。
    Employee.State currentState=e.currentState;
    动作动作=评估(当前状态);
    针对(行动a:行动){
    currentState=a.apply(当前状态);
    }
    //替换实体的状态会更改
    //实体,但请注意,模型并没有委托该实体。
    e、 currentState=currentState;
    }
    }
    
    技术领域可能受益于DDD战术模式,但创建正确抽象的成本通常高于其他领域,因为它通常需要抽象出复杂的数据结构

    开始思考所需抽象的一个好方法是问问自己,如果要交换底层技术,需要哪些抽象

    这里有一个复杂的基于文本的表达式,规则引擎根据该表达式创建了一个
    ExecutableScript

    如果你想一想,这里有三个主要元素:

  • 专有的基于文本的表达式语法
  • 专有的
    可执行脚本
    ;我假设这是一个带有嵌入式解释器的抽象语法树(AST)
  • 规则评估上下文可能是专有的
  • 如果您要交换底层技术来执行规则,那么另一个规则引擎的表达式语法可能会有所不同,并且它肯定具有完全不同的规则解释机制

    在这一点上,我们已经确定了什么是必须抽象的,而不是什么是正确的抽象

    您可以决定实施自己的express