Skip to content

Commit

Permalink
introduce Fulfillment BC
Browse files Browse the repository at this point in the history
  • Loading branch information
pjurewicz committed May 6, 2024
1 parent 031bf3b commit 56f25ac
Show file tree
Hide file tree
Showing 18 changed files with 304 additions and 0 deletions.
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"
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
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module Fulfillment
class CancelOrder < 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
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module Fulfillment
class ConfirmOrder < 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
@@ -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
@@ -0,0 +1,7 @@
# frozen_string_literal: true

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

module Fulfillment
class OrderConfirmed < Infra::Event
attribute :order_id, Infra::Types::UUID
end
end
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
41 changes: 41 additions & 0 deletions ecommerce/fulfillment/test/confirm_order_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require_relative "test_helper"

module Fulfillment
class ConfirmOrderTest < Test
cover "Fulfillment::OnConfirmOrder*"

def test_not_registered_order_cant_be_confirmed
aggregate_id = SecureRandom.uuid

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

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

assert_events(
stream,
OrderConfirmed.new(data: { order_id: aggregate_id })
) { act(ConfirmOrder.new(order_id: aggregate_id)) }
end

def test_confirmed_order_can_not_be_confirmed
aggregate_id = SecureRandom.uuid

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

assert_raises(Order::InvalidState) do
act(ConfirmOrder.new(order_id: aggregate_id))
end
end
end
end
29 changes: 29 additions & 0 deletions ecommerce/fulfillment/test/register_order_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require_relative "test_helper"

module Fulfillment
class ConfirmOrderTest < Test
cover "Fulfillment::OnRegisterOrder*"

def test_new_order_can_be_registered
aggregate_id = SecureRandom.uuid
stream = "Fulfillment::Order$#{aggregate_id}"

assert_events(
stream,
OrderRegistered.new(data: { order_id: aggregate_id })
) { act(RegisterOrder.new(order_id: aggregate_id)) }
end

def test_registered_order_can_not_be_registered_again
aggregate_id = SecureRandom.uuid

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

assert_raises(Order::InvalidState) do
act(RegisterOrder.new(order_id: aggregate_id))
end
end
end
end
13 changes: 13 additions & 0 deletions ecommerce/fulfillment/test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require "minitest/autorun"
require "mutant/minitest/coverage"

require_relative "../lib/fulfillment"

module Fulfillment
class Test < Infra::InMemoryTest
def before_setup
super
Configuration.new.call(event_store, command_bus)
end
end
end

0 comments on commit 56f25ac

Please sign in to comment.