An Event-Sourced framework for building AI-native systems in Clojure.
Grain is a framework for building Event-Sourced systems using CQRS (Command Query Responsibility Segregation). It provides composable components that snap together like Lego bricks—start with an in-memory event store for quick iteration, then swap in Postgres with a single line change when you're ready.
Grain is also AI-native: agents and events share the same backbone, giving you a coherent architecture where agentic workflows are part of the domain model rather than bolted on as an afterthought.
┌────────────────────────────────────────────────────────┐
│ Write Side │
│ │
POST /command ───────────▶│ Command Processor ──▶ Validate ──▶ Handler ──▶ Events │
│ ▲ ▲ │ │
└─────────│──────────────────│──────────────────────┼────┘
│ │ │
│ │ read │ append
│ ┌─────────┴─────────┐ │
│ │ │ ▼
Todo Processors ──────────┘ │ Read Model │◀───┬───────────┐
▲ │ │ │ Event │
│ └───────────────────┘ │ Store │
│ ▲ proj └───────────┘
│ ┌────────────────────────────│─────────────┐ │
│ │ Read Side │ │ │ publish
│ │ │ │ ▼
│ │ Query Processor ──────────┘ │ ┌─────────┐
POST /query ─────────────▶│ │ │ Pub/Sub │
│ └──────────────────────────────────────────┘ └─────────┘
│ │
└──────────────────────────────────────────────────────────┘
(async)
Commands are the only path to state change—they validate business rules and emit events. Events are immutable facts stored in the event store. Queries read from projections (read models) built from events. Todo Processors react to events asynchronously, enabling event-driven workflows.
Commands change state by generating events:
(defcommand :example create-counter
{:authorized? (constantly true)}
"Creates a new counter."
[context]
(let [id (random-uuid)
name (get-in context [:command :name])]
{:command-result/events
[(->event {:type :example/counter-created
:body {:counter-id id :name name}})]
:command/result {:counter-id id}}))Events are immutable facts about what happened:
{:event/type :example/counter-created
:event/id #uuid "..." ; UUID v7
:event/timestamp #inst "..."
:event/body {:counter-id #uuid "..." :name "My Counter"}
:event/tags #{[:counter #uuid "..."]}} ; for efficient queryingQueries read from projections without causing state changes:
(defquery :example counters
{:authorized? (constantly true)}
"Returns all counters."
[context]
{:query/result (read-models/counters context)})Read models are built by reducing over events:
(defn apply-events [events]
(reduce
(fn [state {:event/keys [type body]}]
(case type
:example/counter-created
(assoc state (:counter-id body) {:id (:counter-id body)
:name (:name body)
:value 0})
:example/counter-incremented
(update-in state [(:counter-id body) :value] inc)
state))
{}
events))Add to your deps.edn:
obneyai/grain-core
{:git/url "https://github.com/ObneyAI/grain.git"
:sha "32a3b154129509c4b01a286a3e1fe7d6e3ad9519"
:deps/root "projects/grain-core"}See bases/example-base and components/example-service for a complete example application. Run development/src/example_app_demo.clj to start and interact with the example system.
| Package | Summary |
|---|---|
| grain-core | CQRS/Event Sourcing + in-memory event store + Behavior Tree engine |
| grain-event-store-postgres-v2 | Protocol-driven Postgres backend—swap with a config change |
| grain-dspy-extensions | DSPy integration for LLM workflows |
| grain-mulog-aws-cloudwatch-emf-publisher | AWS CloudWatch metrics & dashboards |
Package Details
Everything you need for CQRS/Event Sourcing with an in-memory event store:
obneyai/grain-core
{:git/url "https://github.com/ObneyAI/grain.git"
:sha "32a3b154129509c4b01a286a3e1fe7d6e3ad9519"
:deps/root "projects/grain-core"}Postgres backend—require ai.obney.grain.event-store-postgres-v2.interface and switch from :in-memory to :postgres:
obneyai/grain-event-store-postgres-v2
{:git/url "https://github.com/ObneyAI/grain.git"
:sha "32a3b154129509c4b01a286a3e1fe7d6e3ad9519"
:deps/root "projects/grain-event-store-postgres-v2"}DSPy integration for sophisticated LLM workflows. Requires Python 3.12+ (we recommend uv for environment management):
obneyai/grain-dspy-extensions
{:git/url "https://github.com/ObneyAI/grain.git"
:sha "32a3b154129509c4b01a286a3e1fe7d6e3ad9519"
:deps/root "projects/grain-dspy-extensions"}mulog publisher for CloudWatch metrics:
obneyai/grain-mulog-aws-cloudwatch-emf-publisher
{:git/url "https://github.com/ObneyAI/grain.git"
:sha "32a3b154129509c4b01a286a3e1fe7d6e3ad9519"
:deps/root "projects/grain-mulog-aws-cloudwatch-emf-publisher"}Grain includes a behavior tree engine with DSPy integration for building agentic workflows. Agents can reason over the same event-sourced domain as the rest of your system—with short-term program memory and long-term event-sourced memory.
See demos at macroexpand-2-demo.
You can use the agent framework standalone or skip it entirely if you just want Event Sourcing.
We use Event Modeling and Event Sourcing to design Simple systems. Grain combines proven ideas from conventional software architecture with modern agent workflows, giving us a single, composable toolkit for building AI-driven applications.
Polylith enables us to evolve components independently and publish standalone tools from a single repository.
Grain is MIT licensed. We use it in production, but it's actively evolving. The core CQRS/Event Sourcing components are stable; agent-related components may change more rapidly.
- Examples:
bases/example-base,components/example-service,development/src/example_app_demo.clj - Slack: #grain on Clojurians
- Issues: GitHub Issues