preface

When designing the order module of the e-commerce system, the order will involve various states and the flow between states, and the scalability and maintainability are the key points we need to pay attention to! This article shares my technical solution.

As shown in the figure above, golang is used to implement the order flow in the figure above, and can be completed quickly when order status or order events are added later.

purpose

With respect to order status processing, use a unified portal to improve program scalability and maintainability.

Logical analysis

The order status can be Default, booked, Confirmed, or locked.

Order events include creating an order, confirming an order, modifying an order, and paying an order.

From the figure above we also know the relationship between states and events, such that only confirmed orders can be modified.

The following issues need to be considered:

  1. When order status is increased, how to make as few changes as possible or changes that have little impact on history?
  2. What if each event’s handling method requires a different input if called from the same entry?
  3. After an event is completed, SMS message or client Push operation may be performed. How to handle this?
  4. It is possible that the processing logic of an event is somewhat different in different platforms (C terminal, merchant background, management platform). How to deal with it?

How can code be designed to solve these problems?

The following is my code to achieve, for your reference, to achieve in the creation of an order, the incoming parameters and completed to send a short message to the user, the operation of 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: "default ", StatusReserved: "Booked ", StatusConfirmed:" confirmed ", StatusLocked: Var statusEvent = map[State][]Event{StatusDefault: {EventCreate}, StatusReserved: {EventConfirm}, StatusConfirmed: {EventModify, EventPay}, } func StatusText(status State) string { return statusText[status] }Copy the code

When the status of a new order increases, the corresponding state can be added to this file, and the relationship between order status and order events should 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, }Copy the code

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 should be maintained.

Defines the handling of events

Var (// handlerCreate = Handler(func(opt * opt) (State, error) {message := FMT.Sprintf(" "); Order ID(%d), order name (%s)... Done!" , opt.OrderId, opt.OrderName) fmt.Println(message) if opt.HandlerSendSMS ! = nil {_ = opt.HandlerSendSMS("18888888888", "Congratulations on your reservation!") )} return StatusReserved, nil}) handlerConfirm = Handler(func(opt * opt) (State, Error) {return StatusConfirmed, nil}) handlerModify = Handler(func(opt * opt) (State, Error) {return StatusReserved, nil}) // handlerPay = Handler(func(opt * opt) (State, error) { return StatusLocked, nil }) )Copy the code

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

The core code

// Event type Handler func(opt * opt) (State, error) Type FSM struct {mu sync.Mutex // Lock state state // Current state handlers map[state]map[Event]Handler Func (f *FSM) getState() State {return f.state} // Set the State func (f *FSM) setState(newState) State) {f.state = newState} func (f *FSM) addHandlers() (*FSM, error) {... Return f, nil} func (f *FSM) Call(event event, opts... Option) (State, error) { f.mu.Lock() defer f.mu.Unlock() ... Return f.getstate (), nil} // NewFSM instantiates FSM func NewFSM(initState State) (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 }Copy the code

To manipulate order status, just use the Call method!

The code for the addHandlers and Call methods will not be posted. I have provided the source code address for you to download at the end of this article.

Call way

For example, if the current state is the default state, perform the following operations:

  • Create the order, the state becomesbooking;
  • Change orders, cannot be operated (the reserved status cannot be modified);
  • 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 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 ())} // Confirm the order if _, err = orderMachine.Call(order.eventConfirm); err ! = nil {fmt.println (err.error ())} if _, err = orderMachine.Call(order.eventmodify); err ! = nil {fmt.println (err.error ())} // Confirm the order if _, err = orderMachine.Call(order.eventConfirm); err ! = nil {fmt.println (err.error ())} // Pay the order if _, err = orderMachine.Call(order.eventpay); err ! = nil { fmt.Println(err.Error()) }Copy the code

Output:

Processing create order logic, order ID(1), order name (test order)... Done! Send text messages 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 changed from [booked] to [confirmed] operation [pay order], status changed from [confirmed] to [locked]Copy the code

summary

The above is my technical solution, I hope it can be helpful to you. If you are interested in it, you can wrap it again. The above code has been submitted to github go-FSM -order for download and use.