Unified documentation: spiceframework.dev/examples/petclinic.
Spice Petclinic is the standalone reference application for the
Spice Framework. It is a
behavior-first Go port of
Spring Petclinic at
commit f182358d02e4a68e52bdbabf55ca7800288511e7.
This repository is intentionally a real consuming module. Its go.mod pins
the independently versioned Spice core, standalone toolchain, and database
starters, authorizes the CLI and annotation tool through standard Go tool
directives, and contains no local replace. Core owns runtime and annotation
descriptor APIs; toolchain owns compilation, generation, verification, and
development executables. A clone builds, tests, generates, and runs without a
Spice monorepo checkout.
Petclinic exercises a complete, inspectable Spice application rather than a toy dependency-injection graph:
- owners, pets, visits, pet types, veterinarians, and specialties;
- deterministic validation and stable query ordering;
- explicit generated interface bindings and direct constructor calls;
- typed
@ConfigurationPropertiesand package-owned@Configurationclasses whose@Beanmethods are invoked directly by generated Go; - explicit, cycle-free Modulith roots for application assembly, domain, presentation, persistence, owners, veterinarians, and system behavior;
- dependency-first startup, reverse cleanup, and graceful HTTP shutdown;
- complete owner, pet, visit, veterinarian, and welcome HTTP workflows;
- responsive embedded views, localization, security headers, and RFC 9457 problem responses;
- loopback-only generated management endpoints;
- a zero-network, concurrency-safe in-memory application target;
- separate PostgreSQL 18 and MySQL 8.4 application graphs with module-owned migrations, secure defaults, transactions, and real integration tests.
The application source uses one named domain type per file where practical.
Generated code is committed beneath internal/spicegen/<target> and mirrors
the handwritten package/file ownership in sources/. Target-wide contracts,
providers, lifecycle, features, and HTTP wiring are split into named files.
.spice/*.manifest.json records exact source relationships and SHA-256
ownership, so generation rejects manual edits and stale foreign output.
Each target also embeds its validated module canvas in the management
/actuator/modules response. Module-owned configuration and lifecycle metadata
are derived from the same graph rather than inferred at runtime.
Use the standard Go debugger on the complete package. The generated calls are
ordinary Go: there is no runtime reflection container, package scan, or hidden
service locator. spice generated --source <file> maps handwritten source to
its generated unit when deeper graph debugging is useful.
- Go 1.26.5 exactly.
- No database for the default in-memory target.
- PostgreSQL 18 or MySQL 8.4 only for the corresponding integration workflow.
Fetch explicitly selected dependencies once, then verify offline:
make bootstrap
make verify
On Windows without make, use the equivalent commands:
go mod tidy -diff
go -C tools mod tidy -diff
go mod download
go -C tools mod download
go mod vendor
go run ./internal/qualitygate
The two online tidy checks prime the complete selected closure without changing
the module files, including transitive test-only modules. The quality gate then
forces GOPROXY=off so verification cannot hide a missing bootstrap dependency
behind a network download.
The cross-platform quality gate enforces formatting, tidy/vendor reproducibility, vet, allowlisted linting, nil safety, security and vulnerability analysis, shuffled/race tests, at least 85% business-source coverage, vendor-offline builds, generated freshness, and executable checks for all three targets.
Petclinic records explicit minimum and current boundaries for independently
versioned Spice core and toolchain modules.
spice-compatibility.json pins those boundaries;
they may coincide when a source-contract change raises the application floor.
make compatibility verifies each distinct pair through normal Go module integrity, then
uses an isolated vendor-backed mirror to vet, race-test, verify, generation-
checks, and builds all three targets without changing the checkout. The complete
contract and update policy are documented in
docs/compatibility.md. make verify always includes
this proof; CI publishes the minimum and current evidence as separate jobs.
For an equivalent warm body-edit comparison against Spring Petclinic, first prime the reference checkout's Maven dependencies and then run:
make benchmark-spring SPRING_PETCLINIC=<checkout>
The repository-owned manifest fixes the Spring commit, Spring Boot and Java versions, warmups, samples, p90 budgets, and exact source files. The comparison runs Maven offline and restores both edited sources even when a build fails.
The root target uses only memory and has no external I/O requirement:
go tool github.com/spice-framework/toolchain/cmd/spice generate --check --target Petclinic .
go tool github.com/spice-framework/toolchain/cmd/spice run --target Petclinic . -- -check
Inspect the exact architecture graph without starting the application:
go tool github.com/spice-framework/toolchain/cmd/spice modules --format=json .
go tool github.com/spice-framework/toolchain/cmd/spice modules --format=mermaid ./cmd/postgres
go tool github.com/spice-framework/toolchain/cmd/spice modules --format=plantuml ./cmd/mysql
The acceptance suite requires all three graphs to contain exactly seven assigned modules, no cycles, and no unassigned packages.
Start the web application in PowerShell:
$env:SPICE_PETCLINIC_ADDRESS = "127.0.0.1:8080"
go tool github.com/spice-framework/toolchain/cmd/spice run --target Petclinic .Or in a POSIX shell:
export SPICE_PETCLINIC_ADDRESS=127.0.0.1:8080
go tool github.com/spice-framework/toolchain/cmd/spice run --target Petclinic .Open http://127.0.0.1:8080/. Generated /actuator/* routes accept only a
direct loopback peer and do not trust forwarding headers.
For the watched development loop:
go tool github.com/spice-framework/toolchain/cmd/spice dev --target Petclinic .
An invalid annotation produces the same source-positioned compiler/LSP
diagnostic and preserves the last-known-good process. Fixing and saving causes
guarded regeneration and a graceful complete-package restart; source always
retains valid-Go // @... comments.
PostgreSQL generation and execution:
go tool github.com/spice-framework/toolchain/cmd/spice generate --check --target Postgres ./cmd/postgres
$env:SPICE_PETCLINIC_POSTGRES_URL = "postgres://petclinic:petclinic@127.0.0.1:5432/petclinic?sslmode=disable"
$env:SPICE_PETCLINIC_POSTGRES_ALLOW_INSECURE = "true"
go tool github.com/spice-framework/toolchain/cmd/spice run --target Postgres ./cmd/postgresReal PostgreSQL repository test:
$env:SPICE_POSTGRES_TEST_URL = "postgres://petclinic:petclinic@127.0.0.1:5432/petclinic?sslmode=disable"
go test -tags=integration -count=1 ./postgresMySQL generation and execution:
Petclinic consumes the independently versioned
starter-mysql module for
pool construction, secure configuration, and connection validation. The CI
workflow pins the MySQL 8.4.11 image by immutable digest.
go tool github.com/spice-framework/toolchain/cmd/spice generate --check --target Mysql ./cmd/mysql
$env:SPICE_PETCLINIC_MYSQL_URL = "mysql://petclinic:petclinic@127.0.0.1:3306/petclinic?tls=disable"
$env:SPICE_PETCLINIC_MYSQL_ALLOW_INSECURE = "true"
go tool github.com/spice-framework/toolchain/cmd/spice run --target Mysql ./cmd/mysqlReal MySQL repository test:
$env:SPICE_MYSQL_TEST_URL = "mysql://petclinic:petclinic@127.0.0.1:3306/petclinic?tls=disable"
go test -tags=integration -count=1 ./mysqlVerified TLS is the production default for both database targets. Disabled TLS requires the explicit local-development opt-in shown above. MySQL migrations are advisory-locked, checksum-verified, idempotent, and resumable because MySQL DDL cannot truthfully provide cross-statement transactional behavior.
| Method | Path | Purpose |
|---|---|---|
GET |
/ |
Welcome page |
GET |
/owners/find |
Owner search form |
GET |
/owners |
Prefix search and paginated results |
GET, POST |
/owners/new |
Create an owner |
GET |
/owners/{ownerId} |
Owner, pets, and visits |
GET, POST |
/owners/{ownerId}/edit |
Edit an owner |
GET, POST |
/owners/{ownerId}/pets/new |
Add a pet |
GET, POST |
/owners/{ownerId}/pets/{petId}/edit |
Edit a pet |
GET, POST |
/owners/{ownerId}/pets/{petId}/visits/new |
Add a visit |
GET |
/vets.html |
Paginated veterinarian view |
GET |
/vets |
Stable veterinarian JSON collection |
GET |
/actuator/* |
Generated loopback-only management |
Framework documentation: getting started, generated-code architecture, and developer loop.