package main // Fx с ОБЩИМИ интерфейсами — чистый и простой пример. // // Сравните: // cmd/fx-di/main.go — consumer-defined interfaces (Go-идиома) → 330 строк // cmd/fx-di-shared/ — shared interfaces (Java-подход) → вот этот файл // // Разница — только в подходе к определению интерфейсов. // Когда один тип → один интерфейс, Fx работает без fx.Out/fx.In/named-тегов. // // Это НЕ рекомендация так писать на Go. Это демонстрация того, // под какую архитектуру Fx был спроектирован. // // Lifecycle: // Fx управляет жизненным циклом компонентов через хуки OnStart/OnStop. // При запуске — вызывает OnStart в порядке зависимостей (сначала DB, потом сервисы). // При остановке — вызывает OnStop в обратном порядке (сначала сервер, потом DB). // Отправьте SIGINT (Ctrl+C) чтобы увидеть порядок остановки в логах. // // Хотите увидеть как это масштабируется на несколько команд? // Смотрите cmd/fx-di-modules/ — тот же код, но каждый домен в своём файле. import ( "context" "log/slog" "net" "net/http" "go.uber.org/fx" ) func main() { fx.New( fx.Provide( newDB, newCache, newEventBus, newUserRepo, newSessionRepo, newNotificationRepo, newAuthService, newUserService, newNotificationService, newHandler, ), fx.Invoke(startHTTPServer), // Вот и всё. 10 провайдеров, 1 invoke. Fx разрулил сам. // Потому что один тип → один интерфейс → нет неоднозначности. // // А lifecycle хуки (OnStart/OnStop) — внутри провайдеров newDB, newCache // и startHTTPServer. Fx сам вызовет их в правильном порядке. ).Run() } func startHTTPServer(lc fx.Lifecycle, h *handler) { srv := &http.Server{ Addr: ":8080", Handler: h.routes(), } lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { ln, err := net.Listen("tcp", srv.Addr) if err != nil { return err } slog.Info("HTTP-сервер запущен", "addr", srv.Addr) go srv.Serve(ln) return nil }, OnStop: func(ctx context.Context) error { slog.Info("HTTP-сервер останавливается...") return srv.Shutdown(ctx) }, }) }