How would you explain Phoenix Contexts to a newbie?

I find it easier to understand and attempt to demonstrate a new concept by using a worked example. Let’s take the ubiquitous ecommerce website to go through.

Before the introduction of contexts, we’d likely create Phoenix models (Ecto schemas) for our users, products, orders, line items, etc. All bundled together inside web/models. Each model would include concerns related to the entire shopping domain. As an example, the user model would include a billing address, shipping address, and login details.

With contexts in Phoenix 1.3 we would spend additional effort up front deciding how to separate our entire shopping domain into its constituents. This decision is usually driven by the business requirements, often mirroring the physical departments in the business.

So we might decide to create our contexts as follows:

  • Accounts to manage user registration and authentication/login.
  • Product Catalog to allow listing of new products, displaying products for sale.
  • Cart to deal with visitors adding products to their shopping cart.
  • Orders to take the visitor through the checkout process and confirm their shopping cart.
  • Billing to actually charge the visitor for the items in their cart.
  • Shipping to handle the physical, or electronic, delivery of the paid for goods.

A context may share a model (Ecto schema), such as a user, but they will access different fields. For example, the accounts context uses the user’s login/email and password; billing uses their name and billing address; shipping uses their postal address. This demonstrates the shared database table, but different model in each context containing a subset of the entire fields (columns).

By investing the time in defining our contexts we get a number of benefits:

  1. A folder structure that replicates the application we’re building, namespaced per context (e.g. billing, cart, accounts). Any of these could later be extracted into their own Elixir application, contained within an umbrella project, should they become too large.
  2. A context provides a public API. This allows the internal implementation to be somewhat hidden and allows refactoring. Consumers use the public functions, via the context, not the internals.
  3. Contexts help developers locate the code they need to change when adding features or fixing bugs. As an example, if there is a problem with a billing feature, I can immediately navigate straight to that context. I’m also guided as to where new modules and functions should be written. This should help beginners, and experienced developers alike. Since a context is a smaller part of the entire business, there is less mental overhead when working within it.

For more background on the subject, I recommend watching Uncle Bob’s Architecture the Lost Years talk where he explains this concept in detail. It is also the origin of the phrase “Phoenix is not your application”, albeit applied to Rails in his case. He simply means that when you look at the top level folder structure it should indicate the problem domain, not the framework. So we should see a directory structure I’ve outlined above: accounts, product catalog, cart, billing, shipping. Not models, controllers, views, templates. The web folder is a separate context, and one that just provides an external HTTP interface to the other contexts.

In summary, I’m entirely in favour of contexts in Phoenix 1.3. Thanks to @chrismccord and team for taking the time to introduce them, and helping to guide Phoenix developers into a more maintainable approach to building web applications.

27 Likes