ខ្ចប់ operations សម្រាប់ queuing, audit history និង undo/redo ខណៈដែលរក្សា Go error handling ឱ្យច្បាស់លាស់។
The Problem
Operations such as placing an order, cancelling it, and issuing a refund often need the same extra capabilities: audit history, delayed execution, undo/redo, and a clean separation between the code that requests an action and the code that performs it. If those concerns are embedded directly in handlers, the workflow becomes tightly coupled and hard to extend.
The Solution
The Command pattern wraps each operation in a value that satisfies a small interface. An invoker executes commands, stores history, and coordinates undo/redo. In Go, the pattern works well when you combine interfaces, explicit result values, and ordinary synchronization primitives.
Structure
Command interface: Declares Execute, Undo, and Name.
Concrete commands: Order placement, cancellation, and refund operations.
Invoker: Stores execution history and provides undo/redo.
Client: Application code that creates commands and submits them.
Implementation
package mainimport "time"// CommandResult represents the result of executing a commandtype CommandResult struct { Success bool `json:"success"` Message string `json:"message"` Data interface{} `json:"data,omitempty"`}// OrderData represents order informationtype OrderData struct { OrderID string `json:"orderId"` Amount float64 `json:"amount,omitempty"` Status string `json:"status"` Timestamp time.Time `json:"timestamp"`}// Command defines the interface for all commandstype Command interface { Execute() CommandResult Undo() CommandResult Name() string}
package mainimport "time"// PlaceOrderCommand represents a command to place an ordertype PlaceOrderCommand struct { orderID string amount float64 executed bool}// NewPlaceOrderCommand creates a new place order commandfunc NewPlaceOrderCommand(orderID string, amount float64) *PlaceOrderCommand { return &PlaceOrderCommand{ orderID: orderID, amount: amount, }}// Execute performs the place order operationfunc (c *PlaceOrderCommand) Execute() CommandResult { if c.executed { return CommandResult{ Success: false, Message: "Order " + c.orderID + " has already been placed", } } c.executed = true return CommandResult{ Success: true, Message: "Order " + c.orderID + " placed successfully", Data: OrderData{ OrderID: c.orderID, Amount: c.amount, Status: "placed", Timestamp: time.Now(), }, }}// Undo reverses the place order operationfunc (c *PlaceOrderCommand) Undo() CommandResult { if !c.executed { return CommandResult{ Success: false, Message: "Order " + c.orderID + " was never placed — cannot undo", } } c.executed = false return CommandResult{ Success: true, Message: "Order " + c.orderID + " placement reversed", Data: OrderData{ OrderID: c.orderID, Status: "reversed", Timestamp: time.Now(), }, }}// Name returns the command namefunc (c *PlaceOrderCommand) Name() string { return "PlaceOrder"}
package mainimport "time"// CancelOrderCommand represents a command to cancel an ordertype CancelOrderCommand struct { orderID string executed bool}// NewCancelOrderCommand creates a new cancel order commandfunc NewCancelOrderCommand(orderID string) *CancelOrderCommand { return &CancelOrderCommand{ orderID: orderID, }}// Execute performs the cancel order operationfunc (c *CancelOrderCommand) Execute() CommandResult { if c.executed { return CommandResult{ Success: false, Message: "Order " + c.orderID + " has already been cancelled", } } c.executed = true return CommandResult{ Success: true, Message: "Order " + c.orderID + " cancelled successfully", Data: OrderData{ OrderID: c.orderID, Status: "cancelled", Timestamp: time.Now(), }, }}// Undo reverses the cancel order operationfunc (c *CancelOrderCommand) Undo() CommandResult { if !c.executed { return CommandResult{ Success: false, Message: "Order " + c.orderID + " was never cancelled — cannot undo", } } c.executed = false return CommandResult{ Success: true, Message: "Order " + c.orderID + " cancellation reversed", Data: OrderData{ OrderID: c.orderID, Status: "restored", Timestamp: time.Now(), }, }}// Name returns the command namefunc (c *CancelOrderCommand) Name() string { return "CancelOrder"}
package mainimport "time"// RefundOrderCommand represents a command to refund an ordertype RefundOrderCommand struct { orderID string amount float64 executed bool}// NewRefundOrderCommand creates a new refund order commandfunc NewRefundOrderCommand(orderID string, amount float64) *RefundOrderCommand { return &RefundOrderCommand{ orderID: orderID, amount: amount, }}// Execute performs the refund order operationfunc (c *RefundOrderCommand) Execute() CommandResult { if c.executed { return CommandResult{ Success: false, Message: "Order " + c.orderID + " has already been refunded", } } c.executed = true return CommandResult{ Success: true, Message: "Order " + c.orderID + " refunded successfully", Data: OrderData{ OrderID: c.orderID, Amount: c.amount, Status: "refunded", Timestamp: time.Now(), }, }}// Undo reverses the refund order operationfunc (c *RefundOrderCommand) Undo() CommandResult { if !c.executed { return CommandResult{ Success: false, Message: "Order " + c.orderID + " was never refunded — cannot undo", } } c.executed = false return CommandResult{ Success: true, Message: "Order " + c.orderID + " refund reversed", Data: OrderData{ OrderID: c.orderID, Status: "refund_reversed", Timestamp: time.Now(), }, }}// Name returns the command namefunc (c *RefundOrderCommand) Name() string { return "RefundOrder"}
package mainimport ( "errors" "sync")// OrderCommandInvoker manages command execution, undo/redo, and historytype OrderCommandInvoker struct { history []Command current int mu sync.RWMutex}// NewOrderCommandInvoker creates a new command invokerfunc NewOrderCommandInvoker() *OrderCommandInvoker { return &OrderCommandInvoker{ history: make([]Command, 0), current: -1, }}// Execute runs a command and adds it to historyfunc (i *OrderCommandInvoker) Execute(cmd Command) (CommandResult, error) { i.mu.Lock() defer i.mu.Unlock() result := cmd.Execute() if !result.Success { return result, errors.New(result.Message) } // Remove any commands after current position (for redo functionality) i.history = i.history[:i.current+1] // Add command to history i.history = append(i.history, cmd) i.current++ return result, nil}// Undo reverses the last executed commandfunc (i *OrderCommandInvoker) Undo() (CommandResult, error) { i.mu.Lock() defer i.mu.Unlock() if i.current < 0 { return CommandResult{}, errors.New("no commands to undo") } cmd := i.history[i.current] result := cmd.Undo() if !result.Success { return result, errors.New(result.Message) } i.current-- return result, nil}// Redo re-executes the next command in historyfunc (i *OrderCommandInvoker) Redo() (CommandResult, error) { i.mu.Lock() defer i.mu.Unlock() if i.current >= len(i.history)-1 { return CommandResult{}, errors.New("no commands to redo") } i.current++ cmd := i.history[i.current] result := cmd.Execute() if !result.Success { i.current-- // rollback on failure return result, errors.New(result.Message) } return result, nil}// GetHistory returns a list of executed command namesfunc (i *OrderCommandInvoker) GetHistory() []string { i.mu.RLock() defer i.mu.RUnlock() history := make([]string, 0, len(i.history)) for j, cmd := range i.history { status := "pending" if j <= i.current { status = "executed" } history = append(history, cmd.Name()+" ("+status+")") } return history}// ExecuteAsync runs a command asynchronouslyfunc (i *OrderCommandInvoker) ExecuteAsync(cmd Command, resultChan chan<- CommandResult, errorChan chan<- error) { go func() { result, err := i.Execute(cmd) if err != nil { errorChan <- err return } resultChan <- result }()}