Skip to content
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

introduce Fulfillment BC #362

Merged
merged 2 commits into from
May 6, 2024
Merged
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
2 changes: 2 additions & 0 deletions ecommerce/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require_relative "shipping/lib/shipping"
require_relative "invoicing/lib/invoicing"
require_relative "taxes/lib/taxes"
require_relative "fulfillment/lib/fulfillment"
require_relative "processes/lib/processes"

module Ecommerce
Expand Down Expand Up @@ -38,6 +39,7 @@ def configure_bounded_contexts(event_store, command_bus)
Pricing::Configuration.new,
Taxes::Configuration.new(@available_vat_rates),
ProductCatalog::Configuration.new,
Fulfillment::Configuration.new
].each { |c| c.call(event_store, command_bus) }
end

Expand Down
12 changes: 12 additions & 0 deletions ecommerce/fulfillment/.mutant.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
requires:
- ./test/test_helper
integration: minitest
coverage_criteria:
process_abort: true
matcher:
subjects:
- Fulfillment*
ignore:
- Fulfillment::Test*
- Fulfillment::Configuration#call
- Fulfillment::Configuration#initialize
4 changes: 4 additions & 0 deletions ecommerce/fulfillment/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source "https://rubygems.org"

eval_gemfile "../../infra/Gemfile.test"
gem "infra", path: "../../infra"
116 changes: 116 additions & 0 deletions ecommerce/fulfillment/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
PATH
remote: ../../infra
specs:
infra (1.0.0)
aggregate_root (~> 2.13)
arkency-command_bus
dry-struct
dry-types
rake
ruby_event_store (~> 2.13)
ruby_event_store-transformations
sidekiq

GEM
remote: https://oss:[email protected]/
specs:
mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)

GEM
remote: https://rubygems.org/
specs:
activesupport (7.1.3.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
aggregate_root (2.14.0)
base64
ruby_event_store (= 2.14.0)
arkency-command_bus (0.4.1)
concurrent-ruby
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.7)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
diff-lcs (1.5.1)
drb (2.2.1)
dry-core (1.0.1)
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
dry-inflector (1.0.0)
dry-logic (1.5.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-struct (1.6.0)
dry-core (~> 1.0, < 2)
dry-types (>= 1.7, < 2)
ice_nine (~> 0.11)
zeitwerk (~> 2.6)
dry-types (1.7.2)
bigdecimal (~> 3.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0)
dry-inflector (~> 1.0)
dry-logic (~> 1.4)
zeitwerk (~> 2.6)
i18n (1.14.4)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
minitest (5.15.0)
mutant (0.11.26)
diff-lcs (~> 1.3)
parser (~> 3.2.2, >= 3.2.2.4)
regexp_parser (~> 2.8.2)
sorbet-runtime (~> 0.5.0)
unparser (~> 0.6.9)
mutant-minitest (0.11.26)
minitest (~> 5.11)
mutant (= 0.11.26)
mutex_m (0.2.0)
parser (3.2.2.4)
ast (~> 2.4.1)
racc
racc (1.7.3)
rack (3.0.10)
rake (13.2.1)
redis-client (0.22.1)
connection_pool
regexp_parser (2.8.3)
ruby_event_store (2.14.0)
concurrent-ruby (~> 1.0, >= 1.1.6)
ruby_event_store-transformations (0.1.0)
activesupport (>= 5.0)
ruby_event_store (>= 2.0.0, < 3.0.0)
sidekiq (7.2.4)
concurrent-ruby (< 2)
connection_pool (>= 2.3.0)
rack (>= 2.2.4)
redis-client (>= 0.19.0)
sorbet-runtime (0.5.11368)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unparser (0.6.12)
diff-lcs (~> 1.3)
parser (>= 3.2.2.4)
zeitwerk (2.6.13)

PLATFORMS
arm64-darwin-23
ruby

DEPENDENCIES
infra!
minitest (= 5.15.0)!
mutant-license!
mutant-minitest (= 0.11.26)!

BUNDLED WITH
2.5.9
10 changes: 10 additions & 0 deletions ecommerce/fulfillment/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
install:
@bundle install

test:
@bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb

mutate:
@RAILS_ENV=test bundle exec mutant run

.PHONY: install test mutate
21 changes: 21 additions & 0 deletions ecommerce/fulfillment/lib/fulfillment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require "infra"
require_relative "fulfillment/events/order_registered"
require_relative "fulfillment/events/order_confirmed"
require_relative "fulfillment/events/order_cancelled"
require_relative "fulfillment/commands/register_order"
require_relative "fulfillment/commands/confirm_order"
require_relative "fulfillment/commands/cancel_order"
require_relative "fulfillment/on_register_order"
require_relative "fulfillment/on_cancel_order"
require_relative "fulfillment/on_confirm_order"
require_relative "fulfillment/order"

module Fulfillment
class Configuration
def call(event_store, command_bus)
command_bus.register(RegisterOrder, OnRegisterOrder.new(event_store))
command_bus.register(ConfirmOrder, OnConfirmOrder.new(event_store))
command_bus.register(CancelOrder, OnCancelOrder.new(event_store))
end
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module Ordering
# frozen_string_literal: true

module Fulfillment
class CancelOrder < Infra::Command
attribute :order_id, Infra::Types::UUID

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module Ordering
# frozen_string_literal: true

module Fulfillment
class ConfirmOrder < Infra::Command
attribute :order_id, Infra::Types::UUID

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module Fulfillment
class RegisterOrder < Infra::Command
attribute :order_id, Infra::Types::UUID

alias aggregate_id order_id
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module Ordering
# frozen_string_literal: true

module Fulfillment
class OrderCancelled < Infra::Event
attribute :order_id, Infra::Types::UUID
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module Ordering
# frozen_string_literal: true

module Fulfillment
class OrderConfirmed < Infra::Event
attribute :order_id, Infra::Types::UUID
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Fulfillment
class OrderRegistered < Infra::Event
attribute :order_id, Infra::Types::UUID
end
end
15 changes: 15 additions & 0 deletions ecommerce/fulfillment/lib/fulfillment/on_cancel_order.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Fulfillment
class OnCancelOrder
def initialize(event_store)
@repository = Infra::AggregateRootRepository.new(event_store)
end

def call(command)
@repository.with_aggregate(Order, command.aggregate_id) do |order|
order.cancel
end
end
end
end
15 changes: 15 additions & 0 deletions ecommerce/fulfillment/lib/fulfillment/on_confirm_order.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Fulfillment
class OnConfirmOrder
def initialize(event_store)
@repository = Infra::AggregateRootRepository.new(event_store)
end

def call(command)
@repository.with_aggregate(Order, command.aggregate_id) do |order|
order.confirm
end
end
end
end
15 changes: 15 additions & 0 deletions ecommerce/fulfillment/lib/fulfillment/on_register_order.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Fulfillment
class OnRegisterOrder
def initialize(event_store)
@repository = Infra::AggregateRootRepository.new(event_store)
end

def call(command)
@repository.with_aggregate(Order, command.aggregate_id) do |order|
order.register
end
end
end
end
40 changes: 40 additions & 0 deletions ecommerce/fulfillment/lib/fulfillment/order.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

module Fulfillment
class Order
include AggregateRoot

InvalidState = Class.new(StandardError)

def initialize(id)
@id = id
end

def register
raise InvalidState if @state
apply OrderRegistered.new(data: { order_id: @id })
end

def confirm
raise InvalidState unless @state.equal?(:new)
apply OrderConfirmed.new(data: { order_id: @id })
end

def cancel
raise InvalidState unless @state.equal?(:new)
apply OrderCancelled.new(data: { order_id: @id })
end

on OrderRegistered do |event|
@state = :new
end

on OrderConfirmed do |event|
@state = :confirmed
end

on OrderCancelled do |event|
@state = :cancelled
end
end
end
41 changes: 41 additions & 0 deletions ecommerce/fulfillment/test/cancel_order_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require_relative "test_helper"

module Fulfillment
class CancelOrderTest < Test
cover "Fulfillment::OnCancelOrder*"

def test_not_registered_order_cant_be_cancelled
aggregate_id = SecureRandom.uuid

assert_raises(Order::InvalidState) do
act(CancelOrder.new(order_id: aggregate_id))
end
end

def test_registered_order_can_be_cancelled
aggregate_id = SecureRandom.uuid
stream = "Fulfillment::Order$#{aggregate_id}"
arrange(
RegisterOrder.new(order_id: aggregate_id)
)

assert_events(
stream,
OrderCancelled.new(data: { order_id: aggregate_id })
) { act(CancelOrder.new(order_id: aggregate_id)) }
end

def test_confirmed_order_can_not_be_cancelled
aggregate_id = SecureRandom.uuid

arrange(
RegisterOrder.new(order_id: aggregate_id),
ConfirmOrder.new(order_id: aggregate_id)
)

assert_raises(Order::InvalidState) do
act(CancelOrder.new(order_id: aggregate_id))
end
end
end
end
Loading
Loading