Menu

Overview

Relevant source files

Purpose and Scope

ControllerBus is a framework for building modular, decoupled applications where independent components called Controllers communicate through a central Bus using declarative Directives. This documentation covers the complete system architecture, from core components to advanced features like hot-loadable plugins and cross-language integration.

ControllerBus enables:

  • Declarative state management: Controllers request desired states without knowing which other controllers will fulfill them
  • Dynamic composition: Controllers can be added, removed, or reconfigured at runtime
  • Reference-counted resources: Automatic lifecycle management through directive references
  • Cross-platform operation: Native support for Go backends and TypeScript/JavaScript frontends

For information about specific subsystems, see: Core Architecture, Bus System, Directive System, Controller System, Configuration System, and Plugin System.

Core System Components

The following diagram maps the primary architectural components to their code entities:

Sources: bus/bus.go bus/inmem/inmem.go14-28 core/core.go48-101 controller/controller.go directive/directive.go controller/factory.go

Key Concepts

The following table defines the fundamental concepts with their code representations:

ConceptDefinitionPrimary InterfaceKey Implementation
BusCentral communication hub managing controllers and dispatching directivesbus.Bus bus/bus.go12-39inmem.Bus bus/inmem/inmem.go14-28
ControllerStateful component that handles directives and executes long-running logiccontroller.Controller controller/controller.goBase: bus.BusController
DirectiveDeclarative request for data or desired state with reference countingdirective.Directive directive/directive.goVarious directive types in controller packages
ResolverGoroutine that produces values to satisfy a directivedirective.Resolver directive/resolver.godirective.ValueResolver, directive.GetterResolver, etc.
FactoryConstructor providing metadata and instantiation for controllerscontroller.Factory controller/factory.gobus.BusFactory, per-controller factories
ConfigProtobuf configuration object for controller instantiationconfig.Config config/config.goController-specific config messages
ReferenceHandle representing interest in a directive with lifecycle managementdirective.Reference directive/directive.goReturned by AddDirective()

Sources: bus/bus.go12-39 controller/controller.go directive/directive.go directive/resolver.go controller/factory.go config/config.go

Bus Interface

The bus.Bus interface bus/bus.go12-39 embeds directive.Controller and adds controller lifecycle management:

The in-memory implementation inmem.Bus bus/inmem/inmem.go14-28 embeds a directive controller for communication infrastructure while maintaining a list of attached controllers bus/inmem/inmem.go20-23

Sources: bus/bus.go12-39 bus/inmem/inmem.go14-28

Controller Interface

Controllers implement the controller.Controller interface controller/controller.go with four key methods:

MethodPurposeReturn Value
GetControllerInfo()Returns static metadata (*controller.Info)Controller ID, version, description
HandleDirective(ctx, di)Inspects directive and returns resolvers[]directive.Resolver or nil
Execute(ctx)Main execution looperror (nil = success)
Close()Cleanup on shutdownNone

The base implementation bus.BusController[T] bus/controller.go handles common boilerplate like storing the logger, bus reference, and configuration.

Sources: controller/controller.go bus/controller.go

Directive System

Directives are identified by type and parameters. The directive.Directive interface directive/directive.go requires:

  • GetDirectiveType(): Returns unique type string
  • IsEquivalent(directive.Directive): Enables deduplication
  • GetValueOptions(): Specifies value collection behavior

Key options in directive.ValueOptions directive/directive.go:

FieldTypePurpose
MaxValueCountuint32Maximum values to collect (0 = unbounded)
MaxValueHardCapboolWhether limit is strict
UnrefDisposeDurtime.DurationDelay before disposing unreferenced directives
UnrefDisposeEmptyImmediateboolImmediate disposal if no values

Sources: directive/directive.go

Factory and Configuration

Factories implement controller.Factory controller/factory.go and provide:

The static resolver static.Resolver controller/resolver/static maintains a registry of factories and implements controller.FactoryResolver for factory lookup.

Sources: controller/factory.go controller/resolver/static

System Initialization Flow

The following diagram shows the bootstrap sequence with actual function calls:

Sources: core/core.go48-101 directive/controller bus/inmem/inmem.go26-28 controller/loader controller/resolver/static controller/resolver

The initialization order is critical:

  1. Directive Controller: Provides communication infrastructure
  2. In-Memory Bus: Wraps directive controller and manages controller lifecycle
  3. Loader Controller: Enables dynamic controller instantiation via ExecController directive
  4. Static Resolver: Populated with built-in factories (configset, api, etc.)
  5. Resolver Controller: Enables factory lookup via LoadConfigConstructorByID and LoadFactoryByConfig directives

Sources: core/core.go48-101

Polyglot Architecture and Dependencies

ControllerBus supports both Go and TypeScript/JavaScript through shared protocol definitions:

Sources: go.mod14-15 package.json59-60

Dependency Structure

LayerGo PackageTypeScript PackagePurpose
Serializationgithub.com/aperturerobotics/protobuf-go-lite@aptre/protobuf-es-liteLightweight Protobuf encoding/decoding
RPCgithub.com/aperturerobotics/starpcstarpcStreaming RPC over multiplexed connections
Bus APIcontrollerbus/bus/api-gRPC service exposing bus operations

The system uses:

  • protobuf-go-lite go.mod14: Minimalist protobuf implementation without reflection
  • @aptre/protobuf-es-lite package.json59: TypeScript equivalent for browser/Node.js
  • starpc go.mod15 package.json60: RPC framework with streaming and multiplexing
  • Custom libp2p fork go.mod6: Modified for post-quantum cryptography support

Sources: go.mod1-31 package.json1-65

Command Line Interface

The CLI is structured as both daemon and client:

Sources: cmd/controllerbus README.md39-65 README.md142-181

Configuration Format

ControllerBus uses YAML/JSON configuration with the ConfigSet structure controller/configset:

Each entry maps a controller instance ID to:

  • config: Controller-specific configuration object
  • id: Configuration type identifier (maps to a Factory)
  • rev: Revision number for conflict resolution

Sources: README.md73-86 controller/configset

Extension Mechanisms

ControllerBus provides multiple extension points:

MechanismLocationPurposeHot-Reloadable
Controller FactoriesImplement controller.FactoryAdd new controller typesVia plugins
Plugin Systemplugin/ plugin/README.mdBundle factories into loadable modulesYes (.cbus.so)
Bus APIbus/api/Remote control via gRPCConfiguration
Custom ResolversImplement directive.ResolverCustom directive resolution strategiesVia controllers

The plugin system plugin/README.md scans Go packages, discovers factories, generates wrapper code, and compiles to shared libraries (.cbus.so) plugin/loader/shared-library/shared-library.go17 The filesystem loader plugin/loader/filesystem watches for file changes and hot-reloads plugins.

Sources: plugin/README.md plugin/loader/shared-library/shared-library.go17 plugin/loader/filesystem

Reference Counting and Lifecycle

All directives use reference counting for automatic lifecycle management:

  1. Addition: bus.AddDirective(di, refHandler) returns directive.Reference directive/directive.go
  2. Active: Reference holder receives callbacks via directive.ReferenceHandler directive/directive.go
  3. Release: reference.Release() decrements count directive/directive.go
  4. Disposal: When count reaches zero, after UnrefDisposeDur, the directive instance is disposed directive/directive.go

Values produced by resolvers are managed through the directive.ResolverHandler interface directive/resolver.go which provides AddValue(), RemoveValue(), and ClearValues() methods.

Sources: directive/directive.go directive/resolver.go