Files
mev-beta/orig/pkg/lifecycle/dependency_injection.go
Administrator 803de231ba feat: create v2-prep branch with comprehensive planning
Restructured project for V2 refactor:

**Structure Changes:**
- Moved all V1 code to orig/ folder (preserved with git mv)
- Created docs/planning/ directory
- Added orig/README_V1.md explaining V1 preservation

**Planning Documents:**
- 00_V2_MASTER_PLAN.md: Complete architecture overview
  - Executive summary of critical V1 issues
  - High-level component architecture diagrams
  - 5-phase implementation roadmap
  - Success metrics and risk mitigation

- 07_TASK_BREAKDOWN.md: Atomic task breakdown
  - 99+ hours of detailed tasks
  - Every task < 2 hours (atomic)
  - Clear dependencies and success criteria
  - Organized by implementation phase

**V2 Key Improvements:**
- Per-exchange parsers (factory pattern)
- Multi-layer strict validation
- Multi-index pool cache
- Background validation pipeline
- Comprehensive observability

**Critical Issues Addressed:**
- Zero address tokens (strict validation + cache enrichment)
- Parsing accuracy (protocol-specific parsers)
- No audit trail (background validation channel)
- Inefficient lookups (multi-index cache)
- Stats disconnection (event-driven metrics)

Next Steps:
1. Review planning documents
2. Begin Phase 1: Foundation (P1-001 through P1-010)
3. Implement parsers in Phase 2
4. Build cache system in Phase 3
5. Add validation pipeline in Phase 4
6. Migrate and test in Phase 5

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:14:26 +01:00

662 lines
18 KiB
Go

package lifecycle
import (
"context"
"fmt"
"reflect"
"sync"
)
// Container provides dependency injection functionality
type Container struct {
services map[reflect.Type]*ServiceDescriptor
instances map[reflect.Type]interface{}
namedServices map[string]*ServiceDescriptor
namedInstances map[string]interface{}
singletons map[reflect.Type]interface{}
factories map[reflect.Type]FactoryFunc
interceptors []Interceptor
config ContainerConfig
mu sync.RWMutex
parent *Container
scoped map[string]*Container
}
// ServiceDescriptor describes how a service should be instantiated
type ServiceDescriptor struct {
ServiceType reflect.Type
Implementation reflect.Type
Lifetime ServiceLifetime
Factory FactoryFunc
Instance interface{}
Name string
Dependencies []reflect.Type
Tags []string
Metadata map[string]interface{}
Interceptors []Interceptor
}
// ServiceLifetime defines the lifetime of a service
type ServiceLifetime string
const (
Transient ServiceLifetime = "transient" // New instance every time
Singleton ServiceLifetime = "singleton" // Single instance for container lifetime
Scoped ServiceLifetime = "scoped" // Single instance per scope
)
// FactoryFunc creates service instances
type FactoryFunc func(container *Container) (interface{}, error)
// Interceptor can intercept service creation and method calls
type Interceptor interface {
Intercept(ctx context.Context, target interface{}, method string, args []interface{}) (interface{}, error)
}
// ContainerConfig configures the container behavior
type ContainerConfig struct {
EnableReflection bool `json:"enable_reflection"`
EnableCircularDetection bool `json:"enable_circular_detection"`
EnableInterception bool `json:"enable_interception"`
EnableValidation bool `json:"enable_validation"`
MaxDepth int `json:"max_depth"`
CacheInstances bool `json:"cache_instances"`
}
// ServiceBuilder provides a fluent interface for service registration
type ServiceBuilder struct {
container *Container
serviceType reflect.Type
implType reflect.Type
lifetime ServiceLifetime
factory FactoryFunc
instance interface{}
name string
tags []string
metadata map[string]interface{}
interceptors []Interceptor
}
// NewContainer creates a new dependency injection container
func NewContainer(config ContainerConfig) *Container {
container := &Container{
services: make(map[reflect.Type]*ServiceDescriptor),
instances: make(map[reflect.Type]interface{}),
namedServices: make(map[string]*ServiceDescriptor),
namedInstances: make(map[string]interface{}),
singletons: make(map[reflect.Type]interface{}),
factories: make(map[reflect.Type]FactoryFunc),
interceptors: make([]Interceptor, 0),
config: config,
scoped: make(map[string]*Container),
}
// Set default configuration
if container.config.MaxDepth == 0 {
container.config.MaxDepth = 10
}
return container
}
// Register registers a service type with its implementation
func (c *Container) Register(serviceType, implementationType interface{}) *ServiceBuilder {
c.mu.Lock()
defer c.mu.Unlock()
sType := reflect.TypeOf(serviceType)
if sType.Kind() == reflect.Ptr {
sType = sType.Elem()
}
if sType.Kind() == reflect.Interface {
sType = reflect.TypeOf(serviceType).Elem()
}
implType := reflect.TypeOf(implementationType)
if implType.Kind() == reflect.Ptr {
implType = implType.Elem()
}
return &ServiceBuilder{
container: c,
serviceType: sType,
implType: implType,
lifetime: Transient,
tags: make([]string, 0),
metadata: make(map[string]interface{}),
interceptors: make([]Interceptor, 0),
}
}
// RegisterInstance registers a specific instance
func (c *Container) RegisterInstance(serviceType interface{}, instance interface{}) *ServiceBuilder {
c.mu.Lock()
defer c.mu.Unlock()
sType := reflect.TypeOf(serviceType)
if sType.Kind() == reflect.Ptr {
sType = sType.Elem()
}
if sType.Kind() == reflect.Interface {
sType = reflect.TypeOf(serviceType).Elem()
}
return &ServiceBuilder{
container: c,
serviceType: sType,
instance: instance,
lifetime: Singleton,
tags: make([]string, 0),
metadata: make(map[string]interface{}),
interceptors: make([]Interceptor, 0),
}
}
// RegisterFactory registers a factory function for creating instances
func (c *Container) RegisterFactory(serviceType interface{}, factory FactoryFunc) *ServiceBuilder {
c.mu.Lock()
defer c.mu.Unlock()
sType := reflect.TypeOf(serviceType)
if sType.Kind() == reflect.Ptr {
sType = sType.Elem()
}
if sType.Kind() == reflect.Interface {
sType = reflect.TypeOf(serviceType).Elem()
}
return &ServiceBuilder{
container: c,
serviceType: sType,
factory: factory,
lifetime: Transient,
tags: make([]string, 0),
metadata: make(map[string]interface{}),
interceptors: make([]Interceptor, 0),
}
}
// Resolve resolves a service instance by type
func (c *Container) Resolve(serviceType interface{}) (interface{}, error) {
sType := reflect.TypeOf(serviceType)
if sType.Kind() == reflect.Ptr {
sType = sType.Elem()
}
if sType.Kind() == reflect.Interface {
sType = reflect.TypeOf(serviceType).Elem()
}
return c.resolveType(sType, make(map[reflect.Type]bool), 0)
}
// ResolveNamed resolves a named service instance
func (c *Container) ResolveNamed(name string) (interface{}, error) {
c.mu.RLock()
defer c.mu.RUnlock()
// Check if instance already exists
if instance, exists := c.namedInstances[name]; exists {
return instance, nil
}
// Get service descriptor
descriptor, exists := c.namedServices[name]
if !exists {
return nil, fmt.Errorf("named service not found: %s", name)
}
return c.createInstance(descriptor, make(map[reflect.Type]bool), 0)
}
// ResolveAll resolves all services with a specific tag
func (c *Container) ResolveAll(tag string) ([]interface{}, error) {
c.mu.RLock()
defer c.mu.RUnlock()
var instances []interface{}
for _, descriptor := range c.services {
for _, serviceTag := range descriptor.Tags {
if serviceTag == tag {
instance, err := c.createInstance(descriptor, make(map[reflect.Type]bool), 0)
if err != nil {
return nil, fmt.Errorf("failed to resolve service with tag %s: %w", tag, err)
}
instances = append(instances, instance)
break
}
}
}
return instances, nil
}
// TryResolve attempts to resolve a service, returning nil if not found
func (c *Container) TryResolve(serviceType interface{}) interface{} {
instance, err := c.Resolve(serviceType)
if err != nil {
return nil
}
return instance
}
// IsRegistered checks if a service type is registered
func (c *Container) IsRegistered(serviceType interface{}) bool {
sType := reflect.TypeOf(serviceType)
if sType.Kind() == reflect.Ptr {
sType = sType.Elem()
}
if sType.Kind() == reflect.Interface {
sType = reflect.TypeOf(serviceType).Elem()
}
c.mu.RLock()
defer c.mu.RUnlock()
_, exists := c.services[sType]
return exists
}
// CreateScope creates a new scoped container
func (c *Container) CreateScope(name string) *Container {
c.mu.Lock()
defer c.mu.Unlock()
scope := &Container{
services: make(map[reflect.Type]*ServiceDescriptor),
instances: make(map[reflect.Type]interface{}),
namedServices: make(map[string]*ServiceDescriptor),
namedInstances: make(map[string]interface{}),
singletons: make(map[reflect.Type]interface{}),
factories: make(map[reflect.Type]FactoryFunc),
interceptors: make([]Interceptor, 0),
config: c.config,
parent: c,
scoped: make(map[string]*Container),
}
c.scoped[name] = scope
return scope
}
// GetScope retrieves a named scope
func (c *Container) GetScope(name string) (*Container, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
scope, exists := c.scoped[name]
return scope, exists
}
// AddInterceptor adds a global interceptor
func (c *Container) AddInterceptor(interceptor Interceptor) {
c.mu.Lock()
defer c.mu.Unlock()
c.interceptors = append(c.interceptors, interceptor)
}
// GetRegistrations returns all service registrations
func (c *Container) GetRegistrations() map[reflect.Type]*ServiceDescriptor {
c.mu.RLock()
defer c.mu.RUnlock()
registrations := make(map[reflect.Type]*ServiceDescriptor)
for t, desc := range c.services {
registrations[t] = desc
}
return registrations
}
// Validate validates all service registrations
func (c *Container) Validate() error {
c.mu.RLock()
defer c.mu.RUnlock()
if !c.config.EnableValidation {
return nil
}
for serviceType, descriptor := range c.services {
if err := c.validateDescriptor(serviceType, descriptor); err != nil {
return fmt.Errorf("validation failed for service %s: %w", serviceType.String(), err)
}
}
return nil
}
// Dispose cleans up the container and all instances
func (c *Container) Dispose() error {
c.mu.Lock()
defer c.mu.Unlock()
// Dispose all scoped containers
for _, scope := range c.scoped {
scope.Dispose()
}
// Clear all maps
c.services = make(map[reflect.Type]*ServiceDescriptor)
c.instances = make(map[reflect.Type]interface{})
c.namedServices = make(map[string]*ServiceDescriptor)
c.namedInstances = make(map[string]interface{})
c.singletons = make(map[reflect.Type]interface{})
c.factories = make(map[reflect.Type]FactoryFunc)
c.scoped = make(map[string]*Container)
return nil
}
// Private methods
func (c *Container) resolveType(serviceType reflect.Type, resolving map[reflect.Type]bool, depth int) (interface{}, error) {
if depth > c.config.MaxDepth {
return nil, fmt.Errorf("maximum resolution depth exceeded for type %s", serviceType.String())
}
// Check for circular dependencies
if c.config.EnableCircularDetection && resolving[serviceType] {
return nil, fmt.Errorf("circular dependency detected for type %s", serviceType.String())
}
c.mu.RLock()
defer c.mu.RUnlock()
// Check if singleton instance exists
if instance, exists := c.singletons[serviceType]; exists {
return instance, nil
}
// Check if cached instance exists
if c.config.CacheInstances {
if instance, exists := c.instances[serviceType]; exists {
return instance, nil
}
}
// Get service descriptor
descriptor, exists := c.services[serviceType]
if !exists {
// Try parent container
if c.parent != nil {
return c.parent.resolveType(serviceType, resolving, depth+1)
}
return nil, fmt.Errorf("service not registered: %s", serviceType.String())
}
resolving[serviceType] = true
defer delete(resolving, serviceType)
return c.createInstance(descriptor, resolving, depth+1)
}
func (c *Container) createInstance(descriptor *ServiceDescriptor, resolving map[reflect.Type]bool, depth int) (interface{}, error) {
// Use existing instance if available
if descriptor.Instance != nil {
return descriptor.Instance, nil
}
// Use factory if available
if descriptor.Factory != nil {
instance, err := descriptor.Factory(c)
if err != nil {
return nil, fmt.Errorf("factory failed for %s: %w", descriptor.ServiceType.String(), err)
}
if descriptor.Lifetime == Singleton {
c.singletons[descriptor.ServiceType] = instance
}
return c.applyInterceptors(instance, descriptor)
}
// Create instance using reflection
if descriptor.Implementation == nil {
return nil, fmt.Errorf("no implementation or factory provided for %s", descriptor.ServiceType.String())
}
instance, err := c.createInstanceByReflection(descriptor, resolving, depth)
if err != nil {
return nil, err
}
// Store singleton
if descriptor.Lifetime == Singleton {
c.singletons[descriptor.ServiceType] = instance
}
return c.applyInterceptors(instance, descriptor)
}
func (c *Container) createInstanceByReflection(descriptor *ServiceDescriptor, resolving map[reflect.Type]bool, depth int) (interface{}, error) {
if !c.config.EnableReflection {
return nil, fmt.Errorf("reflection is disabled")
}
implType := descriptor.Implementation
if implType.Kind() == reflect.Ptr {
implType = implType.Elem()
}
// Find constructor (assumes first constructor or struct creation)
var constructorFunc reflect.Value
// Look for constructor function
_ = "New" + implType.Name() // constructorName not used yet
if implType.PkgPath() != "" {
// Try to find package-level constructor
// This is simplified - in a real implementation you'd use build tags or reflection
// to find the actual constructor functions
}
// Create instance
if constructorFunc.IsValid() {
// Use constructor function
return c.callConstructor(constructorFunc, resolving, depth)
} else {
// Create struct directly
return c.createStruct(implType, resolving, depth)
}
}
func (c *Container) createStruct(structType reflect.Type, resolving map[reflect.Type]bool, depth int) (interface{}, error) {
// Create new instance
instance := reflect.New(structType)
elem := instance.Elem()
// Inject dependencies into fields
for i := 0; i < elem.NumField(); i++ {
field := elem.Field(i)
fieldType := elem.Type().Field(i)
// Check for dependency injection tags
if tag := fieldType.Tag.Get("inject"); tag != "" {
if !field.CanSet() {
continue
}
var dependency interface{}
var err error
if tag == "true" || tag == "" {
// Inject by type
dependency, err = c.resolveType(field.Type(), resolving, depth)
} else {
// Inject by name
dependency, err = c.ResolveNamed(tag)
}
if err != nil {
// Check if injection is optional
if optionalTag := fieldType.Tag.Get("optional"); optionalTag == "true" {
continue
}
return nil, fmt.Errorf("failed to inject dependency for field %s: %w", fieldType.Name, err)
}
field.Set(reflect.ValueOf(dependency))
}
}
return instance.Interface(), nil
}
func (c *Container) callConstructor(constructor reflect.Value, resolving map[reflect.Type]bool, depth int) (interface{}, error) {
constructorType := constructor.Type()
args := make([]reflect.Value, constructorType.NumIn())
// Resolve constructor arguments
for i := 0; i < constructorType.NumIn(); i++ {
argType := constructorType.In(i)
arg, err := c.resolveType(argType, resolving, depth)
if err != nil {
return nil, fmt.Errorf("failed to resolve constructor argument %d (%s): %w", i, argType.String(), err)
}
args[i] = reflect.ValueOf(arg)
}
// Call constructor
results := constructor.Call(args)
if len(results) == 0 {
return nil, fmt.Errorf("constructor returned no values")
}
instance := results[0].Interface()
// Check for error result
if len(results) > 1 && !results[1].IsNil() {
if err, ok := results[1].Interface().(error); ok {
return nil, fmt.Errorf("constructor error: %w", err)
}
}
return instance, nil
}
func (c *Container) applyInterceptors(instance interface{}, descriptor *ServiceDescriptor) (interface{}, error) {
if !c.config.EnableInterception {
return instance, nil
}
// Apply service-specific interceptors
for _, interceptor := range descriptor.Interceptors {
// Apply interceptor (simplified - real implementation would create proxies)
_ = interceptor
}
// Apply global interceptors
for _, interceptor := range c.interceptors {
// Apply interceptor (simplified - real implementation would create proxies)
_ = interceptor
}
return instance, nil
}
func (c *Container) validateDescriptor(serviceType reflect.Type, descriptor *ServiceDescriptor) error {
// Validate that implementation implements the service interface
if descriptor.Implementation != nil {
if serviceType.Kind() == reflect.Interface {
if !descriptor.Implementation.Implements(serviceType) {
return fmt.Errorf("implementation %s does not implement interface %s",
descriptor.Implementation.String(), serviceType.String())
}
}
}
// Validate dependencies
for _, depType := range descriptor.Dependencies {
if !c.IsRegistered(depType) && (c.parent == nil || !c.parent.IsRegistered(depType)) {
return fmt.Errorf("dependency %s is not registered", depType.String())
}
}
return nil
}
// ServiceBuilder methods
// AsSingleton sets the service lifetime to singleton
func (sb *ServiceBuilder) AsSingleton() *ServiceBuilder {
sb.lifetime = Singleton
return sb
}
// AsTransient sets the service lifetime to transient
func (sb *ServiceBuilder) AsTransient() *ServiceBuilder {
sb.lifetime = Transient
return sb
}
// AsScoped sets the service lifetime to scoped
func (sb *ServiceBuilder) AsScoped() *ServiceBuilder {
sb.lifetime = Scoped
return sb
}
// WithName sets a name for the service
func (sb *ServiceBuilder) WithName(name string) *ServiceBuilder {
sb.name = name
return sb
}
// WithTag adds a tag to the service
func (sb *ServiceBuilder) WithTag(tag string) *ServiceBuilder {
sb.tags = append(sb.tags, tag)
return sb
}
// WithMetadata adds metadata to the service
func (sb *ServiceBuilder) WithMetadata(key string, value interface{}) *ServiceBuilder {
sb.metadata[key] = value
return sb
}
// WithInterceptor adds an interceptor to the service
func (sb *ServiceBuilder) WithInterceptor(interceptor Interceptor) *ServiceBuilder {
sb.interceptors = append(sb.interceptors, interceptor)
return sb
}
// Build finalizes the service registration
func (sb *ServiceBuilder) Build() error {
sb.container.mu.Lock()
defer sb.container.mu.Unlock()
descriptor := &ServiceDescriptor{
ServiceType: sb.serviceType,
Implementation: sb.implType,
Lifetime: sb.lifetime,
Factory: sb.factory,
Instance: sb.instance,
Name: sb.name,
Tags: sb.tags,
Metadata: sb.metadata,
Interceptors: sb.interceptors,
}
// Store by type
sb.container.services[sb.serviceType] = descriptor
// Store by name if provided
if sb.name != "" {
sb.container.namedServices[sb.name] = descriptor
}
return nil
}
// DefaultInterceptor provides basic interception functionality
type DefaultInterceptor struct {
name string
}
func NewDefaultInterceptor(name string) *DefaultInterceptor {
return &DefaultInterceptor{name: name}
}
func (di *DefaultInterceptor) Intercept(ctx context.Context, target interface{}, method string, args []interface{}) (interface{}, error) {
// Basic interception - could add logging, metrics, etc.
return nil, nil
}