Skip to content

Commit

Permalink
Encapsulate Availability read model logic
Browse files Browse the repository at this point in the history
see 1c2845f and read
https://blog.arkency.com/offloading-write-side-with-a-read-model/
to see why read model is used in this case
  • Loading branch information
pjurewicz committed Apr 30, 2024
1 parent e6da5a7 commit 14101a1
Show file tree
Hide file tree
Showing 5 changed files with 10 additions and 24 deletions.
18 changes: 0 additions & 18 deletions ecommerce/ordering/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,6 @@ It doesn't have any impact on the state machine.
It's only needed to some read models, which can retrieve it from elsewhere - most notably the `Pricing`
The implementation of `Basket`, because of this, seems duplicated to Pricing or Inventory.

#### Checking availability in the controllers

```ruby
def add_item
read_model = Orders::OrderLine.where(order_uid: params[:id], product_id: params[:product_id]).first
if Availability::Product.exists?(["uid = ? and available < ?", params[:product_id], (read_model&.quantity || 0) + 1])
redirect_to edit_order_path(params[:id]),
alert: "Product not available in requested quantity!" and return
end
ActiveRecord::Base.transaction do
command_bus.(Ordering::AddItemToBasket.new(order_id: params[:id], product_id: params[:product_id]))
end
head :ok
end
```

This code is duplicated for admin creating orders and clients creating orders.

#### Mapping of events between domains

UI -> Ordering::SubmitOrder -> Ordering::Submitted -> Reservation(process) -> Ordering::AcceptOrder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def edit

def add_item
read_model = ClientOrders::OrderLine.where(order_uid: params[:id], product_id: params[:product_id]).first
if Availability::Product.exists?(["uid = ? and available < ?", params[:product_id], (read_model&.product_quantity || 0) + 1])
unless Availability.approximately_available?(params[:product_id], (read_model&.quantity || 0) + 1)
redirect_to edit_client_order_path(params[:id]),
alert: "Product not available in requested quantity!" and return
end
Expand Down
2 changes: 1 addition & 1 deletion rails_application/app/controllers/orders_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def reset_discount

def add_item
read_model = Orders::OrderLine.where(order_uid: params[:id], product_id: params[:product_id]).first
if Availability::Product.exists?(["uid = ? and available < ?", params[:product_id], (read_model&.quantity || 0) + 1])
unless Availability.approximately_available?(params[:product_id], (read_model&.quantity || 0) + 1)
redirect_to edit_order_path(params[:id]),
alert: "Product not available in requested quantity!" and return
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ class Product < ApplicationRecord
self.table_name = "availability_products"
end

# private_constant :Product

def self.approximately_available?(product_id, desired_quantity)
!Product.exists?(["uid = ? and available < ?", product_id, desired_quantity])
end

class UpdateAvailability < Infra::EventHandler
def call(event)
order = Product.find_or_create_by!(uid: event.data.fetch(:product_id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ def test_availability_updates

event_store.publish(Inventory::AvailabilityChanged.new(data: { product_id: product_id, available: 0 }))

product = Availability::Product.find_by(uid: product_id)
assert_equal 0, product.available
refute Availability.approximately_available?(product_id, 1)

event_store.publish(Inventory::AvailabilityChanged.new(data: { product_id: product_id, available: 1 }))
product.reload
assert_equal 1, product.available
assert Availability.approximately_available?(product_id, 1)
end

private
Expand Down

0 comments on commit 14101a1

Please sign in to comment.