![PhoenixUp](https://raw.githubusercontent.com/henriquefernandez/use_case/master/priv/static/small_logo.png)
# UseCase
![](https://img.shields.io/hexpm/v/use_case?style=social) ![](https://img.shields.io/hexpm/l/use_case?style=social)
A way to increase *Elixir* projects readability and maintenance. Heavily inspired by *Clean Architecture*. We can join it with [clean mixer](https://github.com/miros/clean_mixer) for a very nice *Clean Architecture* code experience.
## Table of contents
- [Installation](#installation)
- [About](#about)
- [Creating Interactors](#creating-interactors)
- [Defining inputs](#defining-inputs)
- [Defining errors](#defining-errors)
- [Default fields](#default-fields)
- [Composing with pipes](#composing-with-pipes)
- [Sending Options](#sending-options)
- [Mix Tasks](#mix-tasks)
- [Contribute](#contribute)
## Installation
The package can be installed by adding `use_case` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:use_case, "~> 0.2.0"}
]
end
```
## About
Lets see some of the benefits from using the library (or only the idea behind use cases interactors). Imagine a library system where we have the context "books". On normal *Phoenix* systems, the design may look like the example below:
```
▾ library/
▾ books/
author.ex
book.ex
application.ex
books.ex
repo.ex
```
There we have some problems:
- It is not yet clear what our application intend to do.
- Contexts files (books.ex) can get extremely fat with a lot of business logic.
Now, thinking in use case interactors we can imagine *Phoenix* contexts as a *Facade* for your use cases, and do that:
```
▾ library/
▾ books/
author.ex
book.ex
create_author.ex
create_book.ex
sell_book.ex
application.ex
books.ex
repo.ex
```
Our context will look like the example above:
```Elixir
defmodule Library.Books do
@moduledoc """
The Books context.
"""
import UseCase, only: [call: 1]
import __MODULE__.{CreateAuthor, SellBook, CreateBook}
@doc """
Creates a authors.
iex> create_authors(name)
{:ok, %Library.Books.CreateAuthor.Output{ ... }}
iex> create_authors(bad_name)
{:error, %Library.Books.CreateAuthor.Error{message: "Bad name given"}}
"""
def create_author(name),
do: call(%CreateAuthor{name: name})
# ...
def create_book(name, author),
do: call(%CreateBook{name: name, author: author})
# ...
def sell_book(book_id),
do: call(%SellBook{book_id: book_id})
```
Let's say that now `CreateBook`, `CreateAuthor` and `SellBook` are gateways for our business rules. *Controllers*, *views* and even *Phoenix* know almost nothing about our business, they know that we can "create books" and "sell books", and for that we need the params "name", "author" or "book_id", but nothing about what goes inside. Goals:
- Its clear what our application intend to do. It screams.
- Contexts files are only facades, an api for our use cases interactors to the external world. They dont know Repos or Schemas.
- When we call an use case interactor, we will get a specific output or an specific error from that use case (and we have a specific input too), making the system code more assertive in relation to what it is doing.
And this is just the tip of the iceberg, to full enjoy this library, i recommend you to read the *Clean Architecture* book.
## Creating interactors
The most basic interactor can be created using the `UseCase.Interactor` module, defining an `output` for it and creating a `call/2` function:
```elixir
defmodule SayHello do
use UseCase.Interactor,
output: [:message]
def call(%{name: name}, _opts), do: ok(message: "Hello #{name}!")
def call(%{name: nil}, _opts), do: error("name is obrigatory")
end
```
Now our `SayHello` module has the `ok` and `error` macros and a struct for `Output` like `%SayHello.Output{message: "something"}`.
The `ok` and `error` macro can be used to define when our interactor success or fail.
After define, we can call it in many ways:
```elixir
iex> UseCase.call(SayHello, %{name: "Henrique"})
iex> {:ok, SayHello.Output{message: "Hello Henrique!", _state: nil}}
iex> SayHello.call(%{name: "Henrique"})
iex> {:ok, SayHello.Output{message: "Hello Henrique!", _state: nil}}
iex> UseCase.call(SayHello, %{name: nil})
iex> {:error, SayHello.Error{message: "name is obrigatory!"}}
iex> UseCase.call!(SayHello, %{name: "Henrique"})
iex> SayHello.Output{message: "Hello Henrique!", _state: nil}
iex> UseCase.call!(SayHello, %{name: nil})
iex> **** SayHello.Error name is obrigatory!
```
### Defining inputs
Sometimes we want to guarantee the inputs our interactors will receive, we can do it defining this way:
```elixir
defmodule SayHello do
use UseCase.Interactor,
output: [:message],
input: [:name] # Add this
def call(%SayHello{name: name}, _opts), do: ok(message: "Hello #{name}!")
def call(%SayHello{name: nil}, _opts), do: error("name is obrigatory")
end
```
Now, with `UseCase` module we can call it using the input directly:
```elixir
iex> UseCase.call %SayHello{name: "Henrique"}
iex> {:ok, SayHello.Output{message: "Hello Henrique!", _state: nil}}
iex> UseCase.call! %SayHello{name: "Henrique"}
iex> SayHello.Output{message: "Hello Henrique!", _state: nil}
```
### Defining errors
If we want to send extra informations in errors, we can do it as `input` and `output`.
```elixir
defmodule SayHello do
use UseCase.Interactor,
output: [:message],
input: [:name],
error: [:code] # Add this
def call(%SayHello{name: name}, _opts), do: ok(message: "Hello #{name}!")
def call(%SayHello{name: nil}, _opts), do: error("name is obrigatory", code: 500) # And use it
end
```
```elixir
iex> UseCase.call(SayHello, %{name: nil})
iex> {:error, SayHello.Error{message: "name is obrigatory!", code: 500}}
```
### Default fields
When not defined, input, output and error defaults to:
```
input: [:_state],
output: [],
error: [:message]
```
Fields `:_state` in `input` and `:message` in `error` are always appended. The `:_state` field is very useful for pipe operations.
### Composing with pipes
Lets define an `LogOperation` interactor:
```elixir
defmodule LogOperation do
use UseCase.Interactor
def call(%{message: message}, _opts) do
# .. log message
ok()
end
end
```
We can compose with our `SayHello` simple as that:
```elixir
iex> %SayHello{name: "Henrique"} |> UseCase.pipe [SayHello, LogOperation]
iex> {:ok, LogOperation.Output{_state: nil}}
iex> UseCase.pipe [%SayHello{name: "Henrique"}, LogOperation]
iex> {:ok, LogOperation.Output{_state: nil}}
iex> UseCase.pipe [%SayHello{name: nil}, LogOperation]
iex> {:error, SayHello.Error{message: "name is obrigatory!", code: 500}}
iex> %SayHello{name: "Henrique"} |> UseCase.pipe! [SayHello, LogOperation]
iex> LogOperation.Output{_state: nil}
iex> UseCase.pipe! [%SayHello{name: "Henrique"}, LogOperation]
iex> LogOperation.Output{_state: nil}
iex> UseCase.pipe! [%SayHello{name: nil}, LogOperation]
iex> **** SayHello.Error name is obrigatory!
```
All we need is match outputs and inputs and use one of pipe `UseCase` functions.
### Sending options
All `UseCase` functions last argument is the options keyword list that is sent to interactors:
```Elixir
import UseCase
call(%SayHello{name: "henrique"}, my_option: true)
%SayHello{name: "Henrique"} |> pipe([SayHello, LogOperation], my_option: true)
pipe([%SayHello{name: "Henrique"}, LogOperation], my_option: true)
```
## Mix Tasks
`mix use_case.gen.interactor`
`mix use_case.gen.phx_resource`
`mix use_case.gen.phx_resource_html`
`mix use_case.gen.phx_resource_json`
`mix use_case.gen.phx_resource_temple`
## Contribute
*UseCase* is not only for me, but for the *Elixir* community.
I'm totally open to new ideas. Fork, open issues and feel free to contribute with no bureaucracy. We only need to keep some patterns to maintain an organization:
#### branchs
*your_branch_name* or *your-branch-name*
#### commits
*[your_branch_name] Your commit* or *[your-branch-name] Your commit*