In the ecommerce marketplace app we build as part of our Ruby on Rails course, I structured the model relationships between Users, Products and Purchases like this:
class Product < ApplicationRecord belongs_to :seller, class_name: "User" has_many :buyers, through: :purchases, class_name: "User" has_many :purchases, dependent: :destroy end class User < ApplicationRecord has_many :products, foreign_key: :seller_id, dependent: :destroy has_many :purchases, foreign_key: :buyer_id, dependent: :destroy end class Purchase < ApplicationRecord belongs_to :product belongs_to :buyer, class_name: "User" end
One User model is used for two types of users - buyers and sellers.
One of my students emailed me a couple of days ago to ask why I had designed it like this.
Leo asked me, "Why didn't you use Single Table Inheritance (STI)?"
Using STI is definitely an option here. I did not use it because it would mean that one user could not be a buyer and seller at the same time. They would have to create two separate accounts to buy and sell.
Let me sketch out how STI would work.
Instead of using a single User model for both buyers and sellers, we would use STI to create separate Buyer and Seller models that inherit from User.
We'd need to add a type column in the users table to store the name of the class ("Buyer" or "Seller") for each record.
All user data would still be stored in a single users table. The type column differentiates between buyers and sellers. Rails automatically sets the type when you create a Buyer or Seller.
This is what the models would look like:
class User < ApplicationRecord # Common user attributes and methods end class Buyer < User has_many :purchases has_many :products, through: :purchases end class Seller < User has_many :products end class Product < ApplicationRecord belongs_to :seller has_many :purchases has_many :buyers, through: :purchases end
I'm not a huge fan of this approach. We're adding more complexity and limiting what a user can do, without much benefit.
It is likely that as the complexity of the app grows, having two different types of users represented by one model would become messy.
If we want separate representations for buyers and sellers, a better approach would be to create totally new models for them with one-to-one relationships with the User model:
class User < ApplicationRecord has_one :buyer has_one :seller delegate :purchases, to: :buyer, allow_nil: true delegate :products, to: :seller, allow_nil: true end class Buyer < ApplicationRecord belongs_to :user has_many :purchases has_many :products, through: :purchases end class Seller < User belongs_to :user has_many :products end class Product < ApplicationRecord belongs_to :seller has_many :purchases has_many :buyers, through: :purchases end class Purchase < ApplicationRecord belongs_to :buyer belongs_to :product end
Now we can keep common attributes and methods in the User model and move role-specific code to the Buyer and Seller models. Also note, we can delegate role-specific logic to those classes.