Plamatio
Production-quality e-commerce platform — Go/PostgreSQL backend, Next.js/Redux frontend, Kafka data streams.
title: Plamatio
Problem
I wanted a case study in production-shaped e-commerce — not a tutorial-ish stack, not a thin frontend over Stripe, but the actual pieces of a real shop: a typed backend, a fast frontend with proper state management, real-time analytics, and the operational hooks (CI, observability, payments, auth) that you'd need before launching.
Plamatio is the result. It became a personal blueprint for "what does a serious e-commerce stack look like, end to end?"
Architecture
Next.js + Redux frontend ─┐
├─ REST → Go API ─ PostgreSQL
Clerk (auth) ─────────────┤ │
Stripe (payments) ────────┘ └─ Redis cache
│
Kafka events ───────┘
│
Confluent → analytics consumers
- Backend: Go REST API on top of PostgreSQL. Redis cache in front of read-heavy endpoints with explicit invalidation on mutations.
- Frontend: Next.js + TypeScript + Redux + RTK Query. Static type checking front to back; RTK Query handles cache and side effects so component code stays declarative.
- Auth and payments: Clerk and Stripe — neither is the place to roll your own.
- Streaming: Apache Kafka via Confluent. Order events, cart events, and product views all go to topics that downstream analytics consume.
Key decisions
- Go on the backend, not Node. I wanted a backend in a language that pushes me toward explicit error handling and clear concurrency. Go's standard library +
pgxis a small, sharp-edged combo that scales well. - Redux + RTK Query, not React Query alone. RTK Query gives a single source of truth for server cache and client state. For a shop with cart/inventory/auth all interacting, the colocation paid off.
- Kafka over webhook-style fan-out. Once you have one consumer (analytics), you'll have three. Doing the Kafka work upfront made adding consumers trivial.
What I'd do differently
- Start with feature flags. I retrofit'd them later for A/B testing of pricing widgets. Easier to design in than to add.
- Test the cache invalidation harder. Caching is the second-hardest problem; my invalidation tests were lighter than they should have been.
- Separate the read model. Eventually you want a denormalized read model for category pages, not joins on the live product table.
Live site · Frontend · Backend · Intro post