package main // Сломанный антипаттерн: тот же код, но строки переставлены. // // Сценарий: два разработчика параллельно добавляли сервисы, // при мерже строки перемешались. Код скомпилировался. // go build прошёл. CI прошёл (юнит-тесты не трогают main.go). // Деплой. Первый запрос — паника. // // Запустите: go run ./cmd/antipattern-broken/ // curl http://localhost:8080/health → 200 ok (всё «работает») // curl http://localhost:8080/users/me → nil pointer dereference (ПАНИКА) // // /health не трогает зависимости — поэтому проходит. // /users/me вызывает userService.GetProfile() → authService.ValidateToken() → nil! import ( "log/slog" "net/http" "os" "github.com/olezhek28/di-demo/internal/api" "github.com/olezhek28/di-demo/internal/cache" "github.com/olezhek28/di-demo/internal/config" "github.com/olezhek28/di-demo/internal/database" "github.com/olezhek28/di-demo/internal/events" "github.com/olezhek28/di-demo/internal/repository" "github.com/olezhek28/di-demo/internal/service" ) func main() { cfg := config.New() // === Инфраструктура === db, err := database.New(cfg.DSN) if err != nil { slog.Error("не удалось подключиться к БД", "err", err) os.Exit(1) } // === Репозитории и сервисы === // После мержа строки перемешались. Всё компилируется — Go проверяет // только что переменная ОБЪЯВЛЕНА, а не что внутри валидное значение. var ( c cache.Cache userRepo repository.UserRepo sessionRepo repository.SessionRepo notificationRepo repository.NotificationRepo eventBus events.EventBus authService service.AuthService userService service.UserService notificationService service.NotificationService ) userRepo = repository.NewUserRepo(db) notificationRepo = repository.NewNotificationRepo(db) // ⚠️ ПРОБЛЕМА 1: sessionRepo создаётся ДО cache. // cache = nil → sessionRepo хранит nil вместо кэша. sessionRepo = repository.NewSessionRepo(db, c) // c == nil! c = cache.New(cfg.RedisAddr) // поздно — sessionRepo уже создан с nil eventBus = events.NewEventBus() // ⚠️ ПРОБЛЕМА 2: userService создаётся ДО authService. // authService = nil → userService хранит nil вместо сервиса авторизации. userService = service.NewUserService(userRepo, authService, eventBus) // authService == nil! authService = service.NewAuthService(userRepo, sessionRepo, c, eventBus) // поздно notificationService = service.NewNotificationService( notificationRepo, userService, eventBus, ) // === Сервер === // Всё скомпилировалось. go vet пройдёт. Линтер пройдёт. // Но внутри userService лежит nil вместо authService. // Первый запрос, который дёрнет авторизацию — паника. handler := api.NewHandler(userService, authService, notificationService) srv := &http.Server{ Addr: cfg.HTTPAddr, Handler: handler.Routes(), } slog.Info("сервер запущен", "addr", cfg.HTTPAddr) if err = srv.ListenAndServe(); err != nil { slog.Error("ошибка сервера", "err", err) } _ = db.Close() _ = c.Close() }