Component Model
Build application code as explicit services, integrations, and runtime extensions.
The component package is how Procyon understands your application graph. It
lets you describe services, repositories, clients, runners, servers, processors,
and framework extensions as constructor-backed components instead of manually
creating everything in main().
Use components when a value has dependencies, lifecycle behavior, or should be available to other parts of the application. A handler can depend on a service, the service can depend on a repository, and the repository can depend on typed configuration without every layer manually wiring the next one.
Most application code follows this flow:
- Register a constructor.
- Express dependencies as constructor parameters.
- Let Procyon create singleton components during context refresh.
- Resolve manually only at runtime boundaries.
- Add lifecycle, processors, custom scopes, or conditions when the component needs startup behavior.
That keeps the application structure visible in code. When something is missing or ambiguous, startup fails with a resolution error instead of hiding the problem behind a nil value or package-level global.
Register components
Turn constructors into named component definitions with singleton or prototype scope.
Wire dependencies
Use constructor parameters, interfaces, and qualifiers to express dependencies.
Resolve components
Resolve by name, type, or collection when code sits at a runtime boundary.
Container
Understand what the runtime container stores and when you should interact with it.
Manage lifecycle
Run initialization, cleanup, and cross-cutting processors around component startup.
Load conditionally
Attach runtime checks so components load only when their environment matches.
Detailed guides
Some component topics are split into smaller guides because the API is usually used in steps. Conditions, for example, start with the concept, then move into writing a custom condition, reading the condition context, and attaching the condition during registration.
Component design
Prefer constructor dependencies over package-level globals. A component should make its requirements visible in the constructor signature:
func NewUserService(repo *UserRepository, mailer Mailer) *UserService {
return &UserService{repo: repo, mailer: mailer}
}This keeps startup errors explicit. If the container cannot find a dependency, the application fails during component creation instead of hiding the problem behind a nil field or global lookup.
What belongs in a component
Good component candidates are values that should be shared, configured, or composed:
- application services
- repositories and data clients
- HTTP controllers and endpoint registries
- command-line runners
- background workers
- configuration-bound factories
- runtime extensions such as context initializers or processors
Small pure functions usually do not need to become components. Keep those as regular Go functions and register the larger objects that own dependencies or runtime behavior.
