{"id":13413440,"url":"https://github.com/i-love-flamingo/dingo","last_synced_at":"2025-04-09T21:20:29.733Z","repository":{"id":39228810,"uuid":"155180696","full_name":"i-love-flamingo/dingo","owner":"i-love-flamingo","description":"Go Dependency Injection Framework","archived":false,"fork":false,"pushed_at":"2025-03-31T15:39:56.000Z","size":119,"stargazers_count":183,"open_issues_count":16,"forks_count":14,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-04-06T08:35:41.836Z","etag":null,"topics":["dependency-injection","flamingo-module","golang","golang-package","hacktoberfest"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/i-love-flamingo.png","metadata":{"files":{"readme":"Readme.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-29T08:55:18.000Z","updated_at":"2025-03-17T09:38:21.000Z","dependencies_parsed_at":"2024-05-16T14:30:49.159Z","dependency_job_id":"b9bf70fb-4d00-438d-a2eb-29f5c0fe971c","html_url":"https://github.com/i-love-flamingo/dingo","commit_stats":{"total_commits":120,"total_committers":13,"mean_commits":9.23076923076923,"dds":0.2583333333333333,"last_synced_commit":"ba907019c616fbf60c5cab29ac152725b073a33b"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i-love-flamingo%2Fdingo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i-love-flamingo%2Fdingo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i-love-flamingo%2Fdingo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i-love-flamingo%2Fdingo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/i-love-flamingo","download_url":"https://codeload.github.com/i-love-flamingo/dingo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248112308,"owners_count":21049632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","flamingo-module","golang","golang-package","hacktoberfest"],"created_at":"2024-07-30T20:01:40.450Z","updated_at":"2025-04-09T21:20:29.705Z","avatar_url":"https://github.com/i-love-flamingo.png","language":"Go","funding_links":[],"categories":["Miscellaneous","杂项","Microsoft Office","其他杂项","Dependency Injection"],"sub_categories":["Dependency Injection","依赖注入","依赖性注入"],"readme":"# Dingo\n\n[![Go Report Card](https://goreportcard.com/badge/flamingo.me/dingo)](https://goreportcard.com/report/flamingo.me/dingo) [![GoDoc](https://godoc.org/flamingo.me/dingo?status.svg)](https://godoc.org/flamingo.me/dingo) [![Tests](https://github.com/i-love-flamingo/dingo/workflows/Tests/badge.svg?branch=master)](https://github.com/i-love-flamingo/dingo/actions?query=branch%3Amaster+workflow%3ATests)\n\nDependency injection for go\n\n## Hello Dingo\n\nDingo works very similar to [Guice](https://github.com/google/guice/wiki/GettingStarted)\n\nBasically one binds implementations/factories to interfaces, which are then resolved by Dingo.\n\nGiven that Dingo's idea is based on Guice we use similar examples in this documentation:\n\nThe following example shows a BillingService with two injected dependencies. Please note\nthat Go's nature does not allow constructors, and does not allow decorations/annotations\nbeside struct-tags, thus, we only use struct tags (and later arguments for providers).\n\nAlso, Go does not have a way to reference types (like Java's `Something.class`) we use either pointers\nor `nil` and cast it to a pointer to the interface we want to specify: `(*Something)(nil)`.\nDingo then knows how to dereference it properly and derive the correct type `Something`.\nThis is not necessary for structs, where we can just use the null value via `Something{}`.\n\nSee the example folder for a complete example.\n\n```go\npackage example\n\ntype BillingService struct {\n\tprocessor CreditCardProcessor\n\ttransactionLog TransactionLog\n}\n\nfunc (billingservice *BillingService) Inject(processor CreditCardProcessor, transactionLog TransactionLog) {\n\tbillingservice.processor = processor\n\tbillingservice.transactionLog = transactionLog\n}\n\nfunc (billingservice *BillingService) ChargeOrder(order PizzaOrder, creditCard CreditCard) Receipt {\n\t// ...\n}\n```\n\nWe want the BillingService to get certain dependencies, and configure this in a `BillingModule`\nwhich implements `dingo.Module`:\n\n```go\npackage example\n\ntype BillingModule struct {}\n\nfunc (module *BillingModule) Configure(injector *dingo.Injector) {\n\t// This tells Dingo that whenever it sees a dependency on a TransactionLog, \n\t// it should satisfy the dependency using a DatabaseTransactionLog. \n\tinjector.Bind(new(TransactionLog)).To(DatabaseTransactionLog{})\n\n\t// Similarly, this binding tells Dingo that when CreditCardProcessor is used in\n\t// a dependency, that should be satisfied with a PaypalCreditCardProcessor. \n\tinjector.Bind(new(CreditCardProcessor)).To(PaypalCreditCardProcessor{})\n}\n```\n\n## Requesting injection\n\nEvery instance that is created through the container can use injection. \n\nDingo supports two ways of requesting dependencies that should be injected:\n\n* usage of struct tags to allow structs to request injection into fields. This should be used for public fields.\n* implement a public Inject(...) method to request injections of private fields. Dingo calls this method automatically and passes the requested injections.\n\nFor every requested injection (unless an exception applies) Dingo does the following:\n\n- Is there a binding? If so: delegate to the binding\n    - Is the binding in a certain scope (Singleton)? If so, delegate to scope (might result in a new loop)\n    - Binding is bound to an instance: inject instance\n    - Binding is bound to a provider: call provider\n    - Binding is bound to a type: request injection of this type (might return in a new loop to resolve the binding)\n- No binding? Try to create (only possible for concrete types, not interfaces or functions)\n\n\n*Example:*\nHere is another example using the Inject method for private fields\n```go\npackage example\n\ntype MyBillingService struct {\n\tprocessor CreditCardProcessor\n\taccountId string\n}\n\nfunc (m *MyBillingService) Inject(\n\tprocessor CreditCardProcessor,\n\tconfig *struct {\n\t\tAccountId  string `inject:\"config:myModule.myBillingService.accountId\"`\n\t},\n) {\n\tm.processor = CreditCardProcessor\n\tm.accountId = config.AccountId\n}\n```\n\n### Usage of Providers\n\nDingo allows to request the injection of provider instead of instances.\nA \"Provider\" for dingo is a function that return a new Instance of a certain type.\n\n```go\npackage example\n\ntype pizzaProvider func() Pizza\n\nfunc (s *Service) Inject(provider pizzaProvider) {\n\ts.provider = provider\n}\n```\n\nIf there is no concrete binding to the type `func() Pizza`, then instead of constructing one `Pizza` instance\nDingo will create a new function which, on every call, will return a new instance of `Pizza`.\n\nThe type must be of `func() T`, a function without any arguments which returns a type, which again has a binding.\n\nThis allows to lazily create new objects whenever needed, instead of requesting the Dingo injector itself.\n\n\nYou can use Providers and call them to always get a new instance.\nDingo will provide you with an automatic implementation of a Provider if you did not bind a specific one.\n\n*Use a Provider instead of requesting the Type directly when*:\n\n* for lazy binding \n* if you need new instances on demand\n* In general, it is best practice using a Provider for everything that has a state that might be changed. This way you will avoid undesired side effects. That is especially important for dependencies in objects that are shared between requests - for example a controller!\n\n*Example 1:*\nThis is the only code required to request a Provider as a dependency:\n```go\nMyStructProvider func() *MyStruct\nMyStruct         struct {}\n\nMyService struct {\n\tMyStructProvider MyStructProvider `inject:\"\"`\n}\n\n```\n\n*Example 2:*\n```go\npackage example\n\nfunc createSomething(thing SomethingElse) Something{\n\treturn \u0026MySomething{somethingElse: thing}\n}\n\ninjector.Bind(new(Something)).ToProvider(createSomething)\n\ntype somethingProvider func() Something\n\ntype service struct {\n\tprovider somethingProvider\n}\n```\n\nwill essentially call `createSomething(new(SomethingElse))` everytime `SomethingProvider()` is called,\npassing the resulting instance through the injection to finalize uninjected fields. \n\n\n### Optional injection\n\nAn injection struct tag can be marked as optional by adding the suffix `,optional` to it.\nThis means that for interfaces, slices, pointers etc where dingo can not resolve a concrete type, the `nil`-type is injected.\n\nYou can check via `if my.Prop == nil` if this is nil.\n\n## Bindings\n\nDingo uses bindings to express dependencies resolutions, and will panic if there is more than one\nbinding for a type with the same name (or unnamed), unless you use multibindings.\n\n### Bind\n\nBind creates a new binding, and tells Dingo how to resolve the type when it encounters a request for this type.\nBindings can chain, but need to implement the correct interfaces.\n\n```go\ninjector.Bind(new(Something))\n```\n\n### AnnotatedWith\n\nBy default a binding is unnamed, and thus requested with the `inject:\"\"` tag.\n\nHowever, you can name bindings to have more concrete kinds of it. Using `AnnotatedWith` you can specify the name:\n\n```go\ninjector.Bind((*Something)(nil)).AnnotatedWith(\"myAnnotation\")\n```\n\nIt is requested via the `inject:\"myAnnotation\"` tag. For example:\n\n```go\nstruct {\n\tPaypalPaymentProcessor PaymentProcessor `inject:\"Paypal\"`\n}\n```\n\n### To\n\nTo defines which type should be created when this type is requested.\nThis can be an Interface which implements to one it is bound to, or a concrete type.\nThe type is then created via `reflect.New`.\n\n```go\ninjector.Bind(new(Something)).To(MyType{})\n```\n\n### ToProvider\n\nIf you want a factory to create your types then you rather use `ToProvider` instead of `To`.\n\n`ToProvider` is a function which returns an instance (which again will go through Dingo to fill dependencies).\n\nAlso, the provider can request arguments from Dingo which are necessary to construct the bounded type.\nIf you need named arguments (e.g. a string instance annotated with a configuration value) you need to request\nan instance of an object with these annotations, because Go does not allow to pass any meta-information on function\narguments.\n\n```go\nfunc MyTypeProvider(se SomethingElse) *MyType {\n\treturn \u0026MyType{\n\t\tSpecial: se.DoSomething(),\n\t}\n}\n\ninjector.Bind(new(Something)).ToProvider(MyTypeProvider)\n```\n\nThis example will make Dingo call `MyTypeProvider` and pass in an instance of `SomethingElse` as it's first argument,\nthen take the result of `*MyType` as the value for `Something`.\n\n`ToProvider` takes precedence over `To`.\n\n### ToInstance\n\nFor situations where you have one, and only one, concrete instance you can use `ToInstance` to bind\nsomething to the concrete instance. This is not the same as a Singleton!\n(Even though the resulting behaviour is very similar.)\n\n```go\nvar myInstance = new(MyType)\nmyInstance.Connect(somewhere)\ninjector.Bind(new(Something)).ToInstance(myInstance)\n```\n\nYou can also bind an instance it to a struct obviously, not only to interfaces.\n\n`ToInstance` takes precedence over both `To` and `ToProvider`.\n\n### In (Singleton scopes)\n\nIf really necessary it is possible to use singletons\n``` \n.AsEagerSingleton() binds as a singleton, and loads it when the application is initialized\n.In(dingo.Singleton) makes it a global singleton\n.In(dingo.ChildSingleton) makes it a singleton limited to the current injector\n```\n\n`In` allows us to bind in a scope, making the created instances scoped in a certain way.\n\nCurrently, Dingo only allows to bind to `dingo.Singleton` and `dingo.ChildSingleton`.\n\n```go\ninjector.Bind(new(Something)).In(dingo.Singleton).To(MyType{})\n```\n\n#### dingo.Singleton\n\nThe `dingo.Singleton` scope makes sure a dependency is only resolved once, and the result is\nreused. Because the Singleton needs synchronisation for types over multiple concurrent\ngoroutines and make sure that a Singleton is only created once, the initial creation\ncan be costly and also the injection of a Singleton is always taking more resources than creation\nof an immutable new object.\n\nThe synchronisation is done on multiple levels, a first test tries to find the singleton,\nif that is not possible a lock-mechanism via a scoped Mutex takes care of delegating\nthe concrete creation to one goroutine via a scope+type specific Mutex which then generates\nthe Singleton and makes it available to other currently waiting injection requests, as\nwell as future injection requests.\n\nBy default, it is advised to not use Singletons whenever possible, and rather use\nimmutable objects you inject whenever you need them.\n\n#### dingo.ChildSingleton\n\nThe ChildSingleton is just another Singleton (actually of the same type), but dingo will create a new one\nfor every derived child injector.\n\nThis allows frameworks like Flamingo to distinguish at a root level between singleton scopes, e.g. for\nmulti-page setups where we need a wide scope for routers.\n\nSince ChildSingleton is very similar to Singleton you should only use it with care.\n\n#### AsEagerSingleton\n\nSingleton creation is always costly due to synchronisation overhead, therefore\nDingo bindings allow to mark a binding `AsEagerSingleton`.\n\nThis makes sure the Singleton is created as soon as possible, before the rest of the Application\nruns. `AsEagerSingleton` implies `In(dingo.Singleton)`.\n\n```go\ninjector.Bind(new(Something)).To(MyType{}).AsEagerSingleton()\n```\n\nIt is also possible to bind a concrete type without `To`:\n\n```go\ninjector.Bind(MyType{}).AsEagerSingleton()\n```\n\nBinding this type as an eager singleton inject the singleton instance whenever `MyType` is requested. `MyType` is a concrete type (struct) here, so we can use this mechanism to create an instance explicitly before the application is run.\n\n### Override\n\nIn rare cases you might have to override an existing binding, which can be done with `Override`:\n\n```go\ninjector.Override(new(Something), \"\").To(MyBetterType{})\n```\n\n`Override` also returns a binding such as `Bind`, but removes the original binding.\n\nThe second argument sets the annotation if you want to override a named binding.\n\n### MultiBindings\n\nMultiBindings provide a way of binding multiple implementations of a type to a type,\nmaking the injection a list.\n\nEssentially this means that multiple modules are able to register for a type, and a user of this\ntype can request an injection of a slice `[]T` to get a list of all registered bindings.\n\n```go\ninjector.BindMulti(new(Something)).To(MyType1{})\ninjector.BindMulti(new(Something)).To(MyType2{})\n\nstruct {\n\tList []Something `inject:\"\"`  // List is a slice of []Something{MyType1{}, MyType2{}}\n}\n```\n\nMultiBindings are used to allow multiple modules to register for a certain type, such as a list of\nencoders, subscribers, etc.\n\nPlease note that MultiBindings are not always a clear pattern, as it might hide certain complexity.\n\nUsually it is easier to request some kind of registry in your module, and then register explicitly.\n\n\n### Bind maps\n\nSimilar to Multibindings, but with a key instead of a list\n```go\nMyService struct {\n\tIfaces map[string]Iface `inject:\"\"`\n}\n\ninjector.BindMap(new(Iface), \"impl1\").To(IfaceImpl{})\ninjector.BindMap(new(Iface), \"impl2\").To(IfaceImpl2{})\n```\n\n### Binding basic types\n\nDingo allows binding values to `int`, `string` etc., such as with any other type.\n\nThis can be used to inject configuration values.\n\nFlamingo makes an annotated binding of every configuration value in the form of:\n\n```go\nvar Configuration map[string]interface{}\n\nfor k, v := range Configuration {\n\tinjector.Bind(v).AnnotatedWith(\"config:\" + k).ToInstance(v)\n}\n```\n\nIn this case Dingo learns the actual type of `v` (such as string, bool, int) and provides the annotated injection.\n\nLater this can be used via\n\n```go\nstruct {\n\tConfigParam string `inject:\"config:myconfigParam\"`\n}\n```\n\n\n\n## Dingo Interception\n\nDingo allows modules to bind interceptors for interfaces.\n\nEssentially this means that whenever the injection of a certain type is happening,\nthe interceptor is injected instead with the actual injection injected into the interceptor's\nfirst field. This mechanism can only work for interface interception.\n\nMultiple interceptors stack upon each other.\n\nInterception should be used with care!\n\n```go\nfunc (m *Module) Configure(injector *dingo.Injector) {\n\tinjector.BindInterceptor(new(template.Engine), TplInterceptor{})\n\tinjector.BindInterceptor(new(template.Function), FunctionInterceptor{})\n}\n\ntype (\n\tTplInterceptor struct {\n\t\ttemplate.Engine\n\t}\n\n\tFunctionInterceptor struct {\n\t\ttemplate.Function\n\t}\n)\n\nfunc (t *TplInterceptor) Render(context web.Context, name string, data interface{}) io.Reader {\n\tslog.Info(\"Before Rendering\", name)\n\tstart := time.Now()\n\tr := t.Engine.Render(context, name, data)\n\tslog.Info(\"After Rendering\", time.Since(start))\n\treturn r\n}\n\nfunc (f *FunctionInterceptor) Name() string {\n\tfuncname := f.Function.Name()\n\tslog.Info(\"Function\", funcname, \"used\")\n\treturn funcname\n}\n```\n\n## Initializing Dingo\nAt the topmost level the injector is created and used in the following way:\n\n```go\npackage main\n\nimport \"flamingo.me/dingo\"\n\nfunc main() {\n\tinjector, err := dingo.NewInjector()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// The injector can be initialized by modules:\n\tinjector.InitModules(new(BillingModule))\n\n\t// Now that we've got the injector, we can build objects.\n\t// We get a new instance, and cast it accordingly:\n\tinstance, err := injector.GetInstance(new(BillingService))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n    billingService := instance.(BillingService) \n\t//...\n}\n```\n\n## Dingo vs. Wire\n\nRecently https://github.com/google/go-cloud/tree/master/wire popped out in the go ecosystem, which seems to be a great choice, also because it supports compile time dependency injection.\nHowever, when Dingo was first introduced wire was not a thing, and wire still lacks features dingo provides. \n\nhttps://gocover.io/github.com/i-love-flamingo/dingo\n\n## ModuleFunc\n\nDingo has a wrapper for `func(*Injector)` called `ModuleFunc`. It is possible to wrap a function with the `ModuleFunc` to become a `Module`.\nThis is similar to the `http` Packages `HandlerFunc` mechanism and allows to save code and easier set up small projects.\n\n## Troubleshooting\n1. To trace possible circular injections Dingo has function `EnableCircularTracing()`, which also switches slog to DEBUG level. This makes execution very heavy in terms of memory, so should be used only for debug purposes.\n2. To trace possible injection issues, like when Dingo tries to inject dependency into unexported field and fails, and user does not know where this happens, Dingo has `EnableInjectionTracing()`, which is also sets slog level to DEBUG. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fi-love-flamingo%2Fdingo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fi-love-flamingo%2Fdingo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fi-love-flamingo%2Fdingo/lists"}