I have a newbie question, but I think it’s tangentially relevant to the discussion as it’s not obvious how to do it, even from the Phoenix documentation.
A many-to-many relationship. A user has many chat rooms. A chat room has many users.
In SQL, this requires another table user_room. I would normally create this as a model, and then on my user model, add:
many_to_many :users, User, join_through: "user_rooms"
The question is:
- In the world of contexts, where do I put user_room, and why?
- If I’m joining tables in Ecto, is that really separate?
1 Like
One thing that doesn’t seem to get emphasized enough is that Phoenix Contexts are primarily about cleaning up the controller code and getting domain logic out of the controllers. Compare from
Programming Phoenix
https://media.pragprog.com/titles/phoenix/code/controllers_views_templates/listings/rumbl/web/controllers/user_controller.ex
defmodule Rumbl.UserController do
use Rumbl.Web, :controller
def index(conn, _params) do
users = Repo.all(Rumbl.User) # accessing the Ecto repo directly
render conn, "index.html", users: users
end
end
to Programming Phoenix 1.4
https://media.pragprog.com/titles/phoenix14/code/controllers_views_templates/listings/rumbl/lib/rumbl_web/controllers/user_controller.ex
defmodule RumblWeb.UserController do
use RumblWeb, :controller
alias Rumbl.Accounts
def index(conn, _params) do
users = Accounts.list_users() # here it's up to the Accounts context to figure out where the data comes from
render(conn, "index.html", users: users)
end
end
From that perspective it’s OK to start with one context. Once domain logic starts piling up in your first context module you’ll probably have a much better idea how to break out additional contexts.
When it comes to Bounded Contexts you need a lot more domain information and understanding up front in order to make some good decisions. Ideally each bounded context would have data autonomy i.e. it’s own database (separate Ecto repo) so you wouldn’t be using Ecto joins any way.
programming-phoenix-1-4_b2_0
p.73:
Sometimes it may be tricky to determine if two resources belong to the same context or not. In those cases, prefer distinct contexts per resource and refactor later if necessary. Otherwise you can easily end-up with large contexts of loosely related entities. In other words: if you are unsure, you should prefer explicit modules (contexts) between resources.
but
Chris McCord warns about premature extraction - i.e. it’s all about tradeoffs
Also:
3 Likes
Since its just an association table you probably do not even need a Schema for it. As you’ve written it, none is required. So there is nothing to put in a context. If you ended up with attributes on that association, probably the nature of the attributes would tell you the appropriate context. I’d guess it belongs in the same context as Rooms though, as User
will likely be related to entities all over the app.
That is done inside the schema files for the models you are trying to link. The context handles mostly controller logic like CRUD, which you will need to update to work with the new relation.
If you’re interested in a example, this is one of the better ones: https://alchemist.camp/episodes/unified-tagging-system
5 Likes