Skip to content

Commit 9ddf271

Browse files
extracted a new OrderHeader read model - Orders was responsible for the whole site and grew huge
1 parent 7e72456 commit 9ddf271

File tree

8 files changed

+357
-30
lines changed

8 files changed

+357
-30
lines changed

apps/rails_application/Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ mutate-orders: ## Run mutation tests for Orders namespace
2222
@echo "Running mutation tests for Orders..."
2323
@RAILS_ENV=test bundle exec mutant run "Orders*"
2424

25+
mutate-order-header: ## Run mutation tests for OrderHeader namespace
26+
@echo "Running mutation tests for OrderHeader..."
27+
@RAILS_ENV=test bundle exec mutant run "OrderHeader*"
28+
2529
mutate-client-orders: ## Run mutation tests for ClientOrders namespace
2630
@echo "Running mutation tests for ClientOrders..."
2731
@RAILS_ENV=test bundle exec mutant run "ClientOrders*"
@@ -101,5 +105,5 @@ test-mutant-runner:
101105
@bin/rails tailwindcss:build
102106
@RAILS_ENV=test bundle exec mutant test -j 4
103107

104-
.PHONY: help install dev test mutate mutate-changes mutate-processes mutate-orders mutate-client-orders mutate-invoices mutate-shipments mutate-vat-rates mutate-coupons mutate-products mutate-public-offer mutate-customers mutate-admin mutate-time-promotions mutate-returns mutate-availability mutate-client-inbox mutate-authorization mutate-client-authentication css web test-mutant-runner
108+
.PHONY: help install dev test mutate mutate-changes mutate-processes mutate-orders mutate-order-header mutate-client-orders mutate-invoices mutate-shipments mutate-vat-rates mutate-coupons mutate-products mutate-public-offer mutate-customers mutate-admin mutate-time-promotions mutate-returns mutate-availability mutate-client-inbox mutate-authorization mutate-client-authentication css web test-mutant-runner
105109
.DEFAULT_GOAL := help

apps/rails_application/app/controllers/orders_controller.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ def index
44
end
55

66
def show
7+
@order_header = OrderHeader.find_by_uid(params[:id])
78
@order = Orders.find_order(params[:id])
89

9-
return not_found unless @order
10-
11-
@shipment = Shipments::Shipment.find_by(order_uid: @order.uid)
12-
@invoice = Invoices::Invoice.find_or_initialize_by(order_uid: @order.uid)
10+
return not_found unless @order_header && @order
1311
end
1412

1513
def new
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
module OrderHeader
2+
class Header < ApplicationRecord
3+
self.table_name = "order_headers"
4+
end
5+
6+
private_constant :Header
7+
8+
class Customer < ApplicationRecord
9+
self.table_name = "orders_customers"
10+
end
11+
12+
private_constant :Customer
13+
14+
def self.find_by_uid(uid)
15+
Header.find_by_uid(uid)
16+
end
17+
18+
class Configuration
19+
def call(event_store)
20+
event_store.subscribe(
21+
->(event) { draft_order_header(event) },
22+
to: [Pricing::OfferDrafted]
23+
)
24+
event_store.subscribe(
25+
->(event) { assign_customer(event) },
26+
to: [Crm::CustomerAssignedToOrder]
27+
)
28+
event_store.subscribe(
29+
->(event) { submit_order(event) },
30+
to: [Fulfillment::OrderRegistered]
31+
)
32+
event_store.subscribe(
33+
->(event) { update_state(event, "Paid") },
34+
to: [Fulfillment::OrderConfirmed]
35+
)
36+
event_store.subscribe(
37+
->(event) { update_state(event, "Cancelled") },
38+
to: [Fulfillment::OrderCancelled]
39+
)
40+
event_store.subscribe(
41+
->(event) { update_state(event, "Expired") },
42+
to: [Pricing::OfferExpired]
43+
)
44+
event_store.subscribe(
45+
->(event) { set_shipping_address(event) },
46+
to: [Shipping::ShippingAddressAddedToShipment]
47+
)
48+
event_store.subscribe(
49+
->(event) { set_billing_address(event) },
50+
to: [Invoicing::BillingAddressSet]
51+
)
52+
event_store.subscribe(
53+
->(event) { issue_invoice(event) },
54+
to: [Invoicing::InvoiceIssued]
55+
)
56+
end
57+
58+
private
59+
60+
def find_or_create_header(order_id)
61+
Header.find_or_create_by!(uid: order_id) { |header| header.state = "Draft" }
62+
end
63+
64+
def draft_order_header(event)
65+
Header.create!(
66+
uid: event.data.fetch(:order_id),
67+
state: "Draft"
68+
)
69+
end
70+
71+
def assign_customer(event)
72+
customer_id = event.data.fetch(:customer_id)
73+
customer_name = Customer.find_by_uid(customer_id).name
74+
find_or_create_header(event.data.fetch(:order_id)).update!(customer: customer_name)
75+
end
76+
77+
def submit_order(event)
78+
find_or_create_header(event.data.fetch(:order_id)).update!(
79+
number: event.data.fetch(:order_number),
80+
state: "Submitted"
81+
)
82+
end
83+
84+
def update_state(event, state)
85+
find_or_create_header(event.data.fetch(:order_id)).update!(state: state)
86+
end
87+
88+
def set_shipping_address(event)
89+
find_or_create_header(event.data.fetch(:order_id)).update!(
90+
shipping_address_present: true
91+
)
92+
end
93+
94+
def set_billing_address(event)
95+
find_or_create_header(event.data.fetch(:invoice_id)).update!(
96+
billing_address_present: true
97+
)
98+
end
99+
100+
def issue_invoice(event)
101+
find_or_create_header(event.data.fetch(:invoice_id)).update!(
102+
invoice_issued: true,
103+
invoice_number: event.data.fetch(:invoice_number)
104+
)
105+
end
106+
end
107+
end

apps/rails_application/app/views/orders/show.html.erb

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,73 @@
11
<% content_for(:header) do %>
2-
Order <%= @order.number %>
2+
Order <%= @order_header.number %>
33
<% end %>
4-
<%= turbo_stream_from "orders_order_#{@order.uid}" %>
4+
<%= turbo_stream_from "orders_order_#{@order_header.uid}" %>
55
<% content_for(:actions) do %>
66
<%= secondary_action_button do %>
7-
<%= order_history_link(@order.uid) %>
7+
<%= order_history_link(@order_header.uid) %>
88
<% end %>
99

1010
<%= secondary_action_button do %>
11-
<%= link_to "Invoice", invoice_path(@order.uid) %>
12-
<% end if @invoice.issued? %>
11+
<%= link_to "Invoice", invoice_path(@order_header.uid) %>
12+
<% end if @order_header.invoice_issued? %>
1313

1414
<%= secondary_action_button do %>
1515
<%= link_to 'Back', orders_path %>
1616
<% end %>
1717

1818
<%= primary_action_button do %>
19-
<%= link_to 'Edit', edit_order_path(@order.uid) %>
20-
<% end if @order.state == "Draft" %>
19+
<%= link_to 'Edit', edit_order_path(@order_header.uid) %>
20+
<% end if @order_header.state == "Draft" %>
2121

22-
<% if @order.state == "Submitted" %>
23-
<%= button_to("Pay", pay_order_path(@order.uid), class: "mr-3 ml-3 inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700") %>
22+
<% if @order_header.state == "Submitted" %>
23+
<%= button_to("Pay", pay_order_path(@order_header.uid), class: "mr-3 ml-3 inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700") %>
2424
<% end %>
2525

26-
<% if @order.state == "Paid" %>
27-
<%= button_to("Return", order_returns_path(order_id: @order.uid), class: "ml-3 inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-50 border-gray-300 text-gray-700 bg-white hover:bg-gray-50") %>
26+
<% if @order_header.state == "Paid" %>
27+
<%= button_to("Return", order_returns_path(order_id: @order_header.uid), class: "ml-3 inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-50 border-gray-300 text-gray-700 bg-white hover:bg-gray-50") %>
2828
<% end %>
2929

30-
<% if (@order.state == "Submitted") %>
31-
<%= button_to("Cancel Order", cancel_order_path(@order.uid), class: "inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-50 border-gray-300 text-gray-700 bg-white hover:bg-gray-50") %>
30+
<% if @order_header.state == "Submitted" %>
31+
<%= button_to("Cancel Order", cancel_order_path(@order_header.uid), class: "inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-50 border-gray-300 text-gray-700 bg-white hover:bg-gray-50") %>
3232
<% end %>
3333
<% end %>
3434

3535
<dl class="mb-8">
3636
<dt class="font-bold">Customer</dt>
37-
<dd class="mb-2"><%= @order.customer || "None" %></dd>
37+
<dd class="mb-2"><%= @order_header.customer || "None" %></dd>
3838
<dt class="font-bold">State</dt>
39-
<dd class="mb-2" id="<%= "orders_order_#{@order.uid}_state" %>"><%= @order.state %></dd>
39+
<dd class="mb-2" id="<%= "orders_order_#{@order_header.uid}_state" %>"><%= @order_header.state %></dd>
4040
<dt class="font-bold">Shipping Details</dt>
4141
<dd class="mb-2">
42-
<% unless @shipment&.full_address.present? %>
42+
<% unless @order_header.shipping_address_present? %>
4343
Shipping address is missing.
4444
<% end %>
4545
</dd>
4646
<dd class="mb-2">
47-
<% unless @shipment&.full_address.present? %>
47+
<% unless @order_header.shipping_address_present? %>
4848
<%= link_to "Add shipment address",
49-
edit_order_shipping_address_path(@order.uid),
49+
edit_order_shipping_address_path(@order_header.uid),
5050
class: 'px-2 py-1 border rounded-md shadow-sm text-xs font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700'
5151
%>
5252
<% else %>
5353
Your shipment has been queued for processing.
5454
<% end %>
5555
</dd>
56-
<% if @order.state != "Draft" %>
56+
<% if @order_header.state != "Draft" %>
5757
<dt class="font-bold">Billing Details</dt>
5858
<dd class="mb-2">
59-
<% if @invoice.issued? %>
60-
<%= link_to @invoice.number, invoice_path(@order.uid) %>
61-
<% elsif !@invoice.address_present? %>
59+
<% if @order_header.invoice_issued? %>
60+
<%= link_to @order_header.invoice_number, invoice_path(@order_header.uid) %>
61+
<% elsif !@order_header.billing_address_present? %>
6262
Billing address is missing.
6363
<%= link_to "Add billing address",
64-
edit_order_billing_address_path(@order.uid),
64+
edit_order_billing_address_path(@order_header.uid),
6565
class: 'px-2 py-1 border rounded-md shadow-sm text-xs font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700'
6666
%>
6767
<% else %>
6868
Invoice not issued
6969
<%= button_to "Issue now",
70-
order_invoice_path(@order.uid),
70+
order_invoice_path(@order_header.uid),
7171
class: 'px-2 py-1 border rounded-md shadow-sm text-xs font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700'
7272
%>
7373
<% end %>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class CreateOrderHeaders < ActiveRecord::Migration[8.0]
2+
def change
3+
create_table :order_headers do |t|
4+
t.uuid :uid, null: false, index: { unique: true }
5+
t.string :number
6+
t.string :customer
7+
t.string :state, null: false
8+
t.boolean :shipping_address_present, default: false
9+
t.boolean :billing_address_present, default: false
10+
t.boolean :invoice_issued, default: false
11+
t.string :invoice_number
12+
13+
t.timestamps
14+
end
15+
end
16+
end

apps/rails_application/db/schema.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.0].define(version: 2025_12_24_213558) do
13+
ActiveRecord::Schema[8.0].define(version: 2025_12_29_231410) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "pg_catalog.plpgsql"
1616
enable_extension "pgcrypto"
@@ -173,6 +173,20 @@
173173
t.boolean "submitted", default: false
174174
end
175175

176+
create_table "order_headers", force: :cascade do |t|
177+
t.uuid "uid", null: false
178+
t.string "number"
179+
t.string "customer"
180+
t.string "state", null: false
181+
t.boolean "shipping_address_present", default: false
182+
t.boolean "billing_address_present", default: false
183+
t.boolean "invoice_issued", default: false
184+
t.string "invoice_number"
185+
t.datetime "created_at", null: false
186+
t.datetime "updated_at", null: false
187+
t.index ["uid"], name: "index_order_headers_on_uid", unique: true
188+
end
189+
176190
create_table "order_lines", force: :cascade do |t|
177191
t.uuid "order_uid", null: false
178192
t.string "product_name"

apps/rails_application/lib/configuration.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ def call(event_store, command_bus)
66
enable_res_infra_event_linking(event_store)
77

88
enable_orders_read_model(event_store)
9+
enable_order_header_read_model(event_store)
910
enable_products_read_model(event_store)
1011
enable_public_offer_products_read_model(event_store)
1112
enable_customers_read_model(event_store)
@@ -54,6 +55,10 @@ def enable_orders_read_model(event_store)
5455
Orders::Configuration.new.call(event_store)
5556
end
5657

58+
def enable_order_header_read_model(event_store)
59+
OrderHeader::Configuration.new.call(event_store)
60+
end
61+
5762
def enable_invoices_read_model(event_store)
5863
Invoices::Configuration.new.call(event_store)
5964
end

0 commit comments

Comments
 (0)