- 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
7.4 KiB
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
.wosource 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. 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 sameCustomer/Product/Order/Purchasedefinitions. Row-level policies inside eachtypeblock 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/). Thecheckout,mark_paid,mark_shipped, andrelease_inventoryfunctions inshared/logic/checkout.woare callable from either app (subject to role policy).shared/logic/seed.woruns once when the shared DB daemon starts. - Components (
shared/components/)..htmlxpartials — layout chrome, money formatting, an order-row renderer — reusable from either app's templates.
What's not shared (per-app)
app.wodeclares app-specific routes + role gate. Storefront has no/admin/*routes; admin has no/cartor/product/:sku.ui/is per-app. The storefront'sorders/orders.wo(customer's own orders, filtered by session) and the admin'sorders/orders.wo(all orders with fulfillment actions) are different screens — same underlyingOrdertype, different UI + policy scope.- Each app's
wo.tomlnames its own listen port and its own DB API key.
Running it
# 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
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/ 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 servedaemon split (UI sub-phase 06) - Per-app
api_key_envauthorisation scope (UI sub-phase 07) .htmlxcompilation from##uiblocks (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 for the sub-phase sequence that gets each piece online.
Comparison with the blog sample
The blog sample 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 - Language spec the
##ui/##app/policyblocks obey:../../runtime/database/06-lowcode-fullstack.md - Wire protocol the app binaries speak to the DB daemon:
../../runtime/database/04-client-api.md - v1 template engine that
.htmlxcompilation will reuse:../../../reference/crates/wo-htmlx/ - Checkout transaction that's the canonical cross-paradigm test:
shared/logic/checkout.wo