Single context per schema?

Hey everyone! I did some elixir work about a year ago, but I have started using my moonlight hours to pick up elixir and phoenix again :slight_smile:

As I was building out an app last night, I had a bit of a hard time naming contexts and remembering what I had named them. I definitely see the value of contexts, and they’re part of the reason I’m drawn to Phoenix. It’s just that I’m not used to having an extra ball in the air and I’m quite forgetful.

So I had a thought, what if I named every context the same name as the database table, renamed the methods to rely on the context name for clarity, and only used one context per schema?

I ended up doing things like this:

mix phx.gen.html Articles Article articles title body:text

and renaming the context methods from the generator to look a little more familiar to me (my background is mostly Rails):

defmodule App.Articles do
  def all, do: ...
  def get!(id), do: ...
  def create(attrs \\ %{}), do: ...
  def update(%Article{} = article, attrs), do: ...
  def delete(%Article{} = article), do: ...
  def change(%Article{} = article), do: ...

With this approach and the use of quick_alias I was able to get a lot more comfortable with the framework (I can just hop into iex and use years of Rails muscle memory by typing Articles.all, etc.).

Is this a common approach to working with contexts? Do you think there are any downsides to structuring my app this way?

Imo this is exactly what you don’t want to do long term. What you’re showing is basically crud with a module around it. What your code doesn’t show is what your app actually does. There are some articles, but I don’t know if this is this a newspaper cms or an e-commerce backend?

To me the idea for contexts is two fold. First and foremost it should encourage people to move business logic from being in controllers to being in modules (outside the “web” part of the application), but the second part is about building an useful API for your domain. Sticking to my first guess (newspaper cms): Usually articles go from draft to being revised to getting published. Those things should be part of the api like MyApp.Articles.file_for_review or MyApp.Articles.publish. Behind the scenes both of them might be a plain “update_article” with some params, but a caller on the outside shouldn’t care or know much about the actual implementation.

What you have is not bad though. The actual implementation (db access, changesets, …) need to live somewhere, but it shouldn’t be the public api. @devonestes for example has a blogpost about using what you showed “within a context”:

1 Like

One context per schema may quickly remove a lot of the clarity benefits that contexts can provide. Namely, contexts help me group together pieces of the application into their respective domains. One thing that I like to do with my contexts is use defdelegate to point context functions to more specific modules (like an ArticlesStore) in the related folder.

It would work okay for small apps (5-10 tables), but would not work well for large apps.