Ché Orika
A full-stack, multi-tenant commerce platform I built to run a fashion business end to end, not just to sell online. It pairs a premium storefront with an operational admin portal that is the single source of truth for identity, catalog, inventory, orders, payments, media, and audit. I designed the architecture, wrote the PRDs, made the technical calls, and shipped it with an AI-assisted engineering workflow.
Most small fashion brands stitch together a storefront, a spreadsheet, a POS app, and a separate CRM, and end up with data that never reconciles and no single place that is actually true.
I wanted one system where the storefront, the boutique floor, inventory, and finance all read and write through the same governed backend. Stitching SaaS together could not give me brand-level ownership of data, role-scoped access to money, or a foundation that survives the jump from one boutique to many. So I decided to build my own, with AI as my engineering partner.
- Multi-tenant from birth. A strict hierarchy: a Tenant owns Brands, a Brand owns Stores, and Stores own inventory, orders, and fulfilment. It launched for one brand, but I designed for tenant isolation up front so it can host many brands and stores without a structural redesign.
- The admin portal is the source of truth. The storefront, a future POS, and any integration are clients that consume platform data. No downstream surface owns business state. This is the decision that makes it an operating system rather than a website.
- Host-split identities. Staff authenticate as an admin user on the admin host; customers are a completely separate identity on the storefront. The two never share a session, table, or cookie. Authentication runs on Better Auth; authorization is my own role and permission model, kept separate so identity and policy can evolve independently.
- Membership-based access. A user belongs to a tenant and gains a brand through a membership that carries a role. Every request validates the authenticated user, the brand, an active membership, and the required permission, enforced server-side, never trusting the URL or the frontend.
- Decoupled catalog schema. For the catalog I introduced a fresh, brand-scoped schema physically separate from the legacy storefront tables, so building it could not touch live revenue.
The stack: Next.js App Router on Vercel, Neon Postgres with Prisma, Better Auth, Cloudinary for media, Paystack for payments, and Resend for transactional email. Money is modelled as Decimal, keys are UUIDs, and business records soft-delete rather than hard-delete so financial and audit history stays intact.






- Customer storefront (live). Browsing, bag, and checkout with server-side cart and totals recomputation, so a crafted payload cannot set its own price. Paystack payments, order tracking, atomic stock locking to prevent oversell, and editorial surfaces (lookbook, journal, edits).
- Identity & auth (live). A single Better Auth login, one account per employee, brand switching for multi-brand users.
- RBAC & user management (live). Four roles, Owner, Admin, Staff, and Trainee, driven by a versioned permission catalog. People management, tokenized single-use invitations, multi-owner support, immutable audit logging, and an activity feed.
- Dashboards (live). A full KPI dashboard for Owners and Admins, and a separate money-free operations dashboard for Staff and Trainees.
- Product catalog. Products as concepts with dynamically generated variants, per-variant pricing with VAT, per-brand auto SKUs plus printable barcodes, hierarchical categories, many-to-many collections, signed media uploads, draft autosave, and soft-delete with restore.
The catalog and inventory model was the deep work. Fashion breaks naive product tables: a "product" is a concept, and the thing you actually sell, price, count, and scan is the variant, so inventory lives at variant-by-store, never on the product.
Getting that right meant per-brand-unique SKUs, scannable barcodes, variant generation that stays efficient and bounded, and money modelled as Decimal to kill a rounding bug at the schema level. The sharpest nuance was the cost and margin firewall: pricing edits are Owner and Admin only, and Staff must never see cost or margin at all. I gated that on the read boundary, not just by hiding UI, so cost and margin are stripped from payloads and the server returns a 403, fail-closed. "Already exposed on another screen" is not an acceptable reason to leak it.
- Build an operating system, not a storefront. The admin portal is the source of truth, designed around operational efficiency, so the storefront is one channel over a governed core rather than the system of record.
- Decouple the catalog to protect live revenue. Rather than evolve the legacy storefront tables under live load, I stood up a parallel brand-scoped schema so a large build could never break checkout, and accepted a deliberate future cutover as the cost.
- Gate money and PII fail-closed by role. Cost, margin, revenue, and exports are enforced at the read boundary with server-side 403s and payload stripping, defaulting to closed unless the role is privileged.
Running an AI engineering team taught me that the product manager's leverage is in the guardrails: crisp specs, decision records that capture why, and release gates. My hardest lesson was that "all green" can still be wrong. A sprint passed per-screen review and endpoint checks, yet a wizard was never wired to its backends. Now I verify render-deep and data-deep, walking the whole journey, not just confirming screens render and endpoints return 200.