// Package app wires the object graph. It is the one place that knows // which concrete types satisfy the application's dependencies, so every // other package can be constructed in a test with substitutes. package app import ( "log/slog" "go.uber.org/fx" "go.uber.org/fx/fxevent" "sneak.berlin/go/simplexcalc/internal/config" "sneak.berlin/go/simplexcalc/internal/database" "sneak.berlin/go/simplexcalc/internal/globals" "sneak.berlin/go/simplexcalc/internal/handlers" "sneak.berlin/go/simplexcalc/internal/logger" "sneak.berlin/go/simplexcalc/internal/middleware" "sneak.berlin/go/simplexcalc/internal/render" "sneak.berlin/go/simplexcalc/internal/server" "sneak.berlin/go/simplexcalc/internal/telemetry" ) // Module is every provider the service needs. Constructor order is // irrelevant to fx; the order here is the order a reader wants: build // metadata, logging, configuration, storage, then the HTTP layer. // //nolint:gochecknoglobals // an fx module is a declaration, not mutable state. var Module = fx.Options( fx.Provide( globals.New, logger.New, config.New, database.New, telemetry.NewSentry, telemetry.NewMetrics, render.New, middleware.New, handlers.New, server.New, ), // fx's own lifecycle events go through the application logger, so // the process emits one stream in one format. Without this, fx // prints its own plain-text output to stderr and a log pipeline // gets two formats from one process. fx.WithLogger(func(l *logger.Logger) fxevent.Logger { return &fxevent.SlogLogger{Logger: l.Get()} }), ) // Invoke forces the graph to be built. fx constructs lazily: without a // request for the Server, a perfectly valid App would start, construct // nothing, and serve nothing. // //nolint:gochecknoglobals // as above. var Invoke = fx.Invoke(func(_ *server.Server, log *logger.Logger, cfg *config.Config) { if cfg.Debug { log.EnableDebugLogging() } log.Identify() }) // New builds the fx application for `serve`. func New(opts ...fx.Option) *fx.App { return fx.New(append([]fx.Option{Module, Invoke}, opts...)...) } // DiscardLogger is a logger that writes nothing, for tests that build // the graph and do not want its startup output. func DiscardLogger() *slog.Logger { return slog.New(slog.DiscardHandler) }