Files

100 lines
3.7 KiB
Go
Raw Permalink Normal View History

2026-04-13 08:14:09 +03:00
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()
}