Encapsulate operations for queuing, audit history, and undo/redo while keeping Go error handling explicit.
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 Pattern
Step 1 of 5
The Command Interface
Command declares Execute, Undo, and Name. Any operation that needs history, queuing, or undo satisfies this contract. The interface stays intentionally narrow.
Think of a restaurant ticket. A server writes down a request as a discrete order, the kitchen executes it later, and the restaurant can track, retry, or cancel that ticket without the customer needing to know the cooking details.
Pros and Cons
Pros
Cons
Treats operations as values that can be queued, retried, or stored.
Adds boilerplate types for each operation.
Separates the requester from the executor cleanly.
Undo logic can be hard or impossible for some commands.
Supports history, undo, and audit-friendly workflows.
May be unnecessary overhead for simple direct service calls.
Best Practices
Keep the command interface small so new commands are cheap to add.
Model failure explicitly. Go code should return results and errors rather than relying on hidden state.
Protect shared history with a mutex if multiple goroutines can execute commands concurrently.
Use commands when you need history, queuing, or undo. For one-off service calls, a plain method call is usually enough.
Avoid coupling commands to transport or framework details. They should represent business operations, not HTTP handlers.
When to Use
You need operation history, undo/redo, queuing, or delayed execution.
The caller should not know how the work is performed internally.
You want to treat operations as values that can be stored, retried, or scheduled.
When NOT to Use
The operation is a simple method call with no history or coordination concerns.
Wrapping every function call in a command would add ceremony without value.
The extra allocation and bookkeeping would hurt a hot path without providing useful decoupling.