Domain.View reference
Source:
packages/domain/src/View.ts
A read model: events folded into state per key, kept up to date as new events arrive. A view can emit events of its own, route one event to several keys, and decide who may read it.
View: Folding events into state
Section titled “View: Folding events into state”import * as Domain from "@alchemy.run/domain";import * as Schema from "effect/Schema";import { Account } from "./Account.ts";
export class AccountSummary extends Domain.View("AccountSummary", { from: [Account], key: Account, state: Schema.Struct({ balance: Schema.Int }), initial: { balance: 0 }, evolve: { MoneyDeposited: (v, e) => ({ ...v, balance: e.balanceAfter }), MoneyWithdrawn: (v, e) => ({ ...v, balance: e.balanceAfter }), },}) {}View: Emitting events
Section titled “View: Emitting events”import * as Domain from "@alchemy.run/domain";import * as Schema from "effect/Schema";import { Account } from "./Account.ts";
export class StatementLine extends Domain.Event("StatementLine", { kind: Schema.Literals(["deposit", "withdrawal"]), amount: Schema.Int,}) {}
export class Statement extends Domain.View("Statement", { from: [Account], key: Account, events: [StatementLine], emit: { MoneyDeposited: (e) => new StatementLine({ kind: "deposit", amount: e.amount }), MoneyWithdrawn: (e) => new StatementLine({ kind: "withdrawal", amount: e.amount }), },}) {}View: Routing one event to several keys
Section titled “View: Routing one event to several keys”keyOf: { TransferRequested: { out: ({ event }) => Account.ref(event.from), in: ({ event }) => Account.ref(event.to), },},View: Keying by a value
Section titled “View: Keying by a value”key: OrgId, // a string SchemakeyOf: { Tour: ({ event }) => event.orgId },View: Validating reads
Section titled “View: Validating reads”The declaration lists the middleware that guards reads and the rejections
they may fail with. Its Layer, one validate function, judges each read: on every
get, snapshot and list, and on every change while a client watches.
A view that declares either needs its Layer.
export class AccountSummary extends Domain.View("AccountSummary", { from: [Account], key: Account, middleware: [Session], rejects: [NotOwner], state: Schema.Struct({ owner: Schema.NullOr(Schema.String), balance: Schema.Int }), initial: { owner: null, balance: 0 }, evolve: { MoneyDeposited: (v, e) => ({ ...v, balance: e.balanceAfter }) },}) {}
export const AccountSummaryLive = AccountSummary.toLayer( Effect.succeed( Effect.fn(function* (_request, summary) { const actor = yield* CurrentActor; if (summary?.owner && summary.owner !== actor.customerId) return yield* new NotOwner(); }), ),);View: Annotating views
Section titled “View: Annotating views”The last argument is its annotations: data for what is built on the
model, read back as X.annotations. Add your own keys by augmenting
Domain.Annotations.
An Api’s read tools, CLI help and OpenAPI document show its description
after the sentence for each call.
export class AccountSummary extends Domain.View( "AccountSummary", { from: [Account], key: Account, state: Schema.Struct({ balance: Schema.Int }), initial: { balance: 0 }, evolve: { MoneyDeposited: (_, e) => ({ balance: e.balanceAfter }) }, }, { description: "An account's balance." },) {}