Skip to content

Domain.Policy reference

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.

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));
});
}),
);
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}`);
});
}),
);