Sample code from ecommerce project. It should be long and wide enough for demo purpose.
module Processes
class ReservationProcess
include Infra::ProcessManager.with_state {
ProcessState
}
subscribes_to(
Pricing::OfferAccepted,
Fulfillment::OrderCancelled,
Fulfillment::OrderConfirmed
)
private
def act
case state
in order: :accepted
unavailable = reserve_stock
if unavailable.any?
reject_order(unavailable)
else
accept_order
end
in order: :cancelled
release_stock(
state.reserved_product_ids
)
in order: :confirmed
dispatch_stock
else
end
end
def apply(event)
case event
when Pricing::OfferAccepted
order_lines_hash =
event
.data
.fetch(:order_lines)
.map do |ol|
[
ol.fetch(:product_id),
ol.fetch(:quantity)
]
end
.to_h
state.with(
order: :accepted,
order_lines: order_lines_hash
)
when Fulfillment::OrderCancelled
state.with(order: :cancelled)
when Fulfillment::OrderConfirmed
state.with(order: :confirmed)
end
end
def reserve_stock
unavailable_products = []
reserved_products = []
state
.order_lines
.each do |product_id, quantity|
command_bus.(
Inventory::Reserve.new(
product_id: product_id,
quantity: quantity
)
)
reserved_products << product_id
rescue Inventory::InventoryEntry::InventoryNotAvailable
unavailable_products << product_id
end
if unavailable_products.any?
release_stock(reserved_products)
end
unavailable_products
end
def release_stock(product_ids)
state
.order_lines
.slice(*product_ids)
.each do |product_id, quantity|
command_bus.(
Inventory::Release.new(
product_id: product_id,
quantity: quantity
)
)
end
end
def dispatch_stock
state
.order_lines
.each do |product_id, quantity|
command_bus.(
Inventory::Dispatch.new(
product_id: product_id,
quantity: quantity
)
)
end
end
def accept_order
command_bus.(
Fulfillment::RegisterOrder.new(
order_id: id
)
)
end
def reject_order(
unavailable_product_ids
)
command_bus.(
Pricing::RejectOffer.new(
order_id: id,
reason:
"Some products were unavailable",
unavailable_product_ids:
)
)
end
def fetch_id(event)
event.data.fetch(:order_id)
end
ProcessState =
Data.define(
:order,
:order_lines
) do
def initialize(
order: nil,
order_lines: []
)
super(
order:,
order_lines:
order_lines.freeze
)
end
def reserved_product_ids =
order_lines.keys
end
end
end
module Processes
class ReservationProcess
include Infra::ProcessManager.with_state { ProcessState }
subscribes_to(
Pricing::OfferAccepted,
Fulfillment::OrderCancelled,
Fulfillment::OrderConfirmed
)
private
def act
case state
in order: :accepted
unavailable = reserve_stock
unavailable.any? ? reject_order(unavailable) : accept_order
in order: :cancelled
release_stock(state.reserved_product_ids)
in order: :confirmed
dispatch_stock
else
end
end
def apply(event)
case event
when Pricing::OfferAccepted
order_lines_hash =
event
.data
.fetch(:order_lines)
.map { |ol| [ol.fetch(:product_id), ol.fetch(:quantity)] }
.to_h
state.with(order: :accepted, order_lines: order_lines_hash)
when Fulfillment::OrderCancelled
state.with(order: :cancelled)
when Fulfillment::OrderConfirmed
state.with(order: :confirmed)
end
end
def reserve_stock
unavailable_products = []
reserved_products = []
state.order_lines.each do |product_id, quantity|
command_bus.(
Inventory::Reserve.new(product_id: product_id, quantity: quantity)
)
reserved_products << product_id
rescue Inventory::InventoryEntry::InventoryNotAvailable
unavailable_products << product_id
end
release_stock(reserved_products) if unavailable_products.any?
unavailable_products
end
def release_stock(product_ids)
state
.order_lines
.slice(*product_ids)
.each do |product_id, quantity|
command_bus.(
Inventory::Release.new(product_id: product_id, quantity: quantity)
)
end
end
def dispatch_stock
state.order_lines.each do |product_id, quantity|
command_bus.(
Inventory::Dispatch.new(product_id: product_id, quantity: quantity)
)
end
end
def accept_order
command_bus.(Fulfillment::RegisterOrder.new(order_id: id))
end
def reject_order(unavailable_product_ids)
command_bus.(
Pricing::RejectOffer.new(
order_id: id,
reason: "Some products were unavailable",
unavailable_product_ids:
)
)
end
def fetch_id(event)
event.data.fetch(:order_id)
end
ProcessState =
Data.define(:order, :order_lines) do
def initialize(order: nil, order_lines: [])
super(order:, order_lines: order_lines.freeze)
end
def reserved_product_ids = order_lines.keys
end
end
end
module Processes
class ReservationProcess
include Infra::ProcessManager.with_state { ProcessState }
subscribes_to(Pricing::OfferAccepted, Fulfillment::OrderCancelled, Fulfillment::OrderConfirmed)
private
def act
case state
in order: :accepted
unavailable = reserve_stock
unavailable.any? ? reject_order(unavailable) : accept_order
in order: :cancelled
release_stock(state.reserved_product_ids)
in order: :confirmed
dispatch_stock
else
end
end
def apply(event)
case event
when Pricing::OfferAccepted
order_lines_hash = event.data.fetch(:order_lines).map { |ol| [ol.fetch(:product_id), ol.fetch(:quantity)] }.to_h
state.with(order: :accepted, order_lines: order_lines_hash)
when Fulfillment::OrderCancelled
state.with(order: :cancelled)
when Fulfillment::OrderConfirmed
state.with(order: :confirmed)
end
end
def reserve_stock
unavailable_products = []
reserved_products = []
state.order_lines.each do |product_id, quantity|
command_bus.(Inventory::Reserve.new(product_id: product_id, quantity: quantity))
reserved_products << product_id
rescue Inventory::InventoryEntry::InventoryNotAvailable
unavailable_products << product_id
end
release_stock(reserved_products) if unavailable_products.any?
unavailable_products
end
def release_stock(product_ids)
state
.order_lines
.slice(*product_ids)
.each do |product_id, quantity|
command_bus.(Inventory::Release.new(product_id: product_id, quantity: quantity))
end
end
def dispatch_stock
state.order_lines.each do |product_id, quantity|
command_bus.(Inventory::Dispatch.new(product_id: product_id, quantity: quantity))
end
end
def accept_order
command_bus.(Fulfillment::RegisterOrder.new(order_id: id))
end
def reject_order(unavailable_product_ids)
command_bus.(
Pricing::RejectOffer.new(order_id: id, reason: "Some products were unavailable", unavailable_product_ids:)
)
end
def fetch_id(event)
event.data.fetch(:order_id)
end
ProcessState =
Data.define(:order, :order_lines) do
def initialize(order: nil, order_lines: [])
super(order:, order_lines: order_lines.freeze)
end
def reserved_product_ids = order_lines.keys
end
end
end