From 1dc01ee317e80964aeafd98374c04ee3a34cdcb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Wed, 15 Jul 2026 17:38:21 +0200 Subject: [PATCH 1/9] Failing specs: event_type_resolver ignored on dispatch/apply path Client#publish routes via event.event_type instead of the configured event_type_resolver, so a resolver whose output differs from event.event_type never matches its own subscriptions. AggregateRoot::DefaultApplyStrategy has the same split: on-DSL registers handlers via event_type_resolver, apply looks them up by event.event_type. --- aggregate_root/spec/aggregate_root_spec.rb | 22 ++++++++++++++++++++++ ruby_event_store/spec/client_spec.rb | 11 +++++++++++ 2 files changed, 33 insertions(+) diff --git a/aggregate_root/spec/aggregate_root_spec.rb b/aggregate_root/spec/aggregate_root_spec.rb index a395f25f5d..665a1e8d14 100644 --- a/aggregate_root/spec/aggregate_root_spec.rb +++ b/aggregate_root/spec/aggregate_root_spec.rb @@ -180,6 +180,28 @@ def custom_expired(_event) expect(klass.respond_to?(:on_methods)).to eq(true) end + it "applies event when event_type_resolver output differs from event.event_type" do + klass = + Class.new do + include AggregateRoot.with(event_type_resolver: ->(value) { "prefixed.#{value}" }) + + def initialize + @status = :draft + end + + attr_accessor :status + + on Orders::Events::OrderCreated do |_event| + @status = :created + end + end + order = klass.new + + order.apply(Orders::Events::OrderCreated.new) + + expect(order.status).to eq(:created) + end + it "included modules" do klass = Class.new { include AggregateRoot } diff --git a/ruby_event_store/spec/client_spec.rb b/ruby_event_store/spec/client_spec.rb index 97b927873f..0eda7c52bc 100644 --- a/ruby_event_store/spec/client_spec.rb +++ b/ruby_event_store/spec/client_spec.rb @@ -1006,6 +1006,17 @@ def event_id expect(event.event_id).to eq("8d69cc2b-c6c5-4494-99f6-954c7f583477") end + specify "custom resolver output differing from event.event_type still routes published events to subscribers" do + received = [] + resolver = ->(klass) { "prefixed.#{klass}" } + client = Client.new(event_type_resolver: resolver) + client.subscribe(to: [OrderCreated]) { |event| received << event } + + client.publish(order_created = OrderCreated.new) + + expect(received).to eq([order_created]) + end + describe "#position_in_stream" do specify do client.publish(fact0 = OrderCreated.new, expected_version: :auto, stream_name: "SomeStream") From 1d5e06a28ae3f57000d5833190b353683dd2d2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Wed, 15 Jul 2026 17:45:04 +0200 Subject: [PATCH 2/9] Route dispatch/apply through event_type_resolver Client#publish and AggregateRoot::DefaultApplyStrategy now resolve the published/applied event's type via the configured event_type_resolver (passing event.class, matching the subscribe/on registration side) instead of reading event.event_type directly. A resolver whose output differs from event.event_type now matches its own subscriptions and aggregate handlers. Default resolver (to_s) is unchanged. --- aggregate_root/lib/aggregate_root.rb | 3 ++- .../lib/aggregate_root/default_apply_strategy.rb | 7 ++++--- ruby_event_store/lib/ruby_event_store/client.rb | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/aggregate_root/lib/aggregate_root.rb b/aggregate_root/lib/aggregate_root.rb index 7a520a4d81..7c6102cec4 100644 --- a/aggregate_root/lib/aggregate_root.rb +++ b/aggregate_root/lib/aggregate_root.rb @@ -76,7 +76,8 @@ def marshal_load(vars) end end - def self.with(strategy: -> { DefaultApplyStrategy.new }, event_type_resolver: ->(value) { value.to_s }) + def self.with(strategy: nil, event_type_resolver: ->(value) { value.to_s }) + strategy ||= -> { DefaultApplyStrategy.new(event_type_resolver: event_type_resolver) } Module.new do define_singleton_method :included do |host_class| host_class.extend Constructor diff --git a/aggregate_root/lib/aggregate_root/default_apply_strategy.rb b/aggregate_root/lib/aggregate_root/default_apply_strategy.rb index a41d5d8c3c..4c470104aa 100644 --- a/aggregate_root/lib/aggregate_root/default_apply_strategy.rb +++ b/aggregate_root/lib/aggregate_root/default_apply_strategy.rb @@ -5,12 +5,13 @@ module AggregateRoot NullHandler = Proc.new {} class DefaultApplyStrategy - def initialize(strict: true) + def initialize(strict: true, event_type_resolver: ->(value) { value.to_s }) @strict = strict + @event_type_resolver = event_type_resolver end def call(aggregate, event) - on_handler(aggregate, event.event_type)[event] + on_handler(aggregate, event_type_resolver.call(event.class))[event] end def uses_on_dsl? = true @@ -32,6 +33,6 @@ def missing_handler(aggregate, event_type) end end - attr_reader :strict, :on_methods + attr_reader :strict, :on_methods, :event_type_resolver end end diff --git a/ruby_event_store/lib/ruby_event_store/client.rb b/ruby_event_store/lib/ruby_event_store/client.rb index 4e243a097c..d4e633b8ad 100644 --- a/ruby_event_store/lib/ruby_event_store/client.rb +++ b/ruby_event_store/lib/ruby_event_store/client.rb @@ -64,7 +64,7 @@ def publish(events, topic: nil, stream_name: GLOBAL_STREAM, expected_version: :a enriched_events.zip(records) do |event, record| with_metadata(correlation_id: event.metadata.fetch(:correlation_id), causation_id: event.event_id) do if @broker.public_method(:call).arity == 3 - @broker.call(topic || event.event_type, event, record) + @broker.call(topic || @event_type_resolver.call(event.class), event, record) else warn <<~EOW Message broker shall support topics. From c342fc9103bbe9f6ed722be0614b513a2b1c1a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 16 Jul 2026 11:01:00 +0200 Subject: [PATCH 3/9] Guard: metadata[:event_type] does not affect dispatch routing Dispatch routes by event_type_resolver.call(event.class), not by event.event_type (which honors metadata[:event_type]). This documents the deliberate choice: subscribe and dispatch share one key derived from the resolver, so metadata overrides no longer silently misroute published events. --- ruby_event_store/spec/client_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ruby_event_store/spec/client_spec.rb b/ruby_event_store/spec/client_spec.rb index 0eda7c52bc..1f7e2f0deb 100644 --- a/ruby_event_store/spec/client_spec.rb +++ b/ruby_event_store/spec/client_spec.rb @@ -1017,6 +1017,15 @@ def event_id expect(received).to eq([order_created]) end + specify "dispatch routes by event_type_resolver output, not by metadata[:event_type]" do + received = [] + client.subscribe(to: [OrderCreated]) { |event| received << event } + + client.publish(order_created = OrderCreated.new(metadata: { event_type: "Some.Other.Type" })) + + expect(received).to eq([order_created]) + end + describe "#position_in_stream" do specify do client.publish(fact0 = OrderCreated.new, expected_version: :auto, stream_name: "SomeStream") From c5a9411e61aeedbf9a26d1a369d06aaae7beab67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 16 Jul 2026 11:36:44 +0200 Subject: [PATCH 4/9] Deprecate event_type_resolver in AggregateRoot, route apply by event.event_type AggregateRoot now derives the handler key from event.event_type on the apply path and from value.to_s at on-DSL registration, instead of an injected event_type_resolver. The event_type_resolver keyword is kept on AggregateRoot.with and DefaultApplyStrategy.new for backward compat but is ignored and emits a deprecation warning when passed. --- aggregate_root/lib/aggregate_root.rb | 8 ++-- .../aggregate_root/default_apply_strategy.rb | 19 ++++++++-- aggregate_root/spec/aggregate_root_spec.rb | 37 +++++++++++++------ 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/aggregate_root/lib/aggregate_root.rb b/aggregate_root/lib/aggregate_root.rb index 7c6102cec4..aa2a742739 100644 --- a/aggregate_root/lib/aggregate_root.rb +++ b/aggregate_root/lib/aggregate_root.rb @@ -76,15 +76,17 @@ def marshal_load(vars) end end - def self.with(strategy: nil, event_type_resolver: ->(value) { value.to_s }) - strategy ||= -> { DefaultApplyStrategy.new(event_type_resolver: event_type_resolver) } + def self.with(strategy: -> { DefaultApplyStrategy.new }, event_type_resolver: nil) + unless event_type_resolver.nil? + RubyEventStore::Deprecations.warn(:aggregate_root_event_type_resolver, message: EVENT_TYPE_RESOLVER_DEPRECATION) + end Module.new do define_singleton_method :included do |host_class| host_class.extend Constructor host_class.extend OnDSL if strategy.call.respond_to?(:uses_on_dsl?) host_class.include AggregateMethods host_class.define_singleton_method :event_type_for do |value| - event_type_resolver.call(value) + value.to_s end end diff --git a/aggregate_root/lib/aggregate_root/default_apply_strategy.rb b/aggregate_root/lib/aggregate_root/default_apply_strategy.rb index 4c470104aa..fd1c76b018 100644 --- a/aggregate_root/lib/aggregate_root/default_apply_strategy.rb +++ b/aggregate_root/lib/aggregate_root/default_apply_strategy.rb @@ -1,17 +1,28 @@ # frozen_string_literal: true +require "ruby_event_store/deprecations" + module AggregateRoot MissingHandler = Class.new(StandardError) NullHandler = Proc.new {} + EVENT_TYPE_RESOLVER_DEPRECATION = <<~EOW.freeze + Passing event_type_resolver to AggregateRoot has been deprecated. + + Event type is now derived from event.event_type. The event_type_resolver + argument is ignored and will be removed in a future release. + EOW + class DefaultApplyStrategy - def initialize(strict: true, event_type_resolver: ->(value) { value.to_s }) + def initialize(strict: true, event_type_resolver: nil) @strict = strict - @event_type_resolver = event_type_resolver + unless event_type_resolver.nil? + RubyEventStore::Deprecations.warn(:aggregate_root_event_type_resolver, message: EVENT_TYPE_RESOLVER_DEPRECATION) + end end def call(aggregate, event) - on_handler(aggregate, event_type_resolver.call(event.class))[event] + on_handler(aggregate, event.event_type)[event] end def uses_on_dsl? = true @@ -33,6 +44,6 @@ def missing_handler(aggregate, event_type) end end - attr_reader :strict, :on_methods, :event_type_resolver + attr_reader :strict, :on_methods end end diff --git a/aggregate_root/spec/aggregate_root_spec.rb b/aggregate_root/spec/aggregate_root_spec.rb index 665a1e8d14..9a6e653126 100644 --- a/aggregate_root/spec/aggregate_root_spec.rb +++ b/aggregate_root/spec/aggregate_root_spec.rb @@ -180,21 +180,36 @@ def custom_expired(_event) expect(klass.respond_to?(:on_methods)).to eq(true) end - it "applies event when event_type_resolver output differs from event.event_type" do - klass = - Class.new do - include AggregateRoot.with(event_type_resolver: ->(value) { "prefixed.#{value}" }) + it "deprecates passing event_type_resolver to AggregateRoot.with" do + expect { AggregateRoot.with(event_type_resolver: ->(value) { value.to_s }) }.to output( + /\[DEPRECATION\].*event_type_resolver/m, + ).to_stderr + end - def initialize - @status = :draft - end + it "deprecates passing event_type_resolver to DefaultApplyStrategy.new" do + expect { AggregateRoot::DefaultApplyStrategy.new(event_type_resolver: ->(value) { value.to_s }) }.to output( + /\[DEPRECATION\].*event_type_resolver/m, + ).to_stderr + end - attr_accessor :status + it "routes apply by event.event_type, ignoring a passed event_type_resolver" do + klass = nil + silence_warnings do + klass = + Class.new do + include AggregateRoot.with(event_type_resolver: ->(value) { "prefixed.#{value}" }) - on Orders::Events::OrderCreated do |_event| - @status = :created + def initialize + @status = :draft + end + + attr_accessor :status + + on Orders::Events::OrderCreated do |_event| + @status = :created + end end - end + end order = klass.new order.apply(Orders::Events::OrderCreated.new) From 4c8102633bfbded96af07cd0629c2c3086adcb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 16 Jul 2026 11:58:49 +0200 Subject: [PATCH 5/9] Kill mutants: event_type_resolver deprecation in AggregateRoot Add specs covering the branches mutant found alive: - no deprecation warning on the default path (both AggregateRoot.with and DefaultApplyStrategy.new) - deprecation key is suppressible (locks the warn key, not just the message) - on-DSL registration key derives from the event class name AggregateRoot mutation coverage back to 100%. --- aggregate_root/spec/aggregate_root_spec.rb | 30 ++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/aggregate_root/spec/aggregate_root_spec.rb b/aggregate_root/spec/aggregate_root_spec.rb index 9a6e653126..4e30fba07f 100644 --- a/aggregate_root/spec/aggregate_root_spec.rb +++ b/aggregate_root/spec/aggregate_root_spec.rb @@ -182,16 +182,42 @@ def custom_expired(_event) it "deprecates passing event_type_resolver to AggregateRoot.with" do expect { AggregateRoot.with(event_type_resolver: ->(value) { value.to_s }) }.to output( - /\[DEPRECATION\].*event_type_resolver/m, + "[DEPRECATION] #{AggregateRoot::EVENT_TYPE_RESOLVER_DEPRECATION}", ).to_stderr end + it "does not deprecate when event_type_resolver is not passed to AggregateRoot.with" do + expect { AggregateRoot.with }.not_to output.to_stderr + end + + it "emits the AggregateRoot.with deprecation under a suppressible key" do + RubyEventStore::Deprecations.suppress(:aggregate_root_event_type_resolver) + + expect { AggregateRoot.with(event_type_resolver: ->(value) { value.to_s }) }.not_to output.to_stderr + end + it "deprecates passing event_type_resolver to DefaultApplyStrategy.new" do expect { AggregateRoot::DefaultApplyStrategy.new(event_type_resolver: ->(value) { value.to_s }) }.to output( - /\[DEPRECATION\].*event_type_resolver/m, + "[DEPRECATION] #{AggregateRoot::EVENT_TYPE_RESOLVER_DEPRECATION}", ).to_stderr end + it "does not deprecate when event_type_resolver is not passed to DefaultApplyStrategy.new" do + expect { AggregateRoot::DefaultApplyStrategy.new }.not_to output.to_stderr + end + + it "emits the DefaultApplyStrategy.new deprecation under a suppressible key" do + RubyEventStore::Deprecations.suppress(:aggregate_root_event_type_resolver) + + expect { AggregateRoot::DefaultApplyStrategy.new(event_type_resolver: ->(value) { value.to_s }) }.not_to output.to_stderr + end + + it "derives on-DSL registration key from the event class name" do + klass = Class.new { include AggregateRoot } + + expect(klass.event_type_for(Orders::Events::OrderCreated)).to eq("Orders::Events::OrderCreated") + end + it "routes apply by event.event_type, ignoring a passed event_type_resolver" do klass = nil silence_warnings do From c4da5a069967ab36f9c93d0b40dd97d347b8f848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 16 Jul 2026 12:23:02 +0200 Subject: [PATCH 6/9] Simplify by removing not needed changes --- aggregate_root/lib/aggregate_root.rb | 13 +++++--- .../aggregate_root/default_apply_strategy.rb | 12 +------ aggregate_root/spec/aggregate_root_spec.rb | 31 ++++--------------- 3 files changed, 15 insertions(+), 41 deletions(-) diff --git a/aggregate_root/lib/aggregate_root.rb b/aggregate_root/lib/aggregate_root.rb index aa2a742739..1a6c5dfe30 100644 --- a/aggregate_root/lib/aggregate_root.rb +++ b/aggregate_root/lib/aggregate_root.rb @@ -13,7 +13,7 @@ module OnDSL def on(*event_klasses, &block) event_klasses.each do |event_klass| - name = event_type_for(event_klass) + name = event_klass.to_s raise(ArgumentError, "Anonymous class is missing name") if name.start_with? ANONYMOUS_CLASS handler_name = "on_#{name}" @@ -78,16 +78,19 @@ def marshal_load(vars) def self.with(strategy: -> { DefaultApplyStrategy.new }, event_type_resolver: nil) unless event_type_resolver.nil? - RubyEventStore::Deprecations.warn(:aggregate_root_event_type_resolver, message: EVENT_TYPE_RESOLVER_DEPRECATION) + message = <<~EOW + Passing event_type_resolver to AggregateRoot has been deprecated. + + Event type is now derived from event.event_type. The event_type_resolver + argument is ignored and will be removed in a future release. + EOW + RubyEventStore::Deprecations.warn(:aggregate_root_event_type_resolver, message:) end Module.new do define_singleton_method :included do |host_class| host_class.extend Constructor host_class.extend OnDSL if strategy.call.respond_to?(:uses_on_dsl?) host_class.include AggregateMethods - host_class.define_singleton_method :event_type_for do |value| - value.to_s - end end define_method :apply_strategy do diff --git a/aggregate_root/lib/aggregate_root/default_apply_strategy.rb b/aggregate_root/lib/aggregate_root/default_apply_strategy.rb index fd1c76b018..944d0a800a 100644 --- a/aggregate_root/lib/aggregate_root/default_apply_strategy.rb +++ b/aggregate_root/lib/aggregate_root/default_apply_strategy.rb @@ -6,19 +6,9 @@ module AggregateRoot MissingHandler = Class.new(StandardError) NullHandler = Proc.new {} - EVENT_TYPE_RESOLVER_DEPRECATION = <<~EOW.freeze - Passing event_type_resolver to AggregateRoot has been deprecated. - - Event type is now derived from event.event_type. The event_type_resolver - argument is ignored and will be removed in a future release. - EOW - class DefaultApplyStrategy - def initialize(strict: true, event_type_resolver: nil) + def initialize(strict: true) @strict = strict - unless event_type_resolver.nil? - RubyEventStore::Deprecations.warn(:aggregate_root_event_type_resolver, message: EVENT_TYPE_RESOLVER_DEPRECATION) - end end def call(aggregate, event) diff --git a/aggregate_root/spec/aggregate_root_spec.rb b/aggregate_root/spec/aggregate_root_spec.rb index 4e30fba07f..ca5ed51568 100644 --- a/aggregate_root/spec/aggregate_root_spec.rb +++ b/aggregate_root/spec/aggregate_root_spec.rb @@ -181,9 +181,12 @@ def custom_expired(_event) end it "deprecates passing event_type_resolver to AggregateRoot.with" do - expect { AggregateRoot.with(event_type_resolver: ->(value) { value.to_s }) }.to output( - "[DEPRECATION] #{AggregateRoot::EVENT_TYPE_RESOLVER_DEPRECATION}", - ).to_stderr + expect { AggregateRoot.with(event_type_resolver: ->(value) { value.to_s }) }.to output(<<~EOS).to_stderr + [DEPRECATION] Passing event_type_resolver to AggregateRoot has been deprecated. + + Event type is now derived from event.event_type. The event_type_resolver + argument is ignored and will be removed in a future release. + EOS end it "does not deprecate when event_type_resolver is not passed to AggregateRoot.with" do @@ -196,28 +199,6 @@ def custom_expired(_event) expect { AggregateRoot.with(event_type_resolver: ->(value) { value.to_s }) }.not_to output.to_stderr end - it "deprecates passing event_type_resolver to DefaultApplyStrategy.new" do - expect { AggregateRoot::DefaultApplyStrategy.new(event_type_resolver: ->(value) { value.to_s }) }.to output( - "[DEPRECATION] #{AggregateRoot::EVENT_TYPE_RESOLVER_DEPRECATION}", - ).to_stderr - end - - it "does not deprecate when event_type_resolver is not passed to DefaultApplyStrategy.new" do - expect { AggregateRoot::DefaultApplyStrategy.new }.not_to output.to_stderr - end - - it "emits the DefaultApplyStrategy.new deprecation under a suppressible key" do - RubyEventStore::Deprecations.suppress(:aggregate_root_event_type_resolver) - - expect { AggregateRoot::DefaultApplyStrategy.new(event_type_resolver: ->(value) { value.to_s }) }.not_to output.to_stderr - end - - it "derives on-DSL registration key from the event class name" do - klass = Class.new { include AggregateRoot } - - expect(klass.event_type_for(Orders::Events::OrderCreated)).to eq("Orders::Events::OrderCreated") - end - it "routes apply by event.event_type, ignoring a passed event_type_resolver" do klass = nil silence_warnings do From 94143c9c15952b86d7eb9db6fa0aa01fd82ac630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 16 Jul 2026 16:30:11 +0200 Subject: [PATCH 7/9] Avoid breaking changes by removing public method Deprecate it instead --- aggregate_root/lib/aggregate_root.rb | 12 +++++++++ aggregate_root/spec/aggregate_root_spec.rb | 27 +++++++++++++++++++ .../lib/ruby_event_store/event.rb | 5 +++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/aggregate_root/lib/aggregate_root.rb b/aggregate_root/lib/aggregate_root.rb index 1a6c5dfe30..bddccd179f 100644 --- a/aggregate_root/lib/aggregate_root.rb +++ b/aggregate_root/lib/aggregate_root.rb @@ -91,6 +91,18 @@ def self.with(strategy: -> { DefaultApplyStrategy.new }, event_type_resolver: ni host_class.extend Constructor host_class.extend OnDSL if strategy.call.respond_to?(:uses_on_dsl?) host_class.include AggregateMethods + host_class.define_singleton_method :event_type_for do |value| + RubyEventStore::Deprecations.warn( + :aggregate_root_event_type_for, + message: <<~EOW, + Calling event_type_for on an AggregateRoot class has been deprecated. + + Event type is now derived from event.event_type. This method is ignored + internally, returns value.to_s, and will be removed in a future release. + EOW + ) + value.to_s + end end define_method :apply_strategy do diff --git a/aggregate_root/spec/aggregate_root_spec.rb b/aggregate_root/spec/aggregate_root_spec.rb index ca5ed51568..e0d91da64b 100644 --- a/aggregate_root/spec/aggregate_root_spec.rb +++ b/aggregate_root/spec/aggregate_root_spec.rb @@ -224,6 +224,33 @@ def initialize expect(order.status).to eq(:created) end + it "deprecates calling event_type_for on an aggregate class" do + klass = Class.new { include AggregateRoot } + + expect { klass.event_type_for(Orders::Events::OrderCreated) }.to output(<<~EOS).to_stderr + [DEPRECATION] Calling event_type_for on an AggregateRoot class has been deprecated. + + Event type is now derived from event.event_type. This method is ignored + internally, returns value.to_s, and will be removed in a future release. + EOS + end + + it "event_type_for returns value.to_s" do + klass = Class.new { include AggregateRoot } + result = nil + + silence_warnings { result = klass.event_type_for(Orders::Events::OrderCreated) } + + expect(result).to eq("Orders::Events::OrderCreated") + end + + it "emits the event_type_for deprecation under a suppressible key" do + RubyEventStore::Deprecations.suppress(:aggregate_root_event_type_for) + klass = Class.new { include AggregateRoot } + + expect { klass.event_type_for(Orders::Events::OrderCreated) }.not_to output.to_stderr + end + it "included modules" do klass = Class.new { include AggregateRoot } diff --git a/ruby_event_store/lib/ruby_event_store/event.rb b/ruby_event_store/lib/ruby_event_store/event.rb index 6e9a3bdad1..45ca01f2cf 100644 --- a/ruby_event_store/lib/ruby_event_store/event.rb +++ b/ruby_event_store/lib/ruby_event_store/event.rb @@ -27,7 +27,10 @@ def message_id event_id end - # Type of event. Used when matching with subscribed handlers. + # Type of event, derived from metadata[:event_type] or the class name. + # Used by the AggregateRoot apply strategy to look up on-handlers. + # Note: RubyEventStore dispatch and subscriptions route via the + # event_type_resolver, not this value. # @return [String] def event_type metadata[:event_type] || self.class.name From efdd4d07c241f5a61b08b0f414dfacac70f829d9 Mon Sep 17 00:00:00 2001 From: Szymon Fiedler Date: Thu, 16 Jul 2026 18:14:25 +0200 Subject: [PATCH 8/9] Weave AggregateRoot deprecations as AOP aspects instead of inline warns --- aggregate_root/lib/aggregate_root.rb | 58 ++++++++++++++++++---------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/aggregate_root/lib/aggregate_root.rb b/aggregate_root/lib/aggregate_root.rb index bddccd179f..1b327a417f 100644 --- a/aggregate_root/lib/aggregate_root.rb +++ b/aggregate_root/lib/aggregate_root.rb @@ -76,33 +76,38 @@ def marshal_load(vars) end end + RubyEventStore::Deprecations.register( + :aggregate_root_event_type_resolver, + <<~EOW, + Passing event_type_resolver to AggregateRoot has been deprecated. + + Event type is now derived from event.event_type. The event_type_resolver + argument is ignored and will be removed in a future release. + EOW + ) + + RubyEventStore::Deprecations.register( + :aggregate_root_event_type_for, + <<~EOW, + Calling event_type_for on an AggregateRoot class has been deprecated. + + Event type is now derived from event.event_type. This method is ignored + internally, returns value.to_s, and will be removed in a future release. + EOW + ) + def self.with(strategy: -> { DefaultApplyStrategy.new }, event_type_resolver: nil) - unless event_type_resolver.nil? - message = <<~EOW - Passing event_type_resolver to AggregateRoot has been deprecated. - - Event type is now derived from event.event_type. The event_type_resolver - argument is ignored and will be removed in a future release. - EOW - RubyEventStore::Deprecations.warn(:aggregate_root_event_type_resolver, message:) - end Module.new do define_singleton_method :included do |host_class| host_class.extend Constructor host_class.extend OnDSL if strategy.call.respond_to?(:uses_on_dsl?) host_class.include AggregateMethods - host_class.define_singleton_method :event_type_for do |value| - RubyEventStore::Deprecations.warn( - :aggregate_root_event_type_for, - message: <<~EOW, - Calling event_type_for on an AggregateRoot class has been deprecated. - - Event type is now derived from event.event_type. This method is ignored - internally, returns value.to_s, and will be removed in a future release. - EOW - ) - value.to_s - end + host_class.define_singleton_method(:event_type_for) { |value| value.to_s } + RubyEventStore::Deprecations.deprecate_class_method( + host_class, + :event_type_for, + key: :aggregate_root_event_type_for, + ) end define_method :apply_strategy do @@ -114,4 +119,15 @@ def self.with(strategy: -> { DefaultApplyStrategy.new }, event_type_resolver: ni def self.included(host_class) host_class.include with end + + DeprecatedEventTypeResolver = + Module.new do + def with(event_type_resolver: nil, **) + unless event_type_resolver.nil? + RubyEventStore::Deprecations.warn(:aggregate_root_event_type_resolver) + end + super + end + end + singleton_class.prepend(DeprecatedEventTypeResolver) end From 3c734e3d2b99c28648400b95d003a887629272e0 Mon Sep 17 00:00:00 2001 From: Szymon Fiedler Date: Thu, 16 Jul 2026 18:18:24 +0200 Subject: [PATCH 9/9] Require active_support in aggregate_root_spec so it runs in isolation --- aggregate_root/spec/aggregate_root_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aggregate_root/spec/aggregate_root_spec.rb b/aggregate_root/spec/aggregate_root_spec.rb index e0d91da64b..3dd3a21cd6 100644 --- a/aggregate_root/spec/aggregate_root_spec.rb +++ b/aggregate_root/spec/aggregate_root_spec.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true require "spec_helper" +require "active_support" +require "active_support/notifications" ::RSpec.describe AggregateRoot do let(:uuid) { SecureRandom.uuid }