Getting Started
Directory Structure
How a HAMR project is laid out.
Every HAMR project follows the same conventional layout. The structure is opinionated but unsurprising — anyone who's worked on a Go web app will feel at home.
project layout
myapp/
├── cmd/
│ ├── site/ # main HTTP application
│ │ ├── main.go
│ │ └── Dockerfile
│ └── migrate/ # standalone migration runner
│ └── main.go
│
├── internal/
│ ├── api/ # JSON API routes & handlers
│ │ └── server.go
│ ├── db/ # connection + embedded migrations
│ │ ├── db.go
│ │ ├── models.go
│ │ └── migrations/
│ │ ├── 0001_init.up.sql
│ │ └── 0001_init.down.sql
│ ├── repo/ # data access layer
│ │ ├── repo.go # Store interface
│ │ └── postgres/ # Postgres implementation
│ ├── service/ # business logic
│ │ └── auth.go
│ ├── middleware/ # project-specific middleware
│ └── web/
│ ├── server.go # route registration
│ ├── components/ # shared Templ components & layout
│ └── handler/ # one package per domain
│ ├── home/
│ │ ├── handler.go
│ │ └── home.templ
│ └── auth/
│ ├── handler.go
│ ├── login.templ
│ └── register.templ
│
├── static/ # CSS, JS (vendored HTMX/Alpine), images
├── css/ # Tailwind input (if using Tailwind)
├── docker/ # docker-compose.yaml
├── docs/ # ADRs, design docs
├── e2e-go/ # browser-based E2E tests
├── scripts/ # dev/ops scripts
│
├── go.mod
├── hamr.toml # dev server manifest
├── Makefile
├── CLAUDE.md # AI assistant guide
├── AGENTS.md # project conventions
├── .env.example
└── .envLayered design
HAMR projects use a clean layering: handlers → services → repos → database. Each layer only depends on the one beneath it.
- Handlers live in
internal/web/handler/<domain>/. They parse requests, call services, and render Templ components. - Services live in
internal/service/. They contain business logic and orchestrate repos. - Repos live in
internal/repo/. They expose typed methods backed by SQL queries. - Models live in
internal/db/models.go. They mirror your database tables.
One package per domain
Each handler gets its own package. This keeps related code colocated, prevents circular imports, and makes it easy to find templates next to the handlers that use them.
internal/web/handler/books/
books/
├── handler.go # request methods
├── list.templ # GET /books
├── show.templ # GET /books/:id
├── form.templ # GET /books/new, GET /books/:id/edit
└── form_rules.go # validation rulesTemplates next to handlers
Templ files live next to the handlers that render them — not in a top-level
templates/ directory. This makes refactoring much easier: when you delete a handler, its templates go with it.