{"id":13413446,"url":"https://github.com/NVIDIA/gontainer","last_synced_at":"2025-03-14T19:32:18.525Z","repository":{"id":192923988,"uuid":"684777481","full_name":"NVIDIA/gontainer","owner":"NVIDIA","description":"Dependency Injection container for Golang projects.","archived":false,"fork":false,"pushed_at":"2024-08-17T16:57:39.000Z","size":700,"stargazers_count":34,"open_issues_count":0,"forks_count":3,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-08-17T17:59:34.086Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NVIDIA.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"2023-08-29T20:46:30.000Z","updated_at":"2024-08-17T16:57:14.000Z","dependencies_parsed_at":"2024-05-29T13:19:12.783Z","dependency_job_id":"3530b5fe-8b5f-422e-9aa0-d9bfc0277ec8","html_url":"https://github.com/NVIDIA/gontainer","commit_stats":null,"previous_names":["nvidia/gontainer"],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NVIDIA%2Fgontainer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NVIDIA%2Fgontainer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NVIDIA%2Fgontainer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NVIDIA%2Fgontainer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NVIDIA","download_url":"https://codeload.github.com/NVIDIA/gontainer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221498746,"owners_count":16833056,"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":[],"created_at":"2024-07-30T20:01:40.592Z","updated_at":"2025-03-14T19:32:18.516Z","avatar_url":"https://github.com/NVIDIA.png","language":"Go","readme":"[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![GoDoc](https://pkg.go.dev/badge/github.com/NVIDIA/gontainer)](https://pkg.go.dev/github.com/NVIDIA/gontainer)\n![Test](https://github.com/NVIDIA/gontainer/actions/workflows/go.yml/badge.svg)\n[![Report](https://goreportcard.com/badge/github.com/NVIDIA/gontainer)](https://goreportcard.com/report/github.com/NVIDIA/gontainer)\n\n# Gontainer\n\nDependency injection service container for Golang projects.\n\u003cp align=\"center\"\u003e\u003cimg src=\"splash.png\" width=\"400\"/\u003e\u003c/p\u003e\n\n## Features\n\n- 🚀 Eager services instantiation with automatic dependencies resolution and optional dependencies support.\n- 🛠 Dependency Injection for service factories, avoiding manual fetch through container API.\n- 🔄 Reverse-to-Instantiation order for service termination to ensure proper resource release and shutdown.\n- 📣 Events broker for inter-service container-wide communications.\n- 🤖 Clean, tested and with small codebase based on reflection.\n\n## Examples\n\n* [Basic script example](./examples/01_basic_usage/main.go) showing how to do something useful and then exit.\n* [Daemon service example](./examples/02_daemon_service/main.go) showing how to launch background services.\n\n## Quick Start\n\n1. Define an example service.\n    ```go\n    // MyService performs some crucial tasks.\n    type MyService struct{}\n\n    // SayHello outputs a friendly greeting.\n    func (s *MyService) SayHello(name string) {\n        log.Println(\"Hello,\", name)\n    }\n   ```\n2. Define a service factory.\n   ```go\n   func NewMyService() *MyService {\n      return new(MyService)\n   }\n   ```\n3. Register service factories in the container.\n   ```go\n   container, err := gontainer.New(\n      // Define MyService factory in the container.\n      gontainer.NewFactory(NewMyService),\n   \n      // Here we can define another services depending on `*MyService`.\n      // All dependencies are declared using factory function args.\n      gontainer.NewFactory(func(service *MyService) {\n         service.SayHello(\"Username\")\n      }),\n   )\n   if err != nil {\n      log.Fatalf(\"Failed to init service container: %s\", err)\n   }\n   ```\n5. Start the container and launch all factories.\n   ```go\n   if err := container.Start(); err != nil {\n      log.Fatalf(\"Failed to start service container: %s\", err)\n   }\n   ```\n   \n6. Alternatively to eager start with a `Start()` call it is possible to use `Resolver` or `Invoker` service. It will spawn only explicitly requested services including it's dependencies. \n   ```go\n   var MyService myService\n   if err := container.Resolver().Resolve(\u0026MyService); err != nil {\n       log.Fatalf(\"Failed to resolve MyService dependency: %s\", err)\n   }\n   myServise.DoSomething()\n   ```\n   or\n   ```go\n   if err := container.Invoker().Invoke(func(myService \u0026MyService) {\n       myServise.DoSomething()\n   }); err != nil {\n       log.Fatalf(\"Failed to invoke a function: %s\", err)\n   }\n   ```\n\n## Key Concepts\n\n### Service Factories\n\nThe **Service Factory** is a key component of the service container, serving as a mechanism for creating service instances.\nA **Service Factory** is essentially a function that accepts another services and returns an instances of services of concrete types or an\ninterfaces and optionally spawn an error in the last return argument. Using service factory signature, the service container\nwill resolve and spawn all dependency services using reflection and fail, if there are unresolvable dependencies.\n\n```go\n// MyServiceFactory is an example of a service factory.\nfunc MyServiceFactory( /* service dependencies */) *MyService {\n   // Initialize service instance.\n   return new(MyService)\n}\n\n// MyServiceFactory depends on two services.\nfunc MyServiceFactory(svc1 MyService1, svc2 MyService2) MyService {...}\n\n// MyServiceFactory provides two services.\nfunc MyServiceFactory() (MyService1, MyService2) {...}\n\n// MyServiceFactory provides two services and spawn error.\nfunc MyServiceFactory() (MyService1, MyService2, error) {...}\n\n// MyServiceFactory provides no services an error.\nfunc MyServiceFactory() error {...}\n\n// MyServiceFactory provides nothing. Sic!\nfunc MyServiceFactory() {...}\n```\n\nThe factory function's role is to perform any necessary initializations and return a fully-configured service instance\nto the container.\n\nThere are several predefined by container service types that may be used as a dependencies in the factory arguments.\n\n1. The `context.Context` service provides the per-service context, inherited from the background context.\n   This context is cancelled right before the service's `Close()` call and intended to be used with service functions.\n1. The `gontainer.Events` service provides the events broker. It can be used to send and receive events\n   inside service container between services or outside from the client code.\n1. The `gontainer.Resolver` service provides a service to resolve dependencies dynamically.\n1. The `gontainer.Invoker` service provides a service to invoke functions dynamically.\n\nIn addition, there are several generic types allowing to declare dependencies on a type.\n\n#### Optional Dependency Declaration\n\nThe `gontainer.Optional[T]` type allows to depend on a type that may or may not be present.\nFor example, when developing a factory that uses a telemetry service, this type can be used if the service\nis registered in the container. If the telemetry service is not registered, this is not considered an error,\nand telemetry initialization can be skipped in the factory.\n\n```go\n// MyServiceFactory optionally depends on the service.\nfunc MyServiceFactory(optService1 gontainer.Optional[MyService1]) {\n    // Get will not produce any error if the MyService1 is not registered\n    // in the container: it will return zero value for the service type.\n    service := optSvc1.Get()\n}\n```\n\n#### Multiple Dependencies Declaration\n\nThe `gontainer.Multiple[T]` type allows retrieval of all services that match the type `T`. This feature is \nintended to be used when providing concrete service types from multiple factories (e.g., struct pointers like\n`*passwordauth.Provider`, `*tokenauth.Provider`) and depending on them as services `Multiple[IProvider]`.\nIn this case, the length of the `services` slice could be in the range `[0, N]`.\n\nIf a concrete non-interface type is specified in `T`, then the length of the slice could only be `[0, 1]` \nbecause the container restricts the registration of the same non-interface type more than once.\n\n```go\n// MyServiceFactory depends on the all implementing interface types.\nfunc MyServiceFactory(servicesSlice gontainer.Multiple[MyInterface]) {\n    for _, service := range servicesSlice {\n\n    }\n}\n```\n\n### Services\n\nA service is a functional component of the application, created and managed by a Service Factory. \nThe lifetime of a service is tied to the lifetime of the entire container.\n\nA service may optionally implement a `Close() error` or just `Close()` method, which is called when the container is shutting down.\nThe `Close` call is synchronous: remaining services will not be closed until this method returns.\n\n```go\n// MyService defines example service.\ntype MyService struct {}\n\n// SayHello is service domain method example. \nfunc (s *MyService) SayHello(name string) {\n    fmt.Println(\"Hello,\", name)\n}\n\n// Close is an optional method called from container's Close(). \nfunc (s *MyService) Close() error {\n   // Synchronous cleanup logic here.\n   return nil\n}\n```\n\n### Service Functions\n\nThe **Service Function** is a specialized form of service optimized for simpler tasks. Instead of returning a concrete\ntype object or an interface, the service factory returns a function that conforms to `func() error` or `func()` type.\n\nThe function serves two primary roles:\n\n- It encapsulates the behavior to execute when the container starts asynchronously to the `Start()` method.\n- It returns an error, which is treated as if it were returned by a conventional `Close()` method.\n\n```go\n// MyServiceFactory is an example of a service function usage.\nfunc MyServiceFactory(ctx context.Context) func() error {\n    return func() error {\n        // Await its order in container close.\n        \u003c-ctx.Done()\n      \n        // Return nil from the `service.Close()`.\n        return nil\n    }\n}\n```\n\nIn this design, the factory function is responsible for receiving the context. This context is canceled when the service\nneeds to close, allowing the function to terminate gracefully.\n\nErrors returned by the function are processed as if they were errors returned by a standard `Close()` method to the container.\nThis means the container will synchronously wait until a service function returns an error or nil before closing the next services.\n\n### Events Broker\n\nThe **Events Broker** is an additional part of the service container architecture. It facilitates communication between services\nwithout them having to be directly aware of each other. The Events Broker works on a publisher-subscriber model, enabling services\nto publish events to, and subscribe to events from, a centralized broker.\n\nThis mechanism allows services to remain decoupled while still being able to interact through a centralized medium.\nIn particular, the `gontainer.Events` service provides an interface to the events broker and can be injected as a dependency in any service factory.\n\n#### Triggering Events\n\nTo trigger an event, use the `Trigger()` method. Create an event using `NewEvent()` and pass the necessary arguments:\n\n```go\nevents.Trigger(gontainer.NewEvent(\"Event1\", event, arguments, here))\n```\n\n#### Subscribing to Events\n\nTo subscribe to an event, use the `Subscribe()` method. Two types of handler functions are supported:\n\n- A function that accepts a variable number of any-typed arguments:\n  ```go\n  events.Subscribe(\"Event1\", func(args ...any) {\n      // Handle the event with args slice.\n  })\n  ```\n- A function that accepts concrete argument types:\n  ```go\n  ev.Subscribe(\"Event1\", func(x string, y int, z bool) {\n      // Handle the event with specific args.\n  })\n  ```\n  - The **number of arguments** in the event and the handler can differ because handlers are designed to be flexible and can process varying numbers and types of arguments, allowing for greater versatility in handling different event scenarios.\n  - The **types of arguments** in the event and the handler must be assignable. Otherwise, an error will be returned from a `Trigger()` call.\n  \nEvery handler function could return an `error` which will be joined and returned from `Trigger()` call.\n\n### Container Interface\n\n```go\n// Container defines service container interface.\ntype Container interface {\n    // Start initializes every service in the container.\n    Start() error\n\n    // Close closes service container with all services.\n    // Blocks invocation until the container is closed.\n    Close() error\n\n    // Done is closing after closing of all services.\n    Done() \u003c-chan struct{}\n\n    // Factories returns all defined factories.\n    Factories() []*Factory\n\n    // Services returns all spawned services.\n    Services() []any\n\n    // Events returns events broker instance.\n    Events() Events\n\n    // Resolver returns service resolver instance.\n    // If container is not started, only requested services\n    // will be spawned on `resolver.Resolve(...)` call.\n    Resolver() Resolver\n\n    // Invoker returns function invoker instance.\n    // If container is not started, only requested services\n    // will be spawned to invoke the func.\n    Invoker() Invoker\n}\n```\n\n### Container Events\n\nThe service container emits several events during its lifecycle:\n\n| Event               | Description                                                                   |\n|---------------------|-------------------------------------------------------------------------------|\n| `ContainerStarting` | Emitted when the container's start method is invoked.                         |\n| `ContainerStarted`  | Emitted when the container's start method has completed.                      |\n| `ContainerClosing`  | Emitted when the container's close method is invoked.                         |\n| `ContainerClosed`   | Emitted when the container's close method has completed.                      |\n| `UnhandledPanic`    | Emitted when a panic occurs during container initialization, start, or close. |\n\n### Container Errors\n\nThe service container may return the following errors, which can be checked using `errors.Is`:\n\n| Error                       | Description                                                                           |\n|-----------------------------|---------------------------------------------------------------------------------------|\n| `ErrFactoryReturnedError`   | Occurs when the factory function returns an error during invocation.                  |\n| `ErrServiceNotResolved`     | Occurs when resolving a service fails due to an unregistered service type.            |\n| `ErrServiceDuplicated`      | Occurs when a service type duplicate found during the initialization procedure.       |\n| `ErrCircularDependency`     | Occurs when a circular dependency found during the initialization procedure.          |\n| `ErrHandlerArgTypeMismatch` | Occurs when an event handler's arguments do not match the event's expected arguments. |\n","funding_links":[],"categories":["Miscellaneous","杂项","Microsoft Office"],"sub_categories":["Dependency Injection","依赖注入"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNVIDIA%2Fgontainer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNVIDIA%2Fgontainer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNVIDIA%2Fgontainer/lists"}