alchemy-domain
Build backends with @alchemy.run/domain (domain-driven design on Effect) - turn requirements into aggregates, commands, events, rejections, views, policies, sagas, projections, streams, queries, external systems and Apis; lay them out in a monorepo (domain / server / web packages, kind-suffixed files); test them with stories; host them on Durable Objects (Cloudflare, Rivet or Effect Cluster) with alchemy; and drive them from a foldkit frontend. Use when designing or changing an @alchemy.run/domain model, adding a building block, wiring hosting or a frontend, or reviewing such code.
How do I install this agent skill?
npx skills add https://github.com/alchemy-run/alchemy --skill alchemy-domainIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill provides comprehensive documentation, architectural patterns, and code templates for building backends using the @alchemy.run/domain framework and the Effect ecosystem. No security risks or malicious patterns were detected.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Building with @alchemy.run/domain
@alchemy.run/domain models a backend as typed messages and pure state transitions. Commands are decided by aggregates, which record events. Views fold events into read models. Policies react to events. Sagas run long processes. Projections write outside the model, and queries serve what they wrote. The same model runs in memory for tests and on Cloudflare, Rivet or Effect Cluster Durable Objects in production.
Docs: https://alchemy.run/domain, with every page listed in https://alchemy.run/llms.txt. Every section here and in the reference files ends with a More link to the docs page that covers it in depth. A complete reference project: examples/cloudflare-domain in the alchemy repo (a bank with a foldkit app).
Workflow
Work in this order. Each step has a reference file with templates.
- Break the requirements down into building blocks. See references/modeling.md.
- Lay out the monorepo: a domain package, a server package, a web package, with one kind-suffixed file per block. See references/layout.md.
- Write each block from its template. See references/building-blocks.md.
- Specify behavior with stories before and while you implement it. See references/testing.md.
- Host the model on Cloudflare from the server package, export its traces, and deploy with one alchemy stack. See references/hosting.md.
- Drive it from foldkit in the web package. See references/frontend.md.
More: Tutorial, part 1.
From requirements to building blocks
Read the requirements as a story of facts, and name each kind of thing you find:
- A fact the business records, named in the past tense (
MoneyWithdrawn): an Event. - A request to change something, named
<Verb><Noun>(WithdrawMoney): a Command. - A way a request is refused (
InsufficientFunds): a Rejection. - A thing whose rules must hold in one transaction (one account's balance): an Aggregate. It decides commands against its state.
- A question someone asks (balance, statement, dashboard): a View, keyed by what it's about.
- "Whenever X happens, do Y": a Policy.
- A process with steps, waits and undo (move money: debit, credit, refund on failure): a Saga, started by a policy.
- Something outside the model you call (fraud scoring, payments): an External System.
- Writes to a store outside the model (a ledger table): a Projection. An ordered, readable log per key (account cards): a Stream that a projection appends to.
- A question across keys, answered from that store (an account's ledger, paged; large withdrawals across all accounts, largest first): a Query. Never a hand-written route.
- Who can do and see what: an Api listing commands and reads, with middleware (sessions, MFA) and each block authorizing itself: a command in
decide, a view or stream in its Layer'svalidate, a query in its handler.
A rule that spans aggregates never becomes a cross-aggregate transaction. Give the invariant its own aggregate, keyed by what it protects (UsernameClaim keyed by username). Or reserve first and commit after, in a policy or saga, with a deadline for anything that waits.
More: What is Domain-Driven Design?, Domain Model.
Conventions
- One block per file, named
<Noun>.<kind>.ts. The kinds are.aggregate,.command,.view,.policy,.saga,.projection,.stream,.query,.external,.apiand.model. Plus.middleware,.contextand.rejection. An aggregate's id lives in<Aggregate>.id.tsnext to it. Plain values (Money.ts) and helpers keep plain names. - A command file holds the command, the events it records, and the rejections only it returns. A rejection or event used by several commands gets its own file.
- A block's implementation sits in its file (
TransferExecutionLiveinTransferExecution.policy.ts), unless it touches infrastructure. Then it lives in the server package. - Folders are subdomains (
Account/,Transfer/). The model, Apis, middleware and request context sit at the package root. - The domain package imports only
@alchemy.run/domainandeffect. Cloudflare, Stripe, databases and HTTP session handling belong to the server package. - One domain package per model (bounded context). A second model is a second domain package; one server can host several.
More: Project layout.
Rules that keep a model correct
decideis pure, or anEffect.fnthat reads only what its command's middleware provides (yield* CurrentActor). It returns events or a rejection; it never performs I/O.evolveonly applies an event to state. It never decides anything. It andreplyare total: they cover every state and never fail, becausedecidealready refused what can't happen. Keep a state the event can't follow (s._tag === "Open" ? { ...s, balance: e.balanceAfter } : s), never a guard that throws.- Events carry the outcome
decideworked out (balanceAfter), soevolvecopies it instead of repeating a rule. - Every rejection a command can return is listed in its
rejects. Callers handle each one withEffect.catchTag. - Serve a store a projection writes through a
Domain.Queryon an Api, never a hand-written route: its handler checks the caller and scopes the rows it reads. - Clients never read an aggregate. Read through a view (one key, live), a stream, or a query; a query's handler may read an aggregate's latest state with
yield* Accountthenaccounts.get(ref). - Policies and projections run at least once. Make their effects idempotent: commands sent from a handler get deterministic ids, and stores key rows by
stream:seq. - Don't wait for a saga in a policy.
startit, keyed by the aggregate it's about, and react to the events its steps record. - A handler resolves its dependencies once in
toLayerand requires nothing per call. - Build one Layer and provide it once (
Layer.mergeAll,Layer.provide). Never chainEffect.provide. - Never
throw. Fail with tagged errors (Domain.Rejection,Schema.TaggedError). - Tracing is automatic. Record payload values only with
Effect.annotateCurrentSpanin a handler: it lands on the engine's span for that handler. Make secretsSchema.Redacted, so they never reach a trace or a log. - Test what the model does, through stories: events, replies, rejections, views and saga outcomes. Don't test a saga's internal steps.
More: Domain Model: The aggregate decides, and everything commits at once, Cloudflare: Count on these guarantees.
Checklist for a change
- Name the new facts, commands and rejections. Put each command in its own
<Command>.command.tswith its events and rejections. - Add the command to its aggregate's
commands, with adecidebranch for it and anevolvebranch for each event. - Update the views, policies and projections that care about the new events.
- Expose it through an Api, if a client sends or reads it, with the middleware it needs, and give it a
{ description }as its last argument. - Write stories for the happy path, every rejection, every policy reaction, and every query's authorization.
- Run the domain package's tests, with one that expects
Domain.verify(Model)to be empty. Then type-check the monorepo (tsc -b) and run the server and web tests.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/alchemy-run/alchemy/alchemy-domain">View alchemy-domain on skillZs</a>