Pop — Structure#
Layout pattern#
Custom / Flat Root + Sub-packages for Concerns
Pop does not follow the canonical Standard Go Layout (cmd/, internal/, pkg/). Instead, the entire ORM core lives flat in the root package (github.com/gobuffalo/pop/v6), with a handful of well-defined sub-packages for cross-cutting concerns (associations, columns, slices, logging) and a soda/ directory that acts as a self-contained CLI application. The internal/ directory contains only two tiny utility packages. This flat-root style is common in library-first projects where the root package is the primary public API.
Directory map#
repositories/pop/
├── *.go # Root package: core ORM (connection, query, model, dialects, migrator)
├── associations/ # Association logic (belongs_to, has_many, has_one, many_to_many)
├── columns/ # Struct reflection helpers: mapping struct fields → SQL columns
├── fix/ # Test fixture loader (anko scripting + auto-timestamp controls)
├── genny/ # Code generation templates (config, fizz migrations, model scaffolding)
│ ├── config/ # - database.yml template
│ ├── fizz/ # - Fizz migration file templates (empty/table variants)
│ └── model/ # - Go model scaffolding templates
├── internal/ # Private utilities
│ ├── defaults/ # - String/int default value helpers
│ └── randx/ # - Random string generation
├── logging/ # Logging constants (log level definitions)
├── slices/ # Typed slice helpers (string, int, float, UUID, map) for scan targets
├── soda/ # CLI binary (database management tool)
│ ├── main.go # - Entry point
│ └── cmd/ # - Cobra command implementations
│ ├── root.go # · Root command, env/config flags
│ ├── create.go # · db create
│ ├── drop.go # · db drop
│ ├── migrate.go # · db migrate (up/down/status/reset)
│ ├── fix.go # · db fix
│ ├── schema.go # · db schema (dump/load)
│ ├── version.go # · version
│ ├── generate/ # · generate subcommands (config, fizz, model, sql)
│ └── schema/ # · schema subcommands (dump, load)
├── testdata/ # Migration files and model fixtures for tests
│ ├── migrations/ # - Fizz and SQL migration examples
│ └── models/ # - Generated model fixtures (a, b, ac, bc)
├── Makefile # Build targets: install, build, test, lint, release
├── .goreleaser.yml # Cross-platform release config (soda binary)
├── docker-compose.yml # Local DB containers for test suite
├── database.yml # Default database config used in tests
└── go.mod / go.sumEntry points#
| Binary | Source | Purpose |
|---|---|---|
soda | soda/main.go → soda/cmd.Execute() | CLI tool for database lifecycle: create, drop, migrate, generate models/migrations, dump/load schema |
The library itself (github.com/gobuffalo/pop/v6) has no main.go; it is consumed as a library. The doc.go file in the root provides the package-level godoc.
Package organization#
Internal packages:
internal/defaults— zero-value default helpers (e.g.,defaults.String(val, fallback))internal/randx— random string generation used in migration naming
Public packages (pkg/): Pop uses no
pkg/directory; all exported packages are at top-level sub-directories:associations— Association metadata types and loaders (BelongsToAssociation,HasManyAssociation,HasOneAssociation,ManyToManyAssociation) used by the root package’s preload logiccolumns— Struct-to-column reflection:Columns,Column,ReadableColumns,WriteableColumns; handlesdb:struct tagsslices— Typed scan-target slices (String,Int,Float64,UUID,Map) that implementsql.Scannerfor multi-row result collectionlogging— Single file defining log level constantsfix— Test fixture loading subsystem with Anko scripting support and auto-timestamp overridegenny— Code generation templates and runners for config/migration/model scaffolding (used bysoda generate)
Layering: The architecture is hub-and-spoke rather than layered. The root package is the hub — it imports all sub-packages (
associations,columns,slices,logging,internal/*). Thesoda/cmdpackage imports the root pop package to call its public API. Sub-packages have no dependencies on each other, keeping the dependency graph acyclic and clean.
Build system#
- Build tool:
make(GNU Make) + GoReleaser for cross-platform distribution - Key targets:
make install— builds and installssodabinary with SQLite build tagmake build—go build ./(library check)make test—go test -tags sqlite ./...make lint—golangci-lint runmake release-dry-run/make release— Docker-based GoReleaser cross-compilation
- Docker:
docker-compose.ymlfor local test databases (PostgreSQL, MySQL, MariaDB, CockroachDB); no application Dockerfile. GoReleaser uses agoreleaser/goreleaser-crossDocker image for CGO cross-compilation of the SQLite-enabledsodabinary. - Build tags:
sqlitetag gates SQLite dialect code (filesdialect_sqlite.go,dialect_sqlite_tag.go), since SQLite requires CGO. The tag is applied consistently viaMakefileand GoReleaser config.
Notable structural decisions#
- Flat root package as the library surface: All core types (
Connection,Query,Model,Migrator,dialect) are in the root package. This simplifies import paths for library consumers — one import gets everything — but it creates a large, dense package (72.gofiles) with broad internal coupling. - Dialect isolation by file, not sub-package: Each SQL dialect is a separate file (
dialect_postgresql.go,dialect_mysql.go, etc.) in the root package rather than a sub-package, keeping them accessible to all root-level code without import cycles. The trade-off is that all dialect code is compiled into every binary even when only one dialect is used (except SQLite, which uses a build tag). soda/as a self-contained CLI subtree: The CLI lives undersoda/with its ownmain.goandcmd/package, cleanly separated from the library. This mirrors patterns seen in projects likemigrateorgoosewhere the library and CLI are co-located but architecturally distinct.genny/for scaffolding templates: Rather than embedding templates in the binary or using a separate repository, code-generation templates live ingenny/as Go files with string constants. This avoidsgo:embedcomplexity (predating Go 1.16 embeds) while keeping templates version-controlled alongside the library.fix/package for fixture management: A dedicated package for loading test fixtures using the Anko scripting engine is unusual. It reflects Pop’s early Buffalo ecosystem integration, where fixture loading was a first-class concern.