Categorygithub.com/submodule-org/submodule.go/v2
modulepackage
2.0.4
Repository: https://github.com/submodule-org/submodule.go.git
Documentation: pkg.go.dev

# README

Managing dependency with Submodule

Go Test common case

Does the demonstrated diagram look familiar to you? A lot of applications will look just like so.

Without any IoC or Dependency Injection framework / libraries, those applications will suffer from those issues

  • Everything centralized in main method, and will be scattered to all services
  • The higher level of the component you want to test, it'll be more complicated to setup tests

Tak Register user handler as an example, to setup the test for it

  • You'll need to mock User service
  • Or you'll need to mock Db connection
  • Or you'll need to mock Config

Submodule was born to balance the Ioc wiring, so you don't have to pull your hairs just to test your app

How does Submodule work?

Submodule is built around the concept of function composition. A component will be hidden/lazy initialized behind a function. And if another module requires that component, submodule will associate those factory functions. The initialization will only happen when one of those components got used

So, Submodule will just replicate exactly what described as dependencies in the diagram, and only initialized when one of those components got called

Then, with the knowledge of dependencies like so, Submodule will initialize just what it is needed to support testing

Enough talk, show me the code

package main

import (
	"fmt"
	"net/http"

	"github.com/submodule-org/submodule.go/v2"
)

type Config struct {
	Host     string
	Port     int
	LogLevel string
}

func LoadConfig() Config {
	// load config from ENV etc
	return Config{
		Host:     "",      // value from env or default value
		Port:     0,       // value from env or default value
		LogLevel: "debug", // value from env or default value
	}
}

// ConfigMod will be the singleton container for config value
var ConfigMod = submodule.Make[Config](LoadConfig)

type logger struct {
	LogLevel string
}

type Logger interface {
	Log(msg ...string)
}

func (l *logger) Log(msgs ...string) {
	// log implementation with log level
}

var LoggerMod = submodule.Make[Logger](func(config Config) Logger {
	return &logger{
		LogLevel: config.LogLevel,
	}
}, ConfigMod)

type server struct {
	Config Config
	Logger Logger
}

func (s *server) Start() {
	go func() {
		http.ListenAndServe(fmt.Sprintf("%s:%d", s.Config.Host, s.Config.Port), nil)
	}()
}

var ServerMod = submodule.Resolve(&server{}, ConfigMod, LoggerMod)

func main() {
	server := ServerMod.Resolve()
	server.Start()
}

# Packages

No description provided by the author
No description provided by the author

# Functions

Append global middleware to the global scope.
Apply middleware to the global scope.
Create a new scope with modifiers.
Dispose global scope to free up all resolved values and trigger scope end middlewares.
No description provided by the author
No description provided by the author
Return the global scope.
Group groups submodules and re-advertise as a single value.
No description provided by the author
`Make` help you create a Submodule from a function `Make` input must be a function which # Any types With In embedded, all fields of the struct will be resolved against dependencies func(s *server, l Logger, c Config) (any) { return nil } in the example above, s, l and c will be resolved against dependencies # submodule.In embedded Will be resolved as a whole against dependencies func(p struct { submodule.In Server *Server Logger Logger }, c Config) any { return nil } in the example above, Server, Logger and Config will be resolved against dependencies.
Modifiable constructor.
No description provided by the author
Resolve fields of struct or struct pointer against given dependencies type Server struct { Logger Logger Config Config } var ServerMod = submodule.Resolve[Server](&Server{}, LoggerMod, ConfigMod) In the example above, Server.Logger and Server.Config will be resolved against dependencies.
Provide value as it is.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

Special type to facitliate dependency injection by struct.
A middleware can add behaviors to a scope via decorator pattern.
No description provided by the author
Self is a special type to facitliate dependency injection, it will reflect the current dependency list and scope at execution time.

# Interfaces

ModifiableSubmodule is a submodule that can be modified.
Non generic representation of a submodule.
A scope is a container for retrievable values.
Submodule is a container holding a factory and its meta information Submodule will not hold the result on its own, but rather resolving it against a scope.

# Type aliases

No description provided by the author
No description provided by the author