Skip to content

Component Interface Reference

Complete reference for the module.Component interface that all TinySystems components must implement.

Interface Definition

go
type Component interface {
    // GetInfo returns component metadata
    GetInfo() Info

    // Handle processes incoming messages
    Handle(ctx context.Context, output Handler, port string, message any) error

    // Ports returns the component's port definitions
    Ports() []Port

    // Instance creates a new component instance
    Instance() Component
}

Methods

GetInfo

Returns metadata about the component.

go
func (c *Component) GetInfo() module.Info

Returns: module.Info

go
type Info struct {
    Name        string   // Unique component identifier
    Description string   // Human-readable description
    Icon        string   // Icon name for UI
    Tags        []string // Categorization tags
}

Example:

go
func (c *MyComponent) GetInfo() module.Info {
    return module.Info{
        Name:        "my-transformer",
        Description: "Transforms incoming data based on configuration",
        Icon:        "IconTransform",
        Tags:        []string{"transform", "data", "utility"},
    }
}

Notes:

  • Name must be unique within the module
  • Name should use kebab-case
  • Icon references Tabler Icons (e.g., "IconBox", "IconRouter")

Handle

Processes incoming messages on a specific port.

go
func (c *Component) Handle(
    ctx context.Context,
    output Handler,
    port string,
    message any,
) error

Parameters:

ParameterTypeDescription
ctxcontext.ContextRequest context with tracing, cancellation
outputHandlerFunction to emit output messages
portstringName of the port receiving the message
messageanyThe incoming message data

Returns: error

  • nil - Success
  • error - Failure (will be retried)
  • errors.PermanentError(err) - Permanent failure (no retry)

Example:

go
func (c *MyComponent) Handle(
    ctx context.Context,
    output module.Handler,
    port string,
    message any,
) error {
    switch port {
    case v1alpha1.SettingsPort:
        c.settings = message.(Settings)
        return nil

    case "input":
        input := message.(Input)
        result, err := c.process(input)
        if err != nil {
            return err
        }
        return output(ctx, "output", result)

    default:
        return fmt.Errorf("unknown port: %s", port)
    }
}

Context Values:

go
// Check if this instance is the leader
if utils.IsLeader(ctx) {
    // Leader-only logic
}

// Get tracing span
span := trace.SpanFromContext(ctx)
span.SetAttributes(attribute.String("key", "value"))

Ports

Returns the component's port definitions.

go
func (c *Component) Ports() []module.Port

Returns: []module.Port

go
type Port struct {
    Name          string      // Unique port identifier
    Label         string      // Display label
    Position      Position    // UI position (Left, Right, Top, Bottom)
    Source        bool        // true = input, false = output
    Configuration interface{} // Schema definition struct
}

Example:

go
func (c *MyComponent) Ports() []module.Port {
    return []module.Port{
        {
            Name:          v1alpha1.SettingsPort,
            Label:         "Settings",
            Position:      module.PositionTop,
            Source:        true,
            Configuration: Settings{},
        },
        {
            Name:          "input",
            Label:         "Input",
            Position:      module.PositionLeft,
            Source:        true,
            Configuration: Input{},
        },
        {
            Name:          "output",
            Label:         "Output",
            Position:      module.PositionRight,
            Source:        false,
            Configuration: Output{},
        },
        {
            Name:          "error",
            Label:         "Error",
            Position:      module.PositionBottom,
            Source:        false,
            Configuration: ErrorOutput{},
        },
    }
}

Instance

Creates a new instance of the component.

go
func (c *Component) Instance() module.Component

Returns: module.Component - A new, independent instance

Example:

go
func (c *MyComponent) Instance() module.Component {
    return &MyComponent{}
}

Notes:

  • Must return a fresh instance
  • Do not share state between instances
  • Each TinyNode gets its own instance

Optional Interfaces

ControlHandler

For components with control ports (UI buttons).

go
type ControlHandler interface {
    HandleControl(ctx context.Context, state any, port string, msg any) (any, error)
}

Example:

go
func (c *MyComponent) HandleControl(
    ctx context.Context,
    state any,
    port string,
    msg any,
) (any, error) {
    switch port {
    case "start":
        c.start()
        return &ControlState{Running: true}, nil
    case "stop":
        c.stop()
        return &ControlState{Running: false}, nil
    }
    return state, nil
}

ReconcileHandler

For components that need reconciliation events.

go
type ReconcileHandler interface {
    HandleReconcile(ctx context.Context, node *v1alpha1.TinyNode) error
}

Example:

go
func (c *MyComponent) HandleReconcile(
    ctx context.Context,
    node *v1alpha1.TinyNode,
) error {
    // React to TinyNode changes
    if node.Status.Metadata != nil {
        if port, ok := node.Status.Metadata["http-port"]; ok {
            c.listenPort = port
        }
    }
    return nil
}

Handler Function

The output handler for emitting messages.

go
type Handler func(ctx context.Context, port string, message any) error

Parameters:

ParameterTypeDescription
ctxcontext.ContextMust pass through context
portstringOutput port name
messageanyData to emit

Returns: error

  • nil - Message delivered successfully
  • error - Delivery failed

Blocking Behavior:

go
// Default: blocks until downstream completes
err := output(ctx, "output", result)
// Execution continues after all downstream processing

// Non-blocking: use goroutine
go func() {
    ctx := trace.ContextWithSpanContext(context.Background(),
        trace.SpanContextFromContext(originalCtx))
    output(ctx, "output", result)
}()

Complete Example

go
package mycomponent

import (
    "context"
    "fmt"

    "github.com/tiny-systems/module/api/v1alpha1"
    "github.com/tiny-systems/module/module"
    "github.com/tiny-systems/module/pkg/schema"
)

type Component struct {
    settings Settings
}

type Settings struct {
    Multiplier int `json:"multiplier" default:"1" title:"Multiplier"`
}

type Input struct {
    Value int `json:"value" required:"true"`
}

type Output struct {
    Result int `json:"result"`
}

func (c *Component) GetInfo() module.Info {
    return module.Info{
        Name:        "multiplier",
        Description: "Multiplies input value by configured multiplier",
        Icon:        "IconX",
        Tags:        []string{"math", "transform"},
    }
}

func (c *Component) Ports() []module.Port {
    return []module.Port{
        {
            Name:          v1alpha1.SettingsPort,
            Label:         "Settings",
            Position:      module.PositionTop,
            Source:        true,
            Configuration: Settings{Multiplier: 1},
        },
        {
            Name:          "input",
            Label:         "Input",
            Position:      module.PositionLeft,
            Source:        true,
            Configuration: Input{},
        },
        {
            Name:          "output",
            Label:         "Output",
            Position:      module.PositionRight,
            Source:        false,
            Configuration: Output{},
        },
    }
}

func (c *Component) Handle(
    ctx context.Context,
    output module.Handler,
    port string,
    message any,
) error {
    switch port {
    case v1alpha1.SettingsPort:
        c.settings = message.(Settings)
        return nil

    case "input":
        input := message.(Input)
        result := input.Value * c.settings.Multiplier
        return output(ctx, "output", Output{Result: result})

    default:
        return fmt.Errorf("unknown port: %s", port)
    }
}

func (c *Component) Instance() module.Component {
    return &Component{}
}

Build flow-based applications on Kubernetes