A teaching project for learning how to build with Grain through a small todo workspace.
This repository accompanies Grain Sessions, a video series about how to use Grain:
- Episode 1: https://youtu.be/tO--joFrYUE
- Episode 2: https://youtu.be/plMAG4FASdk
- Episode 3: https://youtu.be/weAsNioiEnI
- Episode 4: https://youtu.be/XRo49q6yCeo
The app is intentionally small, but it exercises the main parts of a Grain application:
- Capturing, renaming, reordering, completing, canceling, archiving, and reactivating tasks.
- Grouping work into projects and tracking project task counts.
- Marking tasks as due within a number of days.
- Reviewing active tasks and projects through a weekly review workflow.
- Building server-rendered UI from Grain query results with the Grain Datastar checked UI DSL and Hiccup.
- Organizing command handlers, event schemas, read models, query handlers, processors, periodic tasks, and UI in a compact Clojure app.
The app uses:
- Grain for commands, queries, events, read models, request handlers, processors, and periodic tasks.
- Datastar for server-rendered reactive pages.
- Integrant for application composition and lifecycle.
- Tailwind CSS and DaisyUI for styling.
- An in-memory event store and temporary LMDB cache for local development.
The code is laid out as a small Clojure project rather than a Polylith system.
Foundation code lives under src/cjbarre/grain_todo_list/foundation/, service
code lives under src/cjbarre/grain_todo_list/service/, and the root app
namespace handles system startup and routes.
Foundation components are small, targeted units of functionality that support the app as a whole. They hold shared infrastructure and reusable primitives, such as app UI chrome, email delivery, JWT helpers, and auth interceptors.
Service components own state changes in an area of the domain. When visualized, they should look like Event Models and information systems: commands introduce changes, events record what happened, read models describe useful views of state, and queries present that information back to users.
.
├── src/cjbarre/grain_todo_list.clj # Integrant system, routes, lifecycle
├── src/cjbarre/grain_todo_list/foundation/
│ ├── ui.clj # App shell and page chrome
│ ├── ui/components.clj # Reusable visual primitives
│ ├── email.clj # Email boundary
│ ├── jwt.clj # JWT helpers
│ └── auth_interceptors.clj # Auth cookie interceptors
├── src/cjbarre/grain_todo_list/service/
│ ├── todo_list_service/ # Todo domain service component
│ │ ├── schemas.clj # Command, event, query, read-model schemas
│ │ ├── read_models.clj # Reducers and projection helpers
│ │ ├── commands.clj # Command validation and event emission
│ │ ├── queries.clj # Query assembly and UI render boundary
│ │ ├── ui.clj # Todo page composition
│ │ ├── ui/components.clj # Todo controls, cards, lists, gallery specimens
│ │ ├── todo_processors.clj # Todo processor lifecycle
│ │ └── periodic_tasks.clj # Periodic trigger lifecycle
│ └── user_service/ # User account service component
├── css/main.css # Tailwind/DaisyUI input CSS
├── resources/public/ # Generated static assets
├── test/ # Clojure tests
├── CLAUDE.md # Primary agent guide (golden path, gates)
├── AGENTS.md # Condensed agent quick-reference
└── .claude/skills/ # Grain build, verification, and spec skills
- Clojure CLI
- Node.js and npm
The app must be started with the :dev alias because LMDB needs Java module
opens.
npm installOne-shot build:
npm run css:buildWatch mode while developing UI:
npm run css:devThe CSS build writes to resources/public/css/main.css.
Start an nREPL:
./scripts/nrepl.shThen evaluate:
(require '[cjbarre.grain-todo-list :as app])
(def system (app/start))Open:
http://localhost:8080/
Stop the app:
(app/stop system)Typical local loop:
npm run css:devIn another terminal:
./scripts/nrepl.shThen work from the REPL. Reload namespaces after edits and restart the Integrant system as needed
Run tests:
clojure -T:build testRun clj-kondo:
clj-kondo --lint src testThe primary agent guide is CLAUDE.md — the Charter, the Grain golden
path, self-verification with code-agent-tools, the Definition of Done, and the
running/reloading discipline. AGENTS.md is the condensed quick
reference. Task-specific skills (schemas, commands, read models, queries, views,
UI components, verification, and the allium spec family) live in
.claude/skills/.
