Skip to content

Introduced state projectors #444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Processes
class DetermineVatRatesOnOrderPlaced
include Infra::ProcessManager.with_state { ProcessState }
include Infra::ProcessManager.with_state { StateProjector }

subscribes_to(
Pricing::OfferAccepted,
Expand All @@ -21,29 +21,35 @@ def determine_vat_rates
end
end

def apply(event)
case event
when Pricing::OfferAccepted
state.with(
offer_accepted: true,
order_lines: event.data.fetch(:order_lines),
order_id: event.data.fetch(:order_id)
)
when Fulfillment::OrderRegistered
state.with(order_placed: true)
end
end

def fetch_id(event)
event.data.fetch(:order_id)
end

ProcessState = Data.define(:offer_accepted, :order_placed, :order_id, :order_lines) do
def initialize(offer_accepted: false, order_placed: false, order_id: nil, order_lines: [])
super
class StateProjector
ProcessState = Data.define(:offer_accepted, :order_placed, :order_id, :order_lines) do
def initialize(offer_accepted: false, order_placed: false, order_id: nil, order_lines: [])
super
end

def placed? = offer_accepted && order_placed
end

def placed? = offer_accepted && order_placed
def self.initial_state_instance
ProcessState.new
end

def self.apply(state_instance, event)
case event
when Pricing::OfferAccepted
state_instance.with(
offer_accepted: true,
order_lines: event.data.fetch(:order_lines),
order_id: event.data.fetch(:order_id)
)
when Fulfillment::OrderRegistered
state_instance.with(order_placed: true)
end
end
end
end
end
61 changes: 33 additions & 28 deletions ecommerce/processes/lib/processes/order_item_invoicing_process.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module Processes
class OrderItemInvoicingProcess
include Infra::ProcessManager.with_state { ProcessState }

include Infra::ProcessManager.with_state { StateProjector }
subscribes_to(
Pricing::PriceItemValueCalculated,
Taxes::VatRateDetermined
Expand All @@ -26,35 +25,9 @@ def act
end
end

def apply(event)
case event
when Pricing::PriceItemValueCalculated
state.with(
order_id: event.data.fetch(:order_id),
product_id: event.data.fetch(:product_id),
quantity: event.data.fetch(:quantity),
discounted_amount: event.data.fetch(:discounted_amount)
)
when Taxes::VatRateDetermined
state.with(
vat_rate: event.data.fetch(:vat_rate)
)
end
end

def fetch_id(event)
"#{event.data.fetch(:order_id)}$#{event.data.fetch(:product_id)}"
end

ProcessState = Data.define(:order_id, :product_id, :quantity, :vat_rate, :discounted_amount) do
def initialize(order_id: nil, product_id: nil, quantity: nil, vat_rate: nil, discounted_amount: nil)
super
end

def can_create_invoice_item?
order_id && product_id && quantity && vat_rate && discounted_amount
end
end
end

class MoneySplitter
Expand Down Expand Up @@ -82,4 +55,36 @@ def call
distributed_amounts
end
end

class StateProjector
ProcessState = Data.define(:order_id, :product_id, :quantity, :vat_rate, :discounted_amount) do
def initialize(order_id: nil, product_id: nil, quantity: nil, vat_rate: nil, discounted_amount: nil)
super
end

def can_create_invoice_item?
order_id && product_id && quantity && vat_rate && discounted_amount
end
end

def self.initial_state_instance
ProcessState.new
end

def self.apply(state_instance, event)
case event
when Pricing::PriceItemValueCalculated
state_instance.with(
order_id: event.data.fetch(:order_id),
product_id: event.data.fetch(:product_id),
quantity: event.data.fetch(:quantity),
discounted_amount: event.data.fetch(:discounted_amount)
)
when Taxes::VatRateDetermined
state_instance.with(
vat_rate: event.data.fetch(:vat_rate)
)
end
end
end
end
55 changes: 30 additions & 25 deletions ecommerce/processes/lib/processes/release_payment_process.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module Processes
class ReleasePaymentProcess
include Infra::ProcessManager.with_state { ProcessState }

include Infra::ProcessManager.with_state { StateProjector }
subscribes_to(
Payments::PaymentAuthorized,
Payments::PaymentReleased,
Expand All @@ -16,24 +15,6 @@ def act
release_payment if state.release?
end

def apply(event)
case event
when Payments::PaymentAuthorized
state.with(payment: :authorized)
when Payments::PaymentReleased
state.with(payment: :released)
when Fulfillment::OrderRegistered
state.with(
order: :placed,
order_id: event.data.fetch(:order_id)
)
when Pricing::OfferExpired
state.with(order: :expired)
when Fulfillment::OrderConfirmed
state.with(order: :confirmed)
end
end

def release_payment
command_bus.call(Payments::ReleasePayment.new(order_id: state.order_id))
end
Expand All @@ -42,13 +23,37 @@ def fetch_id(event)
event.data.fetch(:order_id)
end

ProcessState = Data.define(:order, :payment, :order_id) do
def initialize(order: :draft, payment: :none, order_id: nil)
super
class StateProjector
ProcessState = Data.define(:order, :payment, :order_id) do
def initialize(order: :draft, payment: :none, order_id: nil)
super
end

def release?
payment.eql?(:authorized) && order.eql?(:expired)
end
end

def self.initial_state_instance
ProcessState.new
end

def release?
payment.eql?(:authorized) && order.eql?(:expired)
def self.apply(state_instance, event)
case event
when Payments::PaymentAuthorized
state_instance.with(payment: :authorized)
when Payments::PaymentReleased
state_instance.with(payment: :released)
when Fulfillment::OrderRegistered
state_instance.with(
order: :placed,
order_id: event.data.fetch(:order_id)
)
when Pricing::OfferExpired
state_instance.with(order: :expired)
when Fulfillment::OrderConfirmed
state_instance.with(order: :confirmed)
end
end
end
end
Expand Down
47 changes: 28 additions & 19 deletions ecommerce/processes/lib/processes/reservation_process.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Processes
class ReservationProcess
include Infra::ProcessManager.with_state { ProcessState }
include Infra::ProcessManager.with_state { StateProjector }

subscribes_to(
Pricing::OfferAccepted,
Expand Down Expand Up @@ -28,20 +28,6 @@ def act
end
end

def apply(event)
case event
when Pricing::OfferAccepted
state.with(
order: :accepted,
order_lines: event.data.fetch(:order_lines).map { |ol| [ol.fetch(:product_id), ol.fetch(:quantity)] }.to_h
)
when Fulfillment::OrderCancelled
state.with(order: :cancelled)
when Fulfillment::OrderConfirmed
state.with(order: :confirmed)
end
end

def reserve_stock
unavailable_products = []
reserved_products = []
Expand Down Expand Up @@ -84,12 +70,34 @@ 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)
class StateProjector
ProcessState = Data.define(:order, :order_lines) do
def initialize(order: nil, order_lines: [])
super(order: order, order_lines: order_lines.freeze)
end

def reserved_product_ids
order_lines.keys
end
end

def reserved_product_ids = order_lines.keys
def self.initial_state_instance
ProcessState.new
end

def self.apply(state_instance, event)
case event
when Pricing::OfferAccepted
state_instance.with(
order: :accepted,
order_lines: event.data.fetch(:order_lines).map { |ol| [ol.fetch(:product_id), ol.fetch(:quantity)] }.to_h
)
when Fulfillment::OrderCancelled
state_instance.with(order: :cancelled)
when Fulfillment::OrderConfirmed
state_instance.with(order: :confirmed)
end
end
end

class SomeInventoryNotAvailable < StandardError
Expand All @@ -99,5 +107,6 @@ def initialize(unavailable_products)
@unavailable_products = unavailable_products
end
end

end
end
34 changes: 20 additions & 14 deletions ecommerce/processes/lib/processes/shipment_process.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Processes
class ShipmentProcess
include Infra::ProcessManager.with_state { ProcessState }
include Infra::ProcessManager.with_state { StateProjector }

subscribes_to(
Shipping::ShippingAddressAddedToShipment,
Expand All @@ -21,17 +21,6 @@ def act
end
end

def apply(event)
case event
when Shipping::ShippingAddressAddedToShipment
state.with(shipment: :address_set)
when Fulfillment::OrderRegistered
state.with(order: :placed)
when Fulfillment::OrderConfirmed
state.with(order: :confirmed)
end
end

def submit_shipment
command_bus.call(Shipping::SubmitShipment.new(order_id: id))
end
Expand All @@ -44,8 +33,25 @@ def fetch_id(event)
event.data.fetch(:order_id)
end

ProcessState = Data.define(:order, :shipment) do
def initialize(order: nil, shipment: nil) = super
class StateProjector
ProcessState = Data.define(:order, :shipment) do
def initialize(order: nil, shipment: nil) = super
end

def self.initial_state_instance
ProcessState.new
end

def self.apply(state_instance, event)
case event
when Shipping::ShippingAddressAddedToShipment
state_instance.with(shipment: :address_set)
when Fulfillment::OrderRegistered
state_instance.with(order: :placed)
when Fulfillment::OrderConfirmed
state_instance.with(order: :confirmed)
end
end
end
end
end
Loading