package svc import ( "context" "errors" "sync" "example.com/managed/internal/config" "example.com/managed/internal/middleware" "git.yuntuo.hk/k2safe/go-omni/health" "git.yuntuo.hk/k2safe/go-omni/httpx" "git.yuntuo.hk/k2safe/go-omni/lifecycle" "git.yuntuo.hk/k2safe/go-omni/logx" ) // Context owns infrastructure, while Logger is borrowed from main. // Add business repositories here; HTTP contracts do not define database tables. type ServiceContext struct { Lifecycle *lifecycle.Controller Config config.Config cleanups []func() error Logger *logx.Logger Health *health.Checker Middlewares map[string]httpx.Middleware closeMu sync.Mutex closed bool closeOnce sync.Once closeErr error } // Pass the settings loaded by main. The optional form supports lightweight // tests without infrastructure; selected adapters still require valid config. func New(ctx context.Context, logger *logx.Logger, configs ...config.Config) (result *ServiceContext, err error) { if ctx == nil { ctx = context.Background() } if err = ctx.Err(); err != nil { return nil, errors.Join(err, lifecycle.Cause(ctx)) } if len(configs) > 1 { return nil, errors.New("expected at most one configuration") } cfg := config.Default() if len(configs) == 1 { cfg = configs[0] } _ = cfg checker, err := health.New() if err != nil { return nil, err } c := &ServiceContext{Config: cfg, Logger: logger, Health: checker, Middlewares: middleware.Defaults(), Lifecycle: lifecycle.New(ctx)} ctx = c.Lifecycle.Context() defer func() { if err != nil { err = errors.Join(err, c.Close()) if cause := c.Lifecycle.Err(); cause != nil && !errors.Is(err, cause) { err = errors.Join(err, cause) } } }() return c, nil } func (c *ServiceContext) RequestShutdown(err error) { c.Lifecycle.RequestShutdown(err) }