CLOSE
Updated on 27 Oct, 202523 mins read 6 views

Let's design the database schema for the platform.

Choosing the Database

For a platform like this, the ideal choice is PostgreSQL, because:

  • It supports relational data modeling (users <-> posts <-> subscriptions).
  • It's ACID-compliant โ€“ ensuring reliability even during concurrent writes.
  • It offers full-text search, JSON support, and great indexing options.

ORM layer: Prisma (for Node.js)

Core Entities Overview

Before diving into tables, let's identity the main entities and their relationships.

EntityDescription
UserRepresents both creators and readers
PostA blog/newsletter entry written by a creator
SubscriptionTracks which reader follows which creator
PaymentRecords transactions and plans
EmailQueueManages pending newsletters
AnalyticsStores engagement metrics (views, clicks, etc.)
CommentReader interaction with posts
TagCategorizes posts for discoverability

Entity-Relationship (ER) Diagram

     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚    User      โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚ id (PK)      โ”‚
     โ”‚ name          โ”‚
     โ”‚ email         โ”‚
     โ”‚ role          โ”‚
     โ”‚ password_hash โ”‚
     โ”‚ created_at    โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ”‚1
           โ”‚
           โ”‚โˆž
     โ”Œโ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚    Post       โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚ id (PK)       โ”‚
     โ”‚ author_id (FK)โ”‚
     โ”‚ title         โ”‚
     โ”‚ content       โ”‚
     โ”‚ status        โ”‚
     โ”‚ created_at    โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ”‚
           โ”‚โˆž
     โ”Œโ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚   Comment    โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚ id (PK)      โ”‚
     โ”‚ post_id (FK) โ”‚
     โ”‚ user_id (FK) โ”‚
     โ”‚ content      โ”‚
     โ”‚ created_at   โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚ Subscription โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚ id (PK)      โ”‚
     โ”‚ reader_id FK โ”‚
     โ”‚ creator_id FKโ”‚
     โ”‚ plan_type    โ”‚
     โ”‚ is_active    โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚   Payment    โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚ id (PK)      โ”‚
     โ”‚ subscription_id (FK)โ”‚
     โ”‚ amount        โ”‚
     โ”‚ currency      โ”‚
     โ”‚ status        โ”‚
     โ”‚ created_at    โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚   EmailQueue โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚ id (PK)      โ”‚
     โ”‚ post_id (FK) โ”‚
     โ”‚ recipient_id โ”‚
     โ”‚ status       โ”‚
     โ”‚ sent_at      โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Detailed Table Design

1 User Table

ColumnTypeDescription
idUUIDPrimary key
nameVARCHAR(100)User name
emailVARCHAR(150)Unique
password_hashTEXTSecurely stored password
roleENUM('reader','creator','admin')Role type
bioTEXTCreator bio
profile_imageTEXTURL to avatar
created_atTIMESTAMPDefault now()

Each user can be both creator and a reader depending on context.

2 Post Table

ColumnTypeDescription
idUUIDPrimary key
author_idUUID (FK)References user.id
titleVARCHAR(255)Post title
slugVARCHAR(255)URL-friendly ID
contentTEXTBody content
visibilityENUM('public','paid','draft')Visibility level
created_atTIMESTAMPCreated time
updated_atTIMESTAMPLast updated
published_atTIMESTAMPPublish date

3 Comments Table

ColumnTypeDescription
idUUIDPrimary key
post_idUUID (FK)References post.id
user_idUUID (FK)References user.id
contentTEXTComment text
created_atTIMESTAMPCommented time

4 Subscription Table

ColumnTypeDescription
idUUIDPrimary key
reader_idUUID (FK)References user.id
creator_idUUID (FK)References user.id
plan_typeENUM('free','paid')Subscription level
start_dateTIMESTAMPStart time
end_dateTIMESTAMPRenewal/cancel date
is_activeBOOLEANCurrent status

5 Payment Table

ColumnTypeDescription
idUUIDPrimary key
subscription_idUUID (FK)References subscription.id
amountDECIMAL(10,2)Amount paid
currencyVARCHAR(10)e.g. USD, INR
stripe_txn_idVARCHAR(255)External transaction
statusENUM('success','failed','pending')Payment status
created_atTIMESTAMPPayment date

6 EmailQueue Table

ColumnTypeDescription
idUUIDPrimary key
post_idUUID (FK)References post.id
recipient_idUUID (FK)References user.id
statusENUM('pending','sent','failed')Delivery status
sent_atTIMESTAMPSent timestamp

This helps implement asynchronous email dispatch โ€“ ensuring reliability and retry logic.

7 Analytics Table

ColumnTypeDescription
idUUIDPrimary key
post_idUUID (FK)References post.id
viewsINTNumber of views
clicksINTClick count
email_opensINTEmail open count
created_atTIMESTAMPTime recorded

8 Tag Table

ColumnTypeDescription
idUUIDPrimary key
nameVARCHAR(50)Tag name
slugVARCHAR(50)URL-friendly tag
created_atTIMESTAMPCreation date

and a PostTag junction table (many-to-many):

| post_id (FK) | tag_id (FK) |

Relationship Summary

RelationshipTypeDescription
User โ†’ Post1-to-manyCreator writes many posts
User โ†’ SubscriptionMany-to-manyReaders subscribe to creators
Post โ†’ Comment1-to-manyPost has multiple comments
Post โ†’ TagMany-to-manyPosts have multiple tags
Subscription โ†’ Payment1-to-manyEach subscription has many payments
Post โ†’ EmailQueue1-to-manyEach post triggers multiple emails

Indexing & Optimization

TableIndexed ColumnsPurpose
UseremailFast login lookups
Postslug, author_idQuick post retrieval
Subscriptionreader_id, creator_idFast subscription checks
Paymentstripe_txn_idPayment reconciliation
EmailQueuestatusQueue processing efficiency

Additional Optimizations:

  • Use partial indexes for active subscriptions only.
  • Add foreign key constraints for referential integrity.
  • Enable caching for popular posts (via Redis).

Data Flow Example โ€“ โ€œUser Subscribes to Creatorโ€

[User clicks Subscribe]
        โ”‚
        โ–ผ
Frontend โ†’ Backend (Subscription Service)
        โ”‚
        โ–ผ
Stripe Checkout โ†’ Payment Verified
        โ”‚
        โ–ผ
Backend updates:
- Create subscription record
- Log payment
- Send email confirmation

Reflection

A strong schema is like a well-organized library โ€“ every book (post), reader, and transaction has its place.

This database design ensures:

  • Fast retrieval of posts and subcriptions,
  • Consistent data across users and payments.
  • Extensibility for future modules (plugins, AI summaries, comments, etc.)

โ€œDatabase is like long-term memory โ€“ design it like you will read from it for years.โ€

Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *

Your experience on this site will be improved by allowing cookies Cookie Policy