Domain.Policy reference
Policy
Section titled “Policy”Source:
packages/domain/src/Policy.ts
“Whenever this happens, do that”: one of the business’s standing rules. On an event from an aggregate or a view, it decides what to do next and acts by sending commands and calling external systems. Policies run shortly after the event, never in the same step, so use them only for eventually consistent work. Delivery is at least once; the commands a policy sends get ids derived from the event, so a second run sends the same commands.
Policy: Reacting to an aggregate
Section titled “Policy: Reacting to an aggregate”import * as Domain from "@alchemy.run/domain";import * as Effect from "effect/Effect";import { Account, AccountId, OpenAccount } from "./Account.ts";import { Customer, CustomerRegistered } from "./Customer.ts";
export class MainAccountProvisioning extends Domain.Policy("MainAccountProvisioning", { from: Customer, on: [CustomerRegistered],}) {}
export const MainAccountProvisioningLive = MainAccountProvisioning.toLayer( Effect.gen(function* () { const accounts = yield* Account; return Effect.fn(function* ({ source }) { yield* accounts .send(AccountId.make(`${source.id}-main`), new OpenAccount({ owner: source.id })) .pipe(Effect.catchTag("AlreadyOpen", () => Effect.void)); }); }),);Policy: Calling an external system
Section titled “Policy: Calling an external system”import * as Domain from "@alchemy.run/domain";import * as Effect from "effect/Effect";import { Account, MoneyWithdrawn } from "./Account.ts";import { FraudCheck } from "./FraudCheck.ts";
export class FraudReview extends Domain.Policy("FraudReview", { from: Account, on: [MoneyWithdrawn],}) {}
export const FraudReviewLive = FraudReview.toLayer( Effect.gen(function* () { const fraud = yield* FraudCheck; return Effect.fn(function* ({ source, event }) { const { risk } = yield* fraud.score({ accountId: source.id, amount: event.amount }); yield* Effect.log(`risk ${risk} for ${source.id}`); }); }),);