Here’s a breakdown of 10 key tasks for the development roadmap:
Implement the core user system with role-based access control for Customers, Sellers, and Admins. This includes:
Build the product management system allowing sellers to:
Create the shop management functionality:
Develop the core shopping experience:
Implement the order lifecycle management:
Set up the payment processing system:
Build the administrative interface:
Create comprehensive documentation and testing:
Set up your deployment pipeline:
Implement caching and optimize performance:
Welcome to Pie, an innovative multi-vendor eCommerce platform designed to simplify business and management operations. Firstly I will go with MVP which will meet the minimum requirements to run our initial business, which means I am prioritising the user fetching feature and the manager roll feature that will be developed first. Here I am planning version (v1) requirements.
The system should meet the following requirements:
I will design the system for five diffrent role Customer, Seller, Admin
Customers
Seller
Admin
Let’s start with the estimation and constraints.
Let us assume we have 10K daily active users (DAU). If on average each user performs 10 actions (such as request a check available product, addtoCart, checkout, etc.) we will have to handle 100K requests daily.
\[10,000 \times 10 \space actions = 100,000 \space /day\]What would be Requests Per Second (RPS) for our system?
100000 requests per day translate into 1.2 requests per second.
\[\frac{100,000 \space}{(24 \space hrs \times 3600 \space seconds)} = \sim 1.2 \space requests/second\]If we assume each user comsume on average is 400 bytes, we will require about 0.04 GB of database storage every day.
\[100,000 \space \times 400 \space bytes = \sim 0.04 \space GB/day\]And for 10 years, we will require about 146 GB of storage.
\[0.04 \space GB \times 10 \space years \times 365 \space days = \sim 146 \space GB\]As our system is handling 0.04 GB of ingress every day, we will require a minimum bandwidth of around 0.46 KB per second.
\[\frac{0.04 \space GB}{(24 \space hrs \times 3600 \space seconds)} = \sim 0.46 \space KB/second\]Here is our high-level estimate:
Type | Estimate |
---|---|
Daily active users (DAU) | 10,000 |
Requests per second (RPS) | 1.2/s |
Storage (per day) | ~0.04 GB |
Storage (10 years) | ~146 GB |
Bandwidth | ~0.46 KB/s |
This is the general data model which reflects our requirements.
We have the following tables:
Stores core user data.
id
(UUID, Primary Key)email
(String, Unique)fullName
(String)role
(ENUM: Customer, Seller, Admin)Holds customer-specific details.
id
(UUID, Primary Key)userId
(UUID, Foreign Key → User.id)dateOfBirth
(Date)gender
(ENUM: Male, Female, Other)referralCode
(String)loyaltyPoints
(Integer)Stores seller-specific details.
id
(UUID, Primary Key)userId
(UUID, Foreign Key → User.id, Unique)businessName
(String)taxId
(String, Unique)rating
(Float)totalSales
(Integer)Manages multiple contact numbers for a user.
id
(UUID, Primary Key)userId
(UUID, Foreign Key → User.id)type
(ENUM: Mobile, Work, Home, Emergency)number
(String)Stores multiple addresses for customers, sellers, and shops.
id
(UUID, Primary Key)customerId
(UUID, Foreign Key → Customer.id, Nullable)sellerId
(UUID, Foreign Key → Seller.id, Nullable)shopId
(UUID, Foreign Key → Shop.id, Nullable)type
(ENUM: Home, Work, Billing, Shipping)street
(String)city
(String)state
(String)country
(String)postalCode
(String)Stores shop details.
id
(UUID, Primary Key)shopName
(String)sellerId
(UUID, Foreign Key → Seller.id)rating
(Float, Default: 0.0)isVerified
(Boolean, Default: False)Stores product information.
id
(UUID, Primary Key)productName
(String)mainImage
(String)additionalImages
(String[])categoryId
(UUID, Foreign Key → Category.id)shortDescription
(String)productDescription
(String)price
(Decimal(10,2))discountPrice
(Decimal(10,2))discountPercentage
(Float)inStock
(Boolean)colorId
(UUID, Foreign Key → Color.id, Nullable)sizeId
(UUID, Foreign Key → Size.id, Nullable)Manages all images dynamically for products, shops, categories, and more.
id
(UUID, Primary Key)imageUrl
(String, Not Null)type
(ENUM: Product
, Shop
, Category
, Other
)referenceId
(UUID, Foreign Key → Depends on type
)createdAt
(Timestamp, Default: NOW()
)updatedAt
(Timestamp, Auto-update on change)Manages product categories, supporting hierarchy.
id
(UUID, Primary Key)categoryName
(String, Unique)imageUrl
(String)parentCategoryId
(UUID, Foreign Key → Category.id, Nullable)Tracks all customer orders.
id
(UUID, Primary Key)userId
(UUID, Foreign Key → User.id)subTotal
(Decimal(10,2))shippingCharge
(Decimal(10,2))shopId
(UUID, Foreign Key → Shop.id)tax
(Decimal(10,2))total
(Decimal(10,2))paidAmount
(Decimal(10,2))orderStatus
ENUM could include:
Pending
→ Order placed but not processedProcessing
→ Being packed/shippedShipped
→ Dispatched but not deliveredDelivered
→ Successfully receivedCanceled
→ Canceled by user/admintxnId
(String)couponId
(UUID, Foreign Key → Coupon.id, Nullable)isPaid
(Boolean, Default: False)orderPlacedAt
paymentAcceptedAt
deliveredToCourierAt
beingDelivered
delivered
canceledAt
createdBy
(UUID, Foreign Key → User.id)updatedBy
(UUID, Foreign Key → User.id)orderNotes
(Text, Nullable)We are developing a monolithic application, and we will use a single PostgreSQL database to store all of our data. While our data model is quite relational, keeping everything in one database is suitable for our current architecture. This allows us to leverage PostgreSQL’s powerful relational capabilities, including complex queries, transactions, and referential integrity, which are critical for our application.
Despite using a monolithic architecture, we can still ensure scalability and maintainability by organizing the database schema in a way that logically separates concerns. We will define clear ownership of tables and ensure that the application components interact with the database in a modular and efficient way. This approach enables us to take full advantage of PostgreSQL’s features while avoiding the complexity of managing multiple databases.
Now let us do a high-level design of our system.
We will be using Monolith architecure while we are building the MVP. According to demand we will scale it horizontally and If it reach to limitations we will consider to build to microservice.
Our service operates as follows:
Handling payments at scale is challenging, to simplify our system we can use a third-party payment processor like Razorpay, Stripe or SSLCommerz. Once the payment is complete, the payment processor will redirect the user back to our application and we can set up a webhook to capture all the payment-related data.
We will send notifications through our server in future.
Let us identify and resolve bottlenecks such as single points of failure in our design:
To make our system more resilient we can do the following: (V2)
Backend: Typescript, Node.js, Express.js, Docker, Nginx, Prisma (Supabase/Neon DB/Prisma DB, PostgreSQL), Redis, Razorpay, Socket.io, zod, bcrypt, JWT, etc