Validations
Sequent uses ActiveModel::Validations for validating things like Commands and ValueObjects.
For an in-depth explanation of all available ActiveModel validations, please check out the Active Record Validations Guide.
Command validations
Commands are executed to get things done in Sequent. In a typical web application they are bound
to an HTML form. ActiveModel::Validations
can be used to validate the input values in the Command.
For example:
<form action="/create" method="post">
<input type="text" name="first_name">
<input type="text" name="last_name">
<button>Save</button>
</form>
class CreateUser < Sequent::Command
attrs first_name: String, last_name: String
validates :first_name, presence: true, length: {minimum: 3, maximum: 100}
validates :last_name, presence: true
end
post '/create' do
Sequent.command_service.execute_commands(
CreateUser.new(
params.slice(:first_name, :last_name).merge(
aggregate_id: Sequent.new_uuid
)
)
)
end
The CommandService validates all Commands that are executed by calling valid?
. If a Command is not valid
it raises a Sequent::Core::CommandNotValid
error. The Sequent::Core::CommandNotValid
has a reference to the Command which in
turn, since it is an ActiveModel object, has a reference to the ActiveModel::Errors
. You can use this error to, for instance,
display the errors at their corresponding html form input fields. Unfortunately, at the time of writing, the Rails Form Helpers only support ActiveRecord objects and not ActiveModel objects. Therefore form binding should be done manually.
post '/create' do
Sequent.command_service.execute_commands(
CreateUser.new(
params.slice(:first_name, :last_name).merge(
aggregate_id: Sequent.new_uuid
)
)
)
redirect '/list'
rescue Sequent::Core::CommandNotValid => e
@command = e.command
erb :new
end
It is recommended to manually handle the Sequent::Core::CommandNotValid
error,
since any registered synchronous Workflow executing Commands
can also raise a Sequent::Core::CommandNotValid
.
More complex validations should be done in the AggregateRoot.
Aggregate Root validations
Some validations can not be performed in the Command since they are dependant on the current state of the AggregateRoot
.
Sequent does not provide this functionality out of the box, since it is highly dependant on your domain - Sequent is not
a web framework trying to provide form binding.
Example:
class QueueInvoiceForSending < Sequent::Command; end
class InvoiceAlreadyQueued < My::DomainError; end
class Invoice < Sequent::AggregateRoot
def queue_for_sending
fail InvoiceAlreadyQueued if @queued
apply InvoiceQueuedForSending
end
on InvoiceQueuedForSending do
@queued = true
end
end
In the above example the Command QueueInvoiceForSending
has no validations, so executing it twice will have no effect
on the validity of the Command. Only the AggregateRoot can know if this operation is allowed or not.
In your web application you can then rescue from all My::DomainError
s and show the user
an appropriate error message.