Modular Mart is a cloud-native, microservices-based e-commerce platform designed with a focus on domain isolation, event-driven choreography, and deep observability. It has evolved from a single-vendor store into a full multi-vendor marketplace.
- API Gateway: The single entry point using Kong, handling routing, rate limiting, and auth verification. A NestJS-based gateway exists as a legacy alternative.
- User Service: Manages identity and profiles, synced with Clerk.
- Catalog Service: Manages product inventory, categories, and seller product approvals.
- Order Service: Core domain managing orders, seller-specific splits, and the Transactional Outbox-based saga.
- Payment Service: Handles Stripe payments, webhooks, and asynchronous payment status updates.
- Notification Service: A robust multi-channel notification engine (Email, SMS, Push, In-App) with real-time SSE updates.
- Web Frontend: A modern Next.js storefront with dedicated Customer and Seller dashboards.
- Docs: A Next.js-based documentation site providing developer guides and API references.
The system follows the Database-per-Service and Microservice Chassis patterns, fully integrated with the LGTM observability stack.
graph TB
subgraph Client_Layer [Client Layer]
Web[Next.js Storefront / Dashboards]
Docs[Next.js Docs Site]
end
subgraph Entry_Layer [Entry Layer]
Gateway[Kong API Gateway]
Auth[Clerk Auth]
end
subgraph Logic_Layer [Service Layer]
UserSvc[User Service]
CatalogSvc[Catalog Service]
OrderSvc[Order Service]
PaymentSvc[Payment Service]
NotifSvc[Notification Service]
end
subgraph Data_Layer [Isolated Data Layer]
UserDB[(User PostgreSQL)]
CatalogDB[(Catalog PostgreSQL)]
OrderDB[(Order PostgreSQL)]
PaymentDB[(Payment PostgreSQL)]
NotifDB[(Notification PostgreSQL)]
end
subgraph Cache [Shared Redis Cache]
Redis[(Upstash Redis)]
end
subgraph Messaging [Event-Driven Choreography]
RMQ[RabbitMQ Event Bus]
Outbox[Transactional Outbox]
end
subgraph Observability [Full LGTM Stack]
Grafana[Grafana Dashboards]
Loki[Loki Logging]
Prom[Prometheus Metrics]
Jaeger[Jaeger Tracing]
end
Web --> Gateway
Docs --> Gateway
Gateway --> Auth
Gateway --> UserSvc
Gateway --> CatalogSvc
Gateway --> OrderSvc
Gateway --> PaymentSvc
Gateway --> NotifSvc
UserSvc --> UserDB
CatalogSvc --> CatalogDB
OrderSvc --> OrderDB
PaymentSvc --> PaymentDB
NotifSvc --> NotifDB
UserSvc -- Cache --> Redis
CatalogSvc -- Cache --> Redis
NotifSvc -- Cache --> Redis
OrderSvc -- Events --> RMQ
CatalogSvc -- Events --> RMQ
PaymentSvc -- Events --> RMQ
UserSvc -- Events --> RMQ
RMQ -- Consume --> NotifSvc
Logic_Layer -.-> Loki
Logic_Layer -.-> Prom
Logic_Layer -.-> Jaeger
Loki & Prom & Jaeger --> Grafana
Checkout is handled through an asynchronous Choreographed Saga combined with the Transactional Outbox Pattern. This guarantees that database changes and event publishing are atomic.
Order States: PENDING_STOCK β PAYMENT_PENDING β PAID β SHIPPED β DELIVERED
sequenceDiagram
participant U as User
participant Gateway as Kong API Gateway
participant Order as Order Service
participant Catalog as Catalog Service
participant Payment as Payment Service
participant RMQ as RabbitMQ
U->>Gateway: POST /orders
Gateway->>Order: Create Order (PENDING_STOCK)
Order->>Order: DB: Save Order + STOCK_RESERVE_REQUESTED (Outbox)
Order-->>U: Return Success (OrderId)
Note over Order,RMQ: Outbox Processor publishes event
Order->>RMQ: Publish STOCK_RESERVE_REQUESTED
RMQ->>Catalog: Consume Event
Catalog->>Catalog: DB: Reserve Stock (Pessimistic Lock + Idempotency)
Catalog->>RMQ: Publish STOCK_RESERVED
RMQ->>Order: Consume Event
Order->>Order: DB: Update Status (PAYMENT_PENDING) + ORDER_CREATED (Outbox)
Order->>RMQ: Publish ORDER_CREATED
Note over Payment,U: Payment occurs via Stripe Webhook
Payment->>RMQ: Publish PAYMENT_SUCCEEDED
RMQ->>Order: Consume Event (Idempotent)
Order->>Order: DB: Final Status (PAID)
All services inherit standard behavior from the packages/ directory:
- Tracing: Every request is tagged with an
X-Request-ID(Correlation ID) that persists across HTTP and RabbitMQ boundaries. - Logging: Structured JSON logging via nestjs-pino for centralized aggregation.
- Idempotency: All event consumers use a
processed_messagestable to ensure Exactly-Once processing semantics. - Health: Standardized
/health/liveand/health/readyendpoints.
We implement the full LGTM stack for deep system visibility:
- Loki: Log aggregation with container-level metadata.
- Grafana: Unified dashboards combining logs, metrics, and traces.
- Prometheus: Metrics scraping for performance and health monitoring.
- Jaeger: Distributed tracing to visualize request flow across the entire microservice web.
A shared cache-aside layer backed by a single Upstash Redis instance (ioredis) with single-flight stampede protection:
- CacheService.getOrSet(): Checks Redis first; on miss, acquires a distributed
SET NX PXlock so only one request recomputes while others wait. - Key namespacing:
catalog:*,user:*,notif:*prefixes prevent collisions across services. - TTL strategy: Catalog 5β30 min, User 5β10 min, Notifications 30sβ5 min β tuned per read pattern.
- Invalidation: Pattern-based
SCAN+ pipelineDELfor list keys; targetedDELfor specific entities. Notification invalidation happens before SSE pushes. - Observability: 6 Prometheus metrics (
cache_hits_total,cache_misses_total,cache_lock_acquired_total,cache_lock_contention_total,cache_loader_duration_seconds,cache_invalidation_total), Redis ping in/health/ready, and auto-instrumented OpenTelemetry spans.
Modular Mart is designed for production-grade stability:
- Dead Letter Queues (DLQ): Failed messages are automatically routed to DLQs after retries are exhausted to prevent pipeline stalls.
- Exponential Backoff: Transient failures (like database hiccups) are handled via a custom retry decorator with increasing delays (1s, 2s, 4s).
- Compensating Transactions: The Checkout Saga automatically "rolls back" stock reservations if a downstream payment fails.
- Idempotent Consumers: Every event handler uses a
processed_messagesguard to guarantee Exactly-Once side effects. - Pessimistic Locking: Prevents inventory overselling by locking product rows during the reservation window.
Every distributed-systems claim in this README is backed by automated tests.
| Area | What's tested | Service |
|---|---|---|
| Saga state machine | Happy-path transitions (PENDING_STOCK β PAYMENT_PENDING β PAID), compensations (β CANCELLED / STOCK_FAILED) | order-service |
| Transactional outbox | Event publishing, messageId correlation, error recovery, partial failure continuation |
order-service |
| Stripe webhook leg | Payment recording, idempotency, edge cases (missing metadata, unknown user) | payment-service |
| Stock reservation | Pessimistic locking, overselling prevention, cache invalidation | catalog-service |
| Circuit breaker | CLOSED/OPEN/HALF_OPEN transitions, failure thresholds, per-service isolation | @repo/common |
| Exponential retry | Backoff sequencing, transient vs. permanent classification | @repo/common |
pnpm test # Run all unit tests (Turborepo, cached)
pnpm test:cov # With coverage reports
cd apps/order-service && pnpm test # Single service- Unit tests simulate the full saga flow with mocked TypeORM/RabbitMQ dependencies β no external infra required.
- AAA pattern (Arrange, Act, Assert) with explicit mocks for repositories, event buses, and external APIs.
- Idempotency is tested, not just claimed β duplicate payment/webhook events are verified to be no-ops.
- CI gates every merge:
lint β typecheck β test β buildon every PR.
Managed via Turborepo, the codebase is optimized for sharing types and logic without tight coupling.
e-commerce-microservices/
βββ apps/
β βββ api-gateway/ # Legacy NestJS-based gateway
β βββ catalog-service/ # Products, Inventory, & Categories
β βββ docs/ # Developer documentation site
β βββ kong-gateway/ # Primary API Gateway
β βββ order-service/ # Order lifecycle & Saga coordination
β βββ payment-service/ # Stripe integration & webhooks
β βββ notification-service/ # Multi-channel (SSE, Email, SMS)
β βββ user-service/ # Identity & Clerk sync
β βββ web/ # Storefront & Dashboards (Customer/Seller/Admin)
βββ packages/
β βββ auth/ # Shared Clerk guards & Roles logic
β βββ common/ # Chassis (Logging, Tracing, Health, Metrics)
β βββ contracts/ # Shared DTOs & Event Patterns (Source of Truth)
β βββ database/ # Shared TypeORM config
β βββ eslint-config/ # Shared ESLint configuration
β βββ shared-types/ # Shared TypeScript types and interfaces
β βββ typescript-config/ # Shared tsconfig.json files
β βββ ui/ # Design System (Tailwind + Shadcn)
- Backend: NestJS, TypeORM, PostgreSQL, RabbitMQ
- Caching: Upstash Redis (ioredis) with cache-aside + single-flight
- Frontend: Next.js 14 (App Router), Tailwind CSS, Shadcn UI
- API Gateway: Kong
- Observability: Loki, Prometheus, Grafana, Jaeger
- Identity: Clerk (Auth-as-a-Service)
- Payments: Stripe (Elements + Webhooks)
- Infrastructure: Docker, Turborepo, Render