Уведомление о проверке шаблона состояния DDD

В моем проекте 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() );
        }

    }

person choquero70    schedule 12.10.2017    source источник


Ответы (2)


Я думаю, что ваше перечисление делает слишком много. Помимо наличия фиксированного набора состояний, который вряд ли можно расширить, вы затрудняете введение какой-либо формы контракта для каждого конкретного состояния, что также решило бы вашу проблему с уведомлением.

Введите абстрактный класс состояний, который является базовым классом для всех конкретных состояний. Передается контекст, который позволяет установить состояние преемника для каждого состояния. Этот контекст может быть реализован вашим совокупным корнем. Каждое состояние может управлять вашим уведомлением так, как вы применяете, используя 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());
        }
    }
}

Теперь вы можете собирать ошибки каждого состояния до или после выполнения (вы можете ввести validateStart() внутри каждого AbstractState, которое вызывается перед выполнением) и сообщать о собранных ошибках вызывающей стороне.

person mbnx    schedule 12.10.2017
comment
Привет @mbnx. Куда вы возвращаете следующее состояние? Реализация метода execute не может, так как возвращает уведомление. - person choquero70; 12.10.2017
comment
Состояние можно установить с помощью интерфейса StateContext, который передается методу execute. - person mbnx; 13.10.2017

Я бы смоделировал TaskWorkflow как VO внутри агрегата Task.

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, состоящий из переходов между состояниями, к которым присоединяются действия. Любой вызов метода рабочего процесса создает новое представление рабочего процесса, указывающее на новое состояние. Переходы могут быть смоделированы как прямые или более сложные пользовательские, включающие бизнес-логику, как вы говорите. Если вы используете функциональный язык, вы можете вернуть монаду для обработки ошибок, но в этом случае вы можете овеществить и создать ее или просто создать исключение, представляющее агрегированные сообщения.

Надеюсь, поможет.

person Sebastian Oliveri    schedule 13.10.2017
comment
Я думаю, вы реализуете шаблон состояния в объекте рабочего процесса, не так ли? В методе followWith вы реализуете переход? - person choquero70; 13.10.2017