ExOperation v0.5.0 ExOperation View Source
A library for making domain operations wrapped in a single database transaction.
Example
An operation definition:
defmodule MyApp.Book.Update do
use ExOperation, params: %{
id!: :integer,
title!: :string,
author_id: :integer
}
def validate_params(changeset) do
changeset
|> Ecto.Changeset.validate_length(:title, min: 5)
end
def call(operation) do
operation
|> find(:book, schema: MyApp.Book, preload: [:author])
|> find(:author, schema: MyApp.Author, id_path: [:author_id], optional: true)
|> step(:result, &do_update(operation.context, &1, operation.params))
|> after_commit(&send_notifcation(&1))
end
defp do_update(context, txn, params) do
txn.book
|> Ecto.Changeset.cast(params, [:title])
|> Ecto.Changeset.put_assoc(:author, txn.author)
|> Ecto.Changeset.put_assoc(:updated_by, context.current_user)
|> MyApp.Repo.update()
end
defp send_notification(txn) do
# …
{:ok, txn}
end
end
The call:
context = %{current_user: current_user}
with {:ok, %{result: book}} <- MyApp.Book.Update |> ExOperation.run(context, params) do
# …
end
Features
- Railway oriented domain logic pipeline.
- Running all steps in a single database transaction. It uses Ecto.Multi inside.
- Params casting & validation with
Ecto.Changeset
. Thanks to params library. - Convenient fetching of entitites from the database.
- Context passing. Useful for passing current user, current locale etc.
- Composable operations: one operation can call another through
suboperation/3
function. - After commit hooks for scheduling asynchronous things.
Installation
Add the following to your mix.exs
and then run mix deps.get
:
def deps do
[
{:ex_operation, "~> 0.1.0"}
]
end
Add to your config/config.exs
:
config :ex_operation,
repo: MyApp.Repo
where MyApp.Repo
is the name of your Ecto.Repo module.
Link to this section Summary
Functions
Call an operation from module
.
module
must implement ExOperation.Operation
behaviour
Link to this section Functions
Call an operation from module
.
module
must implement ExOperation.Operation
behaviour.
context
is an arbitrary map for passing application-specific data such as current user.
raw_params
is a map of parameters that will be casted and validated
according to the operation’s params specification.
Keys may be strings either atoms. Nesting is supported.
In case when all steps return {:ok, result}
tuple and DB transaction successfully commited
it returns {:ok, txn}
tuple where txn
is a map where keys are step names
and values are their results.
In case of invalid raw_params
it returns {:error, changeset}
where changeset
is an Ecto.Chageset
struct containing validation errors.
In case of error in one of the steps or database error it returns {:error, name, reason, txn}
tuple where name
is the failed step name, reason
is the error and txn
is changes so far.
If any after commit callbacks are scheduled they get called after database transaction commit and before the return.