diff --git a/src/components/landing/code.astro b/src/components/landing/code.astro index 5f62318..6552879 100644 --- a/src/components/landing/code.astro +++ b/src/components/landing/code.astro @@ -7,16 +7,17 @@ const files = [ title: "Command handler", role: "decides", code: `// app/domain/order/commands/place-order.ts -import { DomainError } from "@bounda-dev/core"; import type { Command } from "./+types/place-order"; export const payload = ({ z }: Command.PayloadArgs) => z.object({ orderId: z.uuid(), customerId: z.string().min(1), total: z.number().positive() }); -export const handler = ({ command, state, events }: Command.HandlerArgs) => { - if (state.status !== "new") { - throw new DomainError(\`Order \${command.aggregateId} was already placed\`); - } +export const rejections = ({ command }: Command.RejectionsArgs) => ({ + AlreadyPlaced: \`Order \${command.aggregateId} was already placed\`, +}); + +export const handler = ({ command, state, events, reject }: Command.HandlerArgs) => { + if (state.status !== undefined) return reject("AlreadyPlaced"); return [ events.orderPlaced({ customerId: command.payload.customerId, total: command.payload.total }), ]; @@ -24,15 +25,14 @@ export const handler = ({ command, state, events }: Command.HandlerArgs) => { }, { title: "Event", - role: "applies", + role: "opens the order", code: `// app/domain/order/order-placed.ts import type { Event } from "./+types/order-placed"; export const payload = ({ z }: Event.PayloadArgs) => z.object({ customerId: z.string(), total: z.number().positive() }); -export const apply = ({ state, event }: Event.ApplyArgs) => ({ - ...state, +export const begin = ({ event }: Event.BeginArgs) => ({ status: "placed" as const, customerId: event.payload.customerId, total: event.payload.total, @@ -63,7 +63,7 @@ export const project = async ({ event, table }: Projection.Args) => {

The code

Three files. Their place and name are the declaration.

- A command handler decides, an event applies, a projection writes a row. bounda generate reads the folders and + A command handler decides, an event becomes the state, a projection writes a row. bounda generate reads the folders and infers every type, so there is no registry to keep in sync.

app/
diff --git a/src/components/landing/facts.astro b/src/components/landing/facts.astro
index e45dbc6..2f37350 100644
--- a/src/components/landing/facts.astro
+++ b/src/components/landing/facts.astro
@@ -8,7 +8,7 @@ const facts = [
   {
     label: "bounda rebuild",
     title: "Read models rebuild.",
-    body: "Replay the global stream into a fresh read model without touching a single event.",
+    body: "A wrong dashboard is a fixed projection and a rebuild, not a migration script. The events are never touched.",
   },
   {
     label: "Processes and policies",
@@ -18,7 +18,7 @@ const facts = [
   {
     label: "One database",
     title: "Runs where you are.",
-    body: "PostgreSQL, SQLite or libSQL, or a Cloudflare Durable Object per tenant. React Router integration and OpenTelemetry.",
+    body: "PostgreSQL, SQLite or libSQL. On Cloudflare, one Durable Object per tenant holds the whole domain, driven by its alarm. React Router integration and OpenTelemetry.",
   },
 ];
 ---
diff --git a/src/components/landing/hero.astro b/src/components/landing/hero.astro
index eee110f..7127d6d 100644
--- a/src/components/landing/hero.astro
+++ b/src/components/landing/hero.astro
@@ -11,13 +11,13 @@ import Install from "../install.astro";
       ceremony.
     
     

- Keep every change as an ordered fact. Rebuild any view from it, on one database. + Your tables keep the last write. Bounda keeps every change. Rebuild any view from it, on one database.

- Keep every change as an ordered fact and rebuild any view from it. Plain TypeScript modules, inferred types, one - database. No broker to run. + Your tables keep the last write. Bounda keeps every change. Rebuild any view, explain any past state. Plain + TypeScript modules, inferred types, one database, no broker.

diff --git a/src/components/landing/story.astro b/src/components/landing/story.astro index ecccdd7..20163fb 100644 --- a/src/components/landing/story.astro +++ b/src/components/landing/story.astro @@ -18,9 +18,9 @@

OrderPlaced is appended to the global stream at position 4,212. Facts are never edited or deleted.

-

Apply

+

Evolve

State is a fold.

-

The global stream interleaves every aggregate. apply folds only order/7f3a's events, in order: created, item added, placed.

+

The global stream interleaves every aggregate. evolve folds only order/7f3a's events, in order: created, item added, placed.

Projection

diff --git a/src/layouts/base.astro b/src/layouts/base.astro index 0339d76..f01c869 100644 --- a/src/layouts/base.astro +++ b/src/layouts/base.astro @@ -9,7 +9,7 @@ interface Props { const { title = "Bounda — Event sourcing and CQRS for TypeScript", - description = "Keep every change as an ordered fact and rebuild any view from it. Plain TypeScript modules, inferred types, one database.", + description = "Your tables keep the last write. Bounda keeps every change, so any view can be rebuilt from it. Plain TypeScript modules, inferred types, one database.", } = Astro.props; const canonicalUrl = new URL(Astro.url.pathname, Astro.site ?? "https://bounda.dev"); diff --git a/src/scripts/landing/story.ts b/src/scripts/landing/story.ts index 5ff6a5d..189e443 100644 --- a/src/scripts/landing/story.ts +++ b/src/scripts/landing/story.ts @@ -164,7 +164,7 @@ const readoutAt = (b: number): Readout => { const step = FOLD[k]; if (!step) throw new Error(`no fold step ${k}`); return at( - `Apply ${k + 1} of 3 · order/7f3a`, + `Evolve ${k + 1} of 3 · order/7f3a`, formatPosition(step.position), `${step.name} → ${step.state}`, step.position,