Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/components/landing/code.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@ 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 }),
];
};`,
},
{
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,
Expand Down Expand Up @@ -63,7 +63,7 @@ export const project = async ({ event, table }: Projection.Args) => {
<p class="label">The code</p>
<h2 class="display">Three files. Their place and name are the declaration.</h2>
<p>
A command handler decides, an event applies, a projection writes a row. <code>bounda generate</code> reads the folders and
A command handler decides, an event becomes the state, a projection writes a row. <code>bounda generate</code> reads the folders and
infers every type, so there is no registry to keep in sync.
</p>
<pre class="tree">app/
Expand Down
4 changes: 2 additions & 2 deletions src/components/landing/facts.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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.",
},
];
---
Expand Down
6 changes: 3 additions & 3 deletions src/components/landing/hero.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import Install from "../install.astro";
<span class="line"><span data-rise class="quiet" style="--enter-delay: 0.22s">ceremony.</span></span>
</h1>
<p class="lead-short" data-enter style="--enter-delay: 0.38s">
<b>Keep every change as an ordered fact.</b> Rebuild any view from it, on one database.
<b>Your tables keep the last write. Bounda keeps every change.</b> Rebuild any view from it, on one database.
</p>
</div>
<div class="foot" id="hero-foot">
<p class="lead" data-enter style="--enter-delay: 0.38s">
<b>Keep every change as an ordered fact and rebuild any view from it.</b> Plain TypeScript modules, inferred types, one
database. No broker to run.
<b>Your tables keep the last write. Bounda keeps every change.</b> Rebuild any view, explain any past state. Plain
TypeScript modules, inferred types, one database, no broker.
</p>
<div class="actions" data-enter style="--enter-delay: 0.46s">
<Install />
Expand Down
4 changes: 2 additions & 2 deletions src/components/landing/story.astro
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
<p><code>OrderPlaced</code> is appended to the global stream at position 4,212. Facts are never edited or deleted.</p>
</article>
<article class="caption" data-window="2.55,3.38">
<p class="kicker">Apply</p>
<p class="kicker">Evolve</p>
<h2 class="display">State is a fold.</h2>
<p>The global stream interleaves every aggregate. <code>apply</code> folds only order/7f3a's events, in order: created, item added, placed.</p>
<p>The global stream interleaves every aggregate. <code>evolve</code> folds only order/7f3a's events, in order: created, item added, placed.</p>
</article>
<article class="caption" data-window="3.42,4.26">
<p class="kicker">Projection</p>
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/base.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/landing/story.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading