gontainer

package module
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Apr 17, 2024 License: Apache-2.0, BSD-2-Clause, BSD-3-Clause, + 2 more Imports: 9 Imported by: 0

README

License GoDoc Test Report

Gontainer

Dependency injection service container for Golang projects.

Features

  • 🚀 Eager services instantiation with automatic dependencies resolution and optional dependencies support.
  • 🛠 Dependency Injection for service factories, avoiding manual fetch through container API.
  • 🔄 Reverse-to-Instantiation order for service termination to ensure proper resource release and shutdown.
  • 📣 Events broker for inter-service container-wide communications.
  • 🤖 Clean, tested and with small codebase based on reflection.

Examples

Quick Start

  1. Define an example service object.
    // MyService performs some crucial tasks.
    type MyService struct{}
    
    // SayHello outputs a friendly greeting.
    func (s *MyService) SayHello(name string) {
        log.Println("Hello,", name)
    }
    
  2. Define a service factory.
    func NewMyService() *MyService {
       return new(MyService)
    }
    
  3. Register service factories in the container.
    container, err := gontainer.New(
       // Define MyService factory in the container.
       gontainer.NewFactory(NewMyService),
    
       // Here we can define another services depending on `*MyService`.
       // All dependencies are declared using factory function args.
       gontainer.NewFactory(func(service *MyService) {
          service.SayHello("Username")
       }),
    )
    if err != nil {
       log.Fatalf("Failed to init service container: %s", err)
    }
    
  4. Start the container and launch all factories.
    if err := container.Start(); err != nil {
       log.Fatalf("Failed to start service container: %s", err)
    }
    

Key Concepts

Service Factories

The Service Factory is a key component of the service container, serving as a mechanism for creating service instances. A Service Factory is essentially a function that accepts another services and returns an instances of services of concrete types or an interfaces and optionally spawn an error in the last return argument. Using service factory signature, the service container will resolve and spawn all dependency services using reflection and fail, if there are unresolvable dependencies.

// MyServiceFactory is an example of a service factory.
func MyServiceFactory( /* service dependencies */) *MyService {
   // Initialize service instance.
   return new(MyService)
}

// MyServiceFactory depends on two services.
func MyServiceFactory(svc1 MyService1, svc2 MyService2) MyService {...}

// MyServiceFactory optionally depends on the service.
func MyServiceFactory(optSvc1 gontainer.Optional[MyService1]) {...}

// MyServiceFactory provides two services.
func MyServiceFactory() (MyService1, MyService2) {...}

// MyServiceFactory provides two services and spawn error.
func MyServiceFactory() (MyService1, MyService2, error) {...}

// MyServiceFactory provides no services an error.
func MyServiceFactory() error {...}

// MyServiceFactory provides nothing. Sic!
func MyServiceFactory() {...}

The factory function's role is to perform any necessary initializations and return a fully-configured service instance to the container.

There are two predefined by container service types that may be used as a dependencies in the factory arguments.

  1. The context.Context service provides the per-service context, inherited from the root app context. This context is cancelled right before the service's Close() call and intended to be used with service functions.
  2. The gontainer.Events service provides the events broker. It can be used to send and receive events inside service container between services or outside from the client code.
Services

A service is a functional component of the application, created and managed by a Service Factory. The lifetime of a service is tied to the lifetime of the entire container.

A service may optionally implement a Close() error method, which is called when the container is shutting down.

// MyService defines example service.
type MyService struct {}

// SayHello is service domain method example. 
func (s *MyService) SayHello(name string) {
    fmt.Println("Hello,", name)
}

// Close is an optional method called from container's Close(). 
func (s *MyService) Close() error {
   // Synchronous cleanup logic here.
   return nil
}
Service Functions

The Service Function is a specialized form of service optimized for simpler tasks. Instead of returning an concrete type object or an interface, the service factory returns a function that conforms to func() error type.

The function serves two primary roles:

  • It encapsulates the behavior to execute when the container starts asynchronously to the Start() method.
  • It returns an error, which is treated as if it were returned by a conventional Close() method.
// MyServiceFactory is an example of a service function usage.
func MyServiceFactory(ctx context.Context) func () error {
   return func () error {
      // Await its order in container close.
      <-ctx.Done()
      
      // Return nil from the `service.Close()`.
      return nil
   }
}

In this design, the factory function is responsible for receiving the context. This context is canceled when the service needs to close, allowing the function to terminate gracefully.

Errors returned by the function are processed as if they were errors returned by a standard Close() method to the container.

Events Broker

The Events Broker is an additional part of the service container architecture. It facilitates communication between services without them having to be directly aware of each other. The Events Broker works on a publisher-subscriber model, enabling services to publish events to, and subscribe to events from, a centralized broker.

This mechanism allows services to remain decoupled while still being able to interact through a centralized medium. In particular, the gontainer.Events service provides an interface to the events broker and can be injected as a dependency in any service factory. Even more, subscription is working before container.Start() with the gontainer.WithSubscribe() option, e.g. it can be used for config validation.

Builtin events
  1. ContainerStarting: produced when container start method invoked. Synchronous.
  2. ContainerStarted: produced when container start method finished. Synchronous.
  3. ContainerClosing: produced when container close method invoked. Synchronous.
  4. ContainerClosed: produced when container close method finished. Synchronous.
  5. UnhandledPanic: produced when the panic is happened on container init, start or close.
Container Lifecycle
  1. New: The container is instantiated, and the reflection parsing of service factories is completed. The container ensures that service dependencies are resolved.
  2. Subscription: Factories may subscribe to the corresponding events via the Events Broker.
  3. Start: Service factories are called to instantiate all service instances in container.
  4. Runtime: The container, along with all its services, are now fully operational.
  5. Termination: Upon receiving a close call or event, the container will invoke the Close() method on each service that has one, in the reverse order of their initialization.
  6. Closed: The container is fully terminated, and all resources have been released.

Documentation

Index

Constants

View Source
const (
	// ContainerStarting declares container starting event.
	ContainerStarting = "ContainerStarting"

	// ContainerStarted declares container started event.
	ContainerStarted = "ContainerStarted"

	// ContainerClosing declares container closing event.
	ContainerClosing = "ContainerClosing"

	// ContainerClosed declares container closed event.
	ContainerClosed = "ContainerClosed"

	// UnhandledPanic declares unhandled panic in container.
	UnhandledPanic = "UnhandledPanic"
)

Events declaration.

Variables

This section is empty.

Functions

This section is empty.

Types

type Container

type Container interface {
	// Start initializes every service in the container.
	Start() error

	// Close closes service container with all services.
	// Blocks invocation until the container is closed.
	Close() error

	// Done is closing after closing of all services.
	Done() <-chan struct{}

	// Events returns events broker instance.
	Events() Events
}

Container defines service container interface.

func New

func New(factories ...*Factory) (result Container, err error)

New returns new container instance with a set of configured services. The `factories` specifies factories for services with dependency resolution.

type Event

type Event interface {
	// Name returns event name.
	Name() string

	// Args returns event arguments.
	Args() []any
}

Event declares service container events.

func NewEvent

func NewEvent(name string, args ...any) Event

NewEvent returns new event instance.

type Events

type Events interface {
	// Subscribe registers event handler.
	Subscribe(name string, handler any)

	// Trigger triggers specified event handlers.
	Trigger(event Event) error
}

Events declares event broker type.

type Factory

type Factory struct {
	// contains filtered or unexported fields
}

Factory declares service factory.

func NewFactory

func NewFactory(factoryFn any, opts ...FactoryOpt) *Factory

NewFactory creates new service factory with factory func.

func NewService added in v1.1.0

func NewService[T any](singleton T) *Factory

NewService creates new service factory with predefined service.

type FactoryOpt

type FactoryOpt func(*Factory)

FactoryOpt defines factory option.

func WithSubscribe

func WithSubscribe(event string, handler any) FactoryOpt

WithSubscribe registers event handler for the factory.

type Handler

type Handler func(args ...any) error

Handler declares event handler function.

type Optional

type Optional[T any] struct {
	// contains filtered or unexported fields
}

Optional defines optional service dependency.

func (Optional[T]) Get

func (o Optional[T]) Get() T

Get returns optional service instance.

type Resolver added in v1.4.0

type Resolver interface {
	// Resolve returns specified dependency.
	Resolve(any) error
}

Resolver defines service resolver interface.

Jump to

Keyboard shortcuts

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