Skip to content

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.

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 }),
},
}) {}
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 }),
},
}) {}
keyOf: {
TransferRequested: {
out: ({ event }) => Account.ref(event.from),
in: ({ event }) => Account.ref(event.to),
},
},
middleware: [Session],
rejects: [NotOwner],
validate: Effect.fn(function* (_request, summary) {
const actor = yield* CurrentActor;
if (summary?.owner && summary.owner !== actor.customerId) return yield* new NotOwner();
}),