0
votes

I'm trying to understand Phoenix 1.3 contexts.

I understand the separability into contexts (which in my mind I perceive as micro-apps, with clearly defined API boundaries), but I struggle when trying to figure out how to do a many-to-many relationship between them.

In the case of building a slack clone, a User can have many Chatrooms and a Chatroom can have many users.

In the 'model' based way of doing it, you would create an intermediate table user_rooms (with fields user_id, room_id), and then do join_through.

What's confusing for me is:

  • If I'm supposed to keep these are truly isolated, do I really want to be joining tables? There's nothing separate about that.
  • If I have to keep my intermediate table user_rooms, what context should that go in?

(for background, I'm trying to do Step 4 of this https://medium.com/@benhansen/lets-build-a-slack-clone-with-elixir-phoenix-and-react-part-4-creating-chat-rooms-80dc74f4f704)

1

1 Answers

1
votes

One way to think about contexts is to view them as a layer of abstraction when you are designing your app. As it is put in the docs,

Phoenix projects are structured like Elixir and any other Elixir project – we split our code into contexts. A context will group related functionality, such as posts and comments, often encapsulating patterns such as data access and data validation. By using contexts, we decouple and isolate our systems into manageable, independent parts.

Models themselves with their underlying schema are therefore the units which you group into contexts. Models can have more fine-grained api which is necessary for their inner workings. Contexts in turn only expose methods which are useful to other application components or contexts, thus hiding the underlying implementation detail of the models.

It is not necessary to extend the context boundary down through the models level for absolute isolation. Instead you create your schema as usual, with all the many-to-many relations you need on the models. Then implement whatever low-level methods on the models directly. Put in the context only those methods which are either public api of the context or private methods touching more than one model.

UPDATE

In your scenario you might have e.g. Chats context where you put the rooms method. Its signature might be e.g. Chats.rooms_of(user). Repo.get() also goes to the context as, e.g. def get(id), do: Repo.get(User, id). The models end up containing changesets, validation methods, private methods with no dependencies to other models. The context in turn gets most of the group's publicly available business logic methods.