Stage M1
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
A modern sports-fashion e-commerce platform.
|
||||
|
||||
**Status: milestone 0 — architecture skeleton.** The structure, boundaries, data model and
|
||||
tooling are in place and verified. Business features are not implemented yet; see
|
||||
[Roadmap](#roadmap).
|
||||
**Status: milestone 1 — catalog read API, live end to end, in Vietnamese and English.**
|
||||
The storefront renders real products from the database through the REST API. Cart, checkout,
|
||||
orders and auth are still ahead; see [Roadmap](#roadmap).
|
||||
|
||||
```
|
||||
Storefront (Next.js) ─┐
|
||||
@@ -116,10 +116,12 @@ sport-store/
|
||||
├── apps/
|
||||
│ ├── storefront/ Next.js customer site (:3000)
|
||||
│ │ └── src/
|
||||
│ │ ├── app/ App Router — (shop) (checkout) (account) route groups
|
||||
│ │ ├── app/[locale]/ (shop) (checkout) (account) route groups
|
||||
│ │ ├── components/ Cross-feature UI (layout, chrome)
|
||||
│ │ ├── features/ auth · product · category · collection · search
|
||||
│ │ │ cart · checkout · order · wishlist · account
|
||||
│ │ ├── i18n/ next-intl routing, request config, navigation
|
||||
│ │ ├── messages/ vi.json · en.json (UI strings)
|
||||
│ │ ├── hooks/ lib/ services/ stores/ styles/ types/
|
||||
│ │
|
||||
│ ├── admin/ Next.js back office (:3001)
|
||||
@@ -151,7 +153,7 @@ sport-store/
|
||||
│
|
||||
├── docs/
|
||||
│ ├── architecture.md Boundaries, conventions, risks — read this first
|
||||
│ └── adr/ 12 decision records
|
||||
│ └── adr/ 14 decision records
|
||||
│
|
||||
├── docker-compose.yml Backing services; `--profile full` runs everything
|
||||
├── turbo.json pnpm-workspace.yaml package.json
|
||||
@@ -187,6 +189,28 @@ Full detail in [`docs/architecture.md`](./docs/architecture.md). The rules that
|
||||
or a domain event — enforced by ESLint, which is what keeps a future service extraction
|
||||
possible. ([ADR-0002](./docs/adr/0002-modular-monolith-not-microservices.md))
|
||||
|
||||
8. **Two languages, two mechanisms.** UI strings live in message catalogs; product content lives
|
||||
in database translation tables with per-locale slugs and field-level fallback. Conflating them
|
||||
is why most "add a language" projects end up half-translated.
|
||||
([ADR-0013](./docs/adr/0013-content-translations-in-typed-tables-ui-strings-in-message-catalogs.md))
|
||||
|
||||
### Languages
|
||||
|
||||
Vietnamese is the default and is served from clean URLs; English is prefixed with `/en`.
|
||||
|
||||
| | Vietnamese | English |
|
||||
| ------- | --------------------------- | --------------------------- |
|
||||
| Listing | `/men` | `/en/men` |
|
||||
| Product | `/products/ao-chay-bo-aero` | `/en/products/aero-run-tee` |
|
||||
|
||||
Product slugs are translated too, so each language has its own indexable URL. Every product
|
||||
page emits `hreflang` alternates and a canonical link, and requesting a product by the _other_
|
||||
locale's slug redirects to the canonical one — which is what keeps the language switcher on a
|
||||
product page from 404ing.
|
||||
|
||||
The admin switches language by cookie with no URL segment: it is `noindex` everywhere, so
|
||||
locale-in-path would buy nothing.
|
||||
|
||||
### Storefront routes
|
||||
|
||||
`/` · `/men` · `/women` · `/sports/[running|football|training|gym|badminton|lifestyle]` ·
|
||||
@@ -203,24 +227,23 @@ Full detail in [`docs/architecture.md`](./docs/architecture.md). The rules that
|
||||
|
||||
## Roadmap
|
||||
|
||||
| Milestone | Scope |
|
||||
| --------- | ------------------------------------------------------------------------------------- |
|
||||
| **M0** ✅ | Architecture, tooling, schema, health check, Docker, CI |
|
||||
| **M1** | Catalog read API: products, variants, categories, collections, brands + Redis caching |
|
||||
| **M2** | Auth: login, refresh rotation, RBAC admin, user/role management |
|
||||
| **M3** | Admin catalog: product editor, variant matrix, media uploads, inventory |
|
||||
| **M4** | Storefront catalog: listings, PDP with variant selector, filters |
|
||||
| **M5** | Cart, checkout, orders |
|
||||
| **M6** | Search + faceting |
|
||||
| **M7** | Promotions, coupons, reviews, CMS |
|
||||
| **M8** | Customer account |
|
||||
| **M9** | Payments (VNPay, MoMo, ZaloPay, COD), shipping, notifications |
|
||||
| Milestone | Scope |
|
||||
| --------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **M0** ✅ | Architecture, tooling, schema, health check, Docker, CI |
|
||||
| **M1** ✅ | Catalog read API + Redis caching + vi/en localisation + storefront wired to real data |
|
||||
| **M2** | Auth: login, refresh rotation, RBAC admin, user/role management |
|
||||
| **M3** | Admin catalog: product editor, variant matrix, media uploads, inventory |
|
||||
| **M4** ◐ | Storefront catalog — listings, PDP, variant selector and filters landed with M1; sort UI, pagination and a mobile filter drawer remain |
|
||||
| **M5** | Cart, checkout, orders |
|
||||
| **M6** | Search + faceting |
|
||||
| **M7** | Promotions, coupons, reviews, CMS |
|
||||
| **M8** | Customer account |
|
||||
| **M9** | Payments (VNPay, MoMo, ZaloPay, COD), shipping, notifications |
|
||||
|
||||
**Recommended next step: M1.** It exercises every layer end to end — Prisma repository → service
|
||||
→ controller → envelope → `@sport/api-client` → a rendered page — on read-only endpoints where
|
||||
mistakes are cheap. It also proves the variant model against real data before anything writes to
|
||||
it. Auth (M2) comes second because the enforcement half already exists; only issuance is
|
||||
missing.
|
||||
**Recommended next step: M2 (auth).** The enforcement half already exists — global access-token
|
||||
guard, RBAC permissions guard, audience separation — so only issuance is missing: login, refresh
|
||||
rotation, and the admin user/role screens. Everything after it (cart ownership, orders, the admin
|
||||
write path) depends on knowing who is asking.
|
||||
|
||||
---
|
||||
|
||||
@@ -228,24 +251,24 @@ missing.
|
||||
|
||||
Everything below was run, not assumed:
|
||||
|
||||
- `pnpm install` — 10 workspace projects resolved
|
||||
- `pnpm lint` · `pnpm typecheck` · `pnpm build` — 24/24 Turborepo tasks pass
|
||||
- `pnpm format:check` — clean
|
||||
- `prisma migrate dev` — 25 tables created
|
||||
- `pnpm db:seed` — 36 permissions, 6 roles
|
||||
- API boots; `GET /api/v1/health` returns `status: ok` with PostgreSQL and Redis both `up`
|
||||
- Error envelope confirmed on a 404; `x-request-id` echoed; Helmet, CORS and rate-limit headers
|
||||
present; Swagger served at `/docs`
|
||||
- `pnpm test` — 5 passing RBAC guard tests
|
||||
- Storefront renders 20 routes (`/sports/curling` correctly 404s); admin renders 17
|
||||
- `pnpm lint` · `pnpm typecheck` · `pnpm test` · `pnpm build` — 25/25 Turborepo tasks pass;
|
||||
`pnpm format:check` clean
|
||||
- 5 migrations applied; 32 tables; seed loads 36 permissions, 6 roles, 3 brands, 8 categories,
|
||||
3 collections, 12 products, **155 variants** and 64 generated images uploaded to MinIO
|
||||
- `pnpm test` — 17 passing (RBAC guards, translation fallback, `Accept-Language` negotiation)
|
||||
- API: listings with filters/facets/cursor paging, PDP, navigation, brands and collections all
|
||||
return correctly localised payloads in both `vi` and `en`
|
||||
- Storefront: every route returns 200 in both locales; PDP renders translated options, spec
|
||||
table and variant titles; `/en/products/<vi-slug>` → 307 → `/en/products/<en-slug>`;
|
||||
`hreflang` + canonical emitted per locale
|
||||
- Money formats per locale from one integer: `690.000 ₫` (vi) / `₫690,000` (en)
|
||||
- Admin: renders Vietnamese by default and English with `sport_admin_locale=en`
|
||||
|
||||
Known benign noise: NestJS logs two `Unsupported route path: "/api/*"` warnings at boot. They
|
||||
come from Nest's own global-prefix handling under Express 5 / path-to-regexp v8, are
|
||||
auto-converted correctly, and routing is verified working. Nothing in this repository registers
|
||||
that path.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
- Branches: `feat/…`, `fix/…`, `chore/…`, `docs/…`
|
||||
|
||||
Reference in New Issue
Block a user