Updated on 27 Oct, 202523 mins read 10 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 *