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.

Shoppex hackathon team award photo

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.

The gallery shows the hackathon moment around Shoppex: team context, the check, and the day the project felt real outside the codebase.

Shoppex hackathon team award photo
The team after winning third place in the creative hackathon.
Shoppex hackathon solo award photo
Just me after winning in the hackathon.

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.