Skip to content

Domain.Api reference

Source: packages/domain/src/Api.ts

The public surface of a domain for one kind of client: the commands it may send, and the views and aggregates (with a read contract) it may read. Nothing in an Api needs a handler. The class is an Effect: yield* MyApi gives a Worker its fetch. Call it with a typed client over Domain.protocolWebSocket or Domain.protocolHttp.

import * as Domain from "@alchemy.run/domain";
import { Account, DepositMoney, WithdrawMoney } from "./Account.ts";
import { AccountSummary } from "./AccountSummary.ts";
export class CustomerApi extends Domain.Api(
"CustomerApi",
DepositMoney,
WithdrawMoney,
Account,
AccountSummary,
) {}
import * as Cloudflare from "@alchemy.run/cloudflare";
import * as DomainCloudflare from "@alchemy.run/cloudflare/Domain";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import { Bank, BankPolicies } from "./Bank.ts";
import { CustomerApi } from "./CustomerApi.ts";
import { SessionLive } from "./Session.ts";
const BankLive = SessionLive.pipe(
Layer.provideMerge(Bank.layer(BankPolicies)),
Layer.provide(DomainCloudflare.DurableObjects),
);
export default class BankWorker extends Cloudflare.Worker<BankWorker>()(
"BankWorker",
{ main: import.meta.url },
Effect.gen(function* () {
const api = yield* CustomerApi;
return { fetch: api.fetch };
}).pipe(Effect.provide(BankLive)),
) {}

Domain.protocolWebSocket sends commands, get and list over HTTP, and opens a WebSocket for each watch and events, pushed by the object that owns the key. Domain.protocolHttp sends everything over HTTP.

const bank = yield* CustomerApi.client;
const { reply } = yield* bank.DepositMoney({ target: accountId, amount: 1_000 });
const summary = yield* bank.AccountSummary.get(accountId);
yield* bank.AccountSummary.watch(accountId).pipe(
Stream.runForEach((s) => Effect.log(s?.balance)),
);