Workflow
Workflows are used to add additional functionality (other than updating a Projection) triggered by Events. Common tasks run by Workflows are:
- Execute other Commands
- Schedule something to run in the background
In Sequent, Workflows are committed in the same transaction as committing the Events.
Since Workflows have nothing to do with Projections they do not run when doing a Migration.
If you didn’t set enable_autoregistration
to true
you will need to add your Workflows manually to your Sequent configuration in order to use them:
Sequent.configure do |config|
config.event_handlers = [
SendEmailWorkflow.new,
]
end
A Workflow responds to Events basically the same way as Projectors do. For instance, a Workflow that will schedule a background Job using DelayedJob can look like this:
class SendEmailWorkflow < Sequent::Workflow
on UserCreated do |event|
Delayed::Job.enqueue UserJob.new(event)
end
end
class UserJob
def initialize(event)
@event = event
end
def perform
ExternalService.send_email_to_user('Welcome User!', event.user_email_address)
end
end
If your Workflow has some side effects that can’t be rolled back easily, or if your background jobs processor
is not using the same database connection used for the transaction, you can wrap it in an after_commit
block:
class SendEmailWorkflow < Sequent::Workflow
on UserCreated do |event|
after_commit do
SendEmailJob.perform_async(event)
end
end
end
class SendEmailJob
include Sidekiq::Worker
def perform(event)
ExternalService.send_email_to_user('Welcome User!', event.user_email_address)
end
end
It will run only if the transaction commits. Note that if you execute another command, it will be run
synchronously but in a separate transaction. It will not be able to rollback the first one, resulting in some
Events to be committed and some not. Only use after_commit
if it is the intended behaviour.
Handling Exceptions: If an exception within an after_commit
is not handled by the worker, it will stop
calling the other registered hooks. Make sure that you rescue exceptions and handle them properly. If you can
afford to ignore the errors and want to make sure all hooks are called, you can pass ignore_errors: true
as a parameter.