Arc for Elixir
Arc for Elixir is a CQRS application framework for Phoenix and Plug applications. You write commands and queries as plain modules, and Arc serves them: validated, authorized, recorded in Cratis Chronicle, and reachable from React, LiveView or any HTTP client.
Without Arc, with Arc
Section titled “Without Arc, with Arc”Without a framework, every feature repeats the same plumbing. A controller binds and validates params, checks who is calling, calls a context function, shapes an error response, and a separate channel or PubSub topic pushes updates to the browser. The TypeScript client that calls it is written by hand and drifts.
With Arc, the feature is two modules. A command declares its fields and decides; a read model declares its queries. Binding, validation, authorization, the response envelope, live updates and, if you use React, the typed client are the framework’s job.
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
# A cross-field rule beyond the declarative ones above. def validate(%__MODULE__{name: name}, _ctx) when name in ["admin", "root"], do: {:error, Arc.Validation.error("That name is reserved.", [:name])}
def validate(_command, _ctx), do: :ok
# Decide, and return what happened. Arc appends the event to the command's key. def handle(%__MODULE__{name: name}, _ctx), do: {:append, %Library.Authors.AuthorRegistered{name: name}}endWhat you get
Section titled “What you get”- Commands and queries as modules. Fields, rules and authorization are declarations;
handle/2is the only logic. - The Arc HTTP contract on Plug.
@cratis/arc,@cratis/arc.reactand Cratis Components work against it unchanged, as a single-page application. - LiveView, in process. The same commands and queries, called without an HTTP hop, with forms that show the pipeline’s errors on the right field.
- Observable queries. A query that re-runs when Chronicle changes a read model and pushes the result over WebSocket or Server-Sent Events.
- Opt-in TypeScript proxies. Only if you build a React frontend:
mix arc.gen.proxiesgenerates typed commands and queries, with the same validation rules as the server. - Identity. The signed-in user, their roles and typed application details, served to React from
/.cratis/meand to LiveView in process, with development users for tools such as Lens, the signed forwarded-identity envelope and on-behalf-of, as in Arc on .NET. - The same result envelopes. Commands and queries answer with exactly the shapes the other Arc implementations do, so the frontend packages work unchanged. See Result envelopes.
- Chronicle underneath. Returned events are appended to the event store with the caller’s identity and correlation.
Arc for Elixir works with Chronicle only. It does not persist state any other way.
Where to go next
Section titled “Where to go next”- New here? Get started builds an authors feature end to end.
- Want the model first? How Arc works explains artifacts, the pipeline and routes.
- Building a React frontend? React and generated proxies.
- Using LiveView? LiveView.
- Contributing? Build and release.
Related
Section titled “Related”- The language-neutral Arc HTTP contract that this implementation follows.
- Chronicle Elixir client, which Arc uses to record events.