Kore is a lightweight, clean, and highly extensible Dependency Injection (DI) framework for Go. It focuses on the clean separation of Discovery (locating dependencies) and Identity (component metadata), providing a predictable and type-safe way to manage complex component graphs.
- Clean Architecture: Separates navigation (
Locator) from instantiation context (Handle). - Discovery vs Identity: Components can find others via locators without leaking their own internal state.
- Multidimensional Isolation: Support for Categories, Scopes, and Capability Tags.
- Lazy Initialization: Components are instantiated only when first requested.
- Circular Dependency Detection: Built-in safety against recursive dependency chains.
- Zero External Dependencies: Pure Go implementation.
go get github.com/origadmin/koretype Database struct {
DSN string
}
type Logger struct {
Prefix string
}
type Service struct {
db *Database
logger *Logger
}import "github.com/origadmin/kore"
reg := kore.New()
// Register a Logger
reg.Register("logger", func(ctx context.Context, h kore.Handle) (any, error) {
return &Logger{Prefix: "[APP]"}, nil
})
// Register a Database with dependency on Logger
reg.Register("database", func(ctx context.Context, h kore.Handle) (any, error) {
// Find Logger using Locator
logger, _ := kore.GetDefault[*Logger](ctx, h.Locator().In("logger"))
return &Database{DSN: "localhost:5432"}, nil
})ctx := context.Background()
reg.Load(ctx, nil) // Load configurations
// Retrieve the database
db, err := kore.GetDefault[*Database](ctx, reg.In("database"))Kore organizes components into a three-dimensional coordinate system, allowing for precise control over component visibility and lifecycle.
Categories define what a component is (e.g., database, cache, logger).
- Purpose: Grouping identical types of infrastructure.
- Usage: You can iterate over all components within a category or retrieve a specific one by name.
Scopes define where a component exists (e.g., _global, server, client).
- Hierarchy: Components in a specific scope are isolated from others.
- Fallback: Kore supports intelligent fallback mechanisms, allowing local scopes to inherit or override global defaults.
Tags define which variant or what capability a provider offers (e.g., gateway, mock, production).
- Isolation: A
Locatorcreated with thegatewaytag can only "see" providers registered with that same tag (or common providers). - Perspective: This allows you to create different "perspectives" of the same dependency graph for different parts of your application.
Kore provides a robust isolation model that separates components along two orthogonal axes: Vertical (Contextual) and Horizontal (Functional).
Vertical isolation is achieved through Scopes, partitioning components based on their Runtime Environment.
- Mechanism: Each Scope (e.g.,
_global,server,client) is a physically distinct instance container. - S-Single: For a given
(Category, Scope, Name), there is exactly one instance. This ensures that a component in theserverscope cannot accidentally reference or interfere with a component in theclientscope, even if they share the same name. - Use Case: Environment-specific overrides and lifecycle management.
Horizontal isolation is achieved through Tags, partitioning components based on their Responsibility and Capability.
- Mechanism: Providers register themselves with specific Capability Tags. When you enter a category via
reg.In(cat, WithInTags("gateway")), you create a Perspective. - T-Multi: A single Category can house multiple providers for the same name, but only those matching the requested "Perspective Tags" (the Capability Set) are visible.
- Responsibility Filtering: This allows you to hide internal-only components from public-facing locators or switch between different implementation variants (e.g.,
productionvsmock) without changing the component's name or code. - Inheritance: "Common" providers (those with no tags) are visible across all horizontal perspectives, acting as a shared foundation.
Unlike other DI frameworks that treat configuration as an afterthought, Kore is a Config-First engine.
- The Resolver: Every category can have a
Resolverthat maps raw input (YAML, JSON, or Map) to structuredModuleConfig. - Automatic Instantiation: Once a config entry is mapped (e.g., a database config named
mysql), Kore automatically knows how to use the registeredProviderto turn that config into a live object. - Active Instance: Supports an
Activeflag in configuration to designate which named instance should be the_defaultfor that category.
| Feature | Kore | Google Wire | Uber Fx / Dig |
|---|---|---|---|
| Mechanism | Run-time (Provider-based) | Compile-time (Code Gen) | Run-time (Reflection) |
| Dependency Resolution | Explicit via Locator |
Implicit via Type Matching | Implicit via Type Matching |
| Identity vs Search | Absolute Separation | None (Single Graph) | None (Single Graph) |
| Multi-instance Support | Native (Category/Name/Tag) | Complex (using tags/alias) | Complex (using groups/names) |
| Configuration Binding | Built-in Resolver |
Manual | Manual / External |
| Error Detection | First-use / Initialization | Compile-time | App Start-up |
Kore is not just another object wirer; it is a Structured Discovery Engine. Compared to mainstream frameworks like Google Wire or Uber Fx, Kore provides a multi-dimensional approach to dependency management.
| Feature | Kore | Google Wire | Uber Fx / Dig |
|---|---|---|---|
| Vertical Isolation (S-Single) | Native (Scopes): Physical isolation of instances by environment. | None: Single global graph; naming conflicts are manual. | Weak: Limited lifecycle scoping; lacks multi-layer isolation. |
| Horizontal Filtering (T-Multi) | Native (Tags): Logical filtering of responsibilities via Perspectives. | None: Relies on static types or manual struct naming. | Complex: Requires annotations or groups; hard to maintain. |
| Config Binding (Config-First) | Deeply Integrated: Automatic mapping from raw config to instances. | None: Pure wiring; config handling is entirely external. | None: Requires external libraries; mapping is scattered. |
| Separation (Identity/Locator) | Absolute (Plan A): Identity (Handle) is isolated from Discovery. | Mixed: Components have excessive visibility into the graph. | Obscure: Reflection leads to implicit container coupling. |
| Multi-instance Management | Seamless: Native support for N-instances via Category/Name/Tag. | Verbose: Requires unique types/providers for each instance. | Heavy: Complexity grows exponentially with instance counts. |
- Vertical (S-Single): Ensures that your
serverdatabase andmonitoringdatabase never touch, even if they share the same provider. - Horizontal (T-Multi): Allows you to define implementation variants (e.g.,
public-apivsinternal-tool) in the same category and switch between them simply by changing the locator's perspective.
Kore bridges the gap between static code and dynamic configuration. By defining a Resolver, you transform your DI from a static object graph into a dynamic engine that responds to your environment settings.
By enforcing an explicit h.Locator().Get() pattern, Kore removes the "magic" of reflection and the "clutter" of code generation. You gain absolute traceability of every dependency in your system.
Kore implements the Plan A composition pattern, where the Handle (Identity) provided during instantiation does not inherit from the Locator (Navigation). Instead, it provides a Locator() method. This ensures that:
- Identity is Local: A component's name and config are only accessible via its own
Handle. - Navigation is Explicit: To find another component, you must explicitly use a
Locator. - Automatic State Stripping: Calling
In()on a locator returns a fresh locator without any residual identity from the caller, preventing accidental state leakage.
Kore is released under the MIT License.