Pub/Sub Task Chaining¶
RegisterUser succeeds. Now you need a welcome email, a search index update, and an analytics event. You could call three more tasks from inside work — but then every change to “what happens after signup” edits the same file.
Publish a domain event when the task succeeds. Subscribers enqueue their own tasks. The registering task stays focused on creating the user.
Setup — subscriber¶
Listen for the event and enqueue follow-on work. ActiveSupport::Notifications is built into Rails; swap in Wisper, Kafka, or whatever your app already uses.
class SendWelcomeEmail < ApplicationTask
required :user_id, coerce: :integer
def work
WelcomeMailer.with(user_id:).deliver_later
end
end
ApplicationTask here is the base class from Background Execution with execute_later wired to TaskJob.
ActiveSupport::Notifications.subscribe("user.registered") do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
SendWelcomeEmail.execute_later(user_id: event.payload[:user_id])
end
Usage¶
Put anything subscribers need on context inside work, then publish from an on_success callback.
class RegisterUser < CMDx::Task
required :email, coerce: :string, validate: { format: URI::MailTo::EMAIL_REGEXP }
required :name, coerce: :string
on_success :publish_registration_event
def work
context.user = User.create!(email:, name:)
end
private
def publish_registration_event
ActiveSupport::Notifications.instrument(
"user.registered",
user_id: context.user.id,
email: context.user.email
)
end
end
RegisterUser.execute(email: "jane@example.com", name: "Jane Doe")
Notes¶
Order matters¶
on_success runs after work finishes. Subscribers only see what you wrote to context during work — set context.user (or similar) before the callback fires.
Workflows vs pub/sub¶
For the next step in the same process with shared context, use a workflow. Publish events when subscribers should stay decoupled — mailers, analytics, separate queues.