Box.UseCase behaviour (box v0.15.1)
View SourceUse cases are a way to perform mutation in the system through automatic transactioning. They can perform validation and side effects when the results is a success one.
Examples
The following use case is one that handles User creation from an admin panel.
- It expects an optiona
authenticated_user
to make sure that only admin users can actually create users - It's gonna run the insertion in a transaction, if validation succeeded
- It's gonna broadcast a pub sub message (maybe to update a table in live) and send an email to the user, if the transaction succeeded
- Finally an
{:ok, user}
will be returned from this execution rather than the multi result.
defmodule Accounts.CreateUser do
use Box.UseCase
@impl Box.UseCase
def validate(params, [authenticated_user: %User{role: "admin"}]) do
{:ok, params}
end
def validate(_, _), do: {:error, :insufficient_permissions}
@impl Box.UseCase
def run(multi, params, _options) do
Ecto.Multi.insert(:user, User.changeset(params))
end
@impl Box.UseCase
def after_run(%{user: %User{id: user_id, email: email}}) do
PubSub.broadcast("users", {:new_user, user_id})
Mailer.send_welcome_email(email)
end
@impl Box.UseCase
def return(%{user: %User{} = user}) do
{:ok, user}
end
end
Summary
Callbacks
This callback will run some side effects post-transactions. It receives the Ecto.Multi result as-is so a use can can do various thing from the step. Maybe broadcast a message to a post topic when a new comment is created or send an email to a newly created user. Return value is ignored.
This callback is involved right before returning. We'll use this callback to "simplify" the return value. This can be used to return only a main entity from a 5-6 steps multi result and even preload some relations.
This is where the actual transaction body happens. This receives an empty multi for the use
to add new transaction step to it. It also receives the same options as the validate/2
callback.
Validates if a given use case can be run or not. This can receive options so you can ensure a user's
presence, some sort of roles etc... If it returns an ̀{:ok, _}
tuple, we'll move forward to the
run/3
callback in order to build an ecto multi.
Types
Callbacks
This callback will run some side effects post-transactions. It receives the Ecto.Multi result as-is so a use can can do various thing from the step. Maybe broadcast a message to a post topic when a new comment is created or send an email to a newly created user. Return value is ignored.
This callback is involved right before returning. We'll use this callback to "simplify" the return value. This can be used to return only a main entity from a 5-6 steps multi result and even preload some relations.
@callback run(Ecto.Multi.t(), params(), Keyword.t()) :: Ecto.Mutli.t()
This is where the actual transaction body happens. This receives an empty multi for the use
to add new transaction step to it. It also receives the same options as the validate/2
callback.
Validates if a given use case can be run or not. This can receive options so you can ensure a user's
presence, some sort of roles etc... If it returns an ̀{:ok, _}
tuple, we'll move forward to the
run/3
callback in order to build an ecto multi.
Functions
@spec execute(module(), params(), [input_option()]) :: {:ok, any()} | :ignore | {:error, any()}