@bounda-dev/core
v0.2.3
Published
Bounda runtime: aggregates, commands, events, policies, processes, read models
Downloads
2,718
Readme
@bounda-dev/core
The Bounda runtime: aggregates, commands, events, policies, processes and read models, wired from the modules you write and the types the generator emits.
Bounda is an event sourcing and CQRS framework for TypeScript. Your business logic lives in small files that export a handful of functions; the runtime does the wiring and runs on one database.
npm install @bounda-dev/core @bounda-dev/sqlite
npm install -D @bounda-dev/cliA command handler returns the events to append. Its argument types come from a generated module next to it, so nothing is registered by hand:
// app/domain/order/commands/place-order.ts
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 rejections = () => ({ AlreadyPlaced: "Order already placed" });
export const handler = ({ command, state, events, reject }: Command.HandlerArgs) => {
if (state.status !== undefined) return reject("AlreadyPlaced");
return [events.orderPlaced(command.payload)];
};boot() from @bounda-dev/core/node reads bounda.config.ts and the generated registry and
returns the running app, typed for your project. createTestApp() from
@bounda-dev/core/testing gives the same app on an in-memory adapter with a clock you control.
Operating
The runtime carries what a production app needs to recover from its own mistakes, and the
bounda CLI exposes it: bounda rebuild <read-model> projects the stream into a fresh table and
swaps it in without taking the read model offline; bounda dead-letters lists, retries or
discards the handler runs that gave up; an <event>.upcast.ts next to an event brings stored
payloads of an older shape up to date as they are read. Every command, batch and handler run is
an OpenTelemetry span with bounda.correlation_id, the lag of every subscriber is a gauge, and on
PostgreSQL the dispatcher is woken by NOTIFY instead of polling. Snapshots are not there yet, on
purpose; the deployment guide says why.
Exports
| Subpath | Holds |
| --- | --- |
| @bounda-dev/core | The public API: createApp, errors, contracts, module and registry types |
| @bounda-dev/core/config | defineConfig and the configuration types |
| @bounda-dev/core/node | boot() and the console logger |
| @bounda-dev/core/testing | createTestApp() |
| @bounda-dev/core/memory | The in-memory adapter |
| @bounda-dev/core/adapter | The interfaces an adapter implements |
| @bounda-dev/core/adapter/sql | Helpers shared by the SQL adapters |
| @bounda-dev/core/adapter/sqlite | The storage and read models over any SQLite, for a host that brings the connection: createSqliteAdapter |
| @bounda-dev/core/register | Where the generator registers your registry type |
Status
0.x. Until 1.0 the API can still change between minor versions, and every change that breaks something is called out in the changelog.
Docs: docs.bounda.dev. Source and issues: github.com/bounda-dev/bounda.
