125 lines
5.1 KiB
Go
125 lines
5.1 KiB
Go
|
|
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()
|
|||
|
|
}
|