Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

thebigk's avatar
Level 13

Please review my DB design

I'm attempting to develop an app that is likely to be deployed as a replacement of an existing similar app and we'd be looking at adding about ~1000 members daily. It's therefore very important for me to begin with a good database design, which won't become a pain in the ass as the site grows. Here's what the app's supposed to do:

###WebApp Overview:

  1. After signing-up, user becomes a member of the site.

  2. User is presented with an app-dashboard with apps such as:

    • Discussions [Forum]
    • Quizzr [Online Quizzes]
    • Jobs [Online Jobs Board]
    • Events [Online Events] ...and so on.
  3. User may 'subscribe' to any app of his/her choice. Once subscribed, the user can access the webapp. For example: If the user is interested in 'Quizzr App' and subscribes to it, then he/she will receive notifications for upcoming quizzes and can participate in quizzes.

If the user subscribes to 'Discussions' webapp, then they can participate in forum

  1. Each of the app will have different user roles as required.

Example I: Discussions webapp - Will need moderators - May need admins (who can only administer the Discussions app)

Example II: Quizzr webapp - Will need 'QuizCreators' - who can add/remove questions - Will need 'QuizAdmins' - who can create new quiz, schedule them etc. - Will have premium subscribers: -- Aero Plan: Have special access to their performance report -- Rocket Plan: Aero Plan + can get SMS notifications before starting quiz + other features

  1. All users can create a profile [Engineering Branch, College, Work etc.]
  2. Social features like follow other user .............................................................

The database structure I've planned is as follows:

'users'

id | email | first_name | last_name | phone | city | state | country

// Not sure if I should split it into user_meta table and move columns 'phone, city, state, country' to it. 


'apps' 
id | name
1   | Discussions
2  | Jobs
3  | Events

// Whenever a new app is created, we make an entry here

'app_users'
id | user_id | app_id
1   |    1          | 2
2  |    1          | 1
3  |    1          | 3
4 |    2          | 3

//Tracking which which apps any user is subscribed to, so that we know the popularity of //each app and also customize app-specific notifications to be sent to users. The combination //for user_id and app_id entries will need to be unique.

'teams'
id | name
1   | SuperAdmins
2  | Admins
3  | Moderators
4  | Members              // Default team for every registered user
5  | JobCreators
6  | JobSeekers
7  | EventCreators

// New teams are created as and when a new app requires it. 


'user_teams'
id | user_id | team_id
1   |       1       |     1
2  |       1       |     2
3  |       1       |     4

//Every registered user of the site must belong to 'Members' (id #4) and additional teams. //Only super admins can define who belongs to which additional teams, and get extra //privileges for specific apps. 


My main concerns are -

  1. Requiring multiple joins for almost each and every user action on the site.
  2. Possibilities of a better database design. Nothing else comes to my mind. Perhaps am I normalising too much?
  3. Perhaps I should create new tables for each new app that stores user_id and their roles? Would that be better?
0 likes
5 replies
Snapey's avatar

one issue that jumps out is that as a user, I might be an admin but only for one application. Maybe the 'teams' need to be application specific. How can I be a member of the Jobs but an admin of the forum?

These are more logically close to roles rather than team and you might want to put an app_id column in that table?

thebigk's avatar
Level 13

Yeah, in fact I changed to 'roles'. Teams turned out to be complicated when it came to naming stuff.

one issue that jumps out is that as a user, I might be an admin but only for one >>application. Maybe the 'teams' need to be application specific. How can I be a >>member of the Jobs but an admin of the forum?

Any registered user will have the member role, sitewide. Jobs app will check if the user has 'member' role and let them perform allowed actions.

We'll need special checks only when user is accessing special areas. For example, the super admin can make any member an admin for the forum by granting them ForumAdmin role.

Perhaps, I'll have to prefix the roles with app names.

Does that look right? Or is there a better approach?

xmarks's avatar

city | state | country

Could each be in it's separate Table: cities, states, countries. where:

  • City belongsTo State
  • State belongsTo Country

You then only give a city_id to the users table, and you do not have duplicate Content.

With 1000 users / day or more, the Users Table Size might grow a lot if you keep it like it is.

1 like
thebigk's avatar
Level 13

@xmarks - Yes, that's another normalization I might do. Maybe I could have a user_meta table :

id | user_id | meta_key | meta_value 

1   |     1         | city             | Panama City
2  |     1         | country     | Republic of Panama
3  |     1         | state           | Panama State
4  |    2         | country     | India

and so on. I'm assuming that the table size should not be a problem at all for databases. Or should it be a matter of concern?

I'm wondering if there's any advantage of storing data like that as compared to adding more columns on the users table.

xmarks's avatar

Umm.. not really. I mean I would say splitting something from 1-table into 2-tables, and have the same data anyway, you end up actually with more data. As you have to specify a meta_key field, and the meta_value is again duplicated.

On your example just now you have duplicates:

  • user_id => shown 3 times => 3(city, state, country) x 4bytes(Int) = 12 bytes (for each user)
  • meta_key 'city' => let's say 1000 users are from same city => 1000 x 1 byte(varchar 255) => 1000 bytes ~ 1MB
  • for a specific city you also have a state and country so you end up with ~ 3 MB for 1000 users.

So the growth of your user_meta table is now virtually Infinite

What I meant is, you should split the data so there are no Duplicates. In your case above:

  • users => id | email | first_name | last_name | phone | city_id
  • cities => id | name | state_id
  • states => id | name | country_id
  • countries => id | name

The growth of these 3 tables is Finite. There is a limit to Cities, States and Countries. And you just connect this data to the users table as needed through city_id. You are not even required to specify state and country as they are related to cities and thus you can access them through that.

Now, if you want to access the data, after you have defined the relations:

  • user->city->name
  • user->city->state->name
  • user->city->state->country->name

You can also use the city_id, on any other tables you may have as well, as this is simply data which you can connect and reuse elsewhere.

There is no reason to split data, like phone from users table. The drawback of having a lot of data on users table is when you select all() fields. Like: User::all(); But if this is getting out of hand (if you have hundreds of columns) then you can just select them specifically like User::select('first_name', 'last_name', 'email')->get()

1 like

Please or to participate in this conversation.