
Unity Shop
A multi-vendor marketplace, built on Next.js with real-time order updates.
A team-built marketplace covering 22 product categories, twelve currencies, seller onboarding, flash-sale countdowns and a real-time order surface. The published frontend talks to MongoDB directly from Next.js route handlers.
- Role
- Frontend developer within a team. This page documents the shipped codebase, not an individual contribution split.
- Source status
- Verified — Read from the package manifests of both public repositories, and from the server-rendered homepage of the deployed site.
+3 more in the case study
5 min read · 10 sections · 10 technologies
Unity Shop is the largest application I have worked on, and the first one built with other people rather than alone. It is a multi-vendor marketplace: vendors list products, buyers browse and check out, and administrators moderate the catalogue.
The version documented here is the one deployed at the link below. Everything in the next sections was read out of the published repository or out of the deployed page itself.
What the product does
22 product categories
Electronics, Fashion, Home & Living, Kitchen, Bedroom, Office, Mobiles, Watches, Audio, Cameras, Gaming, Lighting, Beauty, Health, Sports, Outdoor, Books, Stationery, Toys & Baby, Grocery, Tools and Automotive.
Twelve-currency storefront
A currency switcher covering USD, EUR, GBP, JPY, CNY, INR, AED, CAD, AUD, SAR and BDT alongside the Bangladeshi Taka.
Seller onboarding
A "New Seller Products" rail promotes recently listed inventory from vendors who joined the platform.
Flash sales with countdowns
Time-boxed offers with a live countdown and a per-offer discount figure.
Local payment methods
bKash and Nagad are surfaced directly in the checkout messaging, alongside free-delivery thresholds.
Real-time channel
Socket.IO and its client are both in the dependency list, which is what backs the live order surface.
Admin analytics
Recharts is used for the reporting surfaces behind the administrative views.
Branch-per-contributor
The backend repository carries one branch per contributor, which is the clearest record of how the team split the work.
Architecture
- 01BrowserServer-rendered Next.js pages
- 02Route handlersapp/api/*
- 03MongooseModels defined in-process
- 04MongoDBPrimary datastore
- 05Socket.IOOrder events pushed to clients
A separate Express backend exists on another branch of a teammate's repository; it is described below.
The interesting decision is that Mongoose is a direct dependency of the frontend repository. Rather than running a second Express service for the deployed build, the data access layer lives in Next.js route handlers. For a team that wants one deployment, one environment variable set and one place to look at logs, that is a defensible trade. It also means the database schema is coupled to the app's deploy cycle, and the frontend bundle is responsible for the availability of the data layer.
The server repository
There is a second, separate backend: an Express service in a teammate's repository. Its work lives on a branch named iqram, and the repository carries one branch per contributor (Arif, ahsan, develop, iqram, main, rimiruma, sakib), which is the clearest available evidence of how the team divided the work. The manifest below was read from that branch.
{
"name": "backend",
"dependencies": {
"@google/genai": "*",
"bcrypt": "*",
"bcryptjs": "*",
"cloudinary": "*",
"cors": "*",
"dotenv": "*",
"express": "*",
"jsonwebtoken": "*",
"mongodb": "*",
"mongoose": "*",
"multer": "*",
"node-cron": "*",
"nodemailer": "*",
"openai": "*",
"replicate": "*",
"sharp": "*",
"socket.io": "*",
"stripe": "*",
"ws": "*"
},
"devDependencies": {
"nodemon": "*"
}
}- Two authentication libraries.
bcryptandbcryptjsare both present. They implement the same algorithm, so one of them is redundant; a codebase that installs both usually accumulated them at different times. - Two real-time transports.
socket.ioand the rawwspackage are both installed. They solve the same problem at different abstraction levels, which is worth knowing before writing any new live feature. sharpfor server-side image processing alongsidemulterfor uploads andcloudinaryfor storage, so media is transformed as well as stored.node-cronfor scheduled work, which only makes sense on a long-lived process. This service cannot be deployed to serverless functions without replacing it with a platform cron.nodemaileragain on the server side, so transactional mail exists on both sides of this project.
Engineering notes
Media pipeline
multer and cloudinary are both present. Multer handles the multipart request in the route handler and Cloudinary stores the asset, which keeps the Next.js server from holding user uploads on disk. Product imagery on the deployed page is served through the Next.js image optimiser rather than being hot-linked.
{
"dependencies": {
"cloudinary": "^2.9.0",
"jspdf": "^4.2.0",
"mongoose": "^9.2.1",
"multer": "^2.0.2",
"next": "16.1.6",
"nodemailer": "^8.0.1",
"react": "19.2.3",
"recharts": "^3.7.0",
"socket.io": "^4.8.3",
"socket.io-client": "^4.8.3",
"stripe": "^20.3.1"
}
}{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
}
}Document generation
jspdf is in the dependency list, which is how the application produces downloadable order documents from the browser without a server-side PDF service. That keeps invoice generation out of the request path that handles payments.
Transactional email
nodemailer handles outbound mail. On a Vercel deployment there is no long-lived process, so any scheduled or retried mail has to be driven from a request or a platform cron rather than an in-process queue.
Trade-offs
- One deployable instead of two. Fewer moving parts and a single environment variable set, at the cost of losing a hard network boundary between the UI and the data layer.
- Client-side PDF generation. No rendering service to operate, but layout fidelity is limited to what the browser engine supports.
- Currency formatting in the client. A twelve-currency switcher is simple to implement locally and becomes a correctness problem as soon as prices are persisted in more than one currency.