As all of you might know, Phoenix 1.3 comes with a new folder structure and introduces contexts/ API boundaries. I’m now trying to move my application to 1.3.0-rc.0, but I have some problems to find my contexts. I can see how it works for simple things like a blog, but as soon as things get more complicated, I’m lost. I’ve searched the web a bit, but couldn’t find anything helpful.
My application will be used by two groups of users, and I might eventually split it into two interfaces. I can currently share some interfaces and just use scoped queries to show the correct data, but I’m not sure this will be the approach for 1.3 as well, or if I should use different contexts instead.
My first idea was to build one context for each of these interfaces, but in that case I’d end up with just two contexts, and I feel like I wouldn’t be contexts the way they were meant to be used.
Are there good examples of this structure?
What’s your approach to figure out your contexts?
Are there good practices for naming them? I feel like I shouldn’t name a context like one of its schemas
My application has a lot of references across the schemas, so how do you deal with situations where, depending on your contexts, you need them in multiple contexts?
Think of your context as an Elixir library. In the same way it is completely OK for those two interfaces to use the same Elixir library, it is completely OK for those two interfaces to use the same contexts. The interfaces are most likely the ones who will drive the context implementation. The job of the context is exactly to provide functionality that all of your software interfaces (controllers, channels, CLIs, etc) will use.
Figuring out and naming contexts requires some thought and knowledge of your domain but looking at the relationships between schemas can be a good indicator. You will also find that there are some schemas, such as the user schema, that can theoretically have relationships with all other contexts. In such cases, you will have to break it apart. For example, if you have Account.User and Purchase.Order, instead of:
defmodule MyApp.Purchase.Order do
# ...
belongs_to :user, MyApp.Account.User
end
You should do:
defmodule MyApp.Purchase.Order do
# ...
field :user_id, :integer
end
This means you will-end up losing some of the query conveniences but it will help you keep your contexts decoupled. However, for schemas that belong in the same context, associations are totally fine.
Thanks for your response! I still have a couple of questions:
Let’s say I have an embedded schema %Address{} I use in a lot of places. Would I write this for every context or share it? Writing it everywhere would lead to a lot of duplication, I thought about a shared context do define these schemas. When rewriting these schemas for every context, I think I’d use a protocol to ensure I get the right data while every context could store information in its own way.
When defining relationships across modules, where would I put the schema for many_to_many relationships?
I think that I’ll have to experiment with the new structure a bit more to really see how it’s used correctly, but I hope there will be a guide and @StephenGrider and @danielberkompas will cover this in their awesome courses
It is expected that contexts will share modules and data structures (which a schema is), so you can put them at lib/my_app or create a directory where you would put those.