Command Bus
Command Pattern - decoupling what is done from who does it.
Usage
Registering commands and their handlers
require "arkency/command_bus"
command_bus = Arkency::CommandBus.new
register = command_bus.method(:register)
{ FooCommand => FooService.new(event_store: event_store).method(:foo), BarCommand => BarService.new }.map(®ister)
Invoking command bus with a command
command_bus.(FooCommand.new)
Will call FooService#foo
method with the command you just passed.
New instance of a service for every invoked command
If you need a new instance of a service, every time it is called with a command, or you want to lazily load the responsible services, use Proc
when registering commands.
command_bus = Arkency::CommandBus.new
command_bus.register(FooCommand, ->(foo_cmd) { FooService.new(dependency: dep).foo(foo_cmd) })
command_bus.register(BarCommand, ->(bar_cmd) { BarService.new.call(bar_cmd) })
Working with Rails development mode
In Rails development
mode when you change a registered class, it is reloaded, and a new class with same name is constructed.
a = User
a.object_id
# => 40737760
reload!
# Reloading...
b = User
b.object_id
# => 48425300
h = { a => 1, b => 2 }
h[User]
# => 2
a == b
# => false
so your Hash
with mapping from command class to service may not find the new version of reloaded class.
To workaround this problem you can use to_prepare
callback which is executed before every code reload in development, and once in production.
config.to_prepare do
config.command_bus = Arkency::CommandBus.new
register = command_bus.method(:register)
{ FooCommand => FooService.new(event_store: event_store).method(:foo), BarCommand => BarService.new }.map(®ister)
end
and call it with
Rails.configuration.command_bus.call(FooCommand.new)
Convenience alias
require "arkency/command_bus/alias"
From now on you can use top-level CommandBus
instead of Arkency::CommandBus
.