Why were Phoenix Contexts introduced? A (brief) history.
Notes
People frequently struggle with Phoenix Contexts.
Why were they introduced?
When I explain the history to them, it becomes a little more clear. So, here’s a brief history.
Back before Phoenix 1.3, Phoenix had a very MVC directory structure (much like a Rails app has today), meaning we had:
- Models
- Views
- Controllers
But in Phoenix 1.3, the Phoenix team decided to change that. This was the new slogan:
Phoenix is not your app
The Phoenix team separated the “web” portion from what I typically call the “core domain”.
And that’s when we got the _web
separation that we see in Phoenix apps today.
So, now we have MyAppWeb
and MyApp
namespaces that match the my_app_web/
and my_app/
directories.
But that still leaves the question: what should we do with the type of code that lives in a controller or LiveView?
Take this product creation in a controller as an example:
def create(conn, params) do
%Product{}
|> Product.changeset(params)
|> Repo.insert()
# assign and redirect
end
How we create a product is domain knowledge. So it would be nice for the
product-creation pipeline to live in the MyApp
portion of the app.
And that’s why Phoenix Contexts were introduced. They are meant to be the interface through which our web layer talks to our core domain layer.
So, now the web layer calls the Checkout
context:
def create(conn, params) do
Checkout.create_product(params)
# assign and redirect
end
Hope that helps you understand them better.
P.S. If you want more info on how DDD can help with naming your Phoenix Contexts, check out my ElixirConf 2023 talk: Creating Better Phoenix Contexts with DDD