I'm developing a web application with several roles for its users. The DB in use is Postgres 10. The roles are very different and have much non-overlapping data so that oftentimes a specific table should belong only to one role and not to another.
In the examples below, I'll use two roles: seller and buyer. The first role is assigned to a user which has some company and sells goods. The second role is assigned to a user who buys goods and can receive gifts. Seller cannot receive gifts and buyer cannot have a company. Both seller and buyer can have an avatar.
I'm considering the following four designs with their cons and pros.
Case 1
Here we have a role reference table which enumerates all the roles in the system (seller or buyer), user table with password and email of the user which references the role table, avatar, company and gifts_recieved tables reference user table.
PROs
- No tables have NULL-able attributes
CONs
- Trigger should be used to check referential integrity. For example, if a user with id=1 is a buyer, we should prevent insertion of a row to the company table with user_id=1. Thus, we should create a trigger on every role-specific table which references user table to guard referential integrity.
Case 2
Here, user table contains nullable role attributes: seller_id and buyer_id (or other roles which are not in the example). Only one of them is not NULL and it defines user's role. All role-specific tables reference either seller or buyer table. Avatar table which is common for both roles continues to reference the user table.
PROs
- No need for triggers to guard referential integrity of role-specific tables: they all reference either seller or buyer table.
CONs
- NULL-able role attributes.
- A single trigger (not triggers for all tables as in case 1) is required to ensure only one of role attributes (seller_id or buyer_id) is not NULL and all other are NULL.
Case 3
To avoid NULL-able role attributes, we'll use Postgres' inheritance for tables (this feature is not supported by MySQL). Here, user table is 'swallowed' into seller and buyer tables. Avatar table has got duplicated and prefixed with role name.
PROs
- No NULL-able role attributes.
- No triggers at all.
CONs
- Tables which are common for different roles are duplicated - a pure waste of resources.
- Inheritance is Postgres-specific feature.
Case 4
To solve the duplication problem from Case 3, avatar_id was moved to user table from which seller and buyer inherit. Thus, for every table which is common to seller and buyer we'll add an attribute to their parent table (user).
PROs
- No triggers for referential integrity
- No table duplication
- No NULL-able atrrributes
CONs
- Postgres inheritance has its caveats: https://www.postgresql.org/docs/10/ddl-inherit.html
Question
What solution of the above fit the best DB design practices (if any)? Should user role be an attribute of user table or be a specific table(s)?
Clarifications
- A seller can have only one company.
- A user identified by email/password pair can be either a seller or a buyer.



