preface

When designing the order module of the e-commerce system, the order will involve various states and the flow between states.
scalability,
maintainabilityIs the focus of our attention! This article shares my technical solution.

As shown in the figure above, use Golang to implement the order flow in the figure above, and when the order status or order event is added later, it can be completed quickly.

purpose

About the processing of order status, the use of unified entry, improve the scalability and maintainability of the program.

Logical analysis

Order status includes: Default, Booked, Confirmed, Locked.

Order events include: create an order, confirm an order, modify an order, and pay an order.

We also know the relationship between states and events from the above figure. For example, only confirmed orders can be modified.

The following issues need to be considered:

  1. When order status increases, how do you make as few changes as possible or changes that have little impact on history?
  2. If it is called on the same entry, each event handler requires a different input parameter, what can be done?
  3. When an event is completed, there may be SMS or client Push operation, how to handle?
  4. It is possible that the processing logic of an event in different platforms (C terminal, merchant background and management platform) is also somewhat different. How to deal with it?

How do you design your code to solve these problems?

The following is my code implementation, for your reference, to achieve the creation of orders, incoming parameters and after the completion of the user to send short messages, other events, the same can be achieved.

Code implementation

Define state

Const (statusDefault = State(0) StatusReserved = State(10) StatusConfirmed = State(20) StatusLocked = Var statusText = map[State]string{statusDefault: "", statusReserved: "Reserved ", StatusConfirmed:" Confirmed ", StatusLocked: } var statusEvent = map[State][]Event{statusDefault: {EventCreate}, StatusReserved: {EventConfirm}, StatusConfirmed: {EventModify, EventPay}, } func StatusText(status State) string { return statusText[status] }

When the new order status is increased, the corresponding state can be added to this file, and the relationship between the order status and the order event can be maintained.

Define events

Const (eventCreate = Event(" Create order ") eventConfirm = Event(" Confirm order ") eventModify = Event(" Modify order ") eventPay = Var EventHandler = map[Event]Handler{eventCreate: handlerCreate, eventConfirm: handlerConfirm, EventModify: handlerModify, EventPay: handlerPay, }

When a new order event is added, the corresponding event can be added to this file, and the relationship between the order event and the event implementation method can be maintained.

Define the handling of the event

Var (// handlerCreate = Handler(func(opt * opt) (State, error) {message := fmt.sprintf (" Order ID(%d), order name (%s)... Deal with it!" , opt.OrderId, opt.OrderName) fmt.Println(message) if opt.HandlerSendSMS ! = nil {_ = opt.handlersendSms ("18888888888", "Congratulations on your reservation!" } return StatusReserved, nil}) // HandlerConfirm = HandlerConfirm (func(opt * opt) (State, Error) {return statusconfirm, nil}) // HandlerModify = Handler(func(opt * opt) (State, Error) {return StatusReserved, nil}) // HandlerPay pay order handlerPay = Handler(func(opt * opt) (State, error) { return StatusLocked, nil }) )

Maintain specific event handling methods in this file, if the logic is more complex, you can consider splitting the file processing.

The core code

Type State int type Event string type Handler func(opt * opt) (State, error) // Handlers map[state]map[Event]Handler // Handlers map[state]Handler // Handlers map[Event]Handler // Handlers map[state]Handler // Handlers map[Event]Handler // Handlers map[Event]Handler // } // Get State func (f *FSM) getState() State {return f.state} // Set State func (f *FSM) setState(newState NewState = newState} func (f *FSM) addHandlers() (*FSM, error) {... Return f, nil} // Call event func (f *FSM) Call(event event, opts... Option) (State, error) { f.mu.Lock() defer f.mu.Unlock() ... Return f.getState(), nil} // newFSM func newFSM (initState) (FSM *FSM, err error) { fsm = new(FSM) fsm.state = initState fsm.handlers = make(map[State]map[Event]Handler) fsm, err = fsm.addHandlers() if err ! = nil { return } return }

For order status operations, just use the Call method!

I will not post the code for the addHandlers and Call methods. I will provide the source address at the end of this article for download.

Call way

For example, if the current state is the default state, proceed as follows:

  • Create the order, the state becomesbooking;
  • Change orders, is not operable (booked status is not modifiable);
  • Make sure the order, the state becomesHave been confirmed;
  • Change orders, the state becomesbooking;
  • Make sure the order, the state becomesHave been confirmed;
  • Payment order, the state becomesHas been locked;
OrderStatus := Order.statusDefault OrderMachine, err := Order.newFsm (OrderStatus) if err! = nil {fmt.println (err.error ()) return} // Create the order, If _, err = OrderMachine.Call(Order.eventCreate, Order.withOrderId (1), Order.withOrderName (" Test Order "), order.WithHandlerSendSMS(sendSMS), ); err ! = nil {fmt.println (err.error ())} if _, err = OrderMachine.Call(Order.EventModify); err ! = nil {fmt.println (err.error ())} if _, err = OrderMachine.Call(Order.eventConfirm); err ! = nil {fmt.println (err.error ())} if _, err = OrderMachine.Call(Order.EventModify); err ! = nil {fmt.println (err.error ())} if _, err = OrderMachine.Call(Order.eventConfirm); err ! = nil {fmt.println (err.error ())} if _, err = OrderMachine.call (order.eventpay); err ! = nil { fmt.Println(err.Error()) }

Output:

Process creating order logic, order ID(1), order name (test order)... Disposal over! Send a text message to (18888888888) sent (congratulations on your reservation success! [] create order operation, state from [the] to [booking] [warning] state (booking) are not allowed (change order) operation order [sure], state from [booking] to [has confirmed] operation [change order], state from [has confirmed] to [booking] operation [order], Status changes from Booked to Confirmed operation Pay order, Status changes from Confirmed to Locked

summary

The above is my technical scheme, I hope it can be helpful to you. If you are interested, you can package it again. The above code has been submitted to GitHub GO-FSM-Order for download and use.