狀態機是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型。狀態機,也就是 State Machine ,不是指一臺實際機器,而是指一個數學模型。說白了,一般就是指一張狀態轉換圖。 ...
1 狀態機簡介
1.1 定義
我們先來給出狀態機的基本定義。一句話:
狀態機是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型。
先來解釋什麼是“狀態”( State )。現實事物是有不同狀態的,例如一個自動門,就有 open 和 closed 兩種狀態。我們通常所說的狀態機是有限狀態機,也就是被描述的事物的狀態的數量是有限個,例如自動門的狀態就是兩個 open 和 closed 。
狀態機,也就是 State Machine ,不是指一臺實際機器,而是指一個數學模型。說白了,一般就是指一張狀態轉換圖。例如,根據自動門的運行規則,我們可以抽象出下麵這麼一個圖。
自動門有兩個狀態,open 和 closed ,closed 狀態下,如果讀取開門信號,那麼狀態就會切換為 open 。open 狀態下如果讀取關門信號,狀態就會切換為 closed 。
狀態機的全稱是有限狀態自動機,自動兩個字也是包含重要含義的。給定一個狀態機,同時給定它的當前狀態以及輸入,那麼輸出狀態時可以明確的運算出來的。例如對於自動門,給定初始狀態 closed ,給定輸入“開門”,那麼下一個狀態時可以運算出來的。
這樣狀態機的基本定義我們就介紹完畢了。重覆一下:狀態機是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型。
1.2 四大概念
下麵來給出狀態機的四大概念。
第一個是 State ,狀態。一個狀態機至少要包含兩個狀態。例如上面自動門的例子,有 open 和 closed 兩個狀態。
第二個是 Event ,事件。事件就是執行某個操作的觸發條件或者口令。對於自動門,“按下開門按鈕”就是一個事件。
第三個是 Action ,動作。事件發生以後要執行動作。例如事件是“按開門按鈕”,動作是“開門”。編程的時候,一個 Action 一般就對應一個函數。
第四個是 Transition ,變換。也就是從一個狀態變化為另一個狀態。例如“開門過程”就是一個變換。
2 DSL
2.1 DSL
DSL是一種工具,它的核心價值在於,它提供了一種手段,可以更加清晰地就系統某部分的意圖進行溝通。
這種清晰並非只是審美追求。一段代碼越容易看懂,就越容易發現錯誤,也就越容易對系統進行修改。因此,我們鼓勵變數名要有意義,文檔要寫清楚,代碼結構要寫清晰。基於同樣的理由,我們應該也鼓勵採用DSL。
按照定義來說,DSL是針對某一特定領域,具有受限表達性的一種電腦程式設計語言。
這一定義包含3個關鍵元素:
語言性(language nature):DSL是一種程式設計語言,因此它必須具備連貫的表達能力——不管是一個表達式還是多個表達式組合在一起。
受限的表達性(limited expressiveness):通用程式設計語言提供廣泛的能力:支持各種數據、控制,以及抽象結構。這些能力很有用,但也會讓語言難於學習和使用。DSL只支持特定領域所需要特性的最小集。使用DSL,無法構建一個完整的系統,相反,卻可以解決系統某一方面的問題。
針對領域(domain focus):只有在一個明確的小領域下,這種能力有限的語言才會有用。這個領域才使得這種語言值得使用。
比如正則表達式,/\d{3}-\d{3}-\d{4}/就是一個典型的DSL,解決的是字元串匹配這個特定領域的問題。
2.2 DSL的分類
按照類型,DSL可以分為三類:內部DSL(Internal DSL)、外部DSL(External DSL)、以及語言工作台(Language Workbench)。
Internal DSL是一種通用語言的特定用法。用內部DSL寫成的腳本是一段合法的程式,但是它具有特定的風格,而且只用到了語言的一部分特性,用於處理整個系統一個小方面的問題。 用這種DSL寫出的程式有一種自定義語言的風格,與其所使用的宿主語言有所區別。例如我們的狀態機就是Internal DSL,它不支持腳本配置,使用的時候還是Java語言,但並不妨礙它也是DSL。
builder.externalTransition()
.from(States.STATE1)
.to(States.STATE2)
.on(Events.EVENT1)
.when(checkCondition())
.perform(doAction());
External DSL是一種“不同於應用系統主要使用語言”的語言。外部DSL通常採用自定義語法,不過選擇其他語言的語法也很常見(XML就是一個常見選 擇)。比如像Struts和Hibernate這樣的系統所使用的XML配置文件。
Workbench是一個專用的IDE,簡單點說,工作台是DSL的產品化和可視化形態。
三個類別DSL從前往後是有一種遞進關係,Internal DSL最簡單,實現成本也低,但是不支持“外部配置”。Workbench不僅實現了配置化,還實現了可視化,但是實現成本也最高。他們的關係如下圖所示:
2.3 DSL示例
2.3.1 內部DSL示例
HTML: 通過自然語言編寫
在Groovy中,通過DSL可以用易讀的寫法生成XML
def s = new StringWriter()
def xml = new MarkupBuilder(s)
xml.html{
head{
title("Hello - DSL")
script(ahref:"https://xxxx.com/vue.js")
meta(author:"marui116")
}
body{
p("JD-ILT-ITMS")
}
}
println s.toString()
最後將生成
<html>
<head>
<title>Hello - DSL</title>
<script ahref='https://xxxx.com/vue.js' />
<meta author='marui116' />
</head>
<body>
<p>JD-ILT-ITMS</p>
</body>
</html>
MarkupBuilder的作用說明:
A helper class for creating XML or HTML markup. The builder supports various 'pretty printed' formats.
Example:
new MarkupBuilder().root {
a( a1:'one' ) {
b { mkp.yield( '3 < 5' ) }
c( a2:'two', 'blah' )
}
}
Will print the following to System.out:
<root>
<a a1='one'>
<b>3 < 5</b>
<c a2='two'>blah</c>
</a>
</root>
這裡相對於Java這樣的動態語言,最為不同的就是xml.html這個並不存在的方法居然可以通過編譯並運行,它內部重寫了invokeMethod方法,併進行閉包遍歷,少寫了許多POJO對象,效率更高。
2.3.2 外部DSL
以plantUML為例,外部DSL不受限於宿主語言的語法,對用戶很友好,尤其是對於不懂宿主語言語法的用戶。但外部DSL的自定義語法需要有配套的語法分析器。常見的語法分析器有:YACC、ANTLR等。
https://github.com/plantuml/plantuml
2.3.3 DSL & DDD(領域驅動)
DDD和DSL的融合有三點:面向領域、模型的組裝方式、分層架構演進。DSL 可以看作是在領域模型之上的一層外殼,可以顯著增強領域模型的能力。
它的價值主要有兩個,一是提升了開發人員的生產力,二是增進了開發人員與領域專家的溝通。外部 DSL 就是對領域模型的一種組裝方式。
3 狀態機實現的調研
3.1 Spring Statemachine
官網:https://spring.io/projects/spring-statemachine#learn
源碼:https://github.com/spring-projects/spring-statemachine
API:https://docs.spring.io/spring-statemachine/docs/3.2.0/api/
Spring Statemachine is a framework for application developers to use state machine concepts with Spring applications. Spring Statemachine 是應用程式開發人員在Spring應用程式中使用狀態機概念的框架。
Spring Statemachine 提供如下特色:
•Easy to use flat one level state machine for simple use cases.(易於使用的扁平單級狀態機,用於簡單的使用案例。)
•Hierarchical state machine structure to ease complex state configuration.(分層狀態機結構,以簡化複雜的狀態配置。)
•State machine regions to provide even more complex state configurations.(狀態機區域提供更複雜的狀態配置。)
•Usage of triggers, transitions, guards and actions.(使用觸發器、transitions、guards和actions。)
•Type safe configuration adapter.(應用安全的配置適配器。)
•Builder pattern for easy instantiation for use outside of Spring Application context(用於在Spring Application上下文之外使用的簡單實例化的生成器模式)
•Recipes for usual use cases(通常用例的手冊)
•Distributed state machine based on a Zookeeper State machine event listeners.(基於Zookeeper的分散式狀態機狀態機事件監聽器。)
•UML Eclipse Papyrus modeling.(UML Eclipse Papyrus 建模)
•Store machine config in a persistent storage.(存儲狀態機配置到持久層)
•Spring IOC integration to associate beans with a state machine.(Spring IOC集成將bean與狀態機關聯起來)
Spring StateMachine提供了papyrus的Eclipse Plugin,用來輔助構建狀態機。
更多Eclipse建模插件可參見文檔:https://docs.spring.io/spring-statemachine/docs/3.2.0/reference/#sm-papyrus
Spring狀態機的配置、定義、事件、狀態擴展、上下文集成、安全性、錯誤處理等,可以參看如下文檔:
https://docs.spring.io/spring-statemachine/docs/3.2.0/reference/#statemachine
3.2 COLA狀態機DSL實現
COLA 是 Clean Object-Oriented and Layered Architecture的縮寫,代表“整潔面向對象分層架構”。 目前COLA已經發展到COLA v4。COLA提供了一個DDD落地的解決方案,其中包含了一個開源、簡單、輕量、性能極高的狀態機DSL實現,解決業務中的狀態流轉問題。
COLA狀態機組件實現一個僅支持簡單狀態流轉的狀態機,該狀態機的核心概念如下圖所示,主要包括:
1.State:狀態
2.Event:事件,狀態由事件觸發,引起變化
3.Transition:流轉,表示從一個狀態到另一個狀態
4.External Transition:外部流轉,兩個不同狀態之間的流轉
5.Internal Transition:內部流轉,同一個狀態之間的流轉
6.Condition:條件,表示是否允許到達某個狀態
7.Action:動作,到達某個狀態之後,可以做什麼
8.StateMachine:狀態機
整個狀態機的核心語義模型(Semantic Model):
4 狀態機DEMO
4.1 Spring狀態機示例
代碼地址:http://xingyun.jd.com/codingRoot/ilt/spring-statemachine-demo/
例如,起始節點為SI、結束節點為SF,起始節點後續有S1、S2、S3三個節點的簡單狀態機。
Spring Boot項目需引入Spring狀態機組件。
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>3.2.0</version>
</dependency>
4.1.1 構造狀態機
@Configuration
@EnableStateMachine
@Slf4j
public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapter<String, String> {
/**
* 定義初始節點、結束節點和狀態節點
* @param states the {@link StateMachineStateConfigurer}
* @throws Exception
*/
@Override
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
states.withStates()
.initial("SI")
.end("SF")
.states(new HashSet<String>(Arrays.asList("S1", "S2", "S3")));
}
/**
* 配置狀態節點的流向和事件
* @param transitions the {@link StateMachineTransitionConfigurer}
* @throws Exception
*/
@Override
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
transitions.withExternal()
.source("SI").target("S1").event("E1").action(initAction())
.and()
.withExternal()
.source("S1").target("S2").event("E2").action(s1Action())
.and()
.withExternal()
.source("S2").target("SF").event("end");
}
/**
* 初始節點到S1
* @return
*/
@Bean
public Action<String, String> initAction() {
return ctx -> log.info("Init Action -- DO: {}", ctx.getTarget().getId());
}
/**
* S1到S2
* @return
*/
@Bean
public Action<String, String> s1Action() {
return ctx -> log.info("S1 Action -- DO: {}", ctx.getTarget().getId());
}
}
4.1.2 狀態機狀態監聽器
@Component
@Slf4j
public class StateMachineListener extends StateMachineListenerAdapter<String, String> {
@Override
public void stateChanged(State from, State to) {
log.info("Transitioned from {} to {}", from == null ? "none" : from.getId(), to.getId());
}
}
4.1.3 狀態機配置
@Configuration
@Slf4j
public class StateMachineConfig implements WebMvcConfigurer {
@Resource
private StateMachine<String, String> stateMachine;
@Resource
private StateMachineListener stateMachineListener;
@PostConstruct
public void init() {
stateMachine.addStateListener(stateMachineListener);
}
}
4.1.4 介面示例
4.1.4.1 獲取狀態機狀態列表
@RequestMapping("info")
public String info() {
return StringUtils.collectionToDelimitedString(
stateMachine.getStates()
.stream()
.map(State::getId)
.collect(Collectors.toList()),
",");
}
4.1.4.2 狀態機開啟
在對Spring狀態機進行事件操作之前,必須先開啟狀態機
@GetMapping("start")
public String start() {
stateMachine.startReactively().block();
return state();
}
4.1.4.3 事件操作
@PostMapping("event")
public String event(@RequestParam(name = "event") String event) {
Message<String> message = MessageBuilder.withPayload(event).build();
return stateMachine.sendEvent(Mono.just(message)).blockLast().getMessage().getPayload();
}
4.1.4.4 獲取狀態機當前狀態
@GetMapping("state")
public String state() {
return Mono.defer(() -> Mono.justOrEmpty(stateMachine.getState().getId())).block();
}
4.1.4.5 一次狀態轉換的控制台輸出
: Completed initialization in 0 ms
: Transitioned from none to SI
: Init Action -- DO: S1
: Transitioned from SI to S1
: S1 Action -- DO: S2
: Transitioned from S1 to S2
: Transitioned from S2 to SF
可以看到,狀態從none到SI開始節點,再到S1、S2,然後S2通過E2事件到SF結束節點。
4.2 COLA狀態機示例
代碼地址:http://xingyun.jd.com/codingRoot/ilt/ilt-component-statemachine/
例如:iTMS中的運輸需求單的狀態目前有:待分配、已分配、運輸中、部分妥投、全部妥投、全部拒收、已取消。
4.2.1 構造狀態機
com.jd.ilt.component.statemachine.demo.component.statemachine.TransNeedStateMachine
StateMachineBuilder<TransNeedStatusEnum, TransNeedEventEnum, Context> builder = StateMachineBuilderFactory.create();
// 接單後,運輸需求單生成運輸規劃單
builder.externalTransition()
.from(None)
.to(UN_ASSIGN_CARRIER)
.on(Create_Event)
.when(checkCondition())
.perform(doAction());
// 運輸規劃單生成調度單,調度單綁定服務商
builder.externalTransition()
.from(UN_ASSIGN_CARRIER)
.to(UN_ASSIGN_CAR)
.on(Assign_Carrier_Event)
.when(checkCondition())
.perform(doAction());
// 服務商分配車輛、司機
builder.externalTransition()
.from(UN_ASSIGN_CAR)
.to(ASSIGNED_CAR)
.on(Assign_Car_Event)
.when(checkCondition())
.perform(doAction());
// 貨物攬收
builder.externalTransition()
.from(ASSIGNED_CAR)
.to(PICKUPED)
.on(Trans_Job_Status_Change_Event)
.when(checkCondition())
.perform(doAction());
// 攬收貨物更新到運輸中
builder.externalTransition()
.from(ASSIGNED_CAR)
.to(IN_TRANSIT)
.on(Trans_Job_Status_Change_Event)
.when(checkCondition())
.perform(doAction());
// 運輸中更新到過海關
builder.externalTransition()
.from(IN_TRANSIT)
.to(PASS_CUSTOMS)
.on(Trans_Job_Status_Change_Event)
// 檢查是否需要過海關
.when(isTransNeedPassCustoms())
.perform(doAction());
// 妥投
builder.externalTransition()
.from(PASS_CUSTOMS)
.to(ALL_DELIVERIED)
.on(All_Delivery_Event)
.when(checkCondition())
.perform(doAction());
// 車輛攬收、運輸、過海關的運輸狀態,都可以直接更新到妥投
Stream.of(PICKUPED, IN_TRANSIT, PASS_CUSTOMS)
.forEach(status ->
builder.externalTransition()
.from(status)
.to(ALL_DELIVERIED)
.on(Trans_Job_Status_Change_Event)
.when(checkCondition())
.perform(doAction())
);
// 待分配、待派車、已派車可取消
Stream.of(UN_ASSIGN_CARRIER, UN_ASSIGN_CAR, ASSIGNED_CAR)
.forEach(status ->
builder.externalTransition()
.from(status)
.to(CANCELED)
.on(Order_Cancel_Event)
.when(checkCondition())
.perform(doAction())
);
// 妥投、和取消可結束歸檔
Stream.of(ALL_DELIVERIED, CANCELED)
.forEach(status ->
builder.externalTransition()
.from(status)
.to(FINISH)
.on(Order_Finish)
.when(checkCondition())
.perform(doAction())
);
stateMachine = builder.build("TransNeedStatusMachine");
從代碼中,可以方便的擴展狀態和對應的事件,狀態機自動進行業務狀態的流轉。生成的狀態流轉圖如下所示:
@startuml
None --> UN_ASSIGN_CARRIER : Create_Event
UN_ASSIGN_CARRIER --> UN_ASSIGN_CAR : Assign_Carrier_Event
UN_ASSIGN_CAR --> ASSIGNED_CAR : Assign_Car_Event
ASSIGNED_CAR --> CANCELED : Order_Cancel_Event
ASSIGNED_CAR --> PICKUPED : Trans_Job_Status_Change_Event
ASSIGNED_CAR --> IN_TRANSIT : Trans_Job_Status_Change_Event
IN_TRANSIT --> PASS_CUSTOMS : Trans_Job_Status_Change_Event
PASS_CUSTOMS --> ALL_DELIVERIED : Trans_Job_Status_Change_Event
PASS_CUSTOMS --> ALL_DELIVERIED : All_Delivery_Event
IN_TRANSIT --> ALL_DELIVERIED : Trans_Job_Status_Change_Event
ALL_DELIVERIED --> FINISH : Order_Finis
UN_ASSIGN_CAR --> CANCELED : Order_Cancel_Event
UN_ASSIGN_CARRIER --> CANCELED : Order_Cancel_Event
PICKUPED --> ALL_DELIVERIED : Trans_Job_Status_Change_Event
CANCELED --> FINISH : Order_Finis
@enduml
4.2.2 狀態機事件處理
/**
* 一種是通過Event來進行事件分發,不同Event通過EventBus走不同的事件響應
* 另一種是在構造狀態機時,直接配置不同的Action
* @return
*/
private Action<TransNeedStatusEnum, TransNeedEventEnum, Context> doAction() {
log.info("do action");
return (from, to, event, ctx) -> {
log.info(ctx.getUserName()+" is operating trans need bill "+ctx.getTransNeedId()+" from:"+from+" to:"+to+" on:"+event);
if (from != None) {
TransNeed transNeed = ctx.getTransNeed();
transNeed.setStatus(to.name());
transNeed.setUpdateTime(LocalDateTime.now());
transNeedService.update(transNeed);
}
eventBusService.invokeEvent(event, ctx);
};
}
Event和EventBus簡單Demo示例:
/**
* @author marui116
* @version 1.0.0
* @className TransNeedAssignCarrierEvent
* @description TODO
* @date 2023/3/28 11:08
*/
@Component
@EventAnnonation(event = TransNeedEventEnum.Assign_Carrier_Event)
@Slf4j
public class TransNeedAssignCarrierEvent implements EventComponent {
@Override
public void invokeEvent(Context context) {
log.info("分配了服務商,給服務商發郵件和簡訊,讓服務商安排");
}
}
/**
* @author marui116
* @version 1.0.0
* @className TransNeedAssignCarEvent
* @description TODO
* @date 2023/3/28 11:05
*/
@Component
@EventAnnonation(event = TransNeedEventEnum.Assign_Car_Event)
@Slf4j
public class TransNeedAssignCarEvent implements EventComponent {
@Override
public void invokeEvent(Context context) {
log.info("分配了車輛信息,給運單中心發送車輛信息");
}
}
/**
* @author marui116
* @version 1.0.0
* @className EventServiceImpl
* @description TODO
* @date 2023/3/28 10:57
*/
@Service
public class EventBusServiceImpl implements EventBusService {
@Resource
private ApplicationContextUtil applicationContextUtil;
private Map<TransNeedEventEnum, EventComponent> eventComponentMap = new ConcurrentHashMap<>();
@PostConstruct
private void init() {
ApplicationContext context = applicationContextUtil.getApplicationContext();
Map<String, EventComponent> eventBeanMap = context.getBeansOfType(EventComponent.class);
eventBeanMap.values().forEach(event -> {
if (event.getClass().isAnnotationPresent(EventAnnonation.class)) {
EventAnnonation eventAnnonation = event.getClass().getAnnotation(EventAnnonation.class);
eventComponentMap.put(eventAnnonation.event(), event);
}
});
}
@Override
public void invokeEvent(TransNeedEventEnum eventEnum, Context context) {
if (eventComponentMap.containsKey(eventEnum)) {
eventComponentMap.get(eventEnum).invokeEvent(context);
}
}
}
4.2.3 狀態機上下文
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Context {
private String userName;
private Long transNeedId;
private TransNeed transNeed;
}
4.2.4 狀態枚舉
public enum TransNeedStatusEnum {
/**
* 開始狀態
*/
None,
/**
* 待分配陸運服務商
*/
UN_ASSIGN_CARRIER,
/**
* 待分配車輛和司機
*/
UN_ASSIGN_CAR,
/**
* 訂單已處理,已安排司機提貨
*/
ASSIGNED_CAR,
/**
* 已完成提貨
*/
PICKUPED,
/**
* 運輸中
*/
IN_TRANSIT,
/**
* 已通過內地海關
*/
PASS_CUSTOMS,
/**
* 您的貨物部分妥投部分投遞失敗
*/
PARTIAL_DELIVERIED,
/**
* 您的貨物妥投
*/
ALL_DELIVERIED,
/**
* 您的貨物被拒收
*/
ALL_REJECTED,
/**
* 委托訂單被取消
*/
CANCELED,
/**
* 單據結束歸檔
*/
FINISH;
}
4.2.5 事件枚舉
public enum TransNeedEventEnum {
// 系統事件
Create_Event,
Normal_Update_Event,
/**
* 分配服務商事件
*/
Assign_Carrier_Event,
/**
* 派車事件
*/
Assign_Car_Event,
// 車輛任務(trans_jbo)執行修改調度單(trans_task)狀態的事件
Trans_Job_Status_Change_Event,
// 派送事件
Partial_Delivery_Event,
All_Delivery_Event,
Partial_Reject_Event,
All_Reject_Event,
// 調度單中的任務單取消事件
Order_Cancel_Event,
// 單據結束
Order_Finish;
public boolean isSystemEvent() {
return this == Create_Event ||
this == Normal_Update_Event;
}
}
4.2.6 介面Demo
4.2.6.1 創建需求單
/**
* 接單
* @return
*/
@RequestMapping("/start/{fsNo}/{remark}")
public Context start(@PathVariable("fsNo") String fsNo, @PathVariable("remark") String remark) {
Context context = contextService.getContext();
Object newStatus = stateMachine.getStateMachine().fireEvent(TransNeedStatusEnum.None, TransNeedEventEnum.Create_Event, context);
TransNeed transNeed = transNeedService.createTransNeed(fsNo, remark, newStatus.toString());
context.setTransNeed(transNeed);
context.setTransNeedId(transNeed.getId());
return context;
}
4.2.6.2 分配服務商
/**
* 運輸規劃單生成調度單,調度單綁定服務商
*/
@RequestMapping("/assignCarrier/{id}")
public Context assignCarrier(@PathVariable("id") Long id) {
Context context = contextService.getContext(id);
TransNeedStatusEnum prevStatus = TransNeedStatusEnum.valueOf(context.getTransNeed().getStatus());
stateMachine.getStateMachine().fireEvent(prevStatus, TransNeedEventEnum.Assign_Carrier_Event, context);
return context;
}
4.2.6.3 分配車輛
@RequestMapping("/assignCar/{id}")
public Context assignCar(@PathVariable("id") Long id) {
Context context = contextService.getContext(id);
TransNeedStatusEnum prevStatus = TransNeedStatusEnum.valueOf(context.getTransNeed().getStatus());
log.info("trans need id: {}, prev status: {}", id, prevStatus);
stateMachine.getStateMachine().fireEvent(prevStatus, TransNeedEventEnum.Assign_Car_Event, context);
return context;
}
5 狀態機對比
維度\組件 | Spring StateMachine | COLA StateMachine |
---|---|---|
API調用 | 使用Reactive的Mono、Flux方式進行API調用 | 同步的API調用,如果有需要也可以將方法通過MQ、定時任務、線程池做成非同步的 |
代碼量 | core包284個介面和類 | 36個介面和類 |
生態 | 非常豐富 | 無 |
定製化難度 | 困難 | 簡單 |
代碼更新狀態 | 將近1年沒有更新 | 半年前 |
綜上,如果是直接使用狀態機的組件庫,可以考慮使用Spring的狀態機,如果是要漸進式的使用狀態機,逐步按照自己的需求去定製化狀態機以滿足業務需求,建議使用COLA的狀態機。
6 iTMS使用狀態機的計劃
iTMS準備漸進式的使用COLA的狀態機組件,先輕量級使用狀態機進行運輸相關域的狀態變更,後續按照DDD的狀態和事件的分析,使用CQRS的設計模式對命令做封裝,調用狀態機進行業務流轉。
作者:京東物流 馬瑞
來源:京東雲開發者社區 自猿其說Tech