Documentation
¶
Overview ¶
Package modulex provides deterministic lifecycle orchestration for modular Go applications.
A Manager coordinates feature modules through an explicit lifecycle (configuring → initializing → initialized → starting → running → stopping → stopped), validates the dependency graph, rolls back partial failures, and supervises background tasks. Modules receive a Registry they can use to register services, access the router, tracer, logger, event bus, config, and start lifecycle-owned tasks.
Typed service registration helpers (Key, Provide, Resolve) are also provided.
EventBus adapter sub-packages such as github.com/mediusfy/modulex/nats, github.com/mediusfy/modulex/rabbitmq, and github.com/mediusfy/modulex/watermill keep framework dependencies out of the core package.
github.com/mediusfy/modulex/app provides an opinionated Run helper that owns the construct-manager/register-modules/signal-context/Init-Start-wait-Stop bootstrap skeleton every service entrypoint otherwise repeats.
Index ¶
- Variables
- func Provide[T any](reg Registry, key Key[T], svc T) error
- func Resolve[T any](reg Registry, key Key[T]) (T, error)
- type ConfigProvider
- type EventBus
- type EventBusProvider
- type EventHandler
- type HealthCheckProvider
- type HealthCheckRegistrar
- type Key
- type LifecycleState
- type LoggerProvider
- type Manager
- func (m *Manager) EventBus() EventBus
- func (m *Manager) ExportDAG() string
- func (m *Manager) GetConfig(target interface{}) error
- func (m *Manager) Go(ctx context.Context, taskName string, fn func(ctx context.Context) error) (*TaskHandle, error)
- func (m *Manager) HealthChecks() map[string]func(context.Context) error
- func (m *Manager) InitModules(ctx context.Context) error
- func (m *Manager) Logger() *slog.Logger
- func (m *Manager) ReadinessChecks() map[string]func(context.Context) error
- func (m *Manager) RegisterHealthCheck(name string, check func(context.Context) error) error
- func (m *Manager) RegisterModule(mod Module) error
- func (m *Manager) RegisterReadinessCheck(name string, check func(context.Context) error) error
- func (m *Manager) RegisterService(name string, svc interface{}) error
- func (m *Manager) ResolveService(name string) (interface{}, error)
- func (m *Manager) StartModules(ctx context.Context) error
- func (m *Manager) State() LifecycleState
- func (m *Manager) StopModules(ctx context.Context) error
- type ManagerOption
- func WithConfigLoader(loader func(target interface{}) error) ManagerOption
- func WithEventBus(eb EventBus) ManagerOption
- func WithLogger(logger *slog.Logger) ManagerOption
- func WithPanicPolicy(policy PanicPolicy) ManagerOption
- func WithTracer(tracer Tracer) ManagerOption
- func WithTypedConfig[T any](cfg T) ManagerOption
- type Module
- type PanicPolicy
- type ReadinessProvider
- type ReadinessRegistrar
- type Registry
- type ServiceRegistrar
- type ServiceRegistry
- type ServiceResolver
- type Span
- type SpanContext
- type Startable
- type Stoppable
- type TaskHandle
- type TaskSpawner
- type Tracer
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrCircularDependency is returned when registered modules contain a circular dependency. ErrCircularDependency = errors.New("circular dependency detected") // ErrServiceNotFound is returned when a requested service is not registered in the locator. ErrServiceNotFound = errors.New("service not found") // ErrRegistryLocked is returned when a module attempts to register a service after registry initialization has completed. ErrRegistryLocked = errors.New("registry is locked: cannot register services after initialization") // ErrModuleNil is returned when a nil module is passed to RegisterModule. ErrModuleNil = errors.New("module must not be nil") // ErrInvalidModuleName is returned when a module name is empty or whitespace-only. ErrInvalidModuleName = errors.New("module name must not be empty") // ErrDuplicateModule is returned when RegisterModule is called with a module name that is already registered. ErrDuplicateModule = errors.New("module already registered") // ErrDuplicateService is returned when RegisterService is called with a service key that is already registered. ErrDuplicateService = errors.New("service already registered") // ErrDuplicateTask is returned when Go is called with a task name that is already in use. ErrDuplicateTask = errors.New("task already exists") // ErrInvalidTaskName is returned when Go is called with an empty or whitespace-only task name. ErrInvalidTaskName = errors.New("task name must not be empty") // ErrDependencyNotFound is returned when a module depends on a module that has not been registered. ErrDependencyNotFound = errors.New("module dependency not found") // ErrSelfDependency is returned when a module declares itself as a dependency. ErrSelfDependency = errors.New("module cannot depend on itself") // ErrInvalidDependencyName is returned when a module declares a dependency with an empty or whitespace-only name. ErrInvalidDependencyName = errors.New("module dependency name must not be empty") // ErrInvalidServiceName is returned when a service is registered with an empty or whitespace-only key. ErrInvalidServiceName = errors.New("service name must not be empty") // ErrServiceTypeMismatch is returned when a resolved service cannot be type-asserted to the requested type. ErrServiceTypeMismatch = errors.New("service type mismatch") // ErrInvalidLifecycleState is returned when a lifecycle operation is requested while the manager is in an incompatible state. ErrInvalidLifecycleState = errors.New("invalid lifecycle state") // ErrNoConfigLoader is returned by GetConfig when no config loader was // configured at construction time or via WithConfigLoader. ErrNoConfigLoader = errors.New("no config loader configured") // ErrInvalidPanicPolicy is returned by NewManager when WithPanicPolicy is // given a value outside the defined PanicPolicy enum. ErrInvalidPanicPolicy = errors.New("invalid panic policy") // ErrInvalidHealthCheckName is returned when a health check name is empty. ErrInvalidHealthCheckName = errors.New("health check name must not be empty") // ErrInvalidReadinessCheckName is returned when a readiness check name is empty. ErrInvalidReadinessCheckName = errors.New("readiness check name must not be empty") )
var ErrConfigTypeMismatch = errors.New("config target type mismatch")
ErrConfigTypeMismatch is returned by the config loader installed via WithTypedConfig when GetConfig's target is not a pointer to the configured type.
Functions ¶
func Provide ¶
Provide registers a typed service implementation in the registry. It is a type-safe wrapper around Registry.RegisterService.
Example ¶
package main
import (
"fmt"
"io"
"log/slog"
"github.com/mediusfy/modulex"
)
type greetingService struct {
greeting string
}
func (s greetingService) Greet(name string) string {
return fmt.Sprintf("%s, %s!", s.greeting, name)
}
func main() {
manager := newExampleManager()
key := modulex.NewKey[greetingService]("example.GreetingService")
if err := modulex.Provide(manager, key, greetingService{greeting: "Hello"}); err != nil {
panic(err)
}
svc, err := modulex.Resolve(manager, key)
if err != nil {
panic(err)
}
fmt.Println(svc.Greet("Modulex"))
}
func newExampleManager() *modulex.Manager {
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
mgr, err := modulex.NewManager(modulex.WithLogger(logger))
if err != nil {
panic(err)
}
return mgr
}
Output: Hello, Modulex!
Types ¶
type ConfigProvider ¶
type ConfigProvider interface {
// GetConfig unmarshals configuration values into the target structure.
// This abstract config retrieval prevents features from directly reading global configurations.
GetConfig(target interface{}) error
}
ConfigProvider is the capability to unmarshal configuration values into a target structure.
type EventBus ¶
type EventBus interface {
// Publish sends a payload to a specific topic/subject.
Publish(ctx context.Context, topic string, payload []byte) error
// Subscribe listens to a topic and invokes the handler when an event is
// received. The adapter determines how handler errors affect
// acknowledgment, retry, and redelivery; see the adapter documentation for
// its policy.
Subscribe(ctx context.Context, topic string, handler EventHandler) error
// Close gracefully disconnects from the broker, shutting down active subscribers.
Close(ctx context.Context) error
}
EventBus abstracts the underlying message broker (NATS, Kafka, RabbitMQ, etc.).
type EventBusProvider ¶
type EventBusProvider interface {
EventBus() EventBus
}
EventBusProvider is the capability to access the pluggable event bus.
type EventHandler ¶
EventHandler processes an event delivered by an EventBus. The meaning of a returned error depends on the concrete adapter: some adapters ack/nack based on the error, others only log it. Callers coding against the EventBus abstraction should not assume specific retry or redelivery semantics.
type HealthCheckProvider ¶ added in v0.3.0
HealthCheckProvider exposes the registered health (liveness) checks.
type HealthCheckRegistrar ¶ added in v0.3.0
type HealthCheckRegistrar interface {
// RegisterHealthCheck registers a health (liveness) check function under a
// unique name.
RegisterHealthCheck(name string, check func(context.Context) error) error
}
HealthCheckRegistrar is the capability to register a module health (liveness) check.
A health check answers "is this process functioning correctly?" A failing health check means the process is broken and should be restarted (e.g. by an orchestrator's liveness probe). Contrast this with ReadinessRegistrar, which answers "should this process currently receive traffic?" — a failing readiness check means the instance should be pulled from load balancing, not restarted.
type Key ¶
type Key[T any] struct { // contains filtered or unexported fields }
Key is a typed service locator key. It couples a string identifier with a compile-time type so that Provide and Resolve can be type-safe.
type LifecycleState ¶
type LifecycleState int
LifecycleState represents the current phase of the Manager's lifecycle.
const ( StateConfiguring LifecycleState = iota StateInitializing StateInitialized StateStarting StateRunning StateStopping StateStopped )
func (LifecycleState) String ¶
func (s LifecycleState) String() string
type LoggerProvider ¶
LoggerProvider is the capability to access the system logger.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager implements the Registry interface and orchestrates the module lifecycles.
func NewManager ¶
func NewManager(opts ...ManagerOption) (*Manager, error)
NewManager creates a new instance of Manager.
eb may be nil, in which case a no-op EventBus is used. logger may be nil, in which case slog.Default() is used. NewManager returns ErrInvalidPanicPolicy if WithPanicPolicy is given a value outside the defined PanicPolicy enum.
func (*Manager) ExportDAG ¶ added in v0.3.0
ExportDAG returns a Mermaid-compatible DAG visualization of the registered modules.
func (*Manager) Go ¶
func (m *Manager) Go(ctx context.Context, taskName string, fn func(ctx context.Context) error) (*TaskHandle, error)
Go implements Registry. It spawns a supervised background routine while preserving trace ancestry when a Tracer is configured, and returns a handle for awaiting completion.
func (*Manager) HealthChecks ¶ added in v0.3.0
HealthChecks returns all registered health (liveness) checks.
func (*Manager) InitModules ¶
InitModules sorts the modules topologically based on dependencies, then initializes them sequentially in dependency order inside trace spans.
If a module fails to initialize, all previously initialized modules are stopped in reverse order and the manager moves to the stopped state.
Example ¶
package main
import (
"context"
"fmt"
"io"
"log/slog"
"github.com/mediusfy/modulex"
)
func main() {
manager := newExampleManager()
mod := &exampleModule{}
if err := manager.RegisterModule(mod); err != nil {
panic(err)
}
if err := manager.InitModules(context.Background()); err != nil {
panic(err)
}
fmt.Println("initialized")
}
type exampleModule struct{}
func (m *exampleModule) Name() string { return "example" }
func (m *exampleModule) DependsOn() []string { return nil }
func (m *exampleModule) Init(context.Context, modulex.Registry) error { return nil }
func newExampleManager() *modulex.Manager {
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
mgr, err := modulex.NewManager(modulex.WithLogger(logger))
if err != nil {
panic(err)
}
return mgr
}
Output: initialized
func (*Manager) ReadinessChecks ¶ added in v0.4.1
ReadinessChecks returns all registered readiness checks.
func (*Manager) RegisterHealthCheck ¶ added in v0.3.0
RegisterHealthCheck registers a health (liveness) check function under a unique name.
func (*Manager) RegisterModule ¶
RegisterModule registers a feature module in the manager. Modules should be registered before calling InitModules.
Registration is rejected if the module is nil, its name is empty, another module with the same name is already registered, or initialization has already started. Independent modules preserve their registration order as the deterministic tie-break during topological sorting.
func (*Manager) RegisterReadinessCheck ¶ added in v0.4.1
RegisterReadinessCheck registers a readiness check function under a unique name.
func (*Manager) RegisterService ¶
RegisterService implements Registry. It registers a service instance to the service locator. Registration is only permitted before InitModules has completed.
func (*Manager) ResolveService ¶
ResolveService implements Registry. It retrieves a registered service by its identifier.
func (*Manager) StartModules ¶
StartModules starts all registered modules in topological dependency order inside trace spans.
If a module fails to start, all previously started modules are stopped in reverse order and the manager moves to the stopped state.
Example ¶
package main
import (
"context"
"fmt"
"io"
"log/slog"
"github.com/mediusfy/modulex"
)
func main() {
manager := newExampleManager()
mod := &exampleModule{}
if err := manager.RegisterModule(mod); err != nil {
panic(err)
}
if err := manager.InitModules(context.Background()); err != nil {
panic(err)
}
if err := manager.StartModules(context.Background()); err != nil {
panic(err)
}
fmt.Println("started")
}
type exampleModule struct{}
func (m *exampleModule) Name() string { return "example" }
func (m *exampleModule) DependsOn() []string { return nil }
func (m *exampleModule) Init(context.Context, modulex.Registry) error { return nil }
func newExampleManager() *modulex.Manager {
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
mgr, err := modulex.NewManager(modulex.WithLogger(logger))
if err != nil {
panic(err)
}
return mgr
}
Output: started
func (*Manager) State ¶
func (m *Manager) State() LifecycleState
State returns the current lifecycle state of the manager.
func (*Manager) StopModules ¶
StopModules cancels supervised tasks, stops registered modules in reverse topological order when they were running, and closes the EventBus.
StopModules is idempotent: calling it multiple times returns nil without re-executing shutdown logic. It is context-aware and joins all shutdown errors so that no failure is silently dropped.
Example ¶
package main
import (
"context"
"fmt"
"io"
"log/slog"
"github.com/mediusfy/modulex"
)
func main() {
manager := newExampleManager()
mod := &exampleModule{}
if err := manager.RegisterModule(mod); err != nil {
panic(err)
}
if err := manager.InitModules(context.Background()); err != nil {
panic(err)
}
if err := manager.StartModules(context.Background()); err != nil {
panic(err)
}
if err := manager.StopModules(context.Background()); err != nil {
panic(err)
}
fmt.Println("stopped")
}
type exampleModule struct{}
func (m *exampleModule) Name() string { return "example" }
func (m *exampleModule) DependsOn() []string { return nil }
func (m *exampleModule) Init(context.Context, modulex.Registry) error { return nil }
func newExampleManager() *modulex.Manager {
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
mgr, err := modulex.NewManager(modulex.WithLogger(logger))
if err != nil {
panic(err)
}
return mgr
}
Output: stopped
type ManagerOption ¶
type ManagerOption func(*Manager)
ManagerOption configures a Manager during construction.
func WithConfigLoader ¶
func WithConfigLoader(loader func(target interface{}) error) ManagerOption
WithConfigLoader configures the config loader after construction. This is useful when using the options pattern and keeps the positional configLoader argument nil-safe.
func WithEventBus ¶ added in v0.3.0
func WithEventBus(eb EventBus) ManagerOption
WithEventBus configures the pluggable event bus. If nil, a no-op event bus is used so event publishing remains optional.
func WithLogger ¶ added in v0.3.0
func WithLogger(logger *slog.Logger) ManagerOption
WithLogger configures the logger used by the manager and its modules. If nil, slog.Default() is used.
func WithPanicPolicy ¶
func WithPanicPolicy(policy PanicPolicy) ManagerOption
WithPanicPolicy sets the panic policy for supervised background tasks.
func WithTracer ¶
func WithTracer(tracer Tracer) ManagerOption
WithTracer injects a Tracer implementation into the Manager. If nil, a no-op tracer is used so tracing remains optional for core consumers.
func WithTypedConfig ¶ added in v0.5.1
func WithTypedConfig[T any](cfg T) ManagerOption
Example:
modulex.NewManager(modulex.WithTypedConfig(cfg))
type Module ¶
type Module interface {
// Name returns the unique, kebab-case name of the feature module.
Name() string
// DependsOn returns the names of other modules that this module depends on.
// The Manager uses this list to sort the modules topologically before initialization.
DependsOn() []string
// Init initializes the module with the registry.
// This is where modules register their services, register routes, and resolve dependencies.
Init(ctx context.Context, reg Registry) error
}
Module represents a self-contained feature module that complies with Hexagonal Architecture. It acts as the composition root of the feature, instantiating services and adapters, and wiring them through the central registry.
Start and Stop are optional lifecycle capabilities. A module that needs to run background work during startup implements Startable; a module that owns resources that must be released implements Stoppable. The manager skips modules that do not implement these interfaces, so simple modules do not require no-op methods.
type PanicPolicy ¶
type PanicPolicy int
PanicPolicy controls how the manager reacts to a panic in a supervised task.
const ( // PanicPolicyLog recovers from the panic, records it as a task error, and logs it. PanicPolicyLog PanicPolicy = iota // PanicPolicyPropagate allows the panic to crash the application. PanicPolicyPropagate )
type ReadinessProvider ¶ added in v0.4.1
ReadinessProvider exposes the registered readiness checks.
type ReadinessRegistrar ¶ added in v0.4.1
type ReadinessRegistrar interface {
// RegisterReadinessCheck registers a readiness check function under a
// unique name.
RegisterReadinessCheck(name string, check func(context.Context) error) error
}
ReadinessRegistrar is the capability to register a module readiness check.
A readiness check answers "should this process currently receive traffic?" A failing readiness check means the instance is temporarily unable to serve requests (e.g. its database pool isn't warm yet, a dependency is unreachable, a cache hasn't primed) and should be pulled from the load balancer — the process itself is otherwise healthy and should not be restarted. Contrast this with HealthCheckRegistrar, whose checks answer "is this process functioning correctly?" and whose failures indicate the process should be restarted.
The consumer defines what "ready" means for their service by registering named check functions; Modulex only abstracts registration, aggregation, and HTTP exposure (see modulex/httpx).
type Registry ¶
type Registry interface {
ServiceRegistry
EventBusProvider
ConfigProvider
LoggerProvider
TaskSpawner
HealthCheckRegistrar
HealthCheckProvider
ReadinessRegistrar
ReadinessProvider
}
Registry manages the collection of features and cross-cutting platform components. It acts as a service locator and event bus hub, preventing features from importing each other directly or coupling to specific messaging architectures.
Registry is a composite of smaller capability interfaces. Modules that only need a subset of these capabilities can depend on the narrower interfaces (ServiceRegistry, EventBusProvider, ConfigProvider, LoggerProvider, or TaskSpawner) instead of the full Registry.
type ServiceRegistrar ¶
type ServiceRegistrar interface {
// RegisterService registers a service implementation under a unique key (e.g. "incidents.Service").
// Returns ErrRegistryLocked if the registry has already finished initialization.
RegisterService(name string, svc interface{}) error
}
ServiceRegistrar is the capability to register a service instance under a unique name. Registrations are only permitted before the registry has finished initialization.
type ServiceRegistry ¶
type ServiceRegistry interface {
ServiceRegistrar
ServiceResolver
}
ServiceRegistry combines service registration and resolution.
type ServiceResolver ¶
type ServiceResolver interface {
// ResolveService resolves a registered service implementation by name.
// If the service is not found, it returns ErrServiceNotFound.
ResolveService(name string) (interface{}, error)
}
ServiceResolver is the capability to resolve a previously registered service by name.
type Span ¶
type Span interface {
// End completes the span.
End()
// RecordError attaches an error to the span.
RecordError(err error)
// SetAttributes attaches key-value pairs to the span.
SetAttributes(attrs map[string]any)
}
Span is a minimal lifecycle span created by a Tracer.
type SpanContext ¶
SpanContext is an opaque span context used for trace propagation.
type Startable ¶
Startable is an optional lifecycle capability for modules that begin background work or listeners during startup. The manager calls Start after all modules have been initialized successfully.
type Stoppable ¶
Stoppable is an optional lifecycle capability for modules that release resources during shutdown. The manager calls Stop in reverse topological order when stopping the application or rolling back a failed init/start.
type TaskHandle ¶
type TaskHandle struct {
// contains filtered or unexported fields
}
TaskHandle identifies a supervised background task started by Manager.Go. Callers can use it to wait for completion or inspect the final error.
func (*TaskHandle) Name ¶
func (h *TaskHandle) Name() string
func (*TaskHandle) Wait ¶
func (h *TaskHandle) Wait() error
Wait blocks until the task finishes and returns its final error.
type TaskSpawner ¶
type TaskSpawner interface {
// Go spawns a supervised goroutine to execute background work. It creates a
// child span when a Tracer is configured, recovers from panics according to
// the manager's panic policy, and returns a handle that can be used to wait
// for the task to finish. Tasks are cancelled and awaited during manager
// shutdown. Returns ErrRegistryLocked if the manager is stopping or stopped,
// or ErrDuplicateTask if a task with the same name already exists.
Go(ctx context.Context, taskName string, fn func(ctx context.Context) error) (*TaskHandle, error)
}
TaskSpawner is the capability to start a supervised background task. Tasks receive a lifecycle-owned context and are cancelled during shutdown.
type Tracer ¶
type Tracer interface {
// Start creates a new span and returns a context that carries it.
Start(ctx context.Context, spanName string, attrs map[string]any) (context.Context, Span)
// SpanContextFromContext extracts the current span context from ctx.
SpanContextFromContext(ctx context.Context) SpanContext
// ContextWithSpanContext returns a new context carrying the provided span
// context. It is used to propagate trace ancestry to manager-owned task
// goroutines that run on a different base context.
ContextWithSpanContext(ctx context.Context, sc SpanContext) context.Context
}
Tracer abstracts span creation so the core package does not depend on a concrete OpenTelemetry implementation. A nil Tracer defaults to a no-op implementation.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package app provides an opinionated bootstrap helper for modulex-based services.
|
Package app provides an opinionated bootstrap helper for modulex-based services. |
|
Package chi provides Chi router integration for Modulex.
|
Package chi provides Chi router integration for Modulex. |
|
examples
|
|
|
bootstrap
command
Package main demonstrates modulex/app's Run helper together with modulex.WithTypedConfig.
|
Package main demonstrates modulex/app's Run helper together with modulex.WithTypedConfig. |
|
deployment/consumer
Package consumer is a dependent feature module that consumes the notification service.
|
Package consumer is a dependent feature module that consumes the notification service. |
|
deployment/monolith
command
Package main demonstrates a monolithic Modulex deployment: every feature module is registered in-process and dependencies are wired directly to local implementations.
|
Package main demonstrates a monolithic Modulex deployment: every feature module is registered in-process and dependencies are wired directly to local implementations. |
|
deployment/notification
Package notification is the composition root for the notification feature.
|
Package notification is the composition root for the notification feature. |
|
deployment/notification/adapters
Package adapters provides infrastructure adapters for the notification feature.
|
Package adapters provides infrastructure adapters for the notification feature. |
|
deployment/notification/ports
Package ports defines the inbound and outbound contracts for the notification feature.
|
Package ports defines the inbound and outbound contracts for the notification feature. |
|
deployment/notification/service
Package service contains the core business logic for the notification feature.
|
Package service contains the core business logic for the notification feature. |
|
deployment/remote/consumer
command
Package main runs the consumer process in a remote deployment.
|
Package main runs the consumer process in a remote deployment. |
|
deployment/remote/notification-server
command
Package main runs the notification service as a standalone process.
|
Package main runs the notification service as a standalone process. |
|
hexagonal
command
|
|
|
quickstart
command
Package main is a minimal runnable example of Modulex lifecycle orchestration.
|
Package main is a minimal runnable example of Modulex lifecycle orchestration. |
|
Package httpx provides HTTP glue for Modulex health and readiness checks, plus a managed net/http.Server lifecycle, without pulling net/http into the core modulex package.
|
Package httpx provides HTTP glue for Modulex health and readiness checks, plus a managed net/http.Server lifecycle, without pulling net/http into the core modulex package. |
|
internal
|
|
|
eventbustest
Package eventbustest provides shared test helpers for modulex.EventBus adapter implementations.
|
Package eventbustest provides shared test helpers for modulex.EventBus adapter implementations. |
|
Package nats provides a Modulex EventBus adapter backed by NATS.
|
Package nats provides a Modulex EventBus adapter backed by NATS. |
|
Package otel provides an OpenTelemetry Tracer adapter for Modulex.
|
Package otel provides an OpenTelemetry Tracer adapter for Modulex. |
|
Package rabbitmq provides a Modulex EventBus adapter backed by RabbitMQ.
|
Package rabbitmq provides a Modulex EventBus adapter backed by RabbitMQ. |
|
tools
|
|
|
modboundary
module
|
|
|
Package watermill provides a Modulex EventBus adapter backed by Watermill's in-memory GoChannel PubSub.
|
Package watermill provides a Modulex EventBus adapter backed by Watermill's in-memory GoChannel PubSub. |