Я работаю над проектом, в котором мы используем конечный автомат для реализации рабочего процесса. У меня возникают некоторые проблемы с нагреванием того, что было сделано, и я хотел бы посмотреть, может ли быть лучший дизайн / реализация для моей проблемы.
Я постараюсь показать, что у нас есть на данный момент.
Пожалуйста, игнорируйте process_agent
на данный момент, я хотел бы сосредоточиться на process_state
только для начала. Я просто хочу создать процесс, и конечный автомат должен немедленно перейти из CREATED в ASSIGNED и сохранить это состояние в таблице Entity (по умолчанию я просто установил бы текущего пользователя в качестве агента на данный момент).
Существует таблица Entity с двумя данными: process_agent
и process_state
На данный момент существует только три State
s, определяемые как Enums: CREATED
, ASSIGNED
и IN_PROCESS
В настоящий момент существует только два Event
с, определяемых как Enums: ASSIGN_TO_AGENT
и START_PROCESS
В контроллере есть конечная точка для создания процесса, который просто передаетсяслужба:
// In the Controller
// mapper is a MapStruct mapper, it simply copies fields from view to entity and vice versa
ResponseEntity<EntityView> create(@RequestBody final EntityView view) {
final Entity createdEntity = service.create(entityView);
final EntityView createdEntityView = mapper.toView(createdEntity); //map the entity to its view
return status(CREATED).body(createdEntityView);
}
// In the Service
// mapper is a MapStruct mapper, it simply copies fields from view to entity and vice versa
// stateHandler is a custom class to handle an event, see below
Entity entity = new Entity();
mapper.updateFromView(entityView, entity);
entity.setInitState(CREATED);
final Message<Event> message = MessageBuilder.withPayload(Event.ASSIGN_TO_AGENT).setHeader("ENTITY_HEADER", entity);
stateHandler.handleEvent(message);
entity.setProcessAgent(...get the current user's id somehow...);
...
return entity;
StateHandler обрабатывает сообщения о событиях. Это та часть, которую я нахожу трудной и чувствую, что должен задаться вопросом. Каждый в основном получает конечный автомат, сбрасывает его в заданное состояние и запускает, чтобы перехватить переход;после перехвата новое целевое состояние сохраняется в таблице сущности:
// stateMachineFactory is auto wired into the state handler
// repository is auto wired in the state handler
public void handleEvent(final Message<Event> message) {
final Entity entity = message.getHeaders().get("ENTITY_HEADER", Entity.class);
final State currentState = entity.getProcessState();
StateMachine<State, Event> machine = stateMachineFactory.getStateMachine();
machine.getStateMachineAccessor().doWithAllRegions(accessor -> accessor.resetStateMachine(
new DefaultStateMachineContext<State, Event>(currentState, null, null, null, null)
));
machine.getStateMachineAccessor().doWithAllRegions(accessor -> accessor.addStateMachineInterceptor(
@Override
public StateContext<State, Event> postTransition(final StateContext<State, Event> stateContext) {
final Entity entity1 = stateContext.getMessage().getHeaders.get("ENTITY_HEADER", Entity.class);
if (entity != null) {
entity1.setState(stateContext.getTarget().getId());
repository.save(entity1);
return stateContext;
}
// if entity is null then throw exception
... omitted exception handling
}
);
log.debug("Starting state machine to process [{}]", entity);
stateMachine.start();
stateMachine.sendEvent(message);
stateMachine.stop();
}
Для полноты следующего StateMachineConfig
:
@Override
public void configure(final StateMachineConfigurationConfigurer<State, Event> config) throws Exception {
config.withConfiguration()
.autoStartup(false);
}
@Override
public void configure(final StateMachineStateConfigurer<State, Event> sates) throws Exception {
states.withStates()
.initial(State.CREATED)
.states(EnumSet.allOf(State.class));
}
@Override
public void configure(final StateMachineTransitionConfigurer<State, Event> transitions) throws Exception {
transitions.withExternal()
.source(State.CREATED)
.target(State.ASSIGNED)
.event(Event.ASSIGN_TO_AGENT)
.and()
.withExternal()
.source(State.ASSIGNED)
.target(State.IN_PROCESS)
.event(Event.START_PROCESS);
}
Я надеюсь, что смогу быть настолько полным, насколько это возможно,Пожалуйста, дайте мне знать, если нужны какие-либо разъяснения.
Мой вопрос: есть ли лучший дизайн для реализации этого конечного автомата, или здесь можно увидеть разумный подход?