pkg/, named after the domain it owns: pkg/rockets, pkg/launches, pkg/telemetry. A package holds everything the feature needs — its models, business logic, database access, and routes — organized into a small set of conventional files.
You may scaffold a new package with the CLI, which creates the essentials and registers the package with the app:
The Conventional Files
Not every package needs every file; start small and add files as the feature grows.The Single-Struct Rule
svc.go, router.go, queries.go, and client.go each contain exactly one struct, its constructor, and its methods. This keeps every file’s purpose obvious: when you open svc.go, you are looking at the feature’s business logic and nothing else. If a second service is growing inside a package, that is usually a sign it wants to be its own package.
Services
Business logic lives in theSvc struct, constructed like every other Copper component with a params struct:
pkg/rockets/svc.go
Queries directly; a service earns its place as soon as there are rules to enforce.
If other packages depend on your service, consider defining a
Svc interface and returning it from NewSvc, keeping the implementation unexported. A hand-written mock in a rocketstest sibling package then drops in anywhere the interface is used — see Testing.Utilities
utils.go is for pure functions: helpers that take arguments and return results, with no injected dependencies and no state. Anything that needs a logger, a querier, or config belongs on one of the structs instead.
Wiring It Together
Each constructor is registered in the package’swire.go, and the module is added to pkg/app/wire.go (the scaffold does this for you):
pkg/rockets/wire.go