The State pattern moves the behaviour that depends on status into one object per state. Order holds a state object. order.pay() hands the work to this.state.pay(order).
For example:
PlacedState.pay()records the payment and switches the order toPaidState.ShippedState.cancel()throws an invalid transition error.
Each state class lists exactly what is allowed from that state. To add a state, you add a class. You don't edit the conditionals in every method.
It is worth it when many operations behave differently per state and the rules are complex. Sometimes the logic is mostly "is this transition allowed?". Then a simpler option is an explicit transition table. For example, a map from each status to its allowed next statuses, checked in one transitionTo() method. This keeps the order a plain object that is easy to save to the database. It also puts the rules in one readable place.
Either way, keep transitions inside the aggregate (the Order domain object). Don't let services set status directly.
- There is one class per state, and the context passes each call to the current state.
- Each state defines its allowed operations and its next state.
- The pattern is worth it when many operations change by state.
- A transition table is often enough for simple allowed-transition rules.
- Keep transitions inside the domain object.