godi

package module
v0.0.0-...-44157ca Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2024 License: MIT Imports: 13 Imported by: 0

README

Godi - Lightweight Modular Dependency Injection Framework for Go

Godi is a lightweight, modular dependency injection framework designed for building scalable Go applications. It simplifies the creation of highly maintainable Go applications through a modular architecture with clear boundaries and flexible configurations powered by Uber's Dig library under the hood.

Key Features

  • Modular Architecture: Organize your application into cohesive modules with well-defined interfaces.
  • Constructor-Based and Direct Injection: Inject dependencies either via constructors or directly.
  • Type-Safe Dependency Management: Ensure compile-time type safety in dependency injection.
  • Routing with Guards, Filters, Pipes, Interceptors: Easily configure guards filters, interceptors for controllers or routes for flexible request handling.

Getting Started

  • Read the detailed documentation, please refer to the Godi package documentation.

  • Add Godi to your project:

    go get github.com/huboh/godi
    

Quick example

Here’s a minimal setup to start a godi app

package main

import (
    "log"
    "github.com/huboh/godi"
)

const (
    port = "5000"
    host = "localhost"
)

func main() {
    app, err := godi.New(&main.module{})
    if err != nil {
        log.Fatal("Failed to create godi app:", err)
    }

    err := app.Listen(host, port);
    if err != nil {
        log.Fatal("Failed to start app server:", err)
    }
}

Authors

License

Godi is released under the MIT License.

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

func GetToken

func GetToken(v any) string

GetToken generates a unique token for the given value based on its type.

Types

type App

type App struct {
	*HttpServer
	// contains filtered or unexported fields
}

App represents the main application

func New

func New(module Module) (*App, error)

New initializes a new instance of App, configuring the root module and dependencies.

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
}

func (*HttpServer) Listen

func (s *HttpServer) Listen(host string, port string) error

Listen starts the HTTP server on the specified host and port, and listens for incoming requests.

Also listens for system signals like SIGINT and SIGTERM to enable graceful shutdown.

func (*HttpServer) Shutdown

func (s *HttpServer) Shutdown(c context.Context) error

Shutdown gracefully shuts down the HTTP server.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL