WASM

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
        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