Skip to content

One entry point for the commands of several aggregates, like Axon’s CommandGateway: the caller hands over a command and the gateway finds the aggregate that takes it.

const gateway = yield* CommandGateway.make([Todo, Account], {
interceptors: [audit]
})
// Typed: the payload and the errors follow from the aggregate and the type.
yield* gateway.send({ aggregateName: "todo", aggregateId: "a", type: "createTodo", payload: { title: "Buy Milk" } })
// Untyped JSON from a transport, routed by `aggregateName`, or by `type` alone
// when a single aggregate has a command of that name.
yield* gateway.dispatch({ aggregateId: "a", type: "completeTodo", payload: {} })

The gateway composes aggregate services. Give it an aggregate and it builds the service with the default options; give it a service built with AggregateService.make to choose them, or to share the service, and its state cache, with code that uses it directly.

Interceptors run around every command the gateway routes, for what Axon’s dispatch interceptors do: authorize, log, add metadata with Metadata.withMetadata. They run in the caller’s fiber, so what they need, such as the current user, comes from the caller’s context rather than the gateway’s.

Defined in: src/CommandGateway.ts:41

The gateway can’t tell which aggregate a command is for: the envelope names an aggregate the gateway doesn’t have, or it names none and the type is a command of no aggregate, or of more than one.

  • object & YieldableError<this>

new UnroutableCommand(…args): UnroutableCommand

Defined in: node_modules/.pnpm/effect@4.0.6/node_modules/effect/dist/Schema.d.ts:10979

…[props: object, options?: MakeOptions]

UnroutableCommand

Schema.TaggedError<UnroutableCommand>()("UnroutableCommand", { type: Schema.String, /** The aggregate the envelope named, if it named one. */ aggregateName: Schema.optional(Schema.String), /** The aggregates of the gateway that have a command of this type. */ candidates: Schema.Array(Schema.String), message: Schema.String }).constructor

readonly optional aggregateName?: string

Defined in: src/CommandGateway.ts:44

The aggregate the envelope named, if it named one.

Schema.TaggedError<UnroutableCommand>()("UnroutableCommand", { type: Schema.String, /** The aggregate the envelope named, if it named one. */ aggregateName: Schema.optional(Schema.String), /** The aggregates of the gateway that have a command of this type. */ candidates: Schema.Array(Schema.String), message: Schema.String }).aggregateName

readonly candidates: readonly string[]

Defined in: src/CommandGateway.ts:46

The aggregates of the gateway that have a command of this type.

Schema.TaggedError<UnroutableCommand>()("UnroutableCommand", { type: Schema.String, /** The aggregate the envelope named, if it named one. */ aggregateName: Schema.optional(Schema.String), /** The aggregates of the gateway that have a command of this type. */ candidates: Schema.Array(Schema.String), message: Schema.String }).candidates

message: string = Schema.String

Defined in: src/CommandGateway.ts:47

Schema.TaggedError<UnroutableCommand>()("UnroutableCommand", { type: Schema.String, /** The aggregate the envelope named, if it named one. */ aggregateName: Schema.optional(Schema.String), /** The aggregates of the gateway that have a command of this type. */ candidates: Schema.Array(Schema.String), message: Schema.String }).message

readonly type: string = Schema.String

Defined in: src/CommandGateway.ts:42

Schema.TaggedError<UnroutableCommand>()("UnroutableCommand", { type: Schema.String, /** The aggregate the envelope named, if it named one. */ aggregateName: Schema.optional(Schema.String), /** The aggregates of the gateway that have a command of this type. */ candidates: Schema.Array(Schema.String), message: Schema.String }).type

Defined in: src/CommandGateway.ts:119

A extends Any

E = never

R = never

readonly dispatch: (input) => Effect<CommandResult<EventOf<A>>, UnknownCommand | CommandFailure | UnroutableCommand | E | HandlerError<A, string>, R>

Defined in: src/CommandGateway.ts:139

Runs a { aggregateName, aggregateId, type, payload } envelope received as JSON from outside the process, with the other fields AggregateService.dispatch takes. aggregateName can be left out when a single aggregate of the gateway has a command of that type.

unknown

Effect<CommandResult<EventOf<A>>, UnknownCommand | CommandFailure | UnroutableCommand | E | HandlerError<A, string>, R>

readonly send: <C>(command) => Effect<CommandResult<Event<Extract<A, { name: C["aggregateName"]; }>>>, CommandFailure | E | HandlerError<Extract<A, { name: C["aggregateName"]; }>, C["type"]>, R>

Defined in: src/CommandGateway.ts:126

Runs a typed command on the aggregate it names.

C extends object & CommandOptions

C

Effect<CommandResult<Event<Extract<A, { name: C["aggregateName"]; }>>>, CommandFailure | E | HandlerError<Extract<A, { name: C["aggregateName"]; }>, C["type"]>, R>

readonly services: { readonly [X in Any as Name<X>]: AggregateService<X> }

Defined in: src/CommandGateway.ts:124

The service of each aggregate, by name, for reading with state and history. Commands run through a service directly skip the interceptors.


Defined in: src/CommandGateway.ts:51

What an interceptor learns about the command it runs around.

readonly aggregateId: string

Defined in: src/CommandGateway.ts:53

readonly aggregateName: string

Defined in: src/CommandGateway.ts:52

readonly type: string

Defined in: src/CommandGateway.ts:54


Defined in: src/CommandGateway.ts:71

Runs around a command. It may run effects before and after next, fail instead of running it, or run it with more context, and adds its own errors E and requirements R to every command of the gateway.

const audit: CommandGateway.Interceptor<Unauthorized, CurrentUser> = (command, next) =>
Effect.gen(function*() {
const user = yield* CurrentUser
if (!user.may(command.type)) return yield* new Unauthorized({ type: command.type })
return yield* Metadata.withMetadata(next, { userId: user.id })
})

E = never

R = never

Interceptor<A, E1, R1>(command, next): Effect<A, E | E1, R | R1>

Defined in: src/CommandGateway.ts:72

Runs around a command. It may run effects before and after next, fail instead of running it, or run it with more context, and adds its own errors E and requirements R to every command of the gateway.

const audit: CommandGateway.Interceptor<Unauthorized, CurrentUser> = (command, next) =>
Effect.gen(function*() {
const user = yield* CurrentUser
if (!user.may(command.type)) return yield* new Unauthorized({ type: command.type })
return yield* Metadata.withMetadata(next, { userId: user.id })
})

A

E1

R1

CommandInfo

Effect<A, E1, R1>

Effect<A, E | E1, R | R1>


Defined in: src/CommandGateway.ts:152

Interceptors extends ReadonlyArray<Interceptor<any, any>>

readonly optional interceptors?: Interceptors

Defined in: src/CommandGateway.ts:154

Run around every command, the first outermost.

AggregateOf<X> = X extends object ? A : X extends Any ? X : never

Defined in: src/CommandGateway.ts:79

The aggregate of an entry.

X


Command<A> = A extends Any ? { readonly [K in CommandName<A>]: { aggregateId: string; aggregateName: Name<A>; payload: Payload<A, K>; type: K } & CommandOptions }[CommandName<A>] : never

Defined in: src/CommandGateway.ts:96

A typed command for one of the aggregates A, what CommandGateway.send takes.

A extends Any


Entry = Any | AggregateService<any>

Defined in: src/CommandGateway.ts:76

What a gateway routes to: an aggregate, or the service of one.


EventOf<A> = A extends Any ? Event<A> : never

Defined in: src/CommandGateway.ts:117

The events of any aggregate of A.

A extends Any


HandlerError<A, Type> = A extends Any ? CommandError<A, Type & CommandName<A>> : never

Defined in: src/CommandGateway.ts:112

The errors the handler of a command declares.

A extends Any

Type


InterceptorContext<I> = I extends Interceptor<any, infer R> ? R : never

Defined in: src/CommandGateway.ts:93

What an interceptor requires from the caller, read from its type like InterceptorError.

I


InterceptorError<I> = I extends Interceptor<infer E, any> ? E : never

Defined in: src/CommandGateway.ts:90

The errors an interceptor adds. They are read from its type, so declare an interceptor as an Interceptor<E, R> rather than inline in the options.

I


Named<A, Name> = Extract<A, { name: Name; }>

Defined in: src/CommandGateway.ts:109

The aggregate of A with this name.

A extends Any

Name


Requirements<X> = X extends Any ? EventStore | Context<X> : never

Defined in: src/CommandGateway.ts:84

What building the services of the entries requires: entries that are services are already built.

X

make<Entries, Interceptors>(entries, options?): Effect<CommandGateway<AggregateOf<Entries[number]>, InterceptorError<Interceptors[number]>, InterceptorContext<Interceptors[number]>>, never, Requirements<Entries[number]>>

Defined in: src/CommandGateway.ts:263

Builds a gateway over aggregates, or over services of them. Two entries for the same aggregate are a defect.

Entries extends readonly Entry[]

Interceptors extends readonly Interceptor<any, any>[] = []

Entries

Options<Interceptors>

Effect<CommandGateway<AggregateOf<Entries[number]>, InterceptorError<Interceptors[number]>, InterceptorContext<Interceptors[number]>>, never, Requirements<Entries[number]>>