package main import ( "context" "fmt" "log/slog" "net/http" "go.uber.org/fx" ) // ===================================================== // Реализации — конструкторы возвращают интерфейсы. // Один тип → один интерфейс → Fx разруливает автоматически. // // DB и Cache используют fx.Lifecycle для регистрации хуков: // OnStart — пинг/проверка соединения при запуске // OnStop — корректное закрытие при остановке // // Fx сам вызывает хуки в правильном порядке: // Старт: DB → Cache → ... → HTTP-сервер // Остановка: HTTP-сервер → ... → Cache → DB // ===================================================== // --- Инфраструктура --- type dbImpl struct{ dsn string } func (db *dbImpl) Query(query string) error { return nil } func (db *dbImpl) QueryRow(query string) error { return nil } func (db *dbImpl) Exec(query string) error { return nil } func (db *dbImpl) BeginTx() error { return nil } func (db *dbImpl) BulkInsert(query string, args ...any) error { return nil } func (db *dbImpl) Close() error { slog.Info("БД: соединение закрыто") return nil } func newDB(lc fx.Lifecycle) (DB, error) { db := &dbImpl{dsn: "postgres://localhost:5432/demo"} lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { slog.Info("БД: подключение установлено", "dsn", db.dsn) return nil // здесь был бы db.Ping(ctx) }, OnStop: func(ctx context.Context) error { return db.Close() }, }) return db, nil } type cacheImpl struct{} func (c *cacheImpl) Get(key string) (string, error) { return "", nil } func (c *cacheImpl) Set(key, value string) error { return nil } func (c *cacheImpl) Close() error { slog.Info("Кэш: соединение закрыто") return nil } func newCache(lc fx.Lifecycle) Cache { c := &cacheImpl{} lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { slog.Info("Кэш: подключение установлено") return nil // здесь был бы c.Ping(ctx) }, OnStop: func(ctx context.Context) error { return c.Close() }, }) return c } type eventBusImpl struct{} func (b *eventBusImpl) Publish(event string) { slog.Info("событие", "event", event) } func (b *eventBusImpl) Subscribe(event string, handler func()) { slog.Info("подписка", "event", event) } func newEventBus(lc fx.Lifecycle) EventBus { bus := &eventBusImpl{} lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { slog.Info("EventBus: запущен") return nil }, OnStop: func(ctx context.Context) error { slog.Info("EventBus: очередь событий дренирована") return nil }, }) return bus } // --- Репозитории --- type userRepo struct{ db DB } type sessionRepo struct { db DB cache Cache } type notificationRepo struct{ db DB } func newUserRepo(db DB) UserRepository { return &userRepo{db: db} } func newSessionRepo(db DB, cache Cache) SessionRepository { return &sessionRepo{db: db, cache: cache} } func newNotificationRepo(db DB) NotificationRepository { return ¬ificationRepo{db: db} } // --- Сервисы --- type authServiceImpl struct { userRepo UserRepository sessionRepo SessionRepository cache Cache events EventBus } func newAuthService(userRepo UserRepository, sessionRepo SessionRepository, cache Cache, events EventBus) AuthService { return &authServiceImpl{userRepo: userRepo, sessionRepo: sessionRepo, cache: cache, events: events} } type userServiceImpl struct { userRepo UserRepository authService AuthService events EventBus } func newUserService(userRepo UserRepository, authService AuthService, events EventBus) UserService { return &userServiceImpl{userRepo: userRepo, authService: authService, events: events} } type notificationServiceImpl struct { notificationRepo NotificationRepository userService UserService events EventBus } func newNotificationService(notificationRepo NotificationRepository, userService UserService, events EventBus) NotificationService { return ¬ificationServiceImpl{notificationRepo: notificationRepo, userService: userService, events: events} } // --- Handler --- type handler struct { userService UserService authService AuthService notificationService NotificationService } func newHandler(userService UserService, authService AuthService, notificationService NotificationService) *handler { return &handler{userService: userService, authService: authService, notificationService: notificationService} } func (h *handler) routes() http.Handler { mux := http.NewServeMux() mux.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, "ok") }) return mux }