We’ve spent a little time in the functional core, and we’ve seen that our schema and changeset code is predictable and certain—it behaves the same way given the same inputs, every time. In this lesson, we’ll shift away from the core and into the places where the uncertain world intrudes on our beautiful assumptions. We’ve come to the Phoenix context.

Phoenix Context

Contexts represent the boundary for an application. As with all boundaries, it defines the point where our single purpose code meets the world outside. That means contexts are APIs responsible for taking unsanitized, unvalidated data and transforming it to work within the core, or rejecting it before it reaches the core.

The boundary code isn’t just an API layer. It’s the place we try to hold all uncertainty. Our context has at least the following responsibilities:

  • Access External Services: The context allows a single point of access for external services.

  • Abstract Away Tedious Details: The context abstracts away tedious, inconvenient concepts.

  • Handle uncertainty: The context handles uncertainty, often by using result tuples.

  • Present a single, common API: The context provides a single access point for a family of services.

Based on what you’re doing in your code, the boundary may have other responsibilities as well. Boundaries might handle process machinery. They might also transform correct outputs to work as inputs to other services. Our generated Phoenix context doesn’t have those issues, though. Let’s dig a little deeper into the context we’ve generated.

How to access external services

External services will always be accessed from the context. Accessing external services may result in failure, and managing this unpredictability is squarely the responsibility of the context.

Our application’s database is an external service and the Catalog context provides the service of database access. This access is enacted using the Ecto code. Just like the rest of our application, the Ecto code can be divided into core and boundary concerns. Ecto code that deals with the certain and predictable work, like of building queries and preparing database transactions belong in the core. That is why, for example, we found the changeset code that sets up database transactions in the Product schema.

Executing database requests, on the other hand, is unpredictable and could fail at any time. Ecto implements the Repo module to do this work and any such code that calls on the Repo module belongs in the context module, our application’s boundary layer.

Here are a few functions from the context module at pento/lib/pento/catalog.ex:

Get hands-on with 1200+ tech skills courses.