Files
di-container/cmd/antipattern/main.go
T
2026-04-13 08:14:09 +03:00

125 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
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)
}
c := cache.New(cfg.RedisAddr) // ⚠️ если перенести ниже sessionRepo — nil pointer
// === Репозитории ===
userRepo := repository.NewUserRepo(db)
sessionRepo := repository.NewSessionRepo(db, c) // ⚠️ зависит от db И cache
notificationRepo := repository.NewNotificationRepo(db) // ⚠️ зависит от db
// === EventBus ===
// Пока EventBus — простой брокер без зависимостей, всё нормально.
// Проблемы начнутся когда EventBus понадобится зависимость (см. комментарии ниже).
eventBus := events.NewEventBus()
// === Сервисы ===
// Тут начинается самое интересное — перекрёстные зависимости.
// Порядок критичен: authService нужен для userService,
// а userService нужен для notificationService.
// Переставь любые два — и привет, nil pointer.
authService := service.NewAuthService(userRepo, sessionRepo, c, eventBus) // ⚠️ зависит от repos, cache, eventBus
userService := service.NewUserService(userRepo, authService, eventBus) // ⚠️ зависит от authService — порядок!
notificationService := service.NewNotificationService( // ⚠️ зависит от userService — порядок!
notificationRepo,
userService,
eventBus,
)
// === Сервер ===
handler := api.NewHandler(userService, authService, notificationService)
srv := &http.Server{
Addr: cfg.HTTPAddr,
Handler: handler.Routes(),
}
// =====================================================================
// === НОВАЯ ЗАДАЧА: EventBus должен сам доставлять уведомления ===
// =====================================================================
//
// Продакт говорит: EventBus должен не просто пересылать события,
// а сам доставлять уведомления. Нужно чтобы EventBus вызывал
// NotificationService напрямую.
//
// Сейчас EventBus — простой брокер:
//
// type EventBus interface {
// Publish(event string)
// Subscribe(event string, handler func())
// }
// func NewEventBus() EventBus
//
// А нужно так:
//
// type EventBus interface {
// Publish(event string)
// Subscribe(event string, handler func())
// }
// func NewEventBus(notifications NotificationSender) EventBus
//
// Казалось бы — добавили одно поле. Но теперь зависимости:
//
// EventBus → нужен NotificationService
// NotificationService → нужен UserService
// UserService → нужен AuthService
// AuthService → нужен EventBus ← ЦИКЛ
//
// Куда воткнуть EventBus в этот main.go?
//
// Попытка 1: создать EventBus после notificationService
// eventBus := events.NewEventBus(notificationService)
// ...но authService и userService уже созданы с eventBus выше (строки 48-49).
// Поздно — они уже инициализированы со старым eventBus.
//
// Попытка 2: создать EventBus до сервисов, передать nil
// eventBus := events.NewEventBus(nil) // NotificationService ещё не существует
// ...потом: eventBus.SetNotificationService(notificationService)
// Костыль с мутабельным состоянием. И гонка: кто-то вызовет
// EventBus до SetNotificationService — получит nil pointer.
//
// Попытка 3: переписать всю цепочку
// Разорвать одну из зависимостей, переделать интерфейсы...
// Пол дня работы. И страшно — вдруг сломаю то что уже работает.
//
// Вот она — хрупкость в действии.
// Одна зависимость с одним полем — и вся инициализация встала колом.
// =====================================================================
slog.Info("всё хорошо, сервер запускается")
if err = srv.ListenAndServe(); err != nil {
slog.Error("ошибка сервера", "err", err)
}
_ = db.Close()
_ = c.Close()
}