diff --git a/VERSION b/VERSION index 8df3f459..c9ec1d54 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.33.1 +0.33.2 diff --git a/cmd/oikos/main.go b/cmd/oikos/main.go index 9308794b..10dc2edc 100644 --- a/cmd/oikos/main.go +++ b/cmd/oikos/main.go @@ -1,6 +1,7 @@ package main import ( + "time" "context" "fmt" "log/slog" @@ -11,6 +12,7 @@ import ( "syscall" "github.com/dtoro/oikos/internal/config" + "github.com/dtoro/oikos/internal/core/app" "github.com/dtoro/oikos/internal/adapters/postgres" "github.com/dtoro/oikos/internal/execworker" "github.com/dtoro/oikos/internal/httpapi" @@ -109,8 +111,13 @@ func main() { go schedulerRunner(ctx, pool, cfg) go execWorkerRunner(ctx, pool, cfg) + // Build composition-root dependencies (ADR 0016). + entityRepo := db.NewEntityRepo(pool) + readModels := db.NewEntityReader(pool) + entities := app.NewEntityService(entityRepo, db.NewOntologyRepo(pool, time.Minute)) + slog.Info("all: starting api with scheduler + execution-worker in background") - if err := httpapi.ListenAndServe(ctx, pool, cfg); err != nil { + if err := httpapi.ListenAndServe(ctx, pool, cfg, entities, entityRepo, readModels); err != nil { slog.Error("api failed", "error", err) os.Exit(1) } @@ -288,7 +295,12 @@ func runAPI(ctx context.Context, cfg config.Config) error { return fmt.Errorf("migrations: %w", err) } - err = httpapi.ListenAndServe(ctx, pool, cfg) + // Composition root — build the service dependencies (ADR 0016, plan §3.5). + entityRepo := db.NewEntityRepo(pool) + readModels := db.NewEntityReader(pool) + entities := app.NewEntityService(entityRepo, db.NewOntologyRepo(pool, time.Minute)) + + err = httpapi.ListenAndServe(ctx, pool, cfg, entities, entityRepo, readModels) if err == http.ErrServerClosed { return nil } diff --git a/internal/httpapi/api_test.go b/internal/httpapi/api_test.go index 0721555d..fb0346de 100644 --- a/internal/httpapi/api_test.go +++ b/internal/httpapi/api_test.go @@ -14,9 +14,11 @@ import ( "os" "strings" "testing" + "time" "github.com/dtoro/oikos/internal/config" "github.com/dtoro/oikos/internal/adapters/postgres" + "github.com/dtoro/oikos/internal/core/app" "github.com/jackc/pgx/v5" ) @@ -93,7 +95,9 @@ func newTestHandler(t *testing.T, cfg config.Config) http.Handler { } } - return NewHandler(handlerCtx, pool, cfg) + repo := db.NewEntityRepo(pool) + onto := db.NewOntologyRepo(pool, time.Minute) + return NewHandler(handlerCtx, pool, cfg, app.NewEntityService(repo, onto), repo, db.NewEntityReader(pool)) } // testAuthToken is the static bearer token devConfig() configures. There is diff --git a/internal/httpapi/server.go b/internal/httpapi/server.go index 00de922d..89c19ab2 100644 --- a/internal/httpapi/server.go +++ b/internal/httpapi/server.go @@ -77,17 +77,17 @@ type Server struct { // holds a dedicated pooled connection for LISTEN. Callers MUST cancel ctx // before closing the pool — otherwise the held connection never releases // and pool.Close() deadlocks. -func NewHandler(ctx context.Context, pool *db.Pool, cfg config.Config) http.Handler { +func NewHandler(ctx context.Context, pool *db.Pool, cfg config.Config, entities *app.EntityService, entityRepo *db.EntityRepo, readModels ports.ReadModels) http.Handler { s := &Server{ pool: pool, cfg: cfg, entityCache: db.NewEntityCache(60 * time.Second), sseBroker: newSSEBroker(10000), sseSubs: make(map[*sseSubscriber]struct{}), + entities: entities, + entityRepo: entityRepo, + readModels: readModels, } - s.entityRepo = db.NewEntityRepo(pool) - s.entities = app.NewEntityService(s.entityRepo, db.NewOntologyRepo(pool, time.Minute)) - s.readModels = db.NewEntityReader(pool) // Wire secrets backend: Infisical primary with SOPS DR fallback. if cfg.InfisicalSiteURL != "" { @@ -935,10 +935,10 @@ main(); // ListenAndServe runs the API server with graceful shutdown on ctx cancel // (SG4): stop accepting, drain in-flight for up to 30s, then exit. -func ListenAndServe(ctx context.Context, pool *db.Pool, cfg config.Config) error { +func ListenAndServe(ctx context.Context, pool *db.Pool, cfg config.Config, entities *app.EntityService, entityRepo *db.EntityRepo, readModels ports.ReadModels) error { srv := &http.Server{ Addr: cfg.APIListen, - Handler: NewHandler(ctx, pool, cfg), + Handler: NewHandler(ctx, pool, cfg, entities, entityRepo, readModels), ReadHeaderTimeout: 10 * time.Second, }