Simple Registration vs Modules
When should you use modules? Always! But let’s understand why.
The Evolution of Your Code
Day 1: Simple Registration
When you first start, you might think this is fine:
func main() {
services := godi.NewServiceCollection()
// Just a few services...
services.AddSingleton(NewLogger)
services.AddSingleton(NewConfig)
services.AddSingleton(NewDatabase)
services.AddScoped(NewUserService)
provider, _ := services.BuildServiceProvider()
// ...
}
Week 2: Growing Pains
Now you have more services:
func main() {
services := godi.NewServiceCollection()
// Getting messy...
services.AddSingleton(NewLogger)
services.AddSingleton(NewConfig)
services.AddSingleton(NewDatabase)
services.AddSingleton(NewCache)
services.AddSingleton(NewEmailClient)
services.AddSingleton(NewSMSClient)
services.AddScoped(NewUserRepository)
services.AddScoped(NewOrderRepository)
services.AddScoped(NewProductRepository)
services.AddScoped(NewUserService)
services.AddScoped(NewOrderService)
services.AddScoped(NewNotificationService)
services.AddScoped(NewAuthService)
// Where does what belong?
// What depends on what?
// How do I test parts of this?
provider, _ := services.BuildServiceProvider()
}
The Module Solution
Organize from the start with modules:
// modules/core.go
var CoreModule = godi.NewModule("core",
godi.AddSingleton(NewLogger),
godi.AddSingleton(NewConfig),
)
// modules/data.go
var DataModule = godi.NewModule("data",
CoreModule, // Depends on core
godi.AddSingleton(NewDatabase),
godi.AddSingleton(NewCache),
godi.AddScoped(NewUserRepository),
godi.AddScoped(NewOrderRepository),
godi.AddScoped(NewProductRepository),
)
// modules/notifications.go
var NotificationModule = godi.NewModule("notifications",
CoreModule, // Also needs logging
godi.AddSingleton(NewEmailClient),
godi.AddSingleton(NewSMSClient),
godi.AddScoped(NewNotificationService),
)
// modules/business.go
var BusinessModule = godi.NewModule("business",
DataModule,
NotificationModule,
godi.AddScoped(NewUserService),
godi.AddScoped(NewOrderService),
godi.AddScoped(NewAuthService),
)
// main.go - Clean and simple!
func main() {
services := godi.NewServiceCollection()
services.AddModules(BusinessModule) // Includes everything!
provider, _ := services.BuildServiceProvider()
}
Why Always Use Modules?
1. Organization
Without modules:
// 50 lines of service registrations
// No clear structure
// Hard to find anything
With modules:
// Clear structure
CoreModule // Config, logging
DataModule // Database, repositories
BusinessModule // Services, use cases
WebModule // HTTP handlers, middleware
2. Dependencies Are Clear
var OrderModule = godi.NewModule("orders",
UserModule, // Orders need users
InventoryModule, // Orders need inventory
PaymentModule, // Orders need payment
godi.AddScoped(NewOrderService),
godi.AddScoped(NewOrderRepository),
)
// Dependencies are explicit!
3. Testing Is Easier
// Test just the order functionality
func TestOrderService(t *testing.T) {
testModule := godi.NewModule("test",
// Mock dependencies
MockUserModule,
MockInventoryModule,
MockPaymentModule,
// Real order services
godi.AddScoped(NewOrderService),
)
// Test in isolation!
}
4. Reusability
// auth/module.go - Reusable auth module
var AuthModule = godi.NewModule("auth",
godi.AddSingleton(NewJWTService),
godi.AddScoped(NewAuthService),
godi.AddScoped(NewPermissionService),
)
// Use in different apps
// app1/main.go
services.AddModules(AuthModule, App1Module)
// app2/main.go
services.AddModules(AuthModule, App2Module)
5. Environment Switching
// environments/dev.go
var DevModule = godi.NewModule("dev",
godi.AddSingleton(func() Database {
return NewSQLiteDatabase(":memory:")
}),
godi.AddSingleton(func() EmailClient {
return NewMockEmailClient()
}),
)
// environments/prod.go
var ProdModule = godi.NewModule("prod",
godi.AddSingleton(func() Database {
return NewPostgresDatabase(os.Getenv("DATABASE_URL"))
}),
godi.AddSingleton(func() EmailClient {
return NewSendGridClient(os.Getenv("SENDGRID_KEY"))
}),
)
// main.go
func main() {
services := godi.NewServiceCollection()
// Core business logic
services.AddModules(BusinessModule)
// Environment-specific
if os.Getenv("ENV") == "production" {
services.AddModules(ProdModule)
} else {
services.AddModules(DevModule)
}
}
Module Best Practices
Start with Modules from Day 1
Even for small apps:
// Even tiny apps benefit from modules
var AppModule = godi.NewModule("app",
godi.AddSingleton(NewConfig),
godi.AddSingleton(NewService),
)
func main() {
services := godi.NewServiceCollection()
services.AddModules(AppModule) // Clean from the start
}
One Module Per Feature/Layer
// ✅ Good - Clear, focused modules
var UserModule = godi.NewModule("user", ...)
var OrderModule = godi.NewModule("order", ...)
var PaymentModule = godi.NewModule("payment", ...)
// ❌ Bad - Everything in one module
var AppModule = godi.NewModule("app",
// 100 services here...
)
Module Naming
// ✅ Good names
var DatabaseModule = ... // Infrastructure
var UserFeatureModule = ... // Feature
var AuthenticationModule = ... // Capability
// ❌ Vague names
var StuffModule = ...
var Module1 = ...
var MiscModule = ...
Migration Path
If you started with simple registration:
Step 1: Group by Purpose
// Before: main.go has everything
services.AddSingleton(NewLogger)
services.AddSingleton(NewDatabase)
services.AddScoped(NewUserService)
// ... 50 more lines
// After: Organized modules
var CoreModule = godi.NewModule("core",
godi.AddSingleton(NewLogger),
)
var DataModule = godi.NewModule("data",
CoreModule,
godi.AddSingleton(NewDatabase),
)
var UserModule = godi.NewModule("user",
DataModule,
godi.AddScoped(NewUserService),
)
Step 2: Extract to Files
// modules/core.go
package modules
var CoreModule = godi.NewModule("core", ...)
// modules/data.go
package modules
var DataModule = godi.NewModule("data", ...)
// main.go
import "myapp/modules"
services.AddModules(modules.AppModule)
Summary
Always use modules because they:
Keep code organized
Make dependencies explicit
Enable easy testing
Support reusability
Allow environment switching
Start with modules from day 1 - even tiny apps benefit from the organization.
The question isn’t “should I use modules?” but “how should I organize my modules?”