Unified documentation: spiceframework.dev/examples/commerce.
Commerce is Spice's standalone production-shaped reference application. It uses six explicit application modules:
inventoryowns stock and compensated reservations;paymentsowns authorization policy and records approvals;notificationsowns typed receipt composition and its selected mail transport;ordersdeclares and uses explicit inventory, notification, payment, and storage APIs;storageowns typed order persistence and module-owned migrations;platformowns the safely configured HTTP server lifecycle and depends on storage readiness.
The ordinary main.go is the compile-time application marker. Its direct blank
imports name the same-module source packages that belong to the application;
the imports are valid Go, visible to tools and reviewers, and replace repeated
CLI package lists or compiler package scanning. Its explicit
@management.Enable allowlist exposes health, liveness, readiness, info, and
metrics plus redacted configuration and generated module reports;
@observability.Logging installs structured lifecycle and HTTP observers. The
inventory module's @schedule.FixedDelay audit demonstrates direct generated,
lifecycle-owned scheduled work. Its @async.Execute inventory verification
demonstrates a readiness-gated typed generated submit method, bounded
admission, and graceful drain before provider cleanup. The placement route's
@data.Transactional boundary passes the generated transaction-owned
data.Executor directly into the repository. Generated @security.Authorize
guards require exact orders:write, orders:read, and orders:notify scopes
on order routes and fail closed with safe 401/403 problems. The read route also
proves the compiler-validated restricted expression contract through explicit
authenticated and hasScope symbols. The public catalog
route's @cache.Cacheable boundary demonstrates configured, bounded, typed
response caching without putting principal-specific data in a shared cache.
Every successful persisted-order lookup publishes a typed OrderViewed event
to the provider-owned ViewAudit listener. Spice generates ordinary direct
construction, command, lifecycle, scheduling, asynchronous, cache, event,
migration, repository, transaction, authorization, and HTTP code under the
owned generated target package. Source-mirror files retain a deterministic
one-source-file-to-one-generated-file relationship; no runtime scan,
reflection, service locator, repeated CLI source list, or marker execution is
used.
The payment module exposes two explicit payments.Processor candidates.
Service is named, qualified as stripe, and primary; OfflineProcessor is
qualified separately and marked fallback. Both use @Implements(Processor)
and Spice emits source-owned Go assertions. The orders constructor requests
@Qualifier("stripe"), and the generated file passes the already constructed
Stripe service directly as the interface—there is no runtime lookup.
Notifications applies the same rule to an external framework interface:
Delivery uses @Implements(mail.Sender) and a generated Go assertion, while
Notifier requests the exact interface. Generated code constructs and passes
the concrete delivery directly. SystemClock is a second explicit interface
binding, keeping message dates caller-owned and deterministic in tests.
Commerce is a real consuming Go module. Its go.mod authorizes the standalone
Spice annotation, CLI, and style tools, pins immutable core, toolchain,
PostgreSQL, and SMTP starter pseudo-versions without local replacements, and owns its
generated target, manifest, acceptance tests, and vendor tree. From this
directory:
go tool github.com/spice-framework/toolchain/cmd/spicestyle --config=.spice/style.json ./...
go tool github.com/spice-framework/toolchain/cmd/spice generate --check --target Commerce .
go tool github.com/spice-framework/toolchain/cmd/spice build --target Commerce .
go tool github.com/spice-framework/toolchain/cmd/spice run --target Commerce . -- -check
go tool github.com/spice-framework/toolchain/cmd/spice run --target Commerce .
The committed direct Spice core and toolchain requirements form Commerce's
provisional minimum compatibility pair. spice-compatibility.json
also pins a current pair without implying that one module versions the other.
make compatibility resolves both modules through normal Go integrity,
asserts the exact MVS-selected core and toolchain plus all three authorized tool
packages, then runs vet, shuffled race tests, spice verify, generation
freshness, and a Spice build against each paired boundary. It uses isolated
alternate modfiles and fails if any handwritten, generated, module, manifest,
artifact, or vendor byte changes. See
docs/compatibility.md for the complete contract.
The application code follows the canonical Spice standard as an enforced
contract, not an aspiration: one owning type per ordinary production file,
methods colocated with that type, explicit constructors and managed scopes,
no loose package helpers, and no mutable package state. The strict policy and
its narrow public-sentinel/constant-carrier boundaries are documented in
docs/code-style.md.
Go 1.26.5 is exact. make verify (or
go run ./internal/qualitygate on every platform) runs the complete local
quality gate, including strict application style, deterministic generation,
offline vendor execution, security analysis, race tests, and the executable
zero-network workflow.
The definitive local gate always includes both paired core/toolchain
boundaries; CI publishes the same minimum/current evidence as parallel jobs.
The server binds 127.0.0.1:8081 by default. Set
SPICE_COMMERCE_ADDRESS=127.0.0.1:0 for an ephemeral test listener. The command
uses the conventional SPICE_ environment source. Set
SPICE_SHUTDOWN_TIMEOUT to override the typed 10s shutdown default. Reusable
generated constructors read no environment, files, or process signals on their
own. Public catalog caching defaults to 256 entries and a five-minute TTL;
SPICE_CACHE_COMMERCE_CATALOG_CAPACITY and
SPICE_CACHE_COMMERCE_CATALOG_TTL override those generated typed
properties. Asynchronous execution defaults to 16 concurrent tasks;
SPICE_ASYNC_MAX_CONCURRENCY overrides that positive bound.
Database configuration is typed and secret-redacted. The default
memory://commerce URL selects an instance-owned transaction-aware
database/sql connector so spice dev needs no external service. Set
SPICE_COMMERCE_DATABASE_URL to a complete PostgreSQL URL to use the reviewed
pgx starter; local sslmode=disable additionally requires the explicit
SPICE_COMMERCE_DATABASE_ALLOW_INSECURE=true opt-in. The database opens without
network I/O during construction. Its module-owned migration runs as the first
lifecycle hook, and the HTTP server has an explicit dependency on the database
bean, so traffic cannot start against an unreconciled schema. The
integration-tagged storage test proves a committed order survives closing and
reopening the PostgreSQL pool:
SPICE_TEST_POSTGRES_URL=postgres://... go test -tags=integration -run PostgreSQLPersistence ./storage
Mail configuration is typed and instance-owned. test is the default
transport: it performs no network I/O and retains a bounded decoded snapshot
for tests. POST /orders/{id}/receipt runs only after the order transaction has
committed, creates deterministic plain-text MIME plus a receipt attachment,
and returns only the message ID. Set
SPICE_COMMERCE_MAIL_TRANSPORT=smtp with
SPICE_COMMERCE_MAIL_SMTP_ADDRESS, optional server name, and paired username
and password to use the secure SMTP starter. SMTP requires verified STARTTLS
by default (or explicit implicit-tls), authenticates only after TLS, observes
caller cancellation and a typed timeout, retries only safe pre-DATA transient
failures, and never replays ambiguous delivery. Recipient and credential
configuration is secret-redacted.
For the local spice dev walkthrough only, set
SPICE_COMMERCE_DEVELOPER_TOKEN to a 16-byte-or-longer bearer token. The
reference platform accepts that token only while the server binds a loopback
address and attaches a fixed developer principal with the three documented
order scopes. The token is disabled by default, compared in constant time, and
secret-redacted. It is not a production authentication mechanism; production
applications compose the OAuth2/OIDC authentication starter ahead of the same
generated authorization guards.
The generated public API is:
GET /catalog, the public cache-safe product;POST /orderswith a strict{"quantity": 2}JSON body andorders:write;GET /orders/{id}withorders:read;POST /orders/{id}/receiptwithorders:notify;- deterministic RFC 9457 errors for invalid, unavailable, declined, and missing orders;
/actuator/health,/actuator/health/liveness,/actuator/health/readiness,/actuator/info, and/actuator/metrics./actuator/configprops, with generated key/type/module/provenance metadata and mandatory secret redaction./actuator/modules, with the generated module/API/dependency canvas and unassigned-package report.
The generated OpenAPI 3.1 contract is
internal/spicegen/commerce/artifacts/openapi.json. Route metrics use compiler-owned
method, pattern, symbol, and module labels rather than raw request paths.
The handwritten main.go explicitly imports the generated commerce target and
only calls os.Exit(spiceapp.Main(os.Args[1:])). Generated Main returns the
exit code and owns signals; it never exits the process itself. Tests and
embedded processes can instead use RunCommand, NewApplication,
NewApplicationWithOptions, Start, Stop, Run, or the typed Components
snapshot with caller-owned writers, loggers, sources, contexts, observers,
middleware, error mapping, and shutdown policy. The ready application exposes
SubmitServiceVerifySKU(admissionContext, sku) and AsyncSnapshot() as its
typed asynchronous boundary.
TestCommerceDeveloperProof in main_test.go is the executable vertical
proof. It uses
spicetest.NewHTTP to construct the real generated application, authenticates
verified principals in caller middleware, proves allowed/unauthenticated/
insufficient-scope decisions, places an order transactionally, retrieves the
persisted record, delivers its test receipt, exercises public caching, and
inspects management metadata through a bounded loopback-only slice. The
notifications tests inspect the exact decoded message, attachment, envelope,
cancellation, and sanitized delivery failures.
The complete edit/save/restart/HTTP walkthrough and its automated evidence map are documented in the Spice developer proof.