hekailiang / squirrel

squirrel-foundation is a State Machine library, which provided a lightweight, easy use, type safe and programmable state machine implementation for Java.
http://hekailiang.github.io/squirrel/
Other
2.19k stars 540 forks source link

状态机与业务逻辑的依赖或耦合关系 #72

Open jncumter opened 7 years ago

jncumter commented 7 years ago

你好。我们目前在做一个项目管理系统。想采用状态机对项目中模块状态的变化进行管理。假设模块有A\B\C三个状态,当模块由A状态向B状态或B状态向C状态变化时,或离开\进入某个状态时需要进行一些业务上的变更。请问,是不是可以将业务(即模块和业务变更)和状态机实例是不是一种紧密耦合的关系,两者必须有对应关系,即一个业务过程会存在一个状态机实例;我如果采用将业务放在Context中,建立一个状态机实例池,业务意义上的模块状态和状态机状态解耦(模块业务信息全部存储在Context中)。当模块状态为A\B\C时,去状态机实例池中取当前状态为A\B\C的状态机实例用于驱动本次状态变化;如果不存在当前状态的状态机实例,则新建状态机实例加入池中。不知道我的方案对状态机的理解是否正确,如果采用我的方案,可以实现我的业务逻辑,请问是否会存在其他问题,谢谢。

hekailiang commented 7 years ago

一般内聚性高的业务逻辑可以直接在transition和entry/exit中实现, 具体在哪里实现取决于业务的粒度. 状态机与外部系统的交互可以通过状态事件来实现. 状态机Builder的创建需要一定时间, 所以状态机builder最好复用. 但是状态机实例创建的成本很低, 没有必要增加状态机池. 加载业务上下文后, 直接构造状态机实例即可.

xiaosazixian commented 7 years ago

@jncumter 你的意思我明白。比如一个下单流程可以用一个状态机来描述。但每条订单数据是一个状态机实例。如果某条订单走到了支付环节,用户下线了,当用户再次上线的时候可以通过数据库里面对应的那条订单数据来恢复那个状态机实例。

lhaiq commented 7 years ago

对 怎么根据数据库恢复状态?

lhaiq commented 7 years ago

@hekailiang

TimGuan commented 7 years ago

@lhaiq 要恢复的前提是要先做持久化,比如记录订单的当前状态;其实对于squirrel状态机创建的开销足够轻量,恢复的动作其实就是根据之前持久化的信息,重新初始化一个statemachine instance。这一点的设计和spring statemachine是不一样的,spring statemachine因为创建的过程过于heavy,不得不引入share statemachine instance(这个也引入了一堆问题,性能、deadlock、上下文清理等,spring statemachine 的作者也在思考这么做是否值得,希望能有个更好的解决方案)。从我个人角度看,我比较偏向squirrel这种方式,简单犯错的概率相对低,为啥我们要选择状态机不选择个臃肿的bpm,不就图个轻量么。

lhaiq commented 7 years ago

@TimGuan 对的。。肯定操作越小越好。。在squirrel中。。我写个listener 当状态发生变化了。。我就往数据库写。。那么怎么恢复呢。。根据持久化的值。。我没有找到例子呢

TimGuan commented 7 years ago

@lhaiq 状态机引擎选型 squirrel-foundation状态机的使用细节 希望对你有帮助

lhaiq commented 7 years ago

嗯。。我看了下 有rmaRepository.update..没得rmaRepository.find()呀。。这个find应该在哪写?

TimGuan commented 7 years ago

@lhaiq 当然应该在创建实例的时候啊,先获取当前持久化的状态,再构造状态机实例,再fire

T stateMachine = stateMachineBuilder.newUntypedStateMachine(
                                    initialState,
                                    //暂时开启debug进行日志trace
StateMachineConfiguration.create().enableDebugMode(true).enableAutoStart(true),
                                    //注入applicationContext
                                    applicationContext);
peterpanzsy commented 6 years ago

@TimGuan @hekailiang 状态机调用逻辑是在业务代码里写:比如controller里, Order order = orderService.find(orderId); initialState = order.status; T stateMachine = stateMachineBuilder.newUntypedStateMachine( initialState, //暂时开启debug进行日志trace StateMachineConfiguration.create().enableDebugMode(true).enableAutoStart(true), //注入applicationContext applicationContext); stateMachine.fire(OrderEvent.refund, context); 是这个意思么?

在callMethod里或者afterTransitionCompleted写持久化对象逻辑: order.setStatus(toStatus); orderService.save(order);

如果业务代码更新实例状态失败呢?我理解的是状态机不需要做回滚,当前状态机对象直接等待垃圾回收,但是我们需要在业务代码里获取到这个更新失败,如果在afterTransitionCompleted写持久化,外部业务无法感知,如果在callMethod写,是否可以throw一个exception出去呢?

peterpanzsy commented 6 years ago

@hekailiang 发现居然是同校同系师兄,请问还更新么?有没有业务应用实践供学习呢?

SuperNoobTao commented 6 years ago

@TimGuan 看了下您的文章中的代码,好像没有状态机的启动与停止(statr&terminate)?

hekailiang commented 6 years ago

@peterpanzsy 这个项目暂时不会有大的更新,主要的功能都已经完善。谢谢你的关注!

TimGuan commented 5 years ago

@SuperNoobTao image

cookiezzjie commented 3 years ago

你好,我们这边现在的需求是对一个任务做父子状态机的区分,但是父状态机实际上都是相同的,这边框架是否有提供父状态机复用的方法。或者能否使用jdk extends来做父状态机的复用?

facelezzzz commented 3 years ago

你好,我们这边现在的需求是对一个任务做父子状态机的区分,但是父状态机实际上都是相同的,这边框架是否有提供父状态机复用的方法。或者能否使用jdk extends来做父状态机的复用?

你要的应该是HSM,这个框架是支持的