TL;DR: a context in Phoenix is nothing more than just a module with a few functions that model the business domain.

Lately, I have been doing a lot of work in Phoenix for a side project. It’s a web framework for Elixir that is based on Plug. It favors clean structures and readable code but doesn’t compromise in performance. I like it’s flexibility and it’s rich feature set.

You may have already experienced (or you definitely will in the future) that structuring code is key to a successful software project. Clean code is necessary to keep a code base maintainable over time.

Modules in Elixir

As Elixir is a functional programming language, functions are first-class citizens and you will encounter them anywhere in the core as well as your application code. But a function alone does not provide much context so they are organized in modules. This is a simple example of a function in a module:

defmodule PrintGreetings do
  def hello(name) do
    IO.puts("Hello, #{name}!")
  end
end

Without a context, hello won’t say much, but the PrintGreetings module name suggests that there will be greetings and printing. As simple as that.

What are contexts?

In an application there will be lots of modules that contain functions that do similar or related stuff. Martin Fowler describes a Bounded Context like this:

DDD deals with large models by dividing them into different Bounded Contexts and being explicit about their interrelationships.

In a Phoenix application, you are encouraged to use contexts to build those explicit relations between contexts. They were introduced in Phoenix 1.3. Models are named “schema” in Phoenix to avoid confusion with what eg. Rails defines as a model. Many developers had problems to grasp the concept because of the naming.

To build a new schema which is managed by a context, you can generate the schema like this:

mix phx.gen.schema Blog.Post blog_posts title:string views:integer

In this example Blog will be the context of the newly generated struct Post, which will contain all your business code regarding a blog. A **.Blog** module will be generated to persist, load and modify instances of all your Posts and other schemas in the context. It's your public API for everything blog-related.

This is an example of a context from the Phoenix Guide:

defmodule Hello.Accounts do
  @moduledoc """
  The Accounts context.
  """

  import Ecto.Query, warn: false

  alias Hello.Repo
  alias Hello.Accounts.User

  @doc """
  Returns the list of users.

  ## Examples

      iex> list_users()
      [%User{}, ...]

  """
  def list_users do
    Repo.all(User)
  end
  ...
end

The context is about user accounts and handles User schemas. If I were to implement logging in or resetting a password, I would put the code in there, so everyone could just use this module.

more on contexts