pal

package module
v0.11.2 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2026 License: MIT Imports: 20 Imported by: 3

README

Pal

GoDoc Build Status License codecov

Pal is an opinionated IoC framework for Go.

Pal is almost feature complete and is already used in production. However, contributions with examples, documentation updates and new features are very welcome.

Motivation

The existing IoC frameworks are either too "heavy" and too flexible for my purpose like fx or wire or too low level and restrictive like do. All of them share the same trait: you need to design your app with them in mind.

For the past few years I've been using do in my personal and commercial projects, and it worked quite well. However, it does not have reflection-based injection, and its API does not really help with implementing your own. This leads to huge amounts of boilerplate code where you fetch your dependencies from the injector over and over again.

Pal inherits some design decisions from fx and do and tries to offer the most nondisruptive IoC experience if you follow a few rules described below.

Goals
  • Nondisruptive API:

    • You can integrate pal with any app, even if it already uses another IoC framework.
    • Even though migrating an existing app to pal may require some app redesign, you can do it gradually, migrating one module at a time.
    • Pal tries not to leak into service implementations, so in most of the cases you won't even need struct tags in your services. But if you really need to interact with pal within your services, you can do it.
  • Versatility:

    • You can use pal to build any kind of app: cli, dbms, web, video games, anything.
    • Pal is aware of other IoC tools and application frameworks, so it tries to coexist with them rather than conflict.
  • Testability:

    • Pal provides tools to simplify testing, such as the ability to register mock services using ProvideConst.
    • The container design allows for easy swapping of real implementations with test doubles.
    • Services can be tested in isolation by creating a test container with only the necessary dependencies.
  • Safety:

    • When following simple rules, pal never explodes in runtime in the middle of the night. It will only explode during initialization, so you can catch it immediately after deployment. Unfortunately, we can't check everything in compile time.
    • Pal tries its best to gracefully shut down the app when interrupted.
    • Pal does not try to recover from errors. It's user's responsibility to design resilient services, and it's the execution environment's responsibility to restart the crashed app.
    • Pal is aware of contexts, all service lifetime callbacks have timeouts: inits, health checks and shutdowns. Users are forced to configure these timeouts.
    • After initialization, pal is goroutine-safe.
Non-goals
  • Performance: it's assumed that pal is only active during app initialization and shutdown, all other time it only performs periodic health checks. Thus, pal's initialization and shutdown should not be blazing fast, it should be fast enough. Using factory services is more expensive than using singleton services, but should generally be fast enough.

  • Extensibility and configurability: pal is not designed to be the most flexible IoC framework, only flexible enough to reach its goals.

  • Lightweightness: while looking simple and having a minimalistic API, pal is not that simple inside. It uses reflection and some other dirty tricks so you don't have to.

  • Fool-proofness: even though pal performs some configuration validation, it does not protect from making all possible mistakes. For instance, it's the user's responsibility to make sure each registered service uses a unique interface.

Glossary

  • IoC — Inversion of Control is a design principle in software engineering that transfers control of a process or object from one part of the program to another.

  • Dependency Injection — specific implementation of the Inversion of Control pattern where objects receive their dependencies through constructor arguments, method calls, or property setters rather than creating them themselves.

  • Container — a registry of services within the app. It is responsible for managing service lifecycle.

  • Service — is an interface that defines a set of methods or operations. Concrete implementations of these services are responsible for providing specific functionalities within the application. Services can perform tasks on their own or can be used by other services. Pal recognizes a few types of services:

    • Singleton service — such services are created only once during application initialization. It can be a client for third party service or a piece of business logic. Most of your services should be singletons. Pal recognizes a special kind of Singleton service:
      • Runner — is a special singleton service that runs in the background. Pal runs such services in the background. Any app should have at least one Runner. For a web service it would be the place where you run http.ListenAndServe(...). For a CLI - where the main logic is. Pal.Run() exits when all runners are finished. Run() will exit immediately if no runners are registered. If another framework already uses Run on your type, implement PalRunner and PalRun instead (same behavior).
    • Factory service — a special type of service. Unlike singletons, a new instance of a factory service is created every time it is invoked.
    • Const service — a service that wraps an existing instance. It's useful for registering already created objects as services.

    Each service can implement optional lifecycle interfaces. Prefer the standard names when they do not conflict with other frameworks; otherwise use the Pal-prefixed pair (same semantics, different method names):

    • Initer / PalIniter — one-time setup after dependencies are injected (e.g. open a DB pool). Use PalInit when Init is already taken.
    • Shutdowner / PalShutdowner — cleanup (e.g. close connections). Use PalShutdown when Shutdown is already taken.
    • HealthChecker / PalHealthChecker — liveness logic for probes. Use PalHealthCheck when HealthCheck is already taken.
    • RunConfiger / PalRunConfiger — optional runner scheduling (Wait vs fire-and-forget). Use PalRunConfig when RunConfig is already taken.

    Dispatch order when more than one mechanism could apply: lifecycle hooks (ToInit, ToShutdown, ToHealthCheck) run first; then Pal-prefixed methods if implemented; then the standard Init / Shutdown / HealthCheck / RunConfig methods. For background work, PalRun is preferred over Run when both exist, and PalRunConfig over RunConfig when both exist. In normal use, implement one style per phase (standard or Pal-prefixed), not both on purpose.

API Functions

Pal provides several functions for registering services:

  • Provide[T any](value T) - Registers an instance of service.
  • ProvideFn[T any](fn func(ctx context.Context) (T, error)) - Registers a singleton service created using the provided function.
  • ProvideFactory{0-5}[I any, T any, {0-5}P any](fn func(ctx context.Context, {0-5}P args) (T, error))) - Registers a factory service created using the provided function with given amount of arguments.
  • ProvideList(...ServiceDef) - Registers multiple services at once, useful when splitting apps into modules, see example
  • There are also Named versions of Provide functions, they can be used along with name tag and Named versions Invoke functions if you want to give your services explicit names.

Pal also provides functions for retrieving services:

  • Invoke[T](ctx, invoker, args...) - Retrieves or creates an instance of type T from the container, factory services may require arguments.
  • InvokeAs[T, C](ctx, invoker, args...) - A wrapper around Invoke, casts the invoked service to C, and returns an error if casting fails.
  • InvokeByInterface[I](ctx, invoker, args...) - Retrieves the only service that implements the given interface I. Returns an error if there are zero or more than one service implementing the interface or if I is not an interface. Note: do not overuse this function as it gets slower the more services you have.
  • Build[S](ctx, invoker) - Creates an instance of S, resolves its dependencies, injects them into its fields.
  • InjectInto[S](ctx, invoker, *S) - Resolves S's dependencies and injects them into its fields.
  • There are Named versions of Invoke functions that allow retrieving services by their explicit names.

All these functions accept nil as invoker, in this case, a Pal instance will be extracted from the context. Pal automatically adds itself into contexts passed to Init, Shutdown, and Run under the pal.CtxValue key. You can extract it manually with pal.FromContext

Service Types

Pal supports several types of services, each designed for different use cases:

Singleton Services

Singleton services are created once during application initialization and reused throughout the application's lifetime. They are ideal for:

  • Database connections and clients
  • HTTP clients and servers
  • Configuration objects
  • Business logic services
  • Any stateful component that should be shared

Registration:

// Register a singleton service
pal.Provide[MyService](&MyServiceImpl{})

// Register a singleton service using a factory function
pal.ProvideFn[MyService](func(ctx context.Context) (MyServiceImpl, error) {
    return &MyServiceImpl{}, nil
})
Factory Services

Factory services create a new instance every time they are invoked. They may accept up to 5 arguments. Factories that accept arguments cannot be explicit dependencies of other services. They are perfect for:

  • Stateless components
  • Request-scoped objects
  • Objects that need different configurations per use
  • Components that should not be shared

Registration:

// Register a factory service with no arguments
pal.ProvideFactory0[MyService](func(ctx context.Context) (*MyServiceImpl, error) {
    return &MyServiceImpl{}, nil
})

// Register a factory service with arguments
pal.ProvideFactory2[MyService](func(ctx context.Context, url string, timeout time.Duration) (*MyServiceImpl, error) {
    return &MyServiceImpl{URL: url, Timeout: timeout}, nil
})
Invocation

There are 2 ways to invoke a factory service:

  • manual invocation:

    pal.Invoke[MyService](ctx, p, "https://example.com", timeout)
    

    this way must never be used during initialization as Pal does not know that your service depends on a factory service and the factory service may not be yet initialized.

  • invocation using injected factory function:

      type SomeService struct {
        ...
        // parameters of a factory function must match the parameters of the function passed to pal.ProvideFactory
        // but the return value must match the first type argument pal.ProvideFactory
        CreateMyService(ctx context.Context, url string, timeout time.Duration) (MyService, error)
        ...
      }
    

    This way Pal can see that SomeService depends on MyService and adjust the initialization process accordingly. It is safe to call CreateMyService from MyService.Init().

Const Services

Const services wrap existing instances. They are useful for:

  • Registering third-party objects
  • Wrapping existing instances that don't implement pal interfaces
  • Testing with mock objects

Registration:

// Register a const service
existingInstance := &MyServiceImpl{}
pal.ProvideConst[MyService](existingInstance)
Runner Services

Runner services are special singleton services that run in the background. They implement the Runner interface and are used for:

  • HTTP servers
  • Message consumers
  • Background workers
  • Long-running processes

Example:

type HTTPServer struct {
    // dependencies...
}

func (s *HTTPServer) Run(ctx context.Context) error {
    // Start HTTP server and run until context is canceled
    return http.ListenAndServe(":8080", s.router)
}

Tags

Pal supports 3 struct tags:

  • pal:"skip" - fields marked with this tag won't be injected.
  • pal:"match_interface" - InvokeByInterface will be used to inject this dependency
  • pal:"name=<name>" - a service will be invoked by its explicit name.

Lifecycle Hooks

Pal provides lifecycle hooks that allow you to customize service behavior without implementing the full lifecycle interfaces. Hooks take precedence over interface methods and are useful for:

  • Adding custom initialization logic
  • Implementing custom shutdown procedures
  • Adding health check functionality
  • Wrapping existing objects with lifecycle behavior
Available Hooks
  • ToInit - Called during service initialization, after dependencies are injected
  • ToShutdown - Called during service shutdown, before dependencies are shut down
  • ToHealthCheck - Called during health checks
Using Hooks

Even though pal supports hooks, using lifecycle management methods is recommended in the first place as they keep lifecycle management code closer to the resources it manages.

Hooks can be used with any service type and provide a flexible way to add lifecycle behavior:

// With const services
pal.ProvideConst[MyService](existingInstance).
    ToInit(func(ctx context.Context, service MyService, pal *pal.Pal) error {
        // Custom initialization logic
        return service.Connect()
    }).
    ToShutdown(func(ctx context.Context, service MyService, pal *pal.Pal) error {
        // Custom shutdown logic
        return service.Disconnect()
    }).
    ToHealthCheck(func(ctx context.Context, service MyService, pal *pal.Pal) error {
        // Custom health check logic
        return service.Ping()
    })

// With function-based services
pal.ProvideFn[MyService](func(ctx context.Context) (*MyServiceImpl, error) {
    return &MyServiceImpl{}, nil
}).
    ToInit(func(ctx context.Context, service MyService, pal *pal.Pal) error {
        return service.Initialize()
    })

Examples

Examples can be found here:

Example apps

Service and container lifecycle

The lifecycle of services and the container in Pal follows a well-defined sequence:

  1. Registration: Services are registered with Pal using the Provide* functions.

  2. Initialization:

    • When Pal.Init() is called, Pal builds a dependency graph of all registered services.
    • Services are initialized in dependency order (dependencies first).
    • For each service, Pal:
      • Creates an instance
      • Injects dependencies into its fields
      • Calls ToInit hook if specified; otherwise PalInit() if it implements PalIniter, otherwise Init() if it implements Initer
      • If ToInit is specified, neither PalInit nor Init is called.
  3. Running:

    • After initialization, Pal starts all services that implement Runner or PalRunner in background goroutines.
    • Pal calls PalRun or Run (respecting the same precedence as above when both exist) with a context that will be canceled during shutdown.
    • Runners that implement RunConfiger or PalRunConfiger supply scheduling via RunConfig or PalRunConfig (Pal-prefixed wins if both are present); otherwise a default applies for types that only implement Run / PalRun.
  4. Health Checking:

    • Developers can use Pal.HealthCheck() to initiate the health check sequence. In a web application it should be called from the /health handler which can be used as a liveness probe.
    • Services may implement health checks via ToHealthCheck, or via PalHealthCheck (PalHealthChecker), or via HealthCheck (HealthChecker).
    • If ToHealthCheck is specified, neither PalHealthCheck nor HealthCheck is called.
    • If any service returns an error, Pal initiates a graceful shutdown.
  5. Shutdown:

    • When Pal.Shutdown() is called or a termination signal is received, Pal initiates the shutdown sequence.
    • Pal cancels the context for all running services (Runners) and awaits for runners to finish.
    • Pal shuts down dependencies in reverse to initialization order. If ToShutdown is set, it runs; otherwise PalShutdown or Shutdown is used in that precedence order.
    • If ToShutdown is specified, neither PalShutdown nor Shutdown is called.
    • If all services shut down successfully, Pal.Run() returns nil, otherwise it returns the collected errors.

Additional features

Integration with slog

Pal can automatically inject *slog.Logger to your services. To enable this behavior call InjectSlog(). Pal will automatically add the name of service to the component attribute of the logger. Attributes added to the injected logger can be customized by passing arguments to InjectSlog().

Embedded healthcheck server

Pal includes an embedded healthcheck server so you don't have to implement it yourself. Just call RunHealthCheckServer(":8081", "/healthz") and specify addr and path and the server will start on the specified addr and respond on GET requests on the specified path. It never responds with a body and does not add any headers beyond the default ones. It may return one of 4 status codes:

  • 200 - all services are healthy
  • 404 - wrong path requested
  • 405 - wrong HTTP method is used
  • 500 - one or more services are unhealthy
Service dependency inspection

Pal includes a built-in inspection module that provides a web interface to visualize your service dependency graph. This is useful for understanding the structure of your application and debugging dependency issues.

To enable the inspection server, register the inspect service:

import "github.com/zhulik/pal/inspect"

// Register with default port (24242)
pal.ProvideList(inspect.Provide())

// Or specify a custom port
pal.ProvideList(inspect.Provide(8080))

The inspection server provides two endpoints:

  • /pal/tree - Interactive HTML visualization of the dependency graph
  • /pal/tree.json - JSON representation of the dependency graph for programmatic access

The visualization shows:

  • Service nodes with their types (singleton, factory)
  • Dependency relationships between services
  • Service capabilities (standard and Pal-prefixed lifecycle interfaces: init, run, run config, health check, shutdown)

Best practices

To get the most out of Pal, follow these best practices:

  1. Service Design:

    • Design services as small, focused components with a single responsibility.
    • Use interfaces to define service contracts, especially for services that might have multiple implementations.
    • Implement the optional lifecycle interfaces (Initer, Shutdowner, HealthChecker, or the Pal-prefixed alternatives when names clash) when appropriate.
    • Use ProvideConst and ProvideFn* functions with ToShutdown hook to register services without dedicated interfaces and struct wrappers.
  2. Dependency Management:

    • Use singleton services for stateful components like database connections, HTTP clients, etc.
    • Use factory services for stateless components that need to be created on demand.
    • Prefer constructor injection (via fields) over service locator pattern (directly using Pal.Invoke()).
  3. Error Handling:

    • Handle errors appropriately in service implementations, especially in Init, Shutdown, and HealthCheck methods.
    • Use the context provided to respect timeouts and cancellation signals.
    • Log errors with appropriate context to aid debugging.
  4. Testing:

    • Create mock implementations of your service interfaces for testing.
    • Use ProvideConst to register mock services in your tests.
    • Test each service in isolation before testing them together.
  5. Application Structure:

    • Organize your code by domain or feature, not by technical concerns.
    • Keep your main function simple - it should just create Pal, register services, and call Run().
    • Use Runners for long-running processes like HTTP servers or message consumers.
  6. Configuration:

    • Set appropriate timeouts for initialization, health checking, and shutdown.
    • Register signal handlers to ensure graceful shutdown on termination signals.
    • Use ToInit hooks to configure services that don't have their own configuration mechanism.

Troubleshooting

Here are solutions to common issues you might encounter when using Pal:

  1. Service Not Found:

    • Symptom: ErrServiceNotFound error when trying to invoke a service.
    • Possible Causes:
      • The service wasn't registered with Pal.
      • The service was registered with a different interface type than the one being requested.
    • Solution: Check that you've registered the service with the correct interface type and that the registration happens before the service is invoked.
  2. Service Initialization Failed:

    • Symptom: ErrServiceInitFailed error during container initialization.
    • Possible Causes:
      • The service's Init method returned an error.
      • A dependency of the service couldn't be initialized.
    • Solution: Check the error message for details about which service failed and why. Ensure all dependencies are properly registered and initialized.
  3. Service Invalid:

    • Symptom: ErrServiceInvalid error when trying to invoke a service.
    • Possible Causes:
      • The service implementation doesn't satisfy the interface it was registered with.
      • Type assertion failed during service retrieval.
    • Solution: Ensure the service implementation correctly implements all methods of the interface it's registered with.
  4. Circular Dependencies:

    • Symptom: Error during container initialization about a cycle in the dependency graph.
    • Possible Causes: Two or more services depend on each other, creating a circular dependency.
    • Solution: Refactor your services to break the circular dependency. Consider using a factory service or restructuring your code.
  5. Timeout During Initialization/Shutdown:

    • Symptom: Panic with "initialization timed out" or "shutdown timed out".
    • Possible Causes: A service's Init or Shutdown method took longer than the configured timeout.
    • Solution: Increase the timeout using Pal.InitTimeout() or Pal.ShutdownTimeout(), or optimize the service to complete faster.
  6. Context Cancellation Not Respected:

    • Symptom: Services don't shut down gracefully when the context is canceled.
    • Possible Causes: The service isn't checking for context cancellation in its Run method.
    • Solution: Ensure all long-running operations in your services respect context cancellation by checking ctx.Done() regularly.

Development

asdf is the recommended package manager for development tools. Tool versions are pinned in .tool-versions.

Basic setup:

asdf plugin add golang
asdf plugin add golangci-lint
asdf plugin add task
asdf install
task

If task runs successfully, your local development setup is ready.

Contributing

Contributions are welcome.

  1. Fork it, implement your feature and open a PR.
  2. Wait for review.
  3. Address possible comments.
  4. ???
  5. You're a contributor now, many thanks!

Documentation

Overview

Example (Container)

This example demonstrates how to create a Pal instance with services and use it.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/zhulik/pal"
)

// SimpleService is a test service interface
type SimpleService interface {
	GetMessage() string
}

// SimpleServiceImpl implements SimpleService
type SimpleServiceImpl struct{}

// GetMessage returns a greeting message
func (s *SimpleServiceImpl) GetMessage() string {
	return "Hello from SimpleService"
}

// This example demonstrates how to create a Pal instance with services and use it.
func main() {
	// Create a Pal instance with the service
	p := pal.New(
		pal.Provide[SimpleService](&SimpleServiceImpl{}),
	).
		InitTimeout(time.Second).
		HealthCheckTimeout(time.Second).
		ShutdownTimeout(3 * time.Second)

	// Initialize Pal
	ctx := context.Background()
	if err := p.Init(ctx); err != nil {
		fmt.Printf("Failed to initialize Pal: %v\n", err)
		return
	}

	// Invoke the service
	instance, err := p.Invoke(ctx, "github.com/zhulik/pal_test.SimpleService")
	if err != nil {
		fmt.Printf("Failed to invoke service: %v\n", err)
		return
	}

	// Use the service
	service := instance.(SimpleService)
	fmt.Println(service.GetMessage())

}
Output:
Hello from SimpleService
Example (Pal_runner)

This example demonstrates how to use Pal with a runner service.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/zhulik/pal"
)

// ExampleService is a service that runs in the background
type ExampleService interface {
	// This is a public API exposed to other components of the app.
	// No need to include pal interfaces here.
	Foo() string
}

// ExampleServiceImpl implements ExampleService and pal.Runner
type ExampleServiceImpl struct {
}

// Init initializes the services
func (s *ExampleServiceImpl) Init(_ context.Context) error {
	fmt.Println("init")

	// In a real app, here you'd create resources or open connections to other services and databases.

	return nil
}

// Run runs the background task
func (s *ExampleServiceImpl) Run(_ context.Context) error {
	fmt.Println("run")

	// In a real application, this would do some work
	return nil
}

// Shutdown initializes the services
func (s *ExampleServiceImpl) Shutdown(_ context.Context) error {
	fmt.Println("shutdown")

	// In a real app, here you'd release resources or close connections to other services and databases.

	return nil
}

// Foo does foo.
func (s *ExampleServiceImpl) Foo() string {
	// In case of a regular service, you put your actual application logic here.
	// In case of a Runner - you may want to interact with the background job via channels, this is the place.
	return "foo"
}

// This example demonstrates how to use Pal with a runner service.
func main() {
	p := pal.New(
		pal.Provide[ExampleService](&ExampleServiceImpl{}),
	).
		InitTimeout(time.Second).
		HealthCheckTimeout(time.Second).
		ShutdownTimeout(3 * time.Second)

	err := p.Run(context.Background())
	if err != nil {
		fmt.Printf("Pal.Run returned error: %v\n", err)
	}

}
Output:
init
run
shutdown

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrServiceNotFound is returned when a requested service is not found in the container.
	// This typically happens when trying to Invoke a service that hasn't been registered.
	ErrServiceNotFound = errors.New("service not found")

	// ErrMultipleServicesFoundByInterface is returned when multiple services are found in the container by interface.
	ErrMultipleServicesFoundByInterface = errors.New("multiple services found by interface")

	// ErrServiceInitFailed is returned when a service fails to initialize.
	// This can happen during container initialization if a service's Init method returns an error.
	ErrServiceInitFailed = errors.New("service initialization failed")

	// ErrServiceInvalid is returned when a service is invalid.
	// This can happen when a service doesn't implement a required interface or when type assertions fail.
	ErrServiceInvalid = errors.New("service invalid")

	// ErrServiceInvalidArgumentsCount is returned when a service is called with incorrect number of arguments.
	ErrServiceInvalidArgumentsCount = errors.New("service called with incorrect number of arguments")

	// ErrServiceInvalidArgumentType is returned when a service is called with incorrect argument type.
	ErrServiceInvalidArgumentType = errors.New("service called with incorrect argument type")

	// ErrFactoryServiceDependency is returned when a service with a factory service dependency is invoked.
	ErrFactoryServiceDependency = errors.New("factory service cannot be a dependency of another service")

	// ErrServiceInvalidCast is returned when a service is cast to a different type.
	ErrServiceInvalidCast = errors.New("failed to cast service to the expected type")

	// ErrInvokerIsNotInContext is returned when a context passed to Invoke does not contain a Pal instance.
	ErrInvokerIsNotInContext = errors.New("invoker is not in context")

	// ErrInvalidTag is returned when a tag is invalid.
	ErrInvalidTag = errors.New("invalid tag")

	// ErrNotAnInterface is returned when a type is not an interface.
	ErrNotAnInterface = errors.New("not an interface")
)

Error variables used throughout the package

View Source
var DefaultShutdownSignals = []os.Signal{syscall.SIGINT, syscall.SIGTERM}

DefaultShutdownSignals is the default signals that will be used to shutdown the app.

View Source
var ErrNoMainRunners = errors.New("no main runners found")

Functions

func Build added in v0.5.1

func Build[T any](ctx context.Context, invoker Invoker) (*T, error)

Build resolves dependencies for a struct of type T using the provided context and Invoker. It initializes the struct's fields by injecting appropriate dependencies based on the field types. Returns the fully initialized struct or an error if dependency resolution fails. Invoker may be nil, in this case an instance of Pal will be extracted from the context, if the context does not contain a Pal instance, an error will be returned.

func InjectInto added in v0.5.1

func InjectInto[T any](ctx context.Context, invoker Invoker, s *T) error

InjectInto populates the fields of a struct of type T with dependencies obtained from the given Invoker. It only sets fields that are exported and match a resolvable dependency, skipping fields when ErrServiceNotFound occurs. Returns an error if dependency invocation fails or other unrecoverable errors occur during injection.

func Invoke

func Invoke[T any](ctx context.Context, invoker Invoker, args ...any) (T, error)

Invoke retrieves or creates an instance of type T from the given Pal container. Invoker may be nil, in this case an instance of Pal will be extracted from the context, if the context does not contain a Pal instance, an error will be returned.

func InvokeAs added in v0.10.0

func InvokeAs[T any, C any](ctx context.Context, invoker Invoker, args ...any) (*C, error)

InvokeAs invokes a service and casts it to the expected type. It returns an error if the cast fails. May be useful when invoking a service with an interface type and you want to cast it to a concrete type. Invoker may be nil, in this case an instance of Pal will be extracted from the context, if the context does not contain a Pal instance, an error will be returned.

func InvokeByInterface added in v0.10.0

func InvokeByInterface[I any](ctx context.Context, invoker Invoker, args ...any) (I, error)

InvokeByInterface invokes a service by interface. It iterates over all services and returns the only one that implements the interface. If no service implements the interface, or multiple services implement the interface, or given I is not an interface an error will be returned. Invoker may be nil, in this case an instance of Pal will be extracted from the context, if the context does not contain a Pal instance, an error will be returned.

func InvokeNamed added in v0.10.0

func InvokeNamed[T any](ctx context.Context, invoker Invoker, name string, args ...any) (T, error)

InvokeNamed is like Invoke but allows to specify a name.

func InvokeNamedAs added in v0.10.0

func InvokeNamedAs[T any, C any](ctx context.Context, invoker Invoker, name string, args ...any) (*C, error)

InvokeNamedAs is like InvokeAs but allows to specify a name.

func MustBuild added in v0.8.1

func MustBuild[T any](ctx context.Context, invoker Invoker) *T

MustBuild is like Build but panics if an error occurs.x

func MustInjectInto added in v0.8.1

func MustInjectInto[T any](ctx context.Context, invoker Invoker, s *T)

MustInjectInto is like InjectInto but panics if an error occurs.

func MustInvoke added in v0.8.1

func MustInvoke[T any](ctx context.Context, invoker Invoker, args ...any) T

MustInvoke is like Invoke but panics if an error occurs.

func MustInvokeAs added in v0.10.0

func MustInvokeAs[T any, C any](ctx context.Context, invoker Invoker, args ...any) *C

MustInvokeAs is like InvokeAs but panics if an error occurs.

func MustInvokeByInterface added in v0.10.0

func MustInvokeByInterface[I any](ctx context.Context, invoker Invoker, args ...any) I

MustInvokeByInterface is like InvokeByInterface but panics if an error occurs.

func MustInvokeNamed added in v0.10.0

func MustInvokeNamed[T any](ctx context.Context, invoker Invoker, name string, args ...any) T

MustInvokeNamed is like InvokeNamed but panics if an error occurs.

func MustInvokeNamedAs added in v0.10.0

func MustInvokeNamedAs[T any, C any](ctx context.Context, invoker Invoker, name string, args ...any) *C

MustInvokeNamedAs is like InvokeNamedAs but panics if an error occurs.

func ParseTag added in v0.10.0

func ParseTag(tags string) (map[Tag]string, error)

func RunServices added in v0.10.0

func RunServices(ctx context.Context, services []ServiceDef) error

RunServices runs the services in 2 runner groups: main and secondary. Block until: - passed context is canceled - any of the runners fails - all runners finish their work It returns ErrNoMainRunners if no main runners among the services. if any of the runners fail, the error is returned and and all other runners are stopped by cancelling the context passed to them.

func WithPal added in v0.10.0

func WithPal(ctx context.Context, pal *Pal) context.Context

Types

type Config

type Config struct {
	InitTimeout        time.Duration `validate:"gt=0"`
	HealthCheckTimeout time.Duration `validate:"gt=0"`
	ShutdownTimeout    time.Duration `validate:"gt=0"`

	AttrSetters []SlogAttributeSetter
}

Config is the configuration for pal.

func (*Config) Validate

func (c *Config) Validate(_ context.Context) error

type Container

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

Container is responsible for storing services, instances and the dependency graph

func NewContainer

func NewContainer(pal *Pal, services ...ServiceDef) *Container

NewContainer creates a new Container instance

func (*Container) Graph added in v0.3.12

func (c *Container) Graph() *dag.DAG[string, ServiceDef]

Graph returns the dependency graph of services. This can be useful for visualization or analysis of the service dependencies.

func (*Container) HealthCheck

func (c *Container) HealthCheck(ctx context.Context) error

func (*Container) Init

func (c *Container) Init(ctx context.Context) error

func (*Container) InjectInto added in v0.5.1

func (c *Container) InjectInto(ctx context.Context, target any) error

func (*Container) Invoke

func (c *Container) Invoke(ctx context.Context, name string, args ...any) (any, error)

func (*Container) InvokeByInterface added in v0.10.0

func (c *Container) InvokeByInterface(ctx context.Context, iface reflect.Type, args ...any) (any, error)

func (*Container) Services

func (c *Container) Services() map[string]ServiceDef

Services returns a map of all registered services in the container, keyed by their names. This can be useful for debugging or introspection purposes.

func (*Container) Shutdown

func (c *Container) Shutdown(ctx context.Context) error

func (*Container) StartRunners

func (c *Container) StartRunners(ctx context.Context) error

StartRunners starts all services that implement the Runner interface in background goroutines. It creates a cancellable context that will be canceled during shutdown. Returns an error if any runner fails, though runners continue to execute independently.

type ContextKey

type ContextKey int

ContextKey is a type used for context value keys to avoid collisions.

const (
	// CtxValue is the key used to store and retrieve the Pal instance from a context.
	// This allows services to access the Pal instance from a context passed to them.
	CtxValue ContextKey = iota
)

type HealthChecker

type HealthChecker interface {
	// HealthCheck is being called when pal is checking the health of the service.
	// If returns an error, pal will consider the service unhealthy and try to gracefully Shutdown the app,
	// Pal.Run() will return an error.
	// ctx has a timeout and only being canceled if it is exceeded.
	//
	// The healthcheck process works as follows:
	// 1. When Pal.HealthCheck() is called Pal initiates the healthcheck sequence. All services are checked concurrently.
	// 2. If any service returns an error, Pal initiates a graceful shutdown
	// 3. Services can use this method to check their internal state or connections to external systems
	// 4. The context provided has a timeout configured via Pal.HealthCheckTimeout()
	HealthCheck(ctx context.Context) error
}

HealthChecker is an optional interface that can be implemented by a service.

type Initer

type Initer interface {
	// Init is being called when pal is initializing the service, after all the dependencies are injected.
	// If returns an error, pal will consider the service unhealthy and try to gracefully Shutdown already initialized services.
	//
	// The initialization process works as follows:
	// 1. During Pal.Init() Pal builds a dependency graph of all registered services
	// 2. Pal initializes services in dependency order.
	// 3. For each service, Pal injects dependencies and then calls Init() if the service implements this interface
	// 4. Services should use this method to perform one-time setup operations like connecting to databases
	// 5. The context provided has a timeout configured via Pal.InitTimeout()
	// 6. If any service returns an error during initialization, Pal will stop the initialization process
	//    and attempt to gracefully shut down any already initialized services
	Init(ctx context.Context) error
}

Initer is an optional interface that can be implemented by a service.

type Invoker

type Invoker interface {
	// Invoke retrieves a service by name from the container.
	// Returns the service instance or an error if the service is not found or cannot be initialized.
	Invoke(ctx context.Context, name string, args ...any) (any, error)

	// InvokeByInterface retrieves a service by interface from the container.
	// Returns the service instance or an error if the service is not found or cannot be initialized.
	InvokeByInterface(ctx context.Context, iface reflect.Type, args ...any) (any, error)

	// InjectInto injects services into the fields of the target struct.
	// It looks at each field's type and tries to find a matching service in the container.
	// Only exported fields can be injected into.
	InjectInto(ctx context.Context, target any) error
}

Invoker is an interface for retrieving services from a container and injecting them into structs. Both Container and Pal implement this interface, allowing services to be retrieved from either.

type LifecycleHook

type LifecycleHook[T any] func(ctx context.Context, service T, pal *Pal) error

LifecycleHook is a function type that can be registered to run at specific points in a service's lifecycle. It receives the service instance, a context, and the Pal instance, and can return an error to indicate failure. These hooks are typically used with ToInit methods to customize service initialization.

type LifecycleHooks added in v0.8.0

type LifecycleHooks[T any] struct {
	Init        LifecycleHook[T]
	Shutdown    LifecycleHook[T]
	HealthCheck LifecycleHook[T]
}

LifecycleHooks is a collection of hooks that can be registered to run at specific points in a service's lifecycle.

type Pal

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

Pal is the main struct that manages the lifecycle of services in the application. It handles service initialization, dependency injection, health checking, and graceful shutdown. Pal implements the Invoker interface, allowing services to be retrieved from it.

func FromContext

func FromContext(ctx context.Context) (*Pal, error)

FromContext retrieves a *Pal from the provided context, expecting it to be stored under the CtxValue key.

func MustFromContext added in v0.10.0

func MustFromContext(ctx context.Context) *Pal

MustFromContext is like FromContext but panics if an error occurs.

func New

func New(services ...ServiceDef) *Pal

New creates and returns a new instance of Pal with the provided Services

func (*Pal) Config added in v0.5.11

func (p *Pal) Config() Config

Config returns a copy of pal's config.

func (*Pal) Container added in v0.3.12

func (p *Pal) Container() *Container

Container returns the underlying Container instance. This can be useful for advanced use cases where direct access to the container is needed.

func (*Pal) HealthCheck

func (p *Pal) HealthCheck(ctx context.Context) error

HealthCheck verifies the health of the service Container within a configurable timeout.

func (*Pal) HealthCheckTimeout

func (p *Pal) HealthCheckTimeout(t time.Duration) *Pal

HealthCheckTimeout sets the timeout for the healthcheck of the services.

func (*Pal) Init

func (p *Pal) Init(ctx context.Context) error

Init initializes Pal. Validates config, creates and initializes all singleton services. If any error occurs during initialization, it will return it and will not try to gracefully shutdown already initialized services. Only first call is effective.

func (*Pal) InitTimeout

func (p *Pal) InitTimeout(t time.Duration) *Pal

InitTimeout sets the timeout for the initialization of the services.

func (*Pal) InjectInto added in v0.5.1

func (p *Pal) InjectInto(ctx context.Context, target any) error

InjectInto injects services into the fields of the target struct. It implements the Invoker interface. The context is enriched with the Pal instance before being passed to the container.

func (*Pal) InjectSlog added in v0.7.0

func (p *Pal) InjectSlog(configs ...SlogAttributeSetter) *Pal

InjectSlog enables automatic slog injection into the services.

func (*Pal) Invoke

func (p *Pal) Invoke(ctx context.Context, name string, args ...any) (any, error)

Invoke retrieves a service by name from the container. It implements the Invoker interface. The context is enriched with the Pal instance before being passed to the container.

func (*Pal) InvokeByInterface added in v0.10.0

func (p *Pal) InvokeByInterface(ctx context.Context, iface reflect.Type, args ...any) (any, error)

InvokeByInterface retrieves a service by interface from the container. It implements the Invoker interface. The context is enriched with the Pal instance before being passed to the container.

func (*Pal) Logger added in v0.5.6

func (p *Pal) Logger() *slog.Logger

Logger returns the logger instance used by Pal. This can be useful for advanced use cases where direct access to the logger is needed.

func (*Pal) Run

func (p *Pal) Run(ctx context.Context, signals ...os.Signal) error

Run eagerly starts runners, then blocks until: - context is canceled - one of the runners fails - one of the given signals is received, if a signal is received again, the app will exit immediately without graceful shutdown - all runners finish their work Not goroutine safe, must only be called once. After one of the events above occurs, the app will be gracefully shot down. Errors returned from runners and during shutdown are collected and returned from Run().

func (*Pal) RunHealthCheckServer added in v0.8.0

func (p *Pal) RunHealthCheckServer(addr, path string) *Pal

RunHealthCheckServer enables the default health check server.

func (*Pal) Services

func (p *Pal) Services() map[string]ServiceDef

Services returns a map of all registered services in the container, keyed by their names. This can be useful for debugging or introspection purposes.

func (*Pal) ShutdownTimeout

func (p *Pal) ShutdownTimeout(t time.Duration) *Pal

ShutdownTimeout sets the timeout for the Shutdown of the services.

type PalHealthChecker added in v0.11.1

type PalHealthChecker interface {
	// PalHealthCheck is equivalent to [HealthChecker.HealthCheck]; see that method's documentation.
	PalHealthCheck(ctx context.Context) error
}

PalHealthChecker is an anternative interface with the same semantics as HealthChecker, using a Pal-prefixed method name so the type can still implement another framework's HealthCheck (or similar) without a clash. Prefer HealthChecker when method names do not conflict. If both PalHealthChecker and HealthChecker are implemented, Pal calls [PalHealthChecker.PalHealthCheck] only.

type PalIniter added in v0.11.1

type PalIniter interface {
	// PalInit is equivalent to [Initer.Init]; see that method's documentation.
	PalInit(ctx context.Context) error
}

PalIniter is an anternative interface with the same semantics as Initer, using a Pal-prefixed method name so the type can still implement another framework's Init without a clash. Prefer Initer when method names do not conflict. If both PalIniter and Initer are implemented, Pal calls [PalIniter.PalInit] only.

type PalRunConfiger added in v0.11.1

type PalRunConfiger interface {
	PalRunConfig() *RunConfig
}

PalRunConfiger is an alternaltive interface with the same semantics as RunConfiger, using a Pal-prefixed method name so the type can still implement another framework's RunConfig without a clash. Prefer RunConfiger when method names do not conflict. If both PalRunConfiger and RunConfiger are implemented, Pal uses [PalRunConfiger.PalRunConfig] only.

type PalRunner added in v0.11.1

type PalRunner interface {
	// PalRun is equivalent to [Runner.Run]; see that method's documentation.
	PalRun(ctx context.Context) error
}

PalRunner is an anternative interface with the same semantics as Runner, using a Pal-prefixed method name so the type can still implement another framework's Run without a clash. Prefer Runner when method names do not conflict. If both PalRunner and Runner are implemented, Pal calls [PalRunner.PalRun] only.

type PalShutdowner added in v0.11.1

type PalShutdowner interface {
	// PalShutdown is equivalent to [Shutdowner.Shutdown]; see that method's documentation.
	PalShutdown(ctx context.Context) error
}

PalShutdowner is an anternative interface with the same semantics as Shutdowner, using a Pal-prefixed method name so the type can still implement another framework's Shutdown without a clash. Prefer Shutdowner when method names do not conflict. If both PalShutdowner and Shutdowner are implemented, Pal calls [PalShutdowner.PalShutdown] only.

type PanicError added in v0.10.1

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

func (*PanicError) Backtrace added in v0.10.1

func (e *PanicError) Backtrace() string

type RunConfig added in v0.5.10

type RunConfig struct {
	Wait bool
}

type RunConfiger added in v0.5.10

type RunConfiger interface {
	RunConfig() *RunConfig
}

RunConfiger is an optional interface that can be implemented by a runner to tell Pal how to handle it.

type Runner

type Runner interface {
	// Run is being called in a background goroutine when Pal is initializing the service, after Init() is called.
	// The provided context will be canceled when Pal is shut down, so the service should monitor it and exit gracefully.
	// This method should implement the main functionality of background services like HTTP servers, message consumers, etc.
	// If this method returns an error, Pal will initiate a graceful shutdown of the application.
	Run(ctx context.Context) error
}

Runner is a service that can be started in a background goroutine. If a service implements this interface, Pal will start this method in a background goroutine when the app is initialized. Can be a one-off or long-running task. Services implementing this interface are initialized eagerly.

type ServiceConst added in v0.5.1

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

ServiceConst is a service that wraps a constant value. It is used to register existing instances as services.

func Provide

func Provide[T any](value T) *ServiceConst[T]

Provide registers a const as a service. `T` is used to generating service name. Typically, `T` would be one of: - An interface, in this case passed value must implement it. Used when T may have multiple implementations like mocks for tests. - A pointer to an instance of `T`. For instance,`Provide[*Foo](&Foo{})`. Used when mocking is not required. If the passed value implements Initer or PalIniter, the matching init method is called after dependency injection, unless a ToInit hook is set on the returned ServiceConst (see ServiceConst.ToInit).

func ProvideNamed added in v0.10.0

func ProvideNamed[T any](name string, value T) *ServiceConst[T]

ProvideNamed registers a const as a service with a given name. Acts like Provide but allows to specify a name.

func (*ServiceConst[T]) HealthCheck added in v0.5.1

func (c *ServiceConst[T]) HealthCheck(ctx context.Context) error

HealthCheck performs a health check on the service if it implements the HealthChecker interface.

func (*ServiceConst[T]) Init added in v0.5.1

func (c *ServiceConst[T]) Init(ctx context.Context) error

Init is a no-op for const services as they are already initialized. It injects dependencies to the stored instance and calls its Init method if it implements Initer.

func (*ServiceConst[T]) Instance added in v0.5.1

func (c *ServiceConst[T]) Instance(_ context.Context, _ ...any) (any, error)

Instance returns the constant instance of the service.

func (*ServiceConst[T]) Make added in v0.5.1

func (c *ServiceConst[T]) Make() any

Make is a no-op for factory services as they are created on demand.

func (*ServiceConst[T]) Run added in v0.5.1

func (c *ServiceConst[T]) Run(ctx context.Context) error

Run executes the service if it implements the Runner interface.

func (*ServiceConst[T]) RunConfig added in v0.5.10

func (c *ServiceConst[T]) RunConfig() *RunConfig

func (*ServiceConst[T]) Shutdown added in v0.5.1

func (c *ServiceConst[T]) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the service if it implements the Shutdowner interface.

func (*ServiceConst[T]) ToHealthCheck added in v0.8.0

func (c *ServiceConst[T]) ToHealthCheck(hook LifecycleHook[T]) *ServiceConst[T]

ToHealthCheck registers a hook function that will be called to perform a health check on the service. If the service implements PalHealthChecker or HealthChecker, those health check methods are not called; the hook has higher priority.

func (*ServiceConst[T]) ToInit added in v0.8.0

func (c *ServiceConst[T]) ToInit(hook LifecycleHook[T]) *ServiceConst[T]

ToInit registers a hook function that will be called to initialize the service. This hook is called after the service is injected with its dependencies. If the service implements PalIniter or Initer, those init methods are not called; the hook has higher priority.

func (*ServiceConst[T]) ToShutdown added in v0.8.0

func (c *ServiceConst[T]) ToShutdown(hook LifecycleHook[T]) *ServiceConst[T]

ToShutdown registers a hook function that will be called to shutdown the service. This hook is called before service's dependencies are shutdown. If the service implements PalShutdowner or Shutdowner, those shutdown methods are not called; the hook has higher priority.

type ServiceDef added in v0.5.1

type ServiceDef interface {
	Initer
	HealthChecker
	Shutdowner
	Runner
	RunConfiger

	// Name returns a name of the service, this will be used to identify the service in the container.
	// The name is typically derived from the interface type the service implements.
	Name() string

	// Make only creates a new instance of the service, it doesn't initialize it.
	// Used only to build the dependency DAG by analyzing the fields of the returned instance.
	// This method should not have side effects as it may be called multiple times.
	Make() any

	// Instance returns a stored instance in the case of singleton service and a new instance in the case of factory.
	// For singletons, this returns the cached instance after initialization.
	// For factories, this creates and returns a new instance each time.
	Instance(ctx context.Context, args ...any) (any, error)

	// Arguments returns the number of arguments the service expects.
	// This is used to validate the number of arguments passed to the service.
	Arguments() int

	// Dependencies allows services to provide their own dependencies.
	Dependencies() []ServiceDef
}

ServiceDef is a definition of a service. In the case of a singleton service, it also holds the instance. This interface embeds the standard lifecycle interfaces (Initer, HealthChecker, Shutdowner, Runner); concrete services may instead implement the Pal-prefixed alternatives (PalIniter, PalHealthChecker, PalShutdowner, PalRunner, PalRunConfiger) where method names would otherwise clash. It adds methods specific to service definition and management.

type ServiceFactory added in v0.5.1

type ServiceFactory[I any, T any] struct {
	ServiceTyped[I]
}

func (*ServiceFactory[I, T]) Make added in v0.5.1

func (c *ServiceFactory[I, T]) Make() any

Make is a no-op for factory services as they are created on demand.

type ServiceFactory0 added in v0.9.0

type ServiceFactory0[I any, T any] struct {
	ServiceFactory[I, T]
	// contains filtered or unexported fields
}

ServiceFactory0 is a factory service that creates a new instance each time it is invoked. It uses the provided function with no arguments to create the instance.

func ProvideFactory0 added in v0.9.0

func ProvideFactory0[I any, T any](fn func(ctx context.Context) (T, error)) *ServiceFactory0[I, T]

ProvideFactory0 registers a factory service that is build with a given function with no arguments.

func ProvideNamedFactory0 added in v0.10.0

func ProvideNamedFactory0[I any, T any](name string, fn func(ctx context.Context) (T, error)) *ServiceFactory0[I, T]

ProvideNamedFactory0 is like ProvideFactory0 but allows to specify a name.

func (*ServiceFactory0[I, T]) Factory added in v0.10.0

func (c *ServiceFactory0[I, T]) Factory() any

Factory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context) (I, error).

func (*ServiceFactory0[I, T]) Instance added in v0.9.0

func (c *ServiceFactory0[I, T]) Instance(ctx context.Context, _ ...any) (any, error)

Instance creates and returns a new instance of the service using the provided function.

func (*ServiceFactory0[I, T]) MustFactory added in v0.10.1

func (c *ServiceFactory0[I, T]) MustFactory() any

MustFactory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context) I. If the instance creation fails, it panics.

type ServiceFactory1 added in v0.9.0

type ServiceFactory1[I any, T any, P1 any] struct {
	ServiceFactory[I, T]
	// contains filtered or unexported fields
}

ServiceFactory1 is a factory service that creates a new instance each time it is invoked. It uses the provided function with one argument to create the instance.

func ProvideFactory1 added in v0.9.0

func ProvideFactory1[I any, T any, P1 any](fn func(ctx context.Context, p1 P1) (T, error)) *ServiceFactory1[I, T, P1]

ProvideFactory1 registers a factory service that is built in runtime with a given function that takes one argument.

func ProvideNamedFactory1 added in v0.10.0

func ProvideNamedFactory1[I any, T any, P1 any](name string, fn func(ctx context.Context, p1 P1) (T, error)) *ServiceFactory1[I, T, P1]

ProvideNamedFactory1 is like ProvideFactory1 but allows to specify a name.

func (*ServiceFactory1[I, T, P1]) Arguments added in v0.9.0

func (c *ServiceFactory1[I, T, P1]) Arguments() int

func (*ServiceFactory1[I, T, P1]) Factory added in v0.10.0

func (c *ServiceFactory1[I, T, P1]) Factory() any

Factory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1) (I, error).

func (*ServiceFactory1[I, T, P1]) Instance added in v0.9.0

func (c *ServiceFactory1[I, T, P1]) Instance(ctx context.Context, args ...any) (any, error)

Instance creates and returns a new instance of the service using the provided function.

func (*ServiceFactory1[I, T, P1]) MustFactory added in v0.10.1

func (c *ServiceFactory1[I, T, P1]) MustFactory() any

MustFactory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1) I. If the instance creation fails, it panics.

type ServiceFactory2 added in v0.9.0

type ServiceFactory2[I any, T any, P1 any, P2 any] struct {
	ServiceFactory[I, T]
	// contains filtered or unexported fields
}

ServiceFactory2 is a factory service that creates a new instance each time it is invoked. It uses the provided function with two arguments to create the instance.

func ProvideFactory2 added in v0.9.0

func ProvideFactory2[I any, T any, P1 any, P2 any](fn func(ctx context.Context, p1 P1, p2 P2) (T, error)) *ServiceFactory2[I, T, P1, P2]

ProvideFactory2 registers a factory service that is built in runtime with a given function that takes two arguments.

func ProvideNamedFactory2 added in v0.10.0

func ProvideNamedFactory2[I any, T any, P1 any, P2 any](name string, fn func(ctx context.Context, p1 P1, p2 P2) (T, error)) *ServiceFactory2[I, T, P1, P2]

ProvideNamedFactory2 is like ProvideFactory2 but allows to specify a name.

func (*ServiceFactory2[I, T, P1, P2]) Arguments added in v0.9.0

func (c *ServiceFactory2[I, T, P1, P2]) Arguments() int

func (*ServiceFactory2[I, T, P1, P2]) Factory added in v0.10.0

func (c *ServiceFactory2[I, T, P1, P2]) Factory() any

Factory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context) (I, error).

func (*ServiceFactory2[I, T, P1, P2]) Instance added in v0.9.0

func (c *ServiceFactory2[I, T, P1, P2]) Instance(ctx context.Context, args ...any) (any, error)

Instance creates and returns a new instance of the service using the provided function.

func (*ServiceFactory2[I, T, P1, P2]) MustFactory added in v0.10.1

func (c *ServiceFactory2[I, T, P1, P2]) MustFactory() any

MustFactory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1, p2 P2) I. If the instance creation fails, it panics.

type ServiceFactory3 added in v0.9.0

type ServiceFactory3[I any, T any, P1 any, P2 any, P3 any] struct {
	ServiceFactory[I, T]
	// contains filtered or unexported fields
}

ServiceFactory3 is a factory service that creates a new instance each time it is invoked. It uses the provided function with three arguments to create the instance.

func ProvideFactory3 added in v0.9.0

func ProvideFactory3[I any, T any, P1 any, P2 any, P3 any](fn func(ctx context.Context, p1 P1, p2 P2, p3 P3) (T, error)) *ServiceFactory3[I, T, P1, P2, P3]

ProvideFactory3 registers a factory service that is built in runtime with a given function that takes three arguments.

func ProvideNamedFactory3 added in v0.10.0

func ProvideNamedFactory3[I any, T any, P1 any, P2 any, P3 any](name string, fn func(ctx context.Context, p1 P1, p2 P2, p3 P3) (T, error)) *ServiceFactory3[I, T, P1, P2, P3]

ProvideNamedFactory3 is like ProvideFactory3 but allows to specify a name.

func (*ServiceFactory3[I, T, P1, P2, P3]) Arguments added in v0.9.0

func (c *ServiceFactory3[I, T, P1, P2, P3]) Arguments() int

func (*ServiceFactory3[I, T, P1, P2, P3]) Factory added in v0.10.0

func (c *ServiceFactory3[I, T, P1, P2, P3]) Factory() any

Factory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1, p2 P2, p3 P3) (I, error).

func (*ServiceFactory3[I, T, P1, P2, P3]) Instance added in v0.9.0

func (c *ServiceFactory3[I, T, P1, P2, P3]) Instance(ctx context.Context, args ...any) (any, error)

Instance creates and returns a new instance of the service using the provided function.

func (*ServiceFactory3[I, T, P1, P2, P3]) MustFactory added in v0.10.1

func (c *ServiceFactory3[I, T, P1, P2, P3]) MustFactory() any

MustFactory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1, p2 P2, p3 P3) I. If the instance creation fails, it panics.

type ServiceFactory4 added in v0.9.0

type ServiceFactory4[I any, T any, P1 any, P2 any, P3 any, P4 any] struct {
	ServiceFactory[I, T]
	// contains filtered or unexported fields
}

ServiceFactory4 is a factory service that creates a new instance each time it is invoked. It uses the provided function with four arguments to create the instance.

func ProvideFactory4 added in v0.9.0

func ProvideFactory4[I any, T any, P1 any, P2 any, P3 any, P4 any](fn func(ctx context.Context, p1 P1, p2 P2, p3 P3, p4 P4) (T, error)) *ServiceFactory4[I, T, P1, P2, P3, P4]

ProvideFactory4 registers a factory service that is built in runtime with a given function that takes four arguments.

func ProvideNamedFactory4 added in v0.10.0

func ProvideNamedFactory4[I any, T any, P1 any, P2 any, P3 any, P4 any](name string, fn func(ctx context.Context, p1 P1, p2 P2, p3 P3, p4 P4) (T, error)) *ServiceFactory4[I, T, P1, P2, P3, P4]

ProvideNamedFactory4 is like ProvideFactory4 but allows to specify a name.

func (*ServiceFactory4[I, T, P1, P2, P3, P4]) Arguments added in v0.9.0

func (c *ServiceFactory4[I, T, P1, P2, P3, P4]) Arguments() int

func (*ServiceFactory4[I, T, P1, P2, P3, P4]) Factory added in v0.10.0

func (c *ServiceFactory4[I, T, P1, P2, P3, P4]) Factory() any

Factory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1, p2 P2, p3 P3, p4 P4) (I, error).

func (*ServiceFactory4[I, T, P1, P2, P3, P4]) Instance added in v0.9.0

func (c *ServiceFactory4[I, T, P1, P2, P3, P4]) Instance(ctx context.Context, args ...any) (any, error)

Instance creates and returns a new instance of the service using the provided function.

func (*ServiceFactory4[I, T, P1, P2, P3, P4]) MustFactory added in v0.10.1

func (c *ServiceFactory4[I, T, P1, P2, P3, P4]) MustFactory() any

MustFactory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1, p2 P2, p3 P3, p4 P4) I. If the instance creation fails, it panics.

type ServiceFactory5 added in v0.9.0

type ServiceFactory5[I any, T any, P1 any, P2 any, P3 any, P4 any, P5 any] struct {
	ServiceFactory[I, T]
	// contains filtered or unexported fields
}

ServiceFactory5 is a factory service that creates a new instance each time it is invoked. It uses the provided function with five arguments to create the instance.

func ProvideFactory5 added in v0.9.0

func ProvideFactory5[I any, T any, P1 any, P2 any, P3 any, P4 any, P5 any](fn func(ctx context.Context, p1 P1, p2 P2, p3 P3, p4 P4, p5 P5) (T, error)) *ServiceFactory5[I, T, P1, P2, P3, P4, P5]

ProvideFactory5 registers a factory service that is built in runtime with a given function that takes five arguments.

func ProvideNamedFactory5 added in v0.10.0

func ProvideNamedFactory5[I any, T any, P1 any, P2 any, P3 any, P4 any, P5 any](name string, fn func(ctx context.Context, p1 P1, p2 P2, p3 P3, p4 P4, p5 P5) (T, error)) *ServiceFactory5[I, T, P1, P2, P3, P4, P5]

ProvideNamedFactory5 is like ProvideFactory5 but allows to specify a name.

func (*ServiceFactory5[I, T, P1, P2, P3, P4, P5]) Arguments added in v0.9.0

func (c *ServiceFactory5[I, T, P1, P2, P3, P4, P5]) Arguments() int

func (*ServiceFactory5[I, T, P1, P2, P3, P4, P5]) Factory added in v0.10.0

func (c *ServiceFactory5[I, T, P1, P2, P3, P4, P5]) Factory() any

Factory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1, p2 P2, p3 P3, p4 P4, p5 P5) (I, error).

func (*ServiceFactory5[I, T, P1, P2, P3, P4, P5]) Instance added in v0.9.0

func (c *ServiceFactory5[I, T, P1, P2, P3, P4, P5]) Instance(ctx context.Context, args ...any) (any, error)

Instance creates and returns a new instance of the service using the provided function.

func (*ServiceFactory5[I, T, P1, P2, P3, P4, P5]) MustFactory added in v0.10.1

func (c *ServiceFactory5[I, T, P1, P2, P3, P4, P5]) MustFactory() any

MustFactory returns a function that creates a new instance of the service. The returned function has the signature func(ctx context.Context, p1 P1, p2 P2, p3 P3, p4 P4, p5 P5) I. If the instance creation fails, it panics.

type ServiceFnSingleton added in v0.5.1

type ServiceFnSingleton[I, T any] struct {
	ServiceFactory[I, T]
	// contains filtered or unexported fields
}

ServiceFnSingleton is a singleton service that is created using a function. It is created during initialization and reused for the lifetime of the application.

func ProvideFn added in v0.5.1

func ProvideFn[I any, T any](fn func(ctx context.Context) (T, error)) *ServiceFnSingleton[I, T]

ProvideFn registers a singleton built with a given function.

func ProvideNamedFn added in v0.10.0

func ProvideNamedFn[I any, T any](name string, fn func(ctx context.Context) (T, error)) *ServiceFnSingleton[I, T]

ProvideFn registers a singleton built with a given function.

func (*ServiceFnSingleton[I, T]) HealthCheck added in v0.5.1

func (c *ServiceFnSingleton[I, T]) HealthCheck(ctx context.Context) error

HealthCheck performs a health check on the service if it implements the HealthChecker interface.

func (*ServiceFnSingleton[I, T]) Init added in v0.5.1

func (c *ServiceFnSingleton[I, T]) Init(ctx context.Context) error

Init initializes the service by calling the provided function to create the instance.

func (*ServiceFnSingleton[I, T]) Instance added in v0.5.1

func (c *ServiceFnSingleton[I, T]) Instance(_ context.Context, _ ...any) (any, error)

Instance returns the singleton instance of the service.

func (*ServiceFnSingleton[I, T]) Run added in v0.5.1

func (c *ServiceFnSingleton[I, T]) Run(ctx context.Context) error

Run executes the service if it implements the Runner interface.

func (*ServiceFnSingleton[I, T]) RunConfig added in v0.5.10

func (c *ServiceFnSingleton[I, T]) RunConfig() *RunConfig

func (*ServiceFnSingleton[I, T]) Shutdown added in v0.5.1

func (c *ServiceFnSingleton[I, T]) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the service if it implements the Shutdowner interface.

func (*ServiceFnSingleton[I, T]) ToHealthCheck added in v0.8.0

func (c *ServiceFnSingleton[I, T]) ToHealthCheck(hook LifecycleHook[T]) *ServiceFnSingleton[I, T]

ToHealthCheck registers a hook function that will be called to perform a health check on the service. If the service implements PalHealthChecker or HealthChecker, those health check methods are not called; the hook has higher priority.

func (*ServiceFnSingleton[I, T]) ToShutdown added in v0.8.0

func (c *ServiceFnSingleton[I, T]) ToShutdown(hook LifecycleHook[T]) *ServiceFnSingleton[I, T]

ToShutdown registers a hook called during shutdown. If the service implements PalShutdowner or Shutdowner, those methods are not called; the hook has higher priority.

type ServiceList added in v0.5.8

type ServiceList struct {
	ServiceTyped[any]
	Services []ServiceDef
}

ServiceList is a proxy service to a list of services.

func ProvideList added in v0.5.8

func ProvideList(services ...ServiceDef) *ServiceList

ProvideList registers a list of given services.

func ProvidePal added in v0.5.8

func ProvidePal(pal *Pal) *ServiceList

ProvidePal registers all services for the given pal instance

func (*ServiceList) Dependencies added in v0.5.8

func (s *ServiceList) Dependencies() []ServiceDef

func (*ServiceList) Instance added in v0.5.8

func (s *ServiceList) Instance(_ context.Context, _ ...any) (any, error)

func (*ServiceList) Name added in v0.5.8

func (s *ServiceList) Name() string

type ServiceRunner added in v0.5.6

type ServiceRunner struct {
	P *Pal
	ServiceTyped[any]
	// contains filtered or unexported fields
}

func ProvideRunner added in v0.5.6

func ProvideRunner(fn func(ctx context.Context) error) *ServiceRunner

ProvideRunner turns the given function into an anounumous runner. It will run in the background, and the passed context will be canceled on app shutdown.

func (*ServiceRunner) Instance added in v0.5.6

func (c *ServiceRunner) Instance(_ context.Context, _ ...any) (any, error)

func (*ServiceRunner) Name added in v0.5.6

func (c *ServiceRunner) Name() string

func (*ServiceRunner) Run added in v0.5.6

func (c *ServiceRunner) Run(ctx context.Context) error

func (*ServiceRunner) RunConfig added in v0.5.10

func (c *ServiceRunner) RunConfig() *RunConfig

type ServiceTyped added in v0.9.0

type ServiceTyped[T any] struct {
	P *Pal
	// contains filtered or unexported fields
}

func (*ServiceTyped[T]) Arguments added in v0.9.0

func (c *ServiceTyped[T]) Arguments() int

func (*ServiceTyped[T]) Dependencies added in v0.9.0

func (c *ServiceTyped[T]) Dependencies() []ServiceDef

func (*ServiceTyped[T]) HealthCheck added in v0.9.0

func (c *ServiceTyped[T]) HealthCheck(_ context.Context) error

HealthCheck is a no-op for factory services as they are created on demand.

func (*ServiceTyped[T]) Init added in v0.9.0

func (c *ServiceTyped[T]) Init(_ context.Context) error

Init is a no-op for factory services as they are created on demand.

func (*ServiceTyped[T]) Make added in v0.9.0

func (c *ServiceTyped[T]) Make() any

Make is a no-op for factory services as they are created on demand.

func (*ServiceTyped[T]) Name added in v0.9.0

func (c *ServiceTyped[T]) Name() string

Name returns the name of the service, which is the type name of T.

func (*ServiceTyped[T]) Run added in v0.9.0

func (c *ServiceTyped[T]) Run(_ context.Context) error

Run is a no-op for factory services as they don't run in the background.

func (*ServiceTyped[T]) RunConfig added in v0.9.0

func (c *ServiceTyped[T]) RunConfig() *RunConfig

func (*ServiceTyped[T]) Shutdown added in v0.9.0

func (c *ServiceTyped[T]) Shutdown(_ context.Context) error

Shutdown is a no-op for factory services as they are created on demand.

type Shutdowner

type Shutdowner interface {
	// Shutdown is being called when pal is shutting down the service.
	// If returns an error, pal will consider this service unhealthy, but will continue to Shutdown the app,
	// Pal.Run() will return an error.
	// ctx has a timeout and only being canceled if it is exceeded.
	// If all the services shutdown successfully, Pal.Run will return nil.
	//
	// The shutdown process works as follows:
	// 1. Whena termination signal is received or the context passed to Pal.Run() is canceled, Pal initiates the shutdown sequence. Services
	// 	  are shutdown in dependency order.
	// 2. Pal cancels the context for all running services (Runners) and awaits for runners to finish.
	// 3. Pal calls Shutdown() on all services that implement this interface in reverse dependency order
	// 4. Services should use this method to clean up resources, close connections, etc.
	// 5. The context provided has a timeout configured via Pal.ShutdownTimeout()
	// 6. If any service returns an error during shutdown, Pal will collect these errors and return them from Run()
	Shutdown(ctx context.Context) error
}

Shutdowner is an optional interface that can be implemented by a service.

type SlogAttributeSetter added in v0.7.0

type SlogAttributeSetter func(target any) (string, string)

SlogAttributeSetter is a function that returns the name and value for the attribute to be added to the slog.Logger. It receives the target struct and returns the name and value for the attribute. Called when logger is being injected.

type Tag added in v0.10.0

type Tag string
const (
	TagSkip           Tag = "skip"
	TagMatchInterface Tag = "match_interface"
	TagName           Tag = "name"
)

Directories

Path Synopsis
examples
cli command
factories command
hooks command
web command
pkg
dag

Jump to

Keyboard shortcuts

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