Skip to content

Domain.Command reference

Source: packages/domain/src/Command.ts

A request to change one aggregate instance. It names the instance it targets, its input, the events it may record, the rejections it may return, the middleware that guards it, and the reply its caller receives.

import * as Domain from "@alchemy.run/domain";
import * as Schema from "effect/Schema";
export const AccountId = Schema.String.pipe(Schema.brand("AccountId"));
export class MoneyWithdrawn extends Domain.Event("MoneyWithdrawn", {
amount: Schema.Int,
balanceAfter: Schema.Int,
}) {}
export class InsufficientFunds extends Domain.Rejection("InsufficientFunds", {
balance: Schema.Int,
requested: Schema.Int,
}) {}
export class WithdrawMoney extends Domain.Command("WithdrawMoney", {
target: AccountId,
input: { amount: Schema.Int },
events: [MoneyWithdrawn],
rejects: [InsufficientFunds],
reply: { balance: Schema.Int },
}) {}

middleware runs in order before the command reaches its aggregate. What it provides, such as the current actor, is what decide may read.

import * as Domain from "@alchemy.run/domain";
import * as Schema from "effect/Schema";
import { AccountId, MoneyWithdrawn } from "./Account.ts";
import { MfaChallenge } from "./MfaChallenge.ts";
import { Session } from "./Session.ts";
export class WithdrawMoney extends Domain.Command("WithdrawMoney", {
target: AccountId,
middleware: [Session, MfaChallenge],
input: { amount: Schema.Int },
events: [MoneyWithdrawn],
reply: { balance: Schema.Int },
}) {}

Pass a commandId, and a resend with the same id returns the first receipt instead of applying the command again.

const receipt = yield* bank.WithdrawMoney({ target: accountId, amount: 2_000, commandId: requestId });