← Back to PatternPip

STATE / THE IDEA IN A MINUTE

Same order.
Different rules as it moves.

An unpaid order can be paid. A paid order can be shipped. A shipped order cannot be cancelled in our shop. The command means something different depending on where the order is in its lifecycle.

State lets an object delegate behavior to its current state object. When the order moves from Pending to Paid, its next command reaches Paid’s rules. The caller keeps using the same Order.

What changes?
The rules for the next command depend on whether the order is Pending, Paid, Shipped or Cancelled.
What stays the same?
The caller sends pay, ship or cancel to the same Order object.
Where do the rules live?
Each state object handles its own commands and proposes the next state. Order applies the decision.

THE STRUCTURE / ONE CONTEXT, CHANGING BEHAVIOR

The order stays. Its current state changes.

A status label tells you where an order is. A state object also knows how it should respond. Explore each stage’s rules below; the live order further down is independent.

01 / CALLER

Checkout or fulfilment UI

Sends a command to the same Order. It does not choose the next state itself.

order.send("ship")

Calls Order through send(event).

02 / CONTEXT

Order

Holds one current OrderState and the payment, shipment and refund counts.

this.state.handle(event)

Delegates to the current state through OrderState.

03 / STATE CONTRACT

OrderState

Every state accepts a command and returns a decision: accept or reject, an optional effect and the next state.

handle(event): Decision

Implemented by Pending, Paid, Shipped and Cancelled.

04 / CONCRETE STATES

Pending · Paid · Shipped · Cancelled

Group the rules for one stage. Pending can pay; Paid can ship. Both can cancel, but only Paid requests a refund.

Returns a decision, including the next state, for Order to install.

Solid arrows show calls: the UI calls Order, which delegates through OrderState. The dashed arrow shows concrete states implementing that contract. The decision returns through the delegated call; Order applies it.
  • pay → PaidRecord one payment; the order is now Paid.
  • ship → rejectedPending rejects ship. Nothing changes.
  • cancel → CancelledCancel the unpaid order. No refund is needed.

In this implementation, states propose transitions and Order applies them. Other State implementations let a state call back into its context. The important boundary is who owns the stage-specific behavior.

FOLLOW ONE ORDER / AT YOUR PACE

Can you ship it before paying?

Try it yourself

Try Ship first, then Pay and Ship. Reset to compare cancellation before and after payment. Commands stay available so you can explore rejection.

Same order · #1042
  1. Pendingpay → Paid / cancel → Cancelled
  2. Paidship → Shipped / cancel → Cancelled
  3. ShippedNo further commands accepted
  4. CancelledNo further commands accepted
Payments recorded
0
Shipments recorded
0
Refunds recorded
0

Order #1042 starts Pending. Choose a command, then use Next step to follow its execution.

Local simulation only. No real payments or deliveries. Shipped and Cancelled are terminal here; returns and delivery tracking are outside this lesson. Back replays an explanation; it does not undo a command.

When stages have real behavior.

Useful when many operations depend on the same lifecycle stage, and their rules keep changing together. Each state gives those rules a home.

Keep the cost in view.

A handful of stable transitions can be clearer as an enum and a transition table. State adds objects and navigation between them. It does not provide persistence, concurrency control or reliable payment processing.

Choose your next step.