Arc for Elixir
Arc for Elixir is an opinionated CQRS application framework for Phoenix and Plug on Cratis Chronicle, with model-bound commands and queries, validation, authorization, observable queries and opt-in TypeScript proxies for React.
Source preview. The package is built and tested here, but the API may still change, and nothing is claimed beyond what Limits and gaps says is verified.
Overview
You write a command or a query as a plain module. Arc binds, validates, authorizes and runs it, records the events it returns in Chronicle, and serves it to React, LiveView or any HTTP client.
defmodule Library.Authors.Registration.RegisterAuthor do
@moduledoc "Registers a new author."
use Arc.Command
schema do
field :id, Library.Authors.AuthorId, key: true, required: true
field :name, :string, required: true, min_length: 2, max_length: 50
end
def handle(%__MODULE__{name: name}, _ctx),
do: {:append, %Library.Authors.AuthorRegistered{name: name}}
end
use Arc.Command/use Arc.ReadModel— declare commands and queries; fields, rules and authorization are declarations,handle/2is the logic- The Arc HTTP contract on Plug —
@cratis/arc,@cratis/arc.reactand Cratis Components work against it unchanged - Phoenix and LiveView — the same commands and queries, in process, with forms that show pipeline errors on the right field
- Observable queries — re-run when Chronicle (or MongoDB) changes a read model, pushed over the multiplexed WebSocket or Server-Sent Events hub, in full or delta mode
- Tenancy — header, query string, claim or subdomain resolvers select the tenant, which scopes the Chronicle namespace and the MongoDB database
- MongoDB —
cratis_arc_mongodb: tenant-aware databases, naming and BSON conventions, connection resolution and change stream observation - Cross-cutting filters — authorize a whole namespace of commands and queries in one place
- Opt-in TypeScript proxies —
mix arc.gen.proxiesgenerates typed commands and queries, with the same validation as the server, only if you add the compiler; they can sit beside the Elixir they come from, as vertical slices - Identity —
/.cratis/me, typed identity details, development users for Lens, signed forwarded identity and on-behalf-of, as Arc on .NET - The exact result envelopes —
CommandResultandQueryResultmatch the Arc contract field for field, pinned by specs - Chronicle underneath — returned events are appended with the caller's identity and correlation; constraint rejections reach the client as validation results
Arc.Test— the real pipeline against Chronicle's in-memory kernel
Arc for Elixir works with Chronicle only. It is part of the Cratis family of Arc implementations for .NET, Kotlin and Java, Go and TypeScript, and follows the same HTTP contract.
Install
defp deps do
[
{:cratis_arc, "~> 0.1"}
]
end
Arc needs Elixir 1.18 or later and brings cratis_chronicle with it. Phoenix, Phoenix.HTML and Phoenix LiveView are optional: the LiveView support compiles only when phoenix_live_view is a dependency. CI builds and tests with Elixir 1.19.5 on Erlang/OTP 28.5.
Add import_deps: [:cratis_arc] to .formatter.exs so field :name, :string is not rewritten with parentheses.
Prerequisite: Chronicle running
docker pull cratis/chronicle:latest-development
docker run -d --name chronicle \
-p 127.0.0.1:35000:35000 \
-p 127.0.0.1:27017:27017 \
cratis/chronicle:latest-development
The 127.0.0.1: prefixes keep both ports on your machine: the development kernel accepts well-known credentials and its MongoDB has no authentication.
Getting started
Get started builds an authors feature end to end: events, a command, a query, the HTTP API, a LiveView page and a test.
Documentation
- How Arc works
- Result envelopes, Commands, Queries and read models, Validation, Authorization and identity
- Hosting on Plug and on Phoenix
- LiveView
- React and generated proxies
- Testing, Observability, Configuration
- Samples and Limits and gaps
- Build and release — the pull request checks and the publishing pipeline
Samples
| Sample | Shows |
|---|---|
Samples/plug |
The smallest Arc application: Bandit and a Plug router |
Samples/phoenix |
The HTTP contract, LiveView and a React app served from one Phoenix endpoint |
Samples/frontend |
A React app on the generated proxies |
Samples/run-tests.sh builds and tests all three.
Contributing
cd Source/arc
mix deps.get
mix format --check-formatted
MIX_ENV=test mix compile --warnings-as-errors
mix test
cd ../..
Samples/run-tests.sh
ContractTests/run.sh
Pull requests need exactly one release label (major, minor, patch or no-release) and a description written as a release note; Build and release explains both. Design decisions are recorded in decisions. Questions and ideas are welcome on Discord; report confirmed bugs as issues.
License
MIT. See LICENSE.