Application
The core module is the foundation of every oryn application: the fx runtime, configuration, OpenTelemetry and logging, and health checks.
go get github.com/go-oryn/oryn/coreCreate and run an application
func main() { core.New( core.WithConfigFS(configs.FS), httpserver.Module, users.Module, ).Run(httpserver.Serve())}-
core.New(options...)returns acore.Appmade of the core module and the given fx options: modules, constructors (fx.Provide), registrations (httpserver.AsHandler…). -
core.WithConfigFS(fsys)loads the configuration files offsys, see Configuration. -
Run(options...)starts the application with the extra options, blocks untilSIGINTorSIGTERM, then stops it gracefully. It exits the process if the application fails to start. -
RunOnce(options...)starts the application then stops it right away, for one-shot commands whose work is done by start hooks, likesql.Migrate(). It exits the process on failure.
Options passed to Run are the things that run with the application, like servers: httpserver.Serve(), health.Serve(). Modules never start anything by themselves, so tests can use the same modules without opening ports.
core.App is immutable: WithOptions(options...) and WithContext(ctx) return a copy, so an App can be shared, for example between main and the tests.
| Method | Description |
|---|---|
New(options...) |
Creates the application |
WithOptions(options...) |
Returns a copy with more fx options |
WithContext(ctx) |
Returns a copy using ctx as base context, injected as context.Context |
Build(options...) |
Returns the *fx.App, without starting it |
Run(options...) |
Builds, starts, and waits for a shutdown signal |
RunOnce(options...) |
Builds, starts, then stops right away |
Options(options...) |
Returns all the fx options, to build an fx.App yourself |
Several entrypoints
Share one App between several commands, each running it with its own options. For example, with one main package per command in cmd/:
// app.go: the application, shared by the commands and the tests.var App = core.New(core.WithConfigFS(configs.FS), httpserver.Module, sql.Module, sql.AsMigrations(migrations.FS), users.Module)// cmd/serve/main.go: go run ./cmd/servefunc main() { app.App.Run(health.Serve(), httpserver.Serve())}// cmd/migrate/main.go: go run ./cmd/migratefunc main() { app.App.RunOnce(sql.Migrate())}The demo application is organized this way.
Organize the code in modules
Group the code of a feature in a module: a fx.Options (or fx.Module) value with its constructors and registrations.
package users
var Module = fx.Options( fx.Provide(NewRepository), httpserver.AsHandlerGroup("/users", httpserver.WithHandler("GET /{id}", NewGetUserHandler), httpserver.WithHandler("POST /{$}", NewCreateUserHandler), ), health.AsProbe(NewRepositoryProbe, health.Readiness),)Extension points always follow the same pattern: an AsXxx helper registers a value, or a constructor whose parameters are injected by fx. Registrations are independent from each other: their order only matters where the documentation says so (middlewares, config options), and it is then the registration order.
Provided dependencies
The core module provides, ready to be injected in any constructor:
| Type | Package | What it is |
|---|---|---|
*config.Config |
core/config |
The configuration |
*slog.Logger |
log/slog |
The application logger, sent to OpenTelemetry |
otel.Telemetry |
core/otel |
The logger, tracer and meter, in one dependency |
trace.Tracer, metric.Meter |
OpenTelemetry API | Named after app.name |
trace.TracerProvider, metric.MeterProvider, log.LoggerProvider |
OpenTelemetry API | For instrumentation libraries |
propagation.TextMapPropagator |
OpenTelemetry API | Context propagation |
*resource.Resource |
OpenTelemetry SDK | The resource describing the application |
*health.Checker |
core/health |
Runs the health probes |
context.Context |
context |
The base context, see WithContext |
type GreetService struct { telemetry otel.Telemetry}
func NewGreetService(telemetry otel.Telemetry) *GreetService { return &GreetService{telemetry: telemetry}}
func (s *GreetService) Greet(ctx context.Context, name string) string { ctx, span := s.telemetry.Tracer().Start(ctx, "greet") defer span.End()
s.telemetry.Logger().InfoContext(ctx, "greeting", "name", name)
return "hello " + name}At debug log level, the fx events (provided constructors, invoked functions, lifecycle hooks) are logged too.
