← Back to work
BUILDINGCommerce Platform

Loomoda

A commerce platform for fashion businesses: storefront, operational admin, and a Go modular monolith covering catalog through fulfillment, invoicing and finance.

Role & focus
Frontend · Backend · Architecture
Core tech stack
Next.jsTypeScriptGoPostgreSQL
01

The Problem

Loomoda started from a familiar situation: a fashion business running on WordPress and WooCommerce, where the product catalog lives inside a CMS, orders are posts with meta fields, and every customization means working around a plugin.

That setup works until it doesn't. Prices, variants, stock and order history end up entangled with content management, and the operational side of the business - managing products, tracking orders, running the store day to day - gets squeezed into tooling designed for publishing websites, not running commerce.

The goal is to replace that with a structured commerce system: real domain objects, a real API, and an admin surface built for operations rather than page management.

02

What I Built

A commerce platform in active development, split into deliberate surfaces: a customer-facing storefront, an operational admin, a shared UI library, and a single Go API organized as a modular monolith over a tenant-aware PostgreSQL schema.

  • Go modular monolith with explicit commerce domains: catalog, pricing, cart, checkout, order, payment, preorder, production, procurement, inventory, fulfillment, invoice, returns, refunds, finance
  • Next.js storefront and admin applications built on a shared internal UI library (Loomoda UI)
  • Tenant-aware schema: every tenant-owned table carries organization_id, with organization-scoped uniqueness enforced at the database level
  • Document-style snapshots: order lines persist price/product snapshots, invoices persist full commercial snapshots - issued documents never depend on live rows
  • Fulfillment lifecycle from allocation to delivery - pick, pack, multi-parcel shipments with per-package tracking
  • Procurement pipeline linking preorder demand to suppliers, purchase orders, receipts and supplier bills
  • OpenAPI contract between the API and both frontends; Go unit, integration and race-condition tests plus Vitest and Playwright on the Next.js surfaces
  • Containerized staging deployment via Docker and Dokploy

03Complexity & Concurrency

5 case-specific challenges

Engineering Challenges

Challenge // 01

Drawing domain boundaries

The hard part isn't writing endpoints - it's deciding where catalog ends and order begins. Each module owns its data and its rules: Catalog knows products, Order knows purchases, and neither reaches into the other's tables.

Engineering challenge
Challenge // 02

Documents that outlive their source rows

An issued invoice has to stay correct even after the order, customer or pricing rows behind it change. Invoices persist a full commercial snapshot - order, customer, billing, lines, payment, shipping fee - under a DRAFT → ISSUED → VOID lifecycle with organization-scoped sequence numbers allocated atomically, never computed from MAX + 1.

Engineering challenge
Challenge // 03

Demand is not supply

Preorder demand and physical stock are different things that have to meet somewhere. Supply requirements reconcile committed demand against purchase-order lines, so procurement traces back to real demand without merging the two models.

Engineering challenge
Challenge // 04

Multi-tenancy without ceremony

The platform is designed to host multiple stores, so every commerce object carries a tenant boundary. Tenancy is modeled in the schema rather than bolted on later, which keeps store data isolated while sharing one deployment.

Engineering challenge
Challenge // 05

Admin is not the storefront

Admin users need dense tables, filters and bulk actions; shoppers need a fast, focused purchase flow. The two surfaces are separate applications with separate needs, sharing only the API contract and the UI language.

Engineering challenge

04System Blueprint

Service boundaries / data model

Architecture & Contracts

Application surfaces

Client-facing

Storefront

Next.js · customer-facing

Operations

Admin

Next.js · Loomoda UI

OpenAPI contract

Central monolith engine

Commerce API

Go modular monolith · OpenAPI contract

Commerce Domains

Catalog & PricingCart & OrderPaymentPreorder & ProductionProcurement & InventoryFulfillmentInvoice & Finance
Tenant-aware data layer

Data / Persistence

PostgreSQL

tenant-aware · single source of truth

05Engineering Trade-offs

Key Decisions

Deliberate choices made to preserve velocity without sacrificing correctness.

  1. 01

    Modular monolith, not microservices

    The current product does not justify distributed-system complexity. Modules keep commerce domains separated inside one deployable unit - the boundaries exist so the system could split later if it ever needs to, not because it needs to today.

  2. 02

    Tenancy enforced in the schema

    Every tenant-owned table carries organization_id and organization-scoped unique constraints, so tenant isolation is a database guarantee rather than an application-level convention.

  3. 03

    Snapshots on anything that becomes a record

    Orders snapshot prices and product details at purchase time; invoices snapshot the whole commercial document when issued. Historical records stay correct no matter how the catalog evolves.

  4. 04

    Procurement by reference, not by foreign key

    Receiving posts into the inventory ledger through logical references - reference_type plus reference_id - instead of physical foreign keys, so procurement and inventory stay independently evolvable while remaining traceable.

  5. 05

    Concurrency treated as testable behavior

    Order-cancellation and allocation paths have dedicated race-condition integration tests, and competing payment and production requests are verified with SQL checks - the places where commerce data can corrupt under parallel writes are exercised deliberately.

  6. 06

    REST + OpenAPI over alternatives

    A documented REST contract gives every endpoint a spec by default and keeps the two Next.js surfaces honest. For a product with an admin app and a storefront, boring and documented beats clever.

06Surface Breakdown

Product Surface

Surface / 01LOOMODA

Storefront

  • Catalog browsing
  • Product detail & variants
  • Cart & checkout
  • Customer orders & authentication

Product surface / Loomoda

Surface / 02LOOMODA

Operations admin

  • Orders, packing slips & fulfillment
  • Invoices, payments & payment plans
  • Refunds & returns
  • Customers, loyalty & resellers

Product surface / Loomoda

Surface / 03LOOMODA

Supply admin

  • Preorders & production batches
  • Suppliers, purchase orders & receipts
  • Inventory & stock operations
  • Price lists & finance

Product surface / Loomoda

07Project Status

Current State

Actively building. The API, schema and both Next.js surfaces run against a Docker/Dokploy staging environment, with the schema at 69 migrations and still evolving.

Status: building - not yet a launched product.

Current project status

BUILDING

08Retrospective & Takeaways

What I Learned

  • Domain boundaries are cheaper to draw early than to untangle later.
  • An order is a historical record - treating it as one simplifies pricing and keeps reporting honest.
  • Once a document is issued, it outlives the data it came from - invoices and receipts own their snapshots.
  • Multi-tenancy is much easier to design in than to retrofit.
  • A shared OpenAPI contract removes a whole category of frontend/backend disagreements.

PROJECT INDEX / 09

Related work

All projects →

The next chapter starts here

Let's build something resilient together.