• Stars
    star
    1,807
  • Rank 25,701 (Top 0.6 %)
  • Language
    Go
  • License
    MIT License
  • Created over 2 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

⚙️ A dependency injection toolkit based on Go 1.18+ Generics.

do - Dependency Injection

tag Go Version GoDoc Build Status Go report Coverage License

⚙️ A dependency injection toolkit based on Go 1.18+ Generics.

This library implements the Dependency Injection design pattern. It may replace the uber/dig fantastic package in simple Go projects. samber/do uses Go 1.18+ generics and therefore offers a typesafe API.

See also:

  • samber/lo: A Lodash-style Go library based on Go 1.18+ Generics
  • samber/mo: Monads based on Go 1.18+ Generics (Option, Result, Either...)

Why this name?

I love short name for such a utility library. This name is the sum of DI and Go and no Go package currently uses this name.

⭕⭕⭕⭕⭕⭕ About v2 ⭕⭕⭕⭕⭕⭕

Check out the beta now!

go get -u github.com/samber/do/[email protected]

Documentation: https://do.samber.dev/

Please report bugs here: #45.

💡 Features

  • Service registration
  • Service invocation
  • Service health check
  • Service shutdown
  • Service lifecycle hooks
  • Named or anonymous services
  • Eagerly or lazily loaded services
  • Dependency graph resolution
  • Default injector
  • Injector cloning
  • Service override
  • Lightweight, no dependencies
  • No code generation

🚀 Services are loaded in invocation order.

🕵️ Service health can be checked individually or globally. Services implementing do.Healthcheckable interface will be called via do.HealthCheck[type]() or injector.HealthCheck().

🛑 Services can be shutdowned properly, in back-initialization order. Services implementing do.Shutdownable interface will be called via do.Shutdown[type]() or injector.Shutdown().

🚀 Install

go get github.com/samber/do@v1

This library is v1 and follows SemVer strictly.

No breaking changes will be made to exported APIs before v2.0.0.

This library has no dependencies except the Go std lib.

💡 Quick start

You can import do using:

import (
    "github.com/samber/do"
)

Then instanciate services:

func main() {
    injector := do.New()

    // provides CarService
    do.Provide(injector, NewCarService)

    // provides EngineService
    do.Provide(injector, NewEngineService)

    car := do.MustInvoke[*CarService](injector)
    car.Start()
    // prints "car starting"

    do.HealthCheck[EngineService](injector)
    // returns "engine broken"

    // injector.ShutdownOnSIGTERM()    // will block until receiving sigterm signal
    injector.Shutdown()
    // prints "car stopped"
}

Services:

type EngineService interface{}

func NewEngineService(i *do.Injector) (EngineService, error) {
    return &engineServiceImplem{}, nil
}

type engineServiceImplem struct {}

// [Optional] Implements do.Healthcheckable.
func (c *engineServiceImplem) HealthCheck() error {
	return fmt.Errorf("engine broken")
}
func NewCarService(i *do.Injector) (*CarService, error) {
    engine := do.MustInvoke[EngineService](i)
    car := CarService{Engine: engine}
    return &car, nil
}

type CarService struct {
	Engine EngineService
}

func (c *CarService) Start() {
	println("car starting")
}

// [Optional] Implements do.Shutdownable.
func (c *CarService) Shutdown() error {
	println("car stopped")
	return nil
}

🤠 Spec

GoDoc: https://godoc.org/github.com/samber/do

Injector:

Service registration:

Service invocation:

Service override:

Injector (DI container)

Build a container for your components. Injector is responsible for building services in the right order, and managing service lifecycle.

injector := do.New()

Or use nil as the default injector:

do.Provide(nil, func (i *Injector) (int, error) {
    return 42, nil
})

service := do.MustInvoke[int](nil)

You can check health of services implementing func HealthCheck() error.

type DBService struct {
    db *sql.DB
}

func (s *DBService) HealthCheck() error {
    return s.db.Ping()
}

injector := do.New()
do.Provide(injector, ...)
do.Invoke(injector, ...)

statuses := injector.HealthCheck()
// map[string]error{
//   "*DBService": nil,
// }

De-initialize all compoments properly. Services implementing func Shutdown() error will be called synchronously in back-initialization order.

type DBService struct {
    db *sql.DB
}

func (s *DBService) Shutdown() error {
    return s.db.Close()
}

injector := do.New()
do.Provide(injector, ...)
do.Invoke(injector, ...)

// shutdown all services in reverse order
injector.Shutdown()

List services:

type DBService struct {
    db *sql.DB
}

injector := do.New()

do.Provide(injector, ...)
println(do.ListProvidedServices())
// output: []string{"*DBService"}

do.Invoke(injector, ...)
println(do.ListInvokedServices())
// output: []string{"*DBService"}

Service registration

Services can be registered in multiple way:

  • with implicit name (struct or interface name)
  • with explicit name
  • eagerly
  • lazily

Anonymous service, loaded lazily:

type DBService struct {
    db *sql.DB
}

do.Provide[DBService](injector, func(i *Injector) (*DBService, error) {
    db, err := sql.Open(...)
    if err != nil {
        return nil, err
    }

    return &DBService{db: db}, nil
})

Named service, loaded lazily:

type DBService struct {
    db *sql.DB
}

do.ProvideNamed(injector, "dbconn", func(i *Injector) (*DBService, error) {
    db, err := sql.Open(...)
    if err != nil {
        return nil, err
    }

    return &DBService{db: db}, nil
})

Anonymous service, loaded eagerly:

type Config struct {
    uri string
}

do.ProvideValue[Config](injector, Config{uri: "postgres://user:pass@host:5432/db"})

Named service, loaded eagerly:

type Config struct {
    uri string
}

do.ProvideNamedValue(injector, "configuration", Config{uri: "postgres://user:pass@host:5432/db"})

Service invocation

Loads anonymous service:

type DBService struct {
    db *sql.DB
}

dbService, err := do.Invoke[DBService](injector)

Loads anonymous service or panics if service was not registered:

type DBService struct {
    db *sql.DB
}

dbService := do.MustInvoke[DBService](injector)

Loads named service:

config, err := do.InvokeNamed[Config](injector, "configuration")

Loads named service or panics if service was not registered:

config := do.MustInvokeNamed[Config](injector, "configuration")

Individual service healthcheck

Check health of anonymous service:

type DBService struct {
    db *sql.DB
}

dbService, err := do.Invoke[DBService](injector)
err = do.HealthCheck[DBService](injector)

Check health of named service:

config, err := do.InvokeNamed[Config](injector, "configuration")
err = do.HealthCheckNamed(injector, "configuration")

Individual service shutdown

Unloads anonymous service:

type DBService struct {
    db *sql.DB
}

dbService, err := do.Invoke[DBService](injector)
err = do.Shutdown[DBService](injector)

Unloads anonymous service or panics if service was not registered:

type DBService struct {
    db *sql.DB
}

dbService := do.MustInvoke[DBService](injector)
do.MustShutdown[DBService](injector)

Unloads named service:

config, err := do.InvokeNamed[Config](injector, "configuration")
err = do.ShutdownNamed(injector, "configuration")

Unloads named service or panics if service was not registered:

config := do.MustInvokeNamed[Config](injector, "configuration")
do.MustShutdownNamed(injector, "configuration")

Service override

By default, providing a service twice will panic. Service can be replaced at runtime using do.Override helper.

do.Provide[Vehicle](injector, func (i *do.Injector) (Vehicle, error) {
    return &CarImplem{}, nil
})

do.Override[Vehicle](injector, func (i *do.Injector) (Vehicle, error) {
    return &BusImplem{}, nil
})

Hooks

2 lifecycle hooks are available in Injectors:

  • After registration
  • After shutdown
injector := do.NewWithOpts(&do.InjectorOpts{
    HookAfterRegistration: func(injector *do.Injector, serviceName string) {
        fmt.Printf("Service registered: %s\n", serviceName)
    },
    HookAfterShutdown: func(injector *do.Injector, serviceName string) {
        fmt.Printf("Service stopped: %s\n", serviceName)
    },

    Logf: func(format string, args ...any) {
        log.Printf(format, args...)
    },
})

Cloning injector

Cloned injector have same service registrations as it's parent, but it doesn't share invoked service state.

Clones are useful for unit testing by replacing some services to mocks.

var injector *do.Injector;

func init() {
    do.Provide[Service](injector, func (i *do.Injector) (Service, error) {
        return &RealService{}, nil
    })
    do.Provide[*App](injector, func (i *do.Injector) (*App, error) {
        return &App{i.MustInvoke[Service](i)}, nil
    })
}

func TestService(t *testing.T) {
    i := injector.Clone()
    defer i.Shutdown()

    // replace Service to MockService
    do.Override[Service](i, func (i *do.Injector) (Service, error) {
        return &MockService{}, nil
    }))

    app := do.Invoke[*App](i)
    // do unit testing with mocked service
}

🛩 Benchmark

// @TODO

🤝 Contributing

Don't hesitate ;)

With Docker

docker-compose run --rm dev

Without Docker

# Install some dev dependencies
make tools

# Run tests
make test
# or
make watch-test

👤 Contributors

Contributors

💫 Show your support

Give a ⭐️ if this project helped you!

GitHub Sponsors

📝 License

Copyright © 2022 Samuel Berthe.

This project is MIT licensed.

More Repositories

1

lo

💥 A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)
Go
17,419
star
2

awesome-prometheus-alerts

🚨 Collection of Prometheus alerting rules
HTML
6,502
star
3

mo

🦄 Monads and popular FP abstractions, powered by Go 1.18+ Generics (Option, Result, Either...)
Go
2,587
star
4

oops

🔥 Error handling library with context, assertion, stack trace and source fragments
Go
384
star
5

slog-multi

🚨 Design workflows of slog handlers: pipeline, middleware, fanout, routing, failover, load balancing...
Go
342
star
6

invoice-as-a-service

💰 Simple invoicing service (REST API): from JSON to PDF
PHP
181
star
7

sync-ssh-keys

🔐 Sync public ssh keys to ~/.ssh/authorized_keys, based on Github/Gitlab organization membership.
Go
138
star
8

slog-gin

🚨 Gin middleware for slog logger
Go
109
star
9

chartjs-plugin-datasource-prometheus

📊 Chart.js plugin for Prometheus
TypeScript
107
star
10

slog-formatter

🚨 slog: Attribute formatting
Go
107
star
11

slog-echo

🚨 Echo middleware for slog logger
Go
100
star
12

go-gpt-3-encoder

Go BPE tokenizer (Encoder+Decoder) for GPT2 and GPT3
Go
78
star
13

the-great-gpt-firewall

🤖 A curated list of websites that restrict access to AI Agents, AI crawlers and GPTs
Python
75
star
14

prometheus-query-js

📊 A Javascript client for Prometheus query API
TypeScript
58
star
15

hot

🌶️ In-memory caching library for Go
Go
58
star
16

github-actions-runner

✅ Docker images for starting self-hosted Github Actions runner(s).
Dockerfile
57
star
17

slog-fiber

🚨 Fiber middleware for slog logger
Go
52
star
18

slog-sampling

🚨 slog sampling: drop repetitive log records
Go
49
star
19

slog-chi

🚨 Chi middleware for slog logger
Go
43
star
20

slog-sentry

🚨 slog: Sentry handler
Go
43
star
21

awesome-olap

A curated list of awesome Online Analytical Processing databases, frameworks, ressources and other awesomeness.
37
star
22

grafana-flamegraph-panel

📊 Flame graph panels for Grafana
JavaScript
37
star
23

slog-loki

🚨 slog: Loki handler
Go
36
star
24

slog-http

🚨 net/http middleware for slog logger
Go
32
star
25

slog-zerolog

🚨 slog: Zerolog handler
Go
30
star
26

workshop-prometheus-grafana

📊 Prometheus and Grafana 101
JavaScript
30
star
27

slog-zap

🚨 slog: Zap handler
Go
23
star
28

go-metered-io

📐 A drop-in replacement to io.Reader and io.Writer with the total number of bytes transfered.
Go
23
star
29

go-amqp-pubsub

Fault tolerant Pub/Sub library for RabbitMQ
Go
21
star
30

go-singleflightx

🧬 x/sync/singleflight but with generics, batching, sharding and nullable result
Go
19
star
31

slog-logrus

🚨 slog: Logrus handler
Go
18
star
32

slog-slack

🚨 slog: Slack handler
Go
18
star
33

arp-spoofing

💥 Simple implementation of arp poisoning attack ;)
C
16
star
34

pg_cron

⏰ PostgreSQL extension for running periodic jobs
C
15
star
35

go-tcp-pool

✨ Drop-in replacement to net.Conn with pooling and auto-reconnect
Go
14
star
36

go-type-to-string

🕵️‍♂️ Extract a string representation of Go type
Go
14
star
37

slog-syslog

🚨 slog: Syslog handler
Go
13
star
38

refined-hn

JavaScript
11
star
39

git-contrib-graph

📊 Displays a github-like contribution graph, of every contributors of a repository
Go
10
star
40

slog-nats

🚨 slog: NATS handler
Go
10
star
41

slog-parquet

🚨 slog: Parquet handler + Object Storage
Go
10
star
42

slog-datadog

🚨 slog: Datadog handler
Go
10
star
43

free_proxy_list

Free proxy list [NOT MAINTAINED ANYMORE - please fork]
Shell
9
star
44

slog-otel

OTEL toolchain for slog
Makefile
8
star
45

slog-graylog

🚨 slog: Graylog handler
Go
7
star
46

powEUr

Python
7
star
47

slog-telegram

🚨 slog: Telegram handler
Go
7
star
48

node-promfiler

Expose a http endpoint for exporting node.js v8 profiling
JavaScript
7
star
49

slog-webhook

🚨 slog: Webhook handler
Go
7
star
50

slog-betterstack

🚨 slog: Betterstack handler
Go
7
star
51

slog-common

Common toolchain for slog
Go
6
star
52

slog-kafka

🚨 slog: Kafka handler
Go
6
star
53

slog-channel

🚨 slog: Go channel handler
Go
6
star
54

slog-logstash

🚨 slog: Logstash handler
Go
5
star
55

ansible-role-airbyte

Ansible role for Airbyte
5
star
56

slog-fluentd

🚨 slog: Fluentd handler
Go
4
star
57

remote-dev-environment

👨‍💻 My development environment is too slow, let's fix that !
4
star
58

GoogleCalendarNotifier-FitbitTracker

Google Calendar notifier for Fitbit Tracker
Gosu
4
star
59

slog-mock

🚨 slog: mock handler
Go
4
star
60

slog-quickwit

🚨 slog: Quickwit handler
Go
4
star
61

criterion-rpm-package

RPM package for Criterion (C unit testing)
Shell
3
star
62

dagobert

A simple Go client for the clip-as-service server
Go
3
star
63

rabbitmq-flooding

Cluster recovery testing. Floods RabbitMQ with random data.
Python
3
star
64

go-psi

🥵 Pressure Stall Informations (PSI) and starvation notifier
Go
3
star
65

slog-microsoft-teams

🚨 slog: Microsoft Teams handler
Go
3
star
66

llvm_dart_binding

Binding Dart/LLVM (using LLVM bytecode from Dart)
Dart
3
star
67

lab-langchain-getting-started

Python
2
star
68

BTCC_api

A basic API wrapper for the BTCC Trading and Market FIX API.
JavaScript
2
star
69

ngx-domarrow

Declarative and template-driven DOMArrow integration for Angular2+
TypeScript
2
star
70

github-stackoverflow-email-scrapping

Scrape top Github and Stack-Overflow users to find email address
Go
2
star
71

nft-http-api

🚦 NFT over HTTP API
Go
2
star
72

go-quickwit

🍱 A Go ingestion client for Quickwit
Go
2
star
73

celery_demonstration

Async worker + scheduling
Python
2
star
74

dockerfiles

Dockerfile
1
star
75

SaaS-Cookbook-List

List of Cookbook about SaaS development (ENG/FR)
1
star
76

dotfiles

@samber's dotfiles
JavaScript
1
star
77

grafana-dashboard-nomad

Grafana dashboards for Nomad (Docker orchestrator from Hashicorp)
1
star
78

go-clevercloud-api

Go library for Clever-Cloud api
Go
1
star
79

lab-langchain

Python
1
star
80

slog-mattermost

🚨 slog: Mattermost handler
Go
1
star
81

jitsi-virtual-background

JavaScript
1
star
82

raw-ip-udp-sockets-chap

Simple implementation of CHAP protocol, with raw socket layers (3+4)
C
1
star
83

lab-parquet

Go
1
star
84

canvas-to-bmp

TypeScript
1
star
85

refined-cycle-app

JavaScript
1
star
86

azure-ad-oauth2-proxy

Dockerfile
1
star
87

packer-qemu-debian

Builds Debian 8 image for Qemu
Shell
1
star
88

poc-selenium-unit-test-css

Python
1
star
89

maxscale-experiments

Demonstration step-by-step of MaxScale for master/slave query spliting/routing #mysql #docker
Shell
1
star
90

google-takeout-to-s3

🚨 Simple script to upload encrypted Google Takeout archives to S3.
1
star
91

messenger-bot-clock

Messenger bot replying with current time
JavaScript
1
star
92

fb-messenger-bot-psychologist

🤖 A Messenger bot talking like a psychologist
Emacs Lisp
1
star
93

promql-exporter

Prometheus exporter for PromQL endpoints (replacing federation and remote-write)
Go
1
star