This is just a sample project trying out the combination of Spring-Statemachine, JPA and Spring Data REST.
The state machine is a simulation of an order fulfillment process, offering two different flows: One for pay-before-shipping (prepayment, paypal etc.), and one for ship-before-payment (cash-on-delivery, invoice). Ultimately they form one state machine. Some transitions have guards depending on the "paid" status of the order, which gets set when the "ReceivePayment" event occurs (and un-set on "Refund"). Other than that, the state machine is pretty straight-forward.
The two flows are shown below.
+----------------------------------------------------------------------------------------------------------------------------+
| pre-payment flow |
+----------------------------------------------------------------------------------------------------------------------------+
| (1) (2) [if paid] (3) [if paid] |
| +------------------+ ReceivePayment +-- ---------------+ Deliver +------------------+ Refund +------------------+ |
| *-->| Open |---------------->| ReadyForDelivery |--------->| Completed |--------->| Canceled | |
| | | | | | | | | |
| | | | ReceivePayment | | | | ReceivePayment | |
| | | | +------------+ | | | | +------------+ | |
| | | | | (11) | | | | | | (12) | | |
| | | | | v | | | | | v | |
| +------------------+ +------------------+ +------------------+ +------------------+ |
| | ^ | | [if paid] (4) Refund ^ ^ | ^ |
| | | | +---------------------------------------------+ | | | |
| | | | | | | |
| | | | [if !paid] (8) Cancel | | | |
| | | (5) Reopen +---------------------------------------------------+ | | |
| | +---------------------------------------------------------------------------------------------------------+ | |
| | (6) Cancel | |
| +-------------------------------------------------------------------------------------------------------------+ |
| |
+----------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
| post-payment flow |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
| (7) (9) [if !paid] (10) (3) [if paid] |
| +------------------+ UnlockDelivery +-- ---------------+ Deliver +------------------+ ReceivePayment +---------------+ Refund +------------------+ |
| *-->| Open |---------------->| ReadyForDelivery |--------->| AwaitingPayment |--------------->| Completed |--------->| Canceled | |
| | | | | | | | | | | |
| | | | ReceivePayment | | | | | | ReceivePayment | |
| | | | +------------+ | | | | | | +------------+ | |
| | | | | (11) | | | | | | | | (12) | | |
| | | | | v | | | | | | | v | |
| +------------------+ +------------------+ +------------------+ +---------------+ +------------------+ |
| | ^ | | [if paid] (4) Refund ^ ^ | ^ |
| | | | +-----------------------------------------------------------------------------+ | | | |
| | | | | | | |
| | | | [if !paid] (8) Cancel | | | |
| | | (5) Reopen +-------------------------------------------------------------------------------------+ | | |
| | +------------------------------------------------------------------------------------------------------------------------------------------+ | |
| | (6) Cancel | |
| +----------------------------------------------------------------------------------------------------------------------------------------------+ |
| |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
I use a simple domain object as state machine context (my Order
entity), which holds
the StateMachineContext in a binary-serialized form, using Kryo.
The REST API exposes links for receiving events to trigger state transitions for
the state machine. The server either responds with 202 Accepted
if an event was accepted,
or 422 Unprocessable Entity
if the event was not accepted by the state machine.
The resource intentionally does not expose its current state as enum, but as a localized text, so that clients are aware of the fact they should not build business logic upon that. Instead the client should use the links for triggering the state transitions that are possible for the given state. You can get more details about why you should expose links instead of status properties in Oliver Gierke's talk about DDD and REST.
{
"_links": {
"cancel": {
"href": "http://localhost:8080/orders/1/receive/Cancel"
},
"order": {
"href": "http://localhost:8080/orders/1"
},
"receive-payment": {
"href": "http://localhost:8080/orders/1/receive/ReceivePayment"
},
"self": {
"href": "http://localhost:8080/orders/1"
},
"unlock-delivery": {
"href": "http://localhost:8080/orders/1/receive/UnlockDelivery"
}
}
}
Further information can be found by the documentation guides generated from the latest master
build.
Order : My entity acting as context object
OrderStateMachineConfiguration : The setup of the state machine.
OrderCreatedEventListener : An AbstractRepositoryEventListener
getting notified
when a new order is created, initializing it with a new state machine.
OrderEventController : RestController for receiving events.
ContextEntity : Interface combining the role of a statemachine context object and spring-hateoas Identifiable
ContextObjectResourceProcessor : For adding the links to the response.
StateMachineContextConverter : The converter from StateMachineContext to byte[] and back.
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。