WHAT IS THE PROBLEM?
It is common in many projects that a user registers as part of a company, account, store, football team .. really whichever group of people doing something together.
The initial project brief
- A user needs to be able to login
- A user needs to be able to perform some operation as part of a company
This seems pretty easy, so you just add a company ID on the user table and set up the association on the model. The user now belongs to a company. That's it. Done. Life is happy.
A quick update
Since the start of the project things have changed and you now have multiple users that manage the same company. You realise that not all of them should be able to do everything.
This, again, seems pretty easy and you just add a role on the user model and use this value to handle permissions and identify what a user can do or not. That's it. Done. Life is happy.
Two months after your product is live, your most important client needs to create his third company. He now has 3 users with different logins and is managing each of them. This is time consuming and frustrating. He wants to be able to switch company within the same login.
Another quick update ?
A user now needs to be linked to multiple companies and might have different roles. A base assumption of your project structure changed, and it is not a quick change.
There is now a not so exhaustive list of things that need to be changed:
- User and Company database structure
- User role and Permissions
- Controller structure
This isn't quick, trust me!
A BETTER APPROACH
No matter what the client says, what the assumptions are, things will change. Do it for yourself. PLAN. I'm not saying to over-engineer your application, but consider the variables.
A user will likely change companies, or be linked to multiple ones. If the initial brief does not include this, let's not create views, let's not create controllers and policies to handle these, but lets at least plan the db, models and folder structure.
Company identifier and role are not information that should be on the main user model. Let's move those out into their own.
User --* Company User *-- Company
The company user will also hold the role.
Our db now is ready to accept that a user might be linked to different companies and he might have a different role in each company. This is better.
Now let's namespace controllers and views.
Let's assume in a company a user should be able to manage orders and products, then our structure will look something like:
app controllers company - base_controller.rb - orders_controller.rb - products_controller.rb - application_controller.rb ... views orders - index.slim products - index.slim ...
Orders and products controllers will inherit base controller from the company, which will define some nice helper methods like current company and current company user. These will be used to scope the information needed.
Now we just need to setup our permissions.
We use pundit policies to achieve this. In our case a product policy needs to answer to basic questions like:
- Which products can the user see?
- Can the user see this product?
- Can the user edit this product?
Now, all these questions can be easily answered, not by the user, but by his role within the product company.
WHAT WE LEARNED
It's ok to simplify and it's ok not to over-engineer your MVP.
No one wants a half-built rocket if it's not needed. But you need to find a balance and work a bit for the future.
Even if your business model is based on simple assumptions it doesn't mean that these will be there forever. You always need to put yourself in a position that your code base can be adapted easily to the new needs.