I’ve started to move away from subscribing listeners globally to Wisper publishers with Wisper.subscribe to instead using a dedicated event bus.

Provided events are namspaced to avoid collisions this might be preferable to subscribing globally to concrete classes.

I even think Wisper.subscribe will be removed in the next major version of Wisper, instead something similar to the solution presented here will be provided to enable similar but explicit and controllable behaviour.

class EventBus
  def initialize
    @publisher = Class.new do
      include Wisper::Publisher
      public :broadcast
    end.new
  end

  def subscribe(listener, options = {})
    @publisher.subscribe(listener, options)
    self
  end

  def broadcast(event_name, payload)
    @publisher.broadcast(event_name, payload)
    self
  end
end

All events are re-broadcast to all subscribed listeners.

RSpec.describe 'event bus' do
  let(:listener) { double }
  let(:payload) { {} }
  let(:event_name) { 'bounded_context.thing_created' }

  subject(:event_bus) { EventBus.new }

  it 'works' do
    expect(listener).to receive(event_name).with(payload)
    event_bus.subscribe(listener)
    event_bus.broadcast(event_name, payload)
  end
end

In an application I would create a single instance of the event bus. You could use the singleton module to ensure that EventBus.new always returns the same instance or define a class dynamically.

EventBus = Class.new do
# ...
end.new