Introduction
Workers are dynamically loaded and configured, often utilizing an Adapter pattern to allow for swappable backend implementations (e.g., swapping an in-memory event bus for a Redis-backed one). The engine provides a trait-based system where workers implement theCoreWorker trait for lifecycle management and the ConfigurableWorker trait for handling configuration and adapter injection.
Worker Architecture
The worker system is built around two primary traits:CoreWorker and ConfigurableWorker.
Core Traits
| Trait | Description | Key Methods |
|---|---|---|
| CoreWorker | The base trait for all workers. Handles lifecycle, identification, and function registration. | name(), create(), initialize(), register_functions(), start_background_tasks(), destroy() |
| ConfigurableWorker | Extends CoreWorker to support typed configuration and pluggable adapters. | build(), registry(), adapter_name_from_config() |
Lifecycle Flow
The following diagram illustrates the lifecycle of a worker from creation to initialization.Implementing a Configurable Worker
Developing a custom worker typically involves defining an adapter interface, implementing specific adapters, and then wrapping them in a worker structure.Step 1: Define the Adapter Trait
Define anasync_trait that specifies the behavior your module’s backend must implement. This allows users to switch implementations via configuration.
Step 2: Implement Adapter Registration
To make adapters discoverable by the configuration system, you must define a registration struct and use theinventory crate.
Step 3: Create Adapter Factories
Define factory functions that instantiate your specific adapter implementations (e.g.,InMemory or Logging).
Step 4: Implement Adapter Logic
Create the actual adapter implementations.In-Memory Adapter
In-Memory Adapter
Simple in-memory implementation for development and testing.
Logging Wrapper Adapter
Logging Wrapper Adapter
Wrapper adapter that logs all events while delegating to another adapter.
Step 5: Implement the Worker Logic
The worker struct holds theEngine reference and the injected Adapter.
Registering Functions
Workers expose functionality to the engine (and thus to SDK workers) by registering functions. This is typically done in theinitialize method or register_functions.
Registration Request Structure
When registering a function, you must provide aRegisterFunctionRequest.
| Field | Type | Description |
|---|---|---|
function_id | String | Unique function ID (e.g., “custom::emit”) |
description | Option<String> | Human-readable description of the function |
request_format | Option<Value> | JSON Schema defining the expected input |
response_format | Option<Value> | JSON Schema defining the expected output |
When using the
#[service] macro with #[function] attributes, request_format and response_format are auto-generated as standard JSON Schema from your Rust types (via schemars). Your input/output types must derive JsonSchema. Manual schema specification is only needed for custom module registration.Example Registration
Handling Function Invocations
To handle invocations, the worker (or a specific handler struct) must implement theFunctionHandler trait.
Registering Triggers
Workers can also act as sources of events by registeringTriggerTypes. This allows the engine to route external events (like Cron ticks or HTTP requests) to specific functions.
Trigger Architecture
Implementation
To support triggers, a worker implementsTriggerRegistrator.
Configuration
Workers are configured viaiii-config.yaml or JSON passed during initialization. The ConfigurableWorker trait maps this configuration to a Rust struct.
Configuration Struct
Usage in Config File
Complete Example
Here’s a complete custom worker implementation:examples/custom_event_module.rs
iii-config.yaml
Best Practices
Use Adapter Pattern
Use Adapter Pattern
Always use adapters for external integrations to allow swapping implementations.
Implement Graceful Shutdown
Implement Graceful Shutdown
Handle cleanup in the module’s drop implementation or provide shutdown hooks.
Provide JSON Schemas
Provide JSON Schemas
Always define request and response formats for functions to enable validation and documentation.
Use Structured Logging
Use Structured Logging
Use the
tracing crate for structured logging with context.Next Steps
Core Workers
Explore built-in core workers
Adapters
Learn more about the adapter pattern