Command Pattern - decoupling what is done from who does it.
Notice this gem is namespaced.
gem install arkency-command_bus
require'arkency/command_bus'command_bus=Arkency::CommandBus.newregister=command_bus.method(:register){FooCommand=>FooService.new(event_store: event_store).method(:foo),BarCommand=>BarService.new,}.map(®ister)command_bus.(FooCommand.new)If need a new instance of a service every time it is called with a command or you want to lazily load the responsible services, you can just use Proc during registration.
command_bus=Arkency::CommandBus.newcommand_bus.register(FooCommand,->(foo_cmd){FooService.new(event_store: event_store).foo(foo_cmd)})command_bus.register(BarCommand,->(bar_cmd){BarService.new.call(bar_cmd)})In Rails development mode when you change a registered class, it is reloaded, and a new class with same name is constructed.
a=Usera.object_id# => 40737760 reload!# Reloading...b=Userb.object_id# => 48425300h={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_preparedoconfig.command_bus=Arkency::CommandBus.newregister=command_bus.method(:register){FooCommand=>FooService.new(event_store: event_store).method(:foo),BarCommand=>BarService.new,}.map(®ister)endand call it with
Rails.configuration.command_bus.call(FooCommand.new)require'arkency/command_bus/alias'From now on you can use top-level CommandBus instead of Arkency::CommandBus.
Command Bus is funded and maintained by Arkency. Check out our other open-source projects.
You can also hire us or read our blog.