Buffalo — Structure#

Layout pattern#

Framework-specific / Root-package library

Buffalo does not follow the standard Go layout (cmd/internal/pkg). There is no cmd/ directory — the framework itself is the product. The root package (package buffalo) contains the entire public API surface: App, routing, middleware, context, sessions, and handlers. Subsystems are organized as sibling packages at the top level. This is the idiomatic layout for Go libraries that are used by user applications rather than executed directly.

Directory map#

repositories/buffalo/
├── *.go                    # Root package: App, routing, middleware, context, sessions, handlers
├── binding/                # HTTP request binding (form, JSON, XML, multipart)
│   └── decoders/           # Custom type decoders (time, null types)
├── internal/               # Private utility packages (not exported to consumers)
│   ├── env/                # ENV var loading (.env / godotenv integration)
│   ├── httpx/              # HTTP helper utilities
│   ├── meta/               # App metadata reader (buffalo-app.toml, database.yml detection)
│   ├── nulls/              # Null type helpers
│   ├── templates/          # Embedded HTML templates (error.dev.html, error.prod.html, notfound.prod.html)
│   └── testdata/           # Test fixtures (disk + embedded)
├── mail/                   # Email subsystem (SMTP sender, attachment, MIME handling)
├── plugins/                # Plugin discovery and event dispatch
│   ├── plugcmds/           # Plugin command registry (available commands, plug maps)
│   └── plugdeps/           # Plugin dependency metadata (pop integration)
├── render/                 # Rendering engine (HTML/plush, JSON, XML, plain, SSE, markdown, download)
├── runtime/                # Build metadata (build.go — version/build info)
├── servers/                # HTTP server implementations (simple, TLS, listener)
└── worker/                 # Background job interface and simple in-process worker

Entry points#

There are no cmd/ entry points. Buffalo is a library. The framework does not ship a binary — user applications create their own main.go that calls buffalo.New(opts) to obtain an App and then call app.Serve().

The only binary-adjacent artifact is the Docker test image (Dockerfile) which runs go test ./... against the framework source itself.

Package organization#

  • Internal packages:

    • internal/env — wraps godotenv for .env file loading; used by App.New() at startup
    • internal/httpx — HTTP utility helpers (content-type detection etc.)
    • internal/meta — reads config/buffalo-app.toml or auto-detects database.yml for plugin context
    • internal/nulls — null-safe wrappers for primitive types (used in binding)
    • internal/templates — embedded error page HTML (dev vs. prod error rendering)
  • Public packages (top-level siblings):

    • binding — content-type–aware request body binding; dispatches to JSON, form (monoculum/formam), XML, or multipart decoders
    • binding/decoders — registers custom time.Time and null-type decoders
    • mail — full SMTP email stack: composer, MIME encoding, attachments, auth
    • plugins — plugin discovery via buffalo-plugins binary convention; dispatches CLI events to registered plugin binaries
    • plugins/plugcmds — resolves available plugin commands; caches results
    • plugins/plugdeps — pop/database dependency metadata for plugin command decoration
    • render — unified Renderer interface with implementations: HTML (plush), JSON, XML, plain, SSE, download, markdown, JS
    • runtime — exposes build-time version and environment metadata
    • serversServer interface with Simple (plain HTTP) and TLS implementations; wraps net/http.Server
    • workerWorker interface for background jobs; Simple implementation runs jobs in goroutines
  • Layering: The root buffalo package is the integration layer. It depends on binding, render, servers, worker, and plugins but these sub-packages are deliberately kept independent of each other (no cross-imports between siblings). This is closer to a hexagonal / ports-and-adapters style: the App at the centre, with render, worker, servers, and binding as exchangeable adapters.

Build system#

  • Build tool: None (no Makefile). Standard go build/go test.
  • Key targets: go test -tags "sqlite integration_test" -cover -race ./...
  • CI: GitHub Actions using a shared gobuffalo/.github reusable workflow (standard-go-test.yml) plus govulncheck on every push/PR.
  • Docker: Yes — Dockerfile is a test runner image (not a production binary image). Also Dockerfile.build and Dockerfile.slim.build for CI matrix variants. Multi-stage is not used; the Dockerfile installs deps and runs tests only.

Notable structural decisions#

  1. Library at root, no cmd/. Buffalo ships as a pure library. This means no cobra CLI wiring in the framework itself — the spf13/cobra dependency is used only in the plugins/ subsystem to model plugin commands, not to build a buffalo binary. Users own their own main.go.

  2. Home struct in-progress extraction. app.go and home.go reveal an in-flight refactor (marked #road-to-v1 TODOs): routing state (Middleware, ErrorHandlers, router, filepaths) is being moved from App into a new Home struct to cleanly separate “application lifecycle” from “routing group concerns.” This architectural tension is visible in the code — App embeds Home and has temporary bridging fields (root, appSelf, children).

  3. Embedded error templates. internal/templates/ holds HTML error pages embedded at compile time (fs.go at root uses embed). The framework ships its own error rendering without runtime file system dependencies — a deliberate distribution convenience.

  4. Plugin system as process boundary. plugins/ implements an unusual pattern: plugin discovery works by running external binaries (buffalo-plugins available) and parsing their JSON output. This allows third-party tools to register themselves as buffalo CLI sub-commands without any Go import-level coupling — a process-level plugin architecture rare in Go frameworks.

  5. Render as standalone subsystem. The render/ package is self-contained (depends on plush, but not on buffalo.App) and implements a Renderer interface that routes through different output formats. This clean boundary means the render subsystem could theoretically be used without the rest of buffalo — a good indicator of sound modular design.