registry

package
v1.11.0-alpha.2 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

README

Provider Registry

A minimal, interface-based provider registry for breaking import cycles in the codebase.

Purpose

This registry is a fallback mechanism for situations where import cycles block development work. It provides runtime indirection through interface-based contracts while maintaining type safety.

Why It Exists

Large, mature codebases sometimes develop import cycles between packages:

  • pkg/appfile needs types from pkg/controller
  • pkg/controller needs functionality from pkg/appfile
  • Result: Import cycle prevents compilation

While the ideal solution is restructuring packages with clear boundaries, this isn't always practical in the short term. The registry unblocks development while allowing refactoring efforts to be planned appropriately.

Design Philosophy

Use as a fallback, not a default:

  • New code should use well-designed package boundaries and constructor injection
  • Existing code can use the registry when cycles genuinely block work
  • Services registered here are candidates for future refactoring

Keep it simple:

  • Interface-only registration (enforced)
  • Thread-safe operations
  • No complex lifecycle management
  • Stdlib dependencies only

How It Works

1. Define an Interface
// In cmd/core/app/bootstrap.go or appropriate location
type MyProvider interface {
    DoSomething(ctx context.Context) error
}
2. Register During Bootstrap
// In cmd/core/app/bootstrap.go
func bootstrapProviderRegistry() {
    // MyProvider - Brief description of what it does
    // Cycle: pkg/foo ↔ pkg/bar
    // Note: Consider refactoring to extract shared interfaces
    provider := foo.NewMyProvider()
    registry.RegisterAs[MyProvider](provider)
}
3. Retrieve Where Needed
// In any package that needs it
provider, ok := registry.Get[MyProvider]()
if !ok {
    return fmt.Errorf("MyProvider not registered")
}
err := provider.DoSomething(ctx)

API

  • RegisterAs[T any](impl T) - Register an implementation for interface T
  • Get[T any]() (T, bool) - Retrieve registered implementation
  • Snapshot() RegistrySnapshot - Save current state (for testing)
  • Restore(snapshot RegistrySnapshot) - Restore saved state (for testing)

Testing

Use Snapshot/Restore to isolate tests:

func TestSomething(t *testing.T) {
    snapshot := registry.Snapshot()
    defer registry.Restore(snapshot)

    // Override with mock
    registry.RegisterAs[MyProvider](mockImpl)

    // Test code using mock
}
// Original providers restored automatically

When NOT to Use

Prefer constructor injection when:

  • Writing new code with clean package boundaries
  • No import cycle exists between packages
  • Extracting interfaces to a neutral package is straightforward
  • Dependencies can be passed explicitly through constructors

Trade-offs

Benefits:

  • Unblocks development immediately
  • Breaks cycles without major refactoring
  • Type-safe through generics
  • Easy to mock in tests

Costs:

  • Less visible than constructor injection
  • Runtime lookups instead of compile-time
  • Can hide architectural issues if overused

Guidance

Use this registry judiciously as a pragmatic tool. Each provider registered represents an opportunity to improve package structure in the future. The goal is to keep usage minimal while unblocking important development work.

For detailed implementation guidelines, see the package documentation in registry.go.

Documentation

Overview

Package registry provides a minimal interface-based provider registry for breaking import cycles in the codebase.

This is a fallback mechanism for situations where import cycles block development.

See README.md in this directory for detailed rationale, usage guidelines, and examples.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Get

func Get[T any]() (T, bool)

Get retrieves the registered implementation for interface type T.

Returns (implementation, true) if a provider is registered for type T, or (zero value, false) if no provider is registered.

This function is thread-safe and optimized for concurrent access.

Example:

if provider, ok := registry.Get[MyProvider](); ok {
    provider.DoSomething()
} else {
    // handle missing provider
}

func RegisterAs

func RegisterAs[T any](impl T)

RegisterAs registers an implementation for an interface type T.

The type parameter T must be an interface type, and impl must be a non-nil implementation of that interface. If T is not an interface or impl is nil, this function panics with a descriptive error message.

If a provider is registered multiple times, the last registration wins. This is intentional to allow test code to override providers.

Example:

type MyProvider interface {
    DoSomething() error
}

type myImpl struct{}
func (m *myImpl) DoSomething() error { return nil }

registry.RegisterAs[MyProvider](&myImpl{})

func Restore

func Restore(snapshot RegistrySnapshot)

Restore replaces the current registry state with a saved snapshot.

This function is intended for testing only. It restores the registry to the exact state it was in when Snapshot() was called.

Example in tests:

func TestSomething(t *testing.T) {
    snapshot := registry.Snapshot()
    defer registry.Restore(snapshot) // Restore original state

    registry.RegisterAs[MyProvider](mockImpl)
    // ... test code
}

Types

type RegistrySnapshot

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

RegistrySnapshot represents a saved state of the registry. Use with Restore() to save and restore registry state in tests.

func Snapshot

func Snapshot() RegistrySnapshot

Snapshot creates a copy of the current registry state.

This is useful in tests to save the registry state (including bootstrap providers), make temporary changes, and then restore the original state.

Example in tests:

func TestSomething(t *testing.T) {
    snapshot := registry.Snapshot()
    defer registry.Restore(snapshot)

    registry.RegisterAs[MyProvider](mockImpl)
    // ... test code that uses mockImpl

} // Restore() brings back all original providers including bootstrap ones

Jump to

Keyboard shortcuts

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