Files

116 lines
5.3 KiB
Go
Raw Permalink Normal View History

2026-04-13 08:14:09 +03:00
//go:build wireinject
package main
// Это файл, который пишет разработчик. Wire анализирует его и генерирует wire_gen.go.
// Build-тег wireinject означает что этот файл НЕ попадает в итоговую сборку —
// вместо него компилируется wire_gen.go.
import (
"github.com/google/wire"
"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"
)
// InitializeHandler — инжектор. Wire смотрит на типы и строит граф зависимостей.
// Разработчик перечисляет провайдеры, Wire разбирается кому что нужно.
func InitializeHandler() (api.Handler, error) {
wire.Build(
// Конфиг
config.AppConfig,
// Инфраструктура — провайдеры-обёртки, потому что конструкторы
// принимают строки (DSN, addr), а Wire различает по типу.
provideDB,
provideCache,
events.NewEventBus,
// Репозитории
repository.NewUserRepo,
repository.NewSessionRepo,
repository.NewNotificationRepo,
// Сервисы
service.NewAuthService,
service.NewUserService,
service.NewNotificationService,
// API
api.NewHandler,
// А теперь самое «весёлое» — wire.Bind.
// Каждый конструктор принимает интерфейс, а Wire работает с типами.
// Нужно ВРУЧНУЮ указать: какой тип реализует какой интерфейс.
//
// database.DB реализует 3 интерфейса в разных пакетах:
wire.Bind(new(repository.UserDB), new(database.DB)),
wire.Bind(new(repository.SessionDB), new(database.DB)),
wire.Bind(new(repository.NotificationDB), new(database.DB)),
// cache.Cache реализует 2 интерфейса:
wire.Bind(new(repository.SessionCache), new(cache.Cache)),
wire.Bind(new(service.AuthCache), new(cache.Cache)),
// events.EventBus реализует 3 интерфейса:
wire.Bind(new(service.AuthEventPublisher), new(events.EventBus)),
wire.Bind(new(service.UserEventPublisher), new(events.EventBus)),
wire.Bind(new(service.NotificationEventSubscriber), new(events.EventBus)),
// repository.UserRepo реализует 2 интерфейса:
wire.Bind(new(service.AuthUserRepository), new(repository.UserRepo)),
wire.Bind(new(service.UserRepository), new(repository.UserRepo)),
// Остальные репозитории — по одному интерфейсу:
wire.Bind(new(service.AuthSessionRepository), new(repository.SessionRepo)),
wire.Bind(new(service.NotificationRepository), new(repository.NotificationRepo)),
// Сервисы тоже реализуют интерфейсы выше по графу:
wire.Bind(new(service.UserAuthService), new(service.AuthService)),
wire.Bind(new(service.NotificationUserService), new(service.UserService)),
wire.Bind(new(api.UserService), new(service.UserService)),
wire.Bind(new(api.AuthService), new(service.AuthService)),
wire.Bind(new(api.NotificationService), new(service.NotificationService)),
)
// 10 провайдеров + 18 wire.Bind = 28 строк конфигурации.
// Для 12 зависимостей. И при каждом новом интерфейсе — новый Bind.
//
// А ТЕПЕРЬ ГЛАВНОЕ.
// Откройте wire_gen.go и посмотрите что Wire нагенерировал.
// Это та же самая каша что в cmd/antipattern/main.go:
//
// db, err := provideDB(configConfig)
// userRepo := repository.NewUserRepo(db)
// sessionRepo := repository.NewSessionRepo(db, cache)
// authService := service.NewAuthService(userRepo, sessionRepo, cache, eventBus)
// ...
//
// Последовательная инициализация. Жёсткий порядок. Eager — всё создаётся сразу.
//
// Wire решил ровно одну задачу: тебе не надо самому вычислять порядок строк.
// Он сделал топологическую сортировку за тебя. Всё.
//
// Но хрупкость никуда не делась:
// - Добавил зависимость → правь wire.go + wire.Bind + go generate
// - Циклическая зависимость → Wire упадёт при генерации (тот же тупик)
// - Ленивой инициализации нет — всё создаётся при старте
//
// По сути Wire автоматизирует неправильное решение.
// Вместо того чтобы убрать хрупкость — он генерирует хрупкий код за тебя.
return nil, nil
}
func provideDB(cfg *config.Config) (database.DB, error) {
return database.New(cfg.DSN)
}
func provideCache(cfg *config.Config) cache.Cache {
return cache.New(cfg.RedisAddr)
}