Skip to content

Architecture

goboot is a code generator, not a runtime container. Work flows through a fixed pipeline; each stage is a package decoupled from the next by an intermediate model.

annotate → scan → analyze → model → graph → generate → plain Go
  1. annotation/ — lexer + parser for the @Name(arg=value, ...) comment language. Each annotation has a registered schema (valid targets, argument types, defaults) used for validation. Parsers never panic on arbitrary input.
  2. compiler/ — loads packages via go/packages, associates comments with declarations, and does type analysis with go/types. Type matching uses types.Implements (checking both T and *T) — never string comparison.
  3. model/ — the intermediate Application model (components, controllers, routes, repositories, advice, …). It depends on no concrete router or DB.
  4. graph/ — the dependency graph (consumer → dependency), topological sort for construction order, cycle detection, reverse-order shutdown.
  5. generator/di/ — emits Go source, then runs go/format + imports. Output is deterministic and written atomically.
  6. runtime/ — the small set of reusable abstractions the generated code depends on (lifecycle, HTTP binding, Problem errors, transactions, observability). It is not a container.

Non-negotiable invariants

Invariant Meaning
Compile-time only No runtime package scanning or global service locator.
Determinism Same input → byte-identical output. Generators sort everything and avoid timestamps.
Type safety via go/types Interface satisfaction and dependency compatibility are decided by the type checker.
Constructor injection Prefer NewXxx(...) *Xxx / (*Xxx, error) / interface returns.
Diagnostics, not panics User/annotation errors surface as source-positioned Diagnostics with stable GOB* codes.

Generated output

Generated files carry // Code generated by goboot. DO NOT EDIT., use the zz_goboot_*.gen.go naming, and land in your configured output package (default internal/generated). They are meant to be committed and read — there is no magic at runtime.

goboot generate removes its own prior output before loading, so a breaking interface change never blocks its own regeneration.