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.
A commerce platform for fashion businesses: storefront, operational admin, and a Go modular monolith covering catalog through fulfillment, invoicing and finance.
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.
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.
03Complexity & Concurrency
5 case-specific challenges
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.
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.
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.
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.
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.
04System Blueprint
Service boundaries / data model
Application surfaces
Client-facing
Next.js · customer-facing
Operations
Next.js · Loomoda UI
Central monolith engine
Go modular monolith · OpenAPI contract
Commerce Domains
Data / Persistence
tenant-aware · single source of truth
05Engineering Trade-offs
Deliberate choices made to preserve velocity without sacrificing correctness.
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.
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.
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.
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.
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.
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 / Loomoda
Product surface / Loomoda
Product surface / Loomoda
07Project Status
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
BUILDING08Retrospective & Takeaways
PROJECT INDEX / 09
The next chapter starts here