15 years of Rails with Domain Driven Design - lessons learnt
How to use AI to transform your Rails app towards a DDD application. The resulting architecture is AI-native and allows non-technical people to create some of the new features - especially read models.
private_constant :Customer def self.customers_for_store(store_id) Customer.where(store_id: store_id) end def self.find_customer_in_store(customer_id, store_id) Customer.where(store_id: store_id).find(customer_id) end class Configuration def call(event_store) event_store.subscribe(RegisterCustomer.new, to: [Crm::CustomerRegistered]) event_store.subscribe(AssignStoreToCustomer.new, to: [Stores::CustomerRegistered]) event_store.subscribe(PromoteToVip.new, to: [Crm::CustomerPromotedToVip]) event_store.subscribe(UpdatePaidOrdersSummary.new, to: [Fulfillment::OrderConfirmed]) event_store.subscribe(ConnectAccount.new, to: [Authentication::AccountConnectedToClient]) end end end 80 LOC Read model
to: [Stores::CustomerRegistered]) event_store.subscribe(PromoteToVip.new, to: [Crm::CustomerPromotedToVip]) event_store.subscribe(UpdatePaidOrdersSummary.new, to: [Fulfillment::OrderConfirmed]) end end end Read model
promote_to_vip(event) find(event.data.fetch(:customer_id)).update(vip: true) end def find(customer_id) Customer.where(id: customer_id).first end end end Read model
Shipping::ShippingAddressAddedToShipment, Fulfillment::OrderRegistered, Fulfillment::OrderConfirmed, Stores::OfferRegistered ) private def act case state in { shipment: :address_set, order: :placed } register_shipment submit_shipment in { shipment: :address_set, order: :confirmed } register_shipment submit_shipment authorize_shipment else end end 67 LOC 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) when Stores::OfferRegistered state.with(store_id: event.data.fetch(:store_id)) end end def register_shipment return unless state.store_id command_bus.call( Stores::RegisterShipment.new( shipment_id: id, store_id: state.store_id ) ) end def submit_shipment command_bus.call(Shipping::SubmitShipment.new(order_id: id)) end def authorize_shipment command_bus.call(Shipping::AuthorizeShipment.new(order_id: id)) end def fetch_id(event) event.data.fetch(:order_id) end ProcessState = Data.define(:order, :shipment, :store_id) do def initialize(order: nil, shipment: nil, store_id: nil) = super end end end Process Managers
act case state in { shipment: :address_set, order: :placed } register_shipment submit_shipment in { shipment: :address_set, order: :confirmed } register_shipment submit_shipment authorize_shipment else end end