import
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/olezhek28/di-demo/internal/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
handler, err := InitializeHandler()
|
||||
if err != nil {
|
||||
slog.Error("не удалось инициализировать приложение", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: config.AppConfig().HTTPAddr,
|
||||
Handler: handler.Routes(),
|
||||
}
|
||||
|
||||
slog.Info("сервер запущен", "addr", config.AppConfig().HTTPAddr)
|
||||
|
||||
if err = srv.ListenAndServe(); err != nil {
|
||||
slog.Error("ошибка сервера", "err", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//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)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
// InitializeHandler — инжектор. Wire смотрит на типы и строит граф зависимостей.
|
||||
// Разработчик перечисляет провайдеры, Wire разбирается кому что нужно.
|
||||
func InitializeHandler() (api.Handler, error) {
|
||||
configConfig := config.AppConfig()
|
||||
db, err := provideDB(configConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userRepo := repository.NewUserRepo(db)
|
||||
cacheCache := provideCache(configConfig)
|
||||
sessionRepo := repository.NewSessionRepo(db, cacheCache)
|
||||
eventBus := events.NewEventBus()
|
||||
authService := service.NewAuthService(userRepo, sessionRepo, cacheCache, eventBus)
|
||||
userService := service.NewUserService(userRepo, authService, eventBus)
|
||||
notificationRepo := repository.NewNotificationRepo(db)
|
||||
notificationService := service.NewNotificationService(notificationRepo, userService, eventBus)
|
||||
handler := api.NewHandler(userService, authService, notificationService)
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
// wire.go:
|
||||
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user