writeonce/docs/examples/ecommerce/README.md
shoney.arickathil 9279175f26 scaffold sibling crates, multi-app ecommerce, REST + concurrency docs
- scaffold 14 placeholder crates (app, db, engine, gen, http, logic,
  policy, ql, service, sub, txn, ui, value, wal) — empty Cargo.toml +
  src/lib.rs to receive code phase-by-phase from `rt`
- restructure docs/examples/ecommerce into multi-app layout: apps/admin
  and apps/storefront, with shared/ types/logic/components, per-app
  app.wo + wo.toml, and reusable .htmlx components (layout, money,
  order-row)
- add reference/rest/{blog,ecommerce}.rest — VS Code/JetBrains HTTP
  request files driving the running prototype, including 501/404/405
  expectations for stubbed endpoints
- add docs/plan/09-concurrency-scaleout.md and docs/plan/ui/00-overview.md;
  refine docs/plan/assembly/02-writeonce-stance.md
- refresh templates (about, article, header/footer, home, layout, styles)
  and add static favicon/logo
- add infra/sync.sh and tighten .gitignore for reference/ symlinks
2026-05-04 13:39:58 +02:00

116 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `ecommerce` — a sample writeonce **monorepo**
Two apps (customer **storefront** + ops **admin**) sharing one database, built from a common pool of types + business logic. Mirrors the Nx / Angular workspace pattern: `apps/*` for deployable binaries, `shared/*` for libraries imported across apps.
> This project is a **docs artifact** — illustrative `.wo` source showing what a production-shaped writeonce workspace looks like. The master plan for the compiler + client runtime + per-app build is at [`../../plan/ui/00-overview.md`](../../plan/ui/00-overview.md). Sub-phases UI/01–07 implement each piece.
## Layout
```
ecommerce/
├── wo.toml # workspace manifest (apps[] + shared[] + database)
├── README.md # this file
│
├── shared/ # code imported by one or more apps
│ ├── types/
│ │ ├── customer.wo # role union, policy, service rest expose
│ │ ├── product.wo # inventory + similar_to graph
│ │ ├── order.wo # tagged-union status + line_items array
│ │ └── purchase.wo # link Customer -> Product
│ ├── logic/
│ │ ├── checkout.wo # fn checkout / mark_paid / mark_shipped
│ │ └── seed.wo # on-startup demo seed + admin-ops bypass policy
│ └── components/ # reusable .htmlx partials (forward-looking — UI track)
│ ├── layout.htmlx # page chrome shared across apps
│ ├── money.htmlx # {{> money amount=total}}
│ └── order-row.htmlx # used by both orders tables
│
├── apps/ # one binary per app
│ ├── storefront/ # customer-facing
│ │ ├── wo.toml # listen :8080, connect WO_DB
│ │ ├── app.wo # routes: / → home, /product/:sku, /cart, /orders
│ │ └── ui/
│ │ ├── home/
│ │ │ └── home.wo # product list, live inventory
│ │ ├── product-detail/ # (future)
│ │ └── orders/
│ │ └── orders.wo # customer's own orders
│ │
│ └── admin/ # ops dashboard
│ ├── wo.toml # listen :8081, connect WO_DB
│ ├── app.wo # role: Admin | Ops; routes: /orders
│ └── ui/
│ └── orders/
│ └── orders.wo # live ops table with fulfillment actions
│
└── tests/ # workspace-level integration
└── checkout_test.wo
```
Each app's UI screens live in their own directory (`apps/<app>/ui/<screen>/`) with the Angular-style one-directory-per-component pattern — `.wo` declarative spec today, `.htmlx` template + `.css` stylesheet once the UI track's sub-phase 01–02 land.
## What the two apps share
- **Types** (`shared/types/`). Both apps see the same `Customer` / `Product` / `Order` / `Purchase` definitions. Row-level policies inside each `type` block control who sees what — the storefront's authenticated customer sees their own orders; the admin app's Admin/Ops role sees everyone's.
- **Logic** (`shared/logic/`). The `checkout`, `mark_paid`, `mark_shipped`, and `release_inventory` functions in `shared/logic/checkout.wo` are callable from either app (subject to role policy). `shared/logic/seed.wo` runs once when the shared DB daemon starts.
- **Components** (`shared/components/`). `.htmlx` partials — layout chrome, money formatting, an order-row renderer — reusable from either app's templates.
## What's **not** shared (per-app)
- **`app.wo`** declares app-specific routes + role gate. Storefront has no `/admin/*` routes; admin has no `/cart` or `/product/:sku`.
- **`ui/`** is per-app. The storefront's `orders/orders.wo` (customer's own orders, filtered by session) and the admin's `orders/orders.wo` (all orders with fulfillment actions) are different screens — same underlying `Order` type, different UI + policy scope.
- Each app's `wo.toml` names its own listen port and its own DB API key.
## Running it
```bash
# 1. Start the shared DB daemon — headless, just the engine + wire protocol
wo db serve --data-dir ./data # listens on wo://127.0.0.1:5555
# 2. Start each app, pointing at the daemon
WO_DB=wo://127.0.0.1:5555 \
STOREFRONT_DB_KEY=$ADMIN_TOKEN \
wo run apps/storefront # HTTP on :8080
WO_DB=wo://127.0.0.1:5555 \
ADMIN_DB_KEY=$ADMIN_TOKEN \
wo run apps/admin # HTTP on :8081
```
Browser:
- `http://localhost:8080/` → storefront home (product list, live inventory)
- `http://localhost:8081/orders` → admin live orders table
## Building binaries
```bash
wo build apps/storefront # → target/wo/storefront
wo build apps/admin # → target/wo/admin
wo build --all # everything in apps/
```
Each binary is a static ELF with only the app's own `app.wo` + `ui/` + the `shared/` it imports baked in. Drop any binary on a server next to a running `wo db serve` and it works.
## Stage 2 caveat
The current runtime at [`crates/rt/`](../../../crates/rt/) is a single-process Stage 2 prototype — it doesn't yet know about:
- `[workspace]` manifests (per-app build — UI sub-phase 05)
- The `wo db serve` daemon split (UI sub-phase 06)
- Per-app `api_key_env` authorisation scope (UI sub-phase 07)
- `.htmlx` compilation from `##ui` blocks (UI sub-phases 01–03)
- Per-app policy composition (UI sub-phase 07)
So `cargo run --bin wo -- run docs/examples/ecommerce` today walks the whole tree, finds every `.wo` file under `shared/` + `apps/`, parses the types, and serves the union REST API on :8080 — treating the monorepo as one giant app. Useful for exercising the types; not reflective of the production shape. See [`docs/plan/ui/00-overview.md`](../../plan/ui/00-overview.md) for the sub-phase sequence that gets each piece online.
## Comparison with the blog sample
The [`blog` sample](../blog/) is still a single-app layout (`types/`, `ui/`, `logic/` at the root) because the blog has exactly one front-end surface — there's no customer-vs-admin split. Both patterns are first-class; pick based on whether your schema serves one app or many. A flat single-app layout is a degenerate workspace with one `apps/` entry.
## Source pointers
- **Master plan:** [`../../plan/ui/00-overview.md`](../../plan/ui/00-overview.md)
- **Language spec the `##ui`/`##app`/`policy` blocks obey:** [`../../runtime/database/06-lowcode-fullstack.md`](../../runtime/database/06-lowcode-fullstack.md)
- **Wire protocol the app binaries speak to the DB daemon:** [`../../runtime/database/04-client-api.md`](../../runtime/database/04-client-api.md)
- **v1 template engine that `.htmlx` compilation will reuse:** [`../../../reference/crates/wo-htmlx/`](../../../reference/crates/wo-htmlx/)
- **Checkout transaction that's the canonical cross-paradigm test:** [`shared/logic/checkout.wo`](shared/logic/checkout.wo)