Categorygithub.com/tuhuynh27/go-ioc
module
0.0.0-20241128033600-769f77110b77
Repository: https://github.com/tuhuynh27/go-ioc.git
Documentation: pkg.go.dev

# README

Go IoC: Bring "@Autowired" to Go!

Go IoC brings Spring-style autowiring to Go, offering a compile-time Inversion of Control (IoC) solution. Unlike Spring's runtime reflection-based @Autowired, Go IoC uses code generation to make dependencies explicit and type-safe at compile time, while keeping the familiar and intuitive struct tag syntax for dependency injection.

Hasn't this been done already?

While other DI solutions exist in Go (Google's Wire, Uber's Dig or Facebook's Inject), Go IoC takes a unique approach by:

  • Providing a familiar Spring-like API that Java developers will recognize
  • Using code generation for compile-time safety and zero runtime overhead (unlike Spring's runtime IoC)
  • Using struct tags and marker structs as clean "annotations"
  • Supporting interface implementations and qualifiers elegantly
  • Enabling automatic component scanning via struct marker
  • Supporting lifecycle hooks via PostConstruct and PreDestroy struct methods

But why bring @Autowired to Go?

For teams transitioning from Spring/Java to Go, especially those with significant Spring experience, dependency injection via @Autowired is more than just a familiar pattern — it's a proven productivity booster. Here's why I built Go IoC:

  • Smoother Java-to-Go Migration: Many teams, including my team, come from a Spring background. Having a similar dependency injection pattern significantly reduces the learning curve and accelerates the transition to Go.

  • Proven Developer Experience: Spring's autowiring has demonstrated for years that automatic dependency scanning and injection leads to:

    • Less boilerplate code for wiring dependencies
    • Cleaner, more maintainable code layers
    • Faster development cycles
    • Easier testing through dependency substitution
  • Best of Both Worlds: While I love Spring's convenience, I also respect Go's philosophy of explicitness. That's why Go IoC:

    • Uses pure compile-time code generation
    • Makes dependencies traceable in generated code
    • Maintains compile-time type safety
    • Keeps the familiar developer experience without any runtime overhead

How does it work?

Go IoC uses code generation to create a dependency injection system in three simple steps:

  1. Discovery

    • Scans your code to find components and their dependencies
    • Identifies how components are connected through struct tags
  2. Analysis

    • Figures out the correct order to create components
    • Validates that all dependencies can be satisfied
  3. Generation

    • Generate type-safe code that initializes all components
    • Generate code to handle the component lifecycle (startup and cleanup)
    • Produces a single generated file that wires everything together

The result is a fast, type-safe dependency injection system with zero runtime reflection.

Usage

To use Go IoC in your project, install the GO IoC code generator:

go install github.com/tuhuynh27/go-ioc/cmd/iocgen@latest

Please ensure that $GOPATH/bin is added to your $PATH.

Defining Components

Components are defined using Go structs with specific annotations:

// message/service.go
package message

type MessageService interface {
    SendMessage(msg string) string
}

type EmailService struct {
    Component struct{} // <- This is like a "@Component" annotation
    Implements struct{} `implements:"MessageService"` // <- Specify the interface that this struct implements
    Qualifier struct{} `value:"email"` // <- This is like a "@Qualifier" annotation
}

func NewEmailService() *EmailService {
    return &EmailService{}
}

func (s *EmailService) SendMessage(msg string) string {
    return fmt.Sprintf("Email: %s", msg)
}

Defining Dependencies

Dependencies are defined using struct tags:

// notification/service.go
package notification

type NotificationService struct {
    Component struct{}
    EmailSender message.MessageService `autowired:"true" qualifier:"email"` // <- This is like a "@Autowired" annotation
    SmsSender   message.MessageService `autowired:"true" qualifier:"sms"` // <- It also support "@Qualifier" annotation
}

func (s *NotificationService) SendNotifications(msg string) {
    s.EmailSender.SendMessage(msg)
    s.SmsSender.SendMessage(msg)
}

Generating Initialization Code

Run the code generator in your project root:

iocgen

This will scan your project for components and generate the initialization code.

Example Generated Code

Here's what the generated initialization code would look like:

// wire/wire_gen.go
// Code generated by Go IoC. DO NOT EDIT.
//go:generate go run github.com/tuhuynh27/go-ioc/cmd/iocgen --dir=../
package wire

import (
    "your/project/message"
    "your/project/notification"
    "your/project/config"
)

type Container struct {
    EmailService        *message.EmailService
    SmsService          *message.SmsService
    NotificationService *notification.NotificationService
}

func Initialize() (*Container, func()) {
    container := &Container{}
    container.EmailService = message.NewEmailService()
    container.SmsService = &message.SmsService{}
    container.NotificationService = &notification.NotificationService{
        EmailSender: container.EmailService,
        SmsSender:   container.SmsService,
    }
    cleanup := func() {}
    return container, cleanup
}

Using the Generated Code

Use the generated struct in your code:

// main.go
package main

import (
    "your/project/wire"
)

func main() {
    container, cleanup := wire.Initialize()
    defer cleanup()
    // Get the service you need
    notificationService := container.NotificationService
    // Use it
    notificationService.SendNotifications("Hello World!")
}

Comparison with Other DI Libraries

FeatureGo IoCGoogle WireUber DigFacebook Inject
Dependency DefinitionStruct tags & marker structsFunction providersConstructor functionsStruct tags
Runtime OverheadNoneNoneReflection-basedReflection-based
Configuration StyleSpring-like annotationsExplicit provider functionsConstructor injectionField tags
Interface BindingBuilt-inManual provider setupManual provider setupLimited support
Qualifier SupportYes, via struct tagsNo built-in supportVia name annotationsNo
Learning CurveLow (familiar to Spring devs)MediumMediumLow
Code GenerationYesYesNoNo
Compile-time SafetyYesYesPartialNo
Auto Component ScanningYesNoNoNo
Lifecycle HooksYesNoNoNo

Test with Go IoC

Please check the testing guide for more information.

Example

Please check the example Git repository (example with Go Gin web framework)

FAQ

What's the performance impact?

None! Go IoC:

  • Uses pure compile-time code generation, without runtime state or reflection, so not runtime cost
  • Generates plain Go code that's as efficient as hand-written dependency injection

# Packages

No description provided by the author