Documentation
¶
Overview ¶
Package godi is a lightweight, modular dependency injection framework designed for building scalable Go applications.
Overview ¶
Godi simplifies the creation of highly maintainable Go applications through a modular architecture powered by dependency injection. It provides a robust foundation for building large-scale applications where components are loosely coupled, easily testable, and highly reusable.
Key features include:
- Modular architecture with clear separation of concerns
- Constructor-based and direct dependency injection
- Flexible module configuration and composition
- Type-safe dependency management
Constructor-based Dependency Injection ¶
Constructors are the building blocks of dependency injection in Godi. They are plain Go functions that:
- Accept zero or more dependencies as parameters
- Return one or more values of any type
- Optionally return an error as the last return value
Example of a constructor:
func NewUserService(dep1 *Dependency1, dep2 *Dependency2) (*UserService, error) {
return &UserService{
dep1: dep1,
dep2: dep2,
}, nil
}
Any arguments that the constructor has are treated as its dependencies. The dependencies are instantiated in an unspecified order along with any dependencies that they might have, creating a dependency graph at runtime.
Direct Dependency Injection ¶
If a dependency itself does not require any other dependencies, you can opt to inject it directly without using a constructor. This can be more convenient than defining a constructor, especially for simple dependencies.
Modules ¶
Modules are the core organizational unit in Godi. Each module encapsulates related functionality and can define its dependencies, exports, and HTTP controllers. A module must implement the godi.Module interface by providing a Config method that returns *godi.ModuleConfig.
Example of a module:
type AuthModule struct{}
func (m *AuthModule) Config() *godi.ModuleConfig {
return &godi.ModuleConfig{
IsGlobal: false, // Makes this module's exports available to all other modules
Imports: []godi.Module{&config.Module{}},
Exports: []godi.Provider{}, // Subset of providers that will be available to other modules
ExportsCtor: []godi.ProviderConstructor{NewAuthService},
Providers: []godi.Provider{}, // Internal components required by this module
ProvidersCtors: []godi.ProviderConstructor{NewAuthService},
Controllers: []godi.Controller{}, // HTTP controllers
ControllersCtors: []godi.ControllerConstructor{},
}
}
Controllers ¶
Controllers handle HTTP routing and request processing. They provide a structured way to define endpoints and their associated handlers. A controller must implement the godi.Controller interface by providing a Config method to return *godi.ControllerConfig.
Example of a controller:
type AuthController struct {
auth *AuthService
}
func newController(s *AuthService) *AuthController {
return &AuthController{
auth: s,
}
}
func (c *AuthController) Config() *godi.ControllerConfig {
return &godi.ControllerConfig{
Pattern: "/auth", // Base path for all routes in the controller
Metadata: map[string]string{}, // Controller-wide metadata accessible by guards for decision making
Guards: []godi.Guard{}, // Controller-wide guards
GuardsCtors: []godi.GuardConstructor{}, // Controller-wide guards constructors
RoutesCfgs: []*godi.RouteConfig{
{
Pattern: "/signin",
Method: http.MethodPost,
Handler: http.HandlerFunc(c.handleSignin),
Metadata: map[string]string{},
Guards: []godi.Guard{},
GuardsCtors: []godi.GuardConstructor{},
},
{
Pattern: "/signup",
Method: http.MethodPost,
Handler: http.HandlerFunc(c.handleSignup),
Metadata: map[string]string{},
Guards: []godi.Guard{},
GuardsCtors: []godi.GuardConstructor{},
},
},
}
}
Guards ¶
Guards are used to control access to controllers or individual routes, providing an additional layer of security by enforcing runtime or compile-time rules through the incoming request or controller/route metadata before handlers are executed. They determine whether a given request will be handled by the route handler or not, depending on certain conditions.
A guard must implement the godi.Guard interface by providing a Allow method to determine if the request should be allowed.
Example of a guard:
type AuthGuard struct {
auth *AuthService
}
func newGuard(a *AuthService) *AuthGuard {
return &AuthGuard{
auth: a,
}
}
func (g *AuthGuard) Allow(gCtx godi.GuardContext) (bool, error) {
validated, err := g.auth.Validate(gCtx.Http.R.Header.Get("Authorization"))
if err != nil {
return false, err
}
return validated, nil
}
**Controller-scoped Guards**:
Guards defined at the controller level will be applied to all routes within that controller. This is useful for applying authorization to an entire controller.
func (c *AuthController) Config() *godi.ControllerConfig {
return &godi.ControllerConfig{
...
Guards: []godi.Guard{}, // Controller-wide guards
GuardsCtors: []godi.GuardConstructor{}, // Controller-wide guards constructors
RoutesCfgs: []*godi.RouteConfig{...},
}
}
**Route-scoped Guards**:
Guards can also be defined at the individual route level, allowing you to apply specific guard to only a subset of a controllers routes.
func (c *AuthController) Config() *godi.ControllerConfig {
return &godi.ControllerConfig{
...
RoutesCfgs: []*godi.RouteConfig{
{
...
Guards: []godi.Guard{}, // Route guards
GuardsCtors: []godi.GuardConstructor{}, // Route guards constructors
},
},
}
}
Structuring Modules ¶
Godi applications are modular, and the main root module is configured to import other modules. The root [app.Module] serves as the main entry point, importing any number of sub-modules.
package app
import (
"github.com/huboh/godi"
"github.com/huboh/godi/pkg/modules/config" // Utility module by Godi for reading environment variables
".../modules/auth"
".../modules/database"
".../modules/user"
)
type Module struct{}
func (mod *Module) Config() *godi.ModuleConfig {
return &godi.ModuleConfig{
Imports: []godi.Module{&config.Module{}, &database.Module{}, &auth.Module{}, &user.Module{}},
}
}
Application Setup ¶
To create a new application with Godi, instantiate a new App by providing a root module. The following is an example of creating and starting a Godi application.
Creating a Godi application involves defining a root module that composes all other modules:
package main
func main() {
app, err := godi.New(&app.Module{})
if err != nil {
log.Fatal("failed to create godi app: ", err)
}
// Start the application's HTTP server, listening on the specified host and port.
err = app.Listen("localhost", "5000")
if err != nil {
log.Fatal("failed to start app server: ", err)
}
}
Best Practices ¶
- Keep modules focused and cohesive - each module should have a single responsibility
- Use constructors to ensure proper initialization and dependency validation
- Use metadata to add extra information to routes and controllers for documentation or tooling
- Structure your application with clear module boundaries and well-defined interfaces
- Consider making commonly used services global by setting `IsGlobal: true` in their module config
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type App ¶
type App struct {
*HttpServer
// contains filtered or unexported fields
}
App represents the main application
type Controller ¶
type Controller interface {
Config() *ControllerConfig
}
Controller is any type that can receive inbound requests and produce responses.
type ControllerConfig ¶
type ControllerConfig struct {
// Pattern is a root prefix appended to each route path registered
// within the controller.
Pattern string
// Metadata holds arbitrary metadata associated with the controller.
Metadata any
// RoutesCfgs lists all routes managed by the controller.
RoutesCfgs []*RouteConfig
// Guards contains guard instances applied globally to all routes in the controller.
Guards []Guard
// GuardsCtors provides constructors for creating guard instances that
// requires dependency injection.
GuardsCtors []GuardConstructor
}
ControllerConfig defines the configuration for a controller. E.g route patterns, guards and metadata.
type ControllerConstructor ¶
type ControllerConstructor constructor
ControllerConstructor is a function type that creates Controller instances. It may have dependencies as parameters and returns instances of the Controller interface, optionally returning an error on failure.
Any arguments that the constructor has are treated as its dependencies. The dependencies are instantiated in an unspecified order along with any dependencies that they might have.
type Guard ¶
type Guard interface {
Allow(GuardContext) (bool, error)
}
Guard is an interface that determines whether a request should be handled by a route handler or rejected based on specific criteria or metadata present at runtime.
type GuardConstructor ¶
type GuardConstructor constructor
GuardConstructor is a function that takes any number of dependencies as its parameters and returns an arbitrary number of values that meets the `Guard` interface and may optionally return an error to indicate that it failed to build the value.
Any arguments that the constructor has are treated as its dependencies. The dependencies are instantiated in an unspecified order along with any dependencies that they might have.
type GuardContext ¶
type GuardContext struct {
// Http contains the request and response information.
Http GuardContextHttp
// RouteCfg contains metadata and configuration specific to the route.
RouteCfg RouteConfig
// ControllerCfg contains metadata and configuration for the controller.
ControllerCfg ControllerConfig
}
GuardContext provides the contextual information that a guard needs to make its decision.
It encapsulates the HTTP request and response, along with route and controller metadata for a more informed decision-making process.
type GuardContextHttp ¶
type GuardContextHttp struct {
R *http.Request
W http.ResponseWriter
}
GuardContextHttp holds HTTP request and response information for GuardContext.
type HttpServer ¶
type HttpServer struct {
// contains filtered or unexported fields
}
type Module ¶
type Module interface {
// Config returns the module config containing providers, exports
// and imports required by the module.
Config() *ModuleConfig
}
Module is an interface representing a self-contained unit of functionality. It exposes providers that can be imported by other modules within the application.
type ModuleConfig ¶
type ModuleConfig struct {
// IsGlobal indicates whether the module's exported providers are globally
// available to every other module without needing an explicit import.
//
// This is useful for shared utilities or database connections.
IsGlobal bool
// Imports specifies other modules that this module depends on.
// Providers from these modules will be available within this module.
Imports []Module
// Exports lists providers from this module that should be accessible to
// other modules that import this module.
Exports []Provider
// ExportsCtors lists constructors for providers that should be accessible
// in other modules importing this module.
ExportsCtors []ProviderConstructor
// Providers lists the providers within the module that are shared across
// the module's other providers.
Providers []Provider
// ProvidersCtors lists constructors for providers that the Godi injector
// will create and share within this module.
ProvidersCtors []ProviderConstructor
// Controllers lists the handlers defined in this module, which handle
// HTTP requests and define the module's endpoints.
Controllers []Controller
// ControllersCtors lists constructors for controllers in this module that
// will be instantiated by the Godi injector.
ControllersCtors []ControllerConstructor
}
ModuleConfig provides configuration settings for a module's functionality, such as its providers, controllers, and imported modules.
type Provider ¶
type Provider interface{}
Provider is a marker interface for types that can be provided as dependencies.
type ProviderConstructor ¶
type ProviderConstructor constructor
ProviderConstructor is a constructor function type for providers. It takes any required dependencies as parameters and returns instances that implement the `Provider` interface. Optionally, the constructor may return an error to signal that it failed to create the provider instance.
Dependencies are resolved and injected into the constructor in an unspecified order. This allows for flexible and lazy-loading dependency injection where each dependency may itself have nested dependencies.
Example:
func NewProvider(dep1 Dependency1, dep2 Dependency2) (Provider, error) {
// Create and return a provider instance
}
Any dependencies needed by the constructor will be resolved and instantiated by the module's DI scope.
type RouteConfig ¶
type RouteConfig struct {
Method string // The HTTP method (e.g., GET, POST) for the route.
Pattern string // The URL pattern that the route will match.
Handler http.Handler // The HTTP handler to process requests on this route.
Metadata any // Optional metadata that can be associated with the route.
Guards []Guard // Guards to enforce conditions before route handling.
GuardsCtors []GuardConstructor // Guard constructors for dynamic guard instantiation.
}
RouteConfig defines the configuration for a route.