Postgres and Ruby - Is there a better way to structure my database?

ToddT

I am planning my db right now. And in the process of putting my ERD together I quickly noticed that all tables are related to BOTH my shops table and my amazon_credentials table. Is there a better/more efficient design or relationship that I can put in place to make my db more efficient and easy to use?

Here is a link to my current ERD in Whimsical

One more caveat, I don't want to have to go thru my amazon_credentials table for all queries. For example.. I would want to be able to do both this Shop.find(1).packing_slips and get all the packing_slips for a shop as well as be able to do this Shop.find(1).amazon_credentials.packing_slips to get the packing_slips for a specific marketplace.

In thinking about this, I'm almost using my amazon_credentials table as a stand in for marketplace as the credentials are inherently scoped to a marketplace

Is this a good design or is there a better way?

Schwern

The duplicated relationships to Shops and AmazonCredentials scattered around the database can be replaced with indirect relationships. Fortunately Rails provides methods to make indirect relationships seem direct: has_many :through.

And you don't need to get it perfect the first time, nor predict every possible use. Migrations help you safely change your design to match changing requirements.

Let's build the basic relationships. I've made some assumptions, like shipping methods are attached to a marketplace.

  • A Product has information about itself.

  • A Shop has Products, ways to market them (a Marketplace), credentials for those Marketplaces, and Orders.

  • An Order has who made the order, from which Shop, through what Marketplace, what Products were ordered and how many at what price, how it's being shipped, and how to track it.

  • A Marketplace has ShippingMethods.

Right away we run into a problem: credentials. A shop needs credentials for each Marketplace. This is solved with an intermediate model: MarketplaceAccount which marries a Shop and Marketplace with the Shop's credentials. Credentials can be made into rows of key/value pairs.

An Order needs to remember the details about which Products were ordered, how many, and for how much. This needs another intermediate model: ProductOrder.

The basic models would look something like this:

class Product < ApplicationRecord
  belongs_to :shop

  has_many :product_orders
  has_many :orders, through: :product_orders
  has_many :customers, through: :orders

  # name
  # description
  # sku
end

class Shop < ApplicationRecord
  has_many :products

  has_many :marketplace_accounts
  has_many :marketplaces, through: :marketplace_accounts
  
  has_many :orders
  has_many :customers, through: :orders

  # name
  # country
end

class Order < ApplicationRecord
  belongs_to :customer

  # Remember which MarketplaceAccount this was done with
  belongs_to :marketplace_account
  has_one :shop, through: :marketplace_account
  has_one :marketplace, through: :marketplace_account

  # How is it being shipped?
  belongs_to :shipping_method
  
  has_many :product_orders
  has_many :products, through: :product_orders

  # tracking number
  # status
end

class ProductOrder < ApplicationRecord
  belongs_to :order
  belongs_to :product

  # price
  # quantity
end

# Holds the Shop's credentials for a Marketplace.
class MarketplaceAccount < ApplicationRecord
  belongs_to :shop
  belongs_to :marketplace
  has_many :credentials
end

# Each row is simply a key and value.
# Consider encrypting this table.
class Credentials < ApplicationRecord
  belongs_to :marketplace_account

  # key
  # value
end

class Marketplace < ApplicationRecord
  has_many :shipping_methods

  has_many :marketplace_accounts
  has_many :shops, through: :marketplace_accounts

  # name
end

class ShippingMethod < ApplicationRecord
  belongs_to :marketplace
  has_many :shops, through: :marketplace

  # speed
  # rate
  # name
end

class Customer < ApplicationRecord
  has_many :orders
  has_many :product_orders, through: :orders
  has_many :products, through: :product_orders
end

By using has_many :through and has_one :through we can avoid duplication while making indirect relationships seem direct. A Shop has_many MarketplaceAccounts, a MarketplaceAccount belongs to a Marketplace; with has_many :marketplaces, through: :marketplace_accounts a Shop can directly access its Marketplaces. shop.marketplaces.

The key/value Credentials table avoids a proliferation of company-specific credentials tables.


More relationships can be fleshed out. A Product can be sold by many Shops, so we can have a ShopProduct which relates a generic Product with how a Shop sells it. This avoids duplicating product information and allows you to see how a single product is sold by many shops.

class Product < ApplicationRecord
  has_many :shop_products

  # sku
  # generic name
  # generic description
  # manufacturer's recommended price
end

class ShopProduct < ApplicationRecord
  belongs_to :shop
  belongs_to :product

  delegate :sku, to: :product

  # shop specific name
  # shop specific description
  # shop price
end

class Shop
  has_many :shop_products
  has_many :products, through: :shop_products
end

And most things would relate to a ShopProduct instead of a Product.


You have a Shop working with a single currency. The design can be expanded to allow a Shop to work with many Currencies.

class Currency < ApplicationRecord
  # symbol
  # conversion rate
end

class ShopCurrencies < ApplicationRecord
  belongs_to :shop
  belongs_to :currency
end

class Shop < ApplicationRecord
  has_many :shop_currencies
  has_many :currencies, through: :shop_currencies
end

has_many :currencies, through: :shop_currencies lets shop.currencies work.


That's the basics. Armed with that you can hammer that basic layout to your needs.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How can I structure my database tables in such a way that their orders can be rearranged at any time?

Better way to structure/new keyword

MySQL - Is there a better way to structure one of my table to reduce the amount of columns?

How to retrieve values in GoLang database/sql, when my structure in db(here postgres) is unknown?

Better/shorter way to close database connection in Go

Is there a better way to structure a C project with cmake?

Is there a better way to get a value from a block in Ruby?

Better packages structure in my Android library

Data mining with postgres in production environment - is there a better way?

Is there a better way to structure my NPM scripts so they are more readable?

Graph or relational database is better for this tree structure?

Is there a better way to structure this scrape?

Is there a better and shorter way of my for loop?

A better way to structure a Mongoose connection module

Better way to check for multiple regex conditions in Ruby?

Better way to get database info

Unable to create a better database structure

Is there a better way to pre-load items into a database?

Better way to design database for online assesment

Is there a better way to structure these function calls?

Better way to structure react app?

which way is better to structure a image caption?

Better way to add configurations files in Ruby?

Is there a better way I can structure my tables, do a 4 table join, or will I have to perform multiple queries?

sequelize to create better database structure

Firebase Database Structure For Better Queries

Splitting structure or duplicate calls or a better way in rust?

Is there a better way to write my if statement?

Better way to implement Trie Data Structure in Rust