ruby 58 lines · 10 steps

Modeling an order lifecycle with AASM in Rails

A state machine drives an order from pending through shipped with guards and side effects at each transition.

Explained by highlit
1class Order < ApplicationRecord
2 include AASM
3 
4 belongs_to :warehouse
5 has_many :line_items
6 
7 validates :shipping_address, presence: true, if: :shipped?
8 
9 aasm column: :state do
10 state :pending, initial: true
11 state :paid
12 state :fulfilled
13 state :shipped
14 state :cancelled
15 
16 event :pay do
17 transitions from: :pending, to: :paid, guard: :payment_captured?
18 end
19 
20 event :fulfill do
21 before do
22 reserve_inventory!
23 end
24 
25 transitions from: :paid, to: :fulfilled, guard: :all_items_in_stock?
26 end
27 
28 event :ship do
29 before do
30 raise AASM::InvalidTransition.new(self, :ship, :state) if shipping_address.blank?
31 self.shipped_at = Time.current
32 end
33 
34 transitions from: :fulfilled, to: :shipped
35 after do
36 ShipmentMailer.with(order: self).dispatched.deliver_later
37 end
38 end
39 
40 event :cancel do
41 transitions from: %i[pending paid], to: :cancelled
42 end
43 end
44 
45 private
46 
47 def payment_captured?
48 payment.present? && payment.captured?
49 end
50 
51 def all_items_in_stock?
52 line_items.all? { |item| warehouse.available?(item.sku, item.quantity) }
53 end
54 
55 def reserve_inventory!
56 line_items.each { |item| warehouse.reserve!(item.sku, item.quantity) }
57 end
58end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Guards keep transitions legal by refusing to fire until the domain preconditions are actually met.
  2. 2before and after hooks bind side effects like inventory reservation and mail directly to the transition that causes them.
  3. 3Encoding the lifecycle as named states and events makes the valid paths through your domain explicit and self-documenting.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Modeling an order lifecycle with AASM in Rails — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code