【问题标题】:ddd state pattern validation notificationddd 状态模式验证通知
【发布时间】:2017-10-12 03:14:46
【问题描述】:

在我的 DDD 项目中,我正在尝试使用 java 枚举来实现状态模式。

在验证行为取决于状态的实体方法时遇到问题。

为了验证,我使用通知模式。

我遵循“始终有效的实体”方法,因此在每个操作中我首先调用“isValidForOperation”验证方法。

这里是代码,只是为了简单起见:

实体:

public class Task extends AggregateRoot<TaskId> {

    ...
    private State state;
    ...


    // Operation with behaviour depending on the state
    // It's a transition from "ASSIGNED" state to "IN_PROGRESS" state
    // I apply the state pattern here

    public void start () {
        State next = this.state.start ( this );
        this.setState ( next );
    }

    ...
}

对状态建模的java枚举:

public enum State {

        ASSIGNED {

            public State start ( Task task ) {

                // Validation method to ensure the operation can be done
                assertTaskIsValidForStart ( task );

                // Business logic
                ...

                // Return the next state
                return ( State.IN_PROGRESS );
            }
        }
        ...
        // more enum values for other states
        ...


        // Default implementation of "start" operation
        // It will be executed when the current state is not "ASSIGNED"
        // So an error would be generated

        public State start ( Task task ) {

            // I can't apply notification pattern here !!!
            // I would have to throw an exception

        }

}

验证方法遵循通知模式。 它收集通知对象中所有可能的错误。 此通知对象被传递给异常。 异常被抛出,然后应用层捕获它并将所有错误消息返回给客户端。

public void assertTaskIsValidForStart ( Task task ) {

        Notification notification = new Notification();
        if ( errorCondition (task) ) {
            notification.addError(...);
        }
        ...
        // more errors
        ...
        if ( notification.hasErrors() ) {
            throw new TaskNotValidForStartException ( notification.errors() );
        }

    }

当错误条件与状态之间的无效转换有关时,如何应用通知模式(与状态模式结合)?

有什么想法吗?

更新:

我找到了解决方案。我将依赖于状态的整个操作放在实体中,并将​​状态模式更细粒度地应用到所需的代码中。这样我就可以应用模式来计算下一个状态,这样我就可以检查是否允许转换并应用通知模式。

代码:

    public class Task extends AggregateRoot<TaskId> {

        ...
        private State state;
        ...


        // Operation with behaviour depending on the state
        // It's a transition from "ASSIGNED" state to "IN_PROGRESS" state
        // I apply fine-grained state pattern here

        public void start () {

            // Validation method to ensure the operation can be done
            // One of the validations will be if the transition is allowed

            assertTaskIsValidForStart ( this );


            // Business logic
            // If it depends on the state, I would apply state pattern delegating to another method
            ...


            // Set the next state

            State next = this.nextStateForStart();
            this.setState ( next );
        }

        ...

        public State currentState() {
            return this.state;
        }
        ...

        public State nextStateForStart() {
            return this.currentState().nextStateForStart();
        }

        ...
    }



public enum State {

        ASSIGNED {
            public State nextStateForstart() {
                return ( State.IN_PROGRESS );
            }
        }
        ...
        // more enum values for other states
        ...


        // Default implementation of "start" transition
        // It will be executed when the current state is not "ASSIGNED"

        public State nextStateForstart() {
            return null;
        }

}



public void assertTaskIsValidForStart ( Task task ) {

        Notification notification = new Notification();

        // Validate the transition is allowed

        if ( task.nextStateForStart() == null ) {
            notification.addError(...);
        }

        ...
        // more errors
        ...

        if ( notification.hasErrors() ) {
            throw new TaskNotValidForStartException ( notification.errors() );
        }

    }

【问题讨论】:

    标签: validation design-patterns notifications domain-driven-design state-pattern


    【解决方案1】:

    我认为你的枚举做得太多了。 除了拥有一组难以扩展的固定状态外,您还很难为每个具体状态引入任何形式的契约,这也将解决您的通知问题。

    引入一个抽象状态类,它是所有具体状态的基类。传递一个允许为每个状态设置后继状态的上下文。此上下文可以由您的聚合根实现。 您的通知可以由每个州以您使用 AbstracftState 强制执行的方式进行管理,例如通过强制状态执行返回一个通知对象:

    interface StateContext {
       setState(AbstractState state);
    }
    
    class AbstractState {
        abstract Notification execute(StateContext context);
    }
    
    class Task extends AggregateRoot implements StateContext {
        AbstractState currentState;
    
        ....
    
        public void start() {
            Notification n = currentState.execute(this);
            if (n.hasErrors()) {
                throw new Exception(n.toErrorReport());
            }
        }
    }
    

    现在您可以在执行之前或之后收集每个状态的错误(您可能希望在执行之前调用的每个 AbstractState 中引入 validateStart())并将收集到的错误报告给调用者。

    【讨论】:

    • 你好@mbnx。你在哪里返回下一个状态? “执行”方法的实现不能,因为它返回通知。
    • 可以使用传递给execute方法的StateContext接口设置状态。
    【解决方案2】:

    我会将 TaskWorkflow 建模为 Task 聚合中的 VO。

    class Task {
    
        private Workflow workflow;
    
        public void start() {
            workflow = workflow.followWith(Action.START, this);
        }
    
        public State currentState() {
            return workflow.state();
        }
    
        public List availableActions() {
            return workflow.nextActions();
        }
    
    }
    

    工作流是一个 FSM,由动作连接的状态之间的转换组成。对工作流方法的任何调用都会创建一个指向新状态的新工作流表示。转换可以建模为直接或更复杂的自定义,如您所说的涉及业务逻辑。 如果您使用函数式语言,您可以返回一个 Monad 来处理错误,但在这种情况下,您可以具体化并创建一个,或者您可以只抛出一个表示聚合消息的异常。

    希望对您有所帮助。

    【讨论】:

    • 我猜你在工作流对象中实现了状态模式,不是吗?方法 followWith 是你实现转换的地方?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 2014-04-06
    • 2012-03-28
    • 1970-01-01
    相关资源
    最近更新 更多