Skip to content

Instantly share code, notes, and snippets.

@mgiacomini
Created June 10, 2025 12:08
Show Gist options
  • Save mgiacomini/ae132c5e5447db842b4a49de87cb783f to your computer and use it in GitHub Desktop.
Save mgiacomini/ae132c5e5447db842b4a49de87cb783f to your computer and use it in GitHub Desktop.
golang DI
// main.go
package main
import (
"context"
"errors"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"go.uber.org/zap"
)
func main() {
// Inicializa a aplicação usando Wire
app, cleanup, err := InitializeApp()
if err != nil {
log.Fatal("Failed to initialize app:", err)
}
// Garante que a limpeza será executada
defer cleanup.Execute()
// Adiciona limpeza do servidor HTTP
cleanup.Add(func() error {
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
app.Logger.Info("shutting down http server")
if err := app.HTTPServer.Shutdown(shutdownCtx); err != nil {
app.Logger.With(zap.Error(err)).Error("error shutting down the server")
return err
}
return nil
})
app.Logger.Info("Started HTTP server")
// Canal para erros do servidor
serverErrors := make(chan error, 1)
// Inicia o servidor HTTP em uma goroutine
go func() {
app.Logger.With(zap.String("address", app.HTTPServer.Addr)).Info("HTTP server listening")
if err := app.HTTPServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
serverErrors <- err
}
}()
// Canal para sinais do sistema
quitChannel := make(chan os.Signal, 1)
signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM)
// Aguarda erro do servidor ou sinal de shutdown
select {
case err := <-serverErrors:
app.Logger.With(zap.Error(err)).Error("error on server")
case sig := <-quitChannel:
app.Logger.With(zap.String("signal", sig.String())).Info("server shutting down due to signal")
}
app.Logger.Info("Application shutdown complete")
}
// wire.go
//go:build wireinject
// +build wireinject
package main
import (
"context"
"net/http"
"github.com/google/wire"
"github.com/google/uuid"
"go.uber.org/zap"
"your-module/config"
"your-module/logger"
"your-module/tracer"
"your-module/jdintegrationmock"
"your-module/services"
"your-module/handler"
"your-module/api"
)
// =============================================================================
// APPLICATION STRUCT
// =============================================================================
// App encapsula todas as dependências da aplicação
type App struct {
Config *config.Config
Logger *zap.Logger
Tracer *tracer.Tracer
HTTPServer *http.Server
Context context.Context
}
// Cleanup contém todas as funções de limpeza
type Cleanup struct {
cleanupFns []func() error
}
func (c *Cleanup) Add(fn func() error) {
c.cleanupFns = append(c.cleanupFns, fn)
}
func (c *Cleanup) Execute() {
for _, fn := range c.cleanupFns {
if err := fn(); err != nil {
// Log error se possível, mas continue a limpeza
}
}
}
// =============================================================================
// PROVIDERS
// =============================================================================
// ConfigProvider carrega a configuração
func ProvideConfig() (*config.Config, error) {
return config.LoadConfig()
}
// ContextProvider cria o contexto base da aplicação
func ProvideContext() context.Context {
ctx, _ := context.WithCancel(context.Background())
return ctx
}
// LoggerProvider cria o logger configurado
func ProvideLogger(cfg *config.Config) (*zap.Logger, error) {
l, err := logger.NewLogger(logger.Config{
Env: cfg.DDEnv,
Service: cfg.DDService,
Version: cfg.DDVersion,
})
if err != nil {
return nil, err
}
l.Info("Zap logger started")
return l, nil
}
// ServiceInstanceIDProvider gera um ID único para o serviço
func ProvideServiceInstanceID() string {
return uuid.NewString()
}
// TracerProvider cria o tracer configurado
func ProvideTracer(ctx context.Context, cfg *config.Config, l *zap.Logger, serviceInstanceID string) (*tracer.Tracer, error) {
// Adiciona logger ao contexto
ctx = logger.WithLogger(ctx, l)
t, err := tracer.NewTracer(ctx, tracer.Config{
Endpoint: cfg.OtelExporterEndpoint,
SamplingRatio: cfg.OtelSamplingRatio,
ServiceInstanceID: serviceInstanceID,
ServiceName: cfg.OtelResourceServiceName,
ServiceNamespace: cfg.OtelResourceServiceNamespace,
})
if err != nil {
return nil, err
}
if err = t.Start(ctx); err != nil {
return nil, err
}
return t, nil
}
// ContextWithTracerProvider adiciona o tracer ao contexto
func ProvideContextWithTracer(ctx context.Context, t *tracer.Tracer) context.Context {
return tracer.WithTracer(ctx, t.OTelTracer)
}
// BeneficiaryRepositoryProvider cria o repository
func ProvideBeneficiaryRepository() (jdintegrationmock.BeneficiaryRepository, error) {
return jdintegrationmock.NewBeneficiaryRepository()
}
// BeneficiaryServiceProvider cria o service
func ProvideBeneficiaryService(repo jdintegrationmock.BeneficiaryRepository) (*services.BeneficiaryService, error) {
return services.NewBeneficiaryService(repo)
}
// HandlerProvider cria o handler
func ProvideHandler(service *services.BeneficiaryService) (*handler.Handler, error) {
return handler.NewHandler(service)
}
// RouterProvider cria o router
func ProvideRouter(ctx context.Context, h *handler.Handler) *api.Router {
return api.NewRouter(logger.Logger(ctx), h)
}
// HTTPServerProvider cria o servidor HTTP
func ProvideHTTPServer(cfg *config.Config, router *api.Router, ctx context.Context) *http.Server {
return &http.Server{
Addr: cfg.ServerAddr,
Handler: router,
ReadTimeout: cfg.ServerReadTimeout,
WriteTimeout: cfg.ServerWriteTimeout,
ReadHeaderTimeout: cfg.ServerReadTimeout,
BaseContext: func(l net.Listener) context.Context {
return ctx
},
}
}
// CleanupProvider cria a estrutura de cleanup
func ProvideCleanup(l *zap.Logger, t *tracer.Tracer) *Cleanup {
cleanup := &Cleanup{}
// Adiciona funções de limpeza
cleanup.Add(func() error {
l.Sync()
return nil
})
cleanup.Add(func() error {
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
l.Info("shutting down tracer")
if err := t.Shutdown(shutdownCtx); err != nil {
l.With(zap.Error(err)).Error("error shutting down the tracer")
return err
}
return nil
})
return cleanup
}
// AppProvider cria a estrutura principal da aplicação
func ProvideApp(
cfg *config.Config,
l *zap.Logger,
t *tracer.Tracer,
server *http.Server,
ctx context.Context,
) *App {
return &App{
Config: cfg,
Logger: l,
Tracer: t,
HTTPServer: server,
Context: ctx,
}
}
// =============================================================================
// WIRE SETS
// =============================================================================
var ConfigSet = wire.NewSet(
ProvideConfig,
)
var LoggingSet = wire.NewSet(
ProvideLogger,
ProvideServiceInstanceID,
)
var TracingSet = wire.NewSet(
ProvideTracer,
ProvideContextWithTracer,
)
var RepositorySet = wire.NewSet(
ProvideBeneficiaryRepository,
)
var ServiceSet = wire.NewSet(
ProvideBeneficiaryService,
)
var HandlerSet = wire.NewSet(
ProvideHandler,
)
var HTTPSet = wire.NewSet(
ProvideRouter,
ProvideHTTPServer,
)
var AppSet = wire.NewSet(
ProvideContext,
ProvideApp,
ProvideCleanup,
)
// =============================================================================
// WIRE INJECTORS
// =============================================================================
// InitializeApp cria toda a aplicação com dependências injetadas
func InitializeApp() (*App, *Cleanup, error) {
wire.Build(
ConfigSet,
LoggingSet,
TracingSet,
RepositorySet,
ServiceSet,
HandlerSet,
HTTPSet,
AppSet,
)
return nil, nil, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment