Product engineer
Shoppex
A multi-tenant commerce platform that taught me where architecture helps and where it gets in the way.
Secondary case study about commerce architecture, product scope, and lessons I carried into PufferPOS.

Context
Shoppex came from e-commerce work I was doing around Xappee. The practical idea was to make it faster to deploy a dashboard for products, orders, sales, settings, and content while a custom storefront was built separately.
The idea grew into a broader commerce platform. That ambition was useful, but it also made the product wider than it was finished.
What I built
I built a PNPM monorepo with an Express/MongoDB API, a Next.js dashboard, shared TypeScript and Zod packages, and a separate image processing service.
The app supports tenant setup, product management, categories, specifications, variations, orders, users, team roles and invites, settings, integrations, PayPal, email infrastructure, 2FA, media uploads, Docker, and PM2 deployment paths.
Architecture
The system used a main database for platform-level data like tenants, users, roles, invites, and integrations. Store data lived in tenant-specific MongoDB databases selected at runtime.
That made tenant isolation explicit. It also made operations harder: connection pooling, tenant context, reporting, and security boundaries all became more complicated.
- Express API with Mongoose
- Next.js dashboard
- Shared TypeScript/Zod packages
- Tenant-aware database selection
- Separate image service with multer, Sharp, and worker threads
- NextAuth plus backend JWTs
- TOTP 2FA and PayPal integration work
What worked
The catalog model was the strongest product area. Real stores need products, variants, images, categories, specifications, statuses, SEO fields, order operations, and settings.
The separate image service was a useful learning decision because product media gets messy quickly. Splitting upload and processing work made the system easier to reason about.
What I would change
For this product, database-per-tenant was probably the wrong default. It looked clean on paper, but it made the product harder to operate before the core commerce loop was fully finished.
I would use a shared database with an organization or tenant ID, strict access checks, good indexes, and a clear permission model unless there was a strong reason to isolate every tenant physically.
I would also finish one complete commerce loop before adding broader platform ideas: tenant setup, catalog, storefront browse, cart, checkout, payment confirmation, order management, and fulfillment email.
Gallery
The gallery shows the hackathon moment around Shoppex: team context, the check, and the day the project felt real outside the codebase.


Lessons
Shoppex taught me that impressive architecture can outrun the product loop.
That lesson directly affects how I think about PufferPOS. The better story is not adding every feature. It is choosing the workflow that matters, connecting the pieces that actually touch each other, and protecting the product loop before widening the product.