ddd状态模式验证通知

时间:2017-10-12 03:14:47

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

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

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

为验证我使用通知模式。

我遵循“始终有效的实体”方法,因此在每次操作中我首先调用“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() );
        }

    }

2 个答案:

答案 0 :(得分: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())并将收集的错误报告给调用者。

答案 1 :(得分:0)

我会将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来处理错误,但在这种情况下,您可以重新创建并创建一个,或者您可以抛出表示聚合消息的异常。

希望它有所帮助。