This boilerplate comes with batteries included, you’ll find:
- Phoenix
- Database integration with Ecto
- Translations with Gettext
- Tests factories using ExMachina and code coverage using ExCoveralls
- CORS management with CorsPlug
- Static code analysis with Credo
- Releases using
mix release
and Docker - Tailwindcss
- warnings_as_errors: true
Here is an opinionated list of other useful libraries to consider:
Category | Libraries |
---|---|
Authentication | ueberauth |
Token based auth | Guardian |
Emails | bamboo , swoosh |
HTTP client | Tesla |
Job processing | Oban |
Pagination | Scrivener (limit/offset), Quarto (cursor based) |
Mocks | Mox , Mimic |
Cache | Cachex, Nebulex (distributed cache) |
Date/Time | Timex |
CSV | NimbleCSV, CSV |
Feature Flags/Toggles | FunWithFlags |
Clustering | libcluster |
- Run
rename.sh
with the camel case name of your project
./rename.sh MyNewProjectName
- Install Docker
- Install Make:
sudo apt install make
orbrew install make
- Clone the project repository:
git clone git@github.com:MAGICMOTORSPORT/elixir-boilerplate.git
- Go to project dir:
cd elixir-boilerplate
- Execute:
make setup
to install dependencies, setup the database, execute migrations, etc. - Get a
.env
file executingcp env.template .env
and set theSECRET_KEY_BASE
value. Get a new value executingmake gen.secret
- Execute:
make run
to run the server at http://localhost:4000
If you want to add new environment variables you need to put the new env var in some places:
- In the
.env.dist
template file to include in new installations - In your
.env
file
NOTE: When you add a new env var you must restart the container, so the container can read the new variable.
For convenience, you can use the commands included in the Makefile:
Command | Description |
---|---|
make bootstrap |
Bootstrap the phoenix project (dependencies & databse) |
make deps.get |
Gets & compile dependencies |
make deps.clean |
Clean unused dependencies & remove from mix.lock |
make seeds |
Run seeds |
make reset |
Resets the project removing deps & builds |
make ecto.setup |
Setup the database for dev |
make ecto.reset |
Resets the database for dev |
make ecto.reset.test |
Resets the database for test |
make test |
Runs tests |
make check.all |
Run all CI verifications (formatter, credo, coverage) |
make run |
Start Phoenix server |
make gettext |
Search & merge new translations |
make format |
Format all phoenix files |
- Install asdf
- Add the asdf erlang plugin
asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git
- Add the asdf elixir plugin
asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git
- Add the asdf nodejs plugin
asdf plugin-add nodejs https://github.com/asdf-vm/asdf-nodejs.git
- Clone the project repository:
git clone git@github.com:MAGICMOTORSPORT/elixir-boilerplate.git my_app
- Go to project dir:
cd my_app
- Install Erlang, Elixir & NodeJS using the
.tools-versions
file with:asdf install
- Copy the
env.dist
file to.env
and set theSECRET_KEY_BASE
value. Get a new value executingmix phx.gen.secret
. - Run
export $(cat .env | xargs)
on project root folder - Run
mix local.hex && mix local.rebar
- Run
mix setup
to download dependencies & setup database - Run
mix phx.server
If you want to add new environment variables you need to put the new env var in some places:
- In the
.env.dist
template file to include in new installations - In your
.env
file.
To start your Phoenix server:
- Install dependencies with
mix deps.get
- Create and migrate your database with
mix ecto.setup
- Start Phoenix endpoint with
mix phx.server
or inside IEx withiex -S mix phx.server
Now you can visit localhost:4000
from your browser.
Ready to run in production? Please check our deployment guides.
In Phoenix, contexts often encapsulate data access and data validation. They often talk to a database or APIs. Overall, think of them as boundaries to decouple and isolate parts of your application.
Contexts are modules and functions defining the public interface of your application.
The choices about how to draw contexts in your application must be rooted in DDD (Domain Driven Development)
In Phoenix contexts can also act as a encapsulation of something that tomorrow can be extracted in a separate microservice.
Naming things is hard. If you're stuck when trying to come up with a context name when the grouped functionality in your system isn't yet clear, you can simply use the plural form of the resource you're creating. For example, a Products
context for managing products. As you grow your application and the parts of your system become clear, you can simply rename the context to a more refined one.
Contexts act as that reusable layer for business logic. The names of modules/functions/parameters should reflect business domain concepts.
Phoenix contexts can easily become quickly huge meatballs of code, to ease things out, we decide to split them into layers:
Public Context (the actual Context, the public API)
Schema (including changesets and validations)
Queries
Here’s what that might look like:
├── posts/
│ ├── posts.ex
│ ├── post_schema.ex
│ ├── post_queries.ex
├── complex_context/
│ ├── schemas/
│ ├── queries/
│ ├── sub_complex_context/
│ ├── complex_context.ex
├── utils/
│ ├── util_a.ex
│ ├── util_a_test.exs
│ └── ...
└── ...
The Schema layer (post_schema.ex
) covers the Ecto schema, with changesets and validations. Now you can see easily which database schemas are in the context. The idea is to have something near to a OO Model without database interactions.
The Queries layer (post_queries.ex
) handles all the database operations, excluding validation. It’s for internal use within its context only. Can be split into schema based files.
The Public Context layer (posts.ex
) is the context’s public API to the rest of your app. Here, you’ll find business logic beyond queries or validation.
Being an interface the public context can be made only of defdelegate
functions calling the queries layer. https://hexdocs.pm/elixir/1.12/Kernel.html#defdelegate/2
Splitting contexts into subcontextes like this and using defdelegate
in the context module can help have all the public interface in one place and at the same time better manage code at the subcontext level.
It’s totally ok for contexts to talk to each other so long as it’s happening through the “aggregate root” (ie, the context file, even if this isn’t the 100% true definition).
Also when facing inter contexts operations Services
can be used. Services must return an Ecto.Multi
from call so that they can be composed together (see documentation resource [4])
[1] https://hexdocs.pm/phoenix/contexts.html [2] https://curiosum.com/blog/elixir-phoenix-context-maintainability-guildelines [3] https://michal.muskala.eu/post/putting-contexts-in-context/ [4] https://luizdamim.com/blog/reorganizing-your-phoenix-contexts-as-use-cases/ [5] https://speakerdeck.com/andrewhao/building-beautiful-systems-with-phoenix-contexts-and-ddd?slide=151
- Official website: https://www.phoenixframework.org/
- Guides: https://hexdocs.pm/phoenix/overview.html
- Docs: https://hexdocs.pm/phoenix
- Forum: https://elixirforum.com/c/phoenix-forum
- Source: https://github.com/phoenixframework/phoenix