How to handle namespacing/clashes of controller and model names

What’s the recommended way to handle namespacing of models and controllers? Let’s say I have

User, Account, and Group, where:

  1. A user has many accounts through memberships
  2. A user has many groups through memberships
  3. A group belongs to an account.

I can…

Option 1

Namespace Group.Membership model and controller using subfolders.

In app/web/controllers

defmodule App.MembershipController

In app/web/controllers/group/

defmodule App.Group.MembershipController

In app/web/models/

defmodule App.Membership

In app/web/models/group/

defmodule App.Group.Membership

Option 2

Or I can keep the structure flat and prefix the group membership model and controller with group

In app/web/controllers

defmodule App.MembershipController
defmodule App.GroupMembershipController

In app/web/models/

defmodule App.Membership
defmodule App.GroupMembership

Option 3

Same as option 1, but namespace both models.

Option 4

Same as option 2, flat structure, but prefix both membership models and controllers with organisation and group.

Option 5

Don’t namespace anything. Just find different names that make sense.

Option 6

None of the above.

It’s great that whatever choice I take doesn’t make things magically blow up (like in Rails, for example, where folder naming and class naming, as well as load paths make a difference).

But I would like to know the best way to usually go about this…

At work we have:

RootNamespace: Which I will call MyApp for this example, everything we make is in this.

In the MyApp everything is in a ‘Module’, so the user management stuff is in User, thus in MyApp.User, the model, controllers that manage it, everything is in it, all tied together by just using use MyApp.User.Router at the right place in the main router. We do not actually have an ecto-based user model since the user exists entirely within LDAP, so calls go to that, however each module often holds extra data tied to the user (which is a unique 64-bit integer ID in the LDAP server, so mapping it is easy). Like take the check in/out system for hardware, it is at module MyApp.CheckInOut, its base controller is MyApp.CheckInOut.Controller, its websocket is MyApp.CheckInOut.Channel, etc... Each module has a definition, a behaviour that it fullfills stating what capabilities it has (controller, websocket, global menu entries, etc...) and using my plugin library (I really should release it open source someday, but it is just Such-A-Hack) modules are auto-detected just by virtue of being compiled in to the system and the callbacks are called on the root behaviour (which is inMyApp.CheckInOut` itself for this one) as necessary to get permission entries or setup websocket channel registrations and so forth (I never ended up having the router use it, probably should just to unify it all).

I do not have a singular model<->controller mapping though, like for the check in/out system there is a controller for fallback on non-javascript clients and for JSON, but most of it is a JSON API through a websocket channel (the front-end dynamically loads up page data and display information on demand and caches it, it makes for a blazing fast website feel). But the models are just data storage, most modules in a ‘module’ like the CheckInOut are just ‘what’ to do, like there is nothing that says to just ‘make a new record of this and do it’ but rather there is a ‘setup these actions’ (built recently with Ecto.Multi, of which a transaction runs it all at the end).

@OvermindDL1 thanks for the thorough explanation. I must admit, some of it went over my head a little, but I understand where you are coming from. It just seems over complex, though? I mean why deviate so much from what Phoenix gives us already? What the gain from introducing such indirection, if you want to call it that?

Elaborate? It seems to be what phoenix already does.

Not much of an indirection, most calls are still straight in to a module.

Also do recall, I come from the erlang world, umbrella projects do not really exist there, in addition that umbrella was not even pushed much in elixir when this project started 8 months ago. ^.^

1 Like

The traditional approach would probably be to have a single “membership” model with multiple fields: one for the user, one for the account, and one for the group. If the membership types really do need to be stored as separate models/tables, then I’d namespace both (so App.Group.Membership and App.Account.Membership, or App.GroupMembership and App.AccountMembership) in order to avoid confusion. It’s also possible to forego the account membership entirely; if groups belong to accounts, then a group membership naturally implies an account membership, and that relationship can be expressed through your run-of-the-mill join.

1 Like