11 KiB
PLBA
PLBA is a reusable platform runtime for business applications.
It solves platform concerns that should not live inside domain code:
- application lifecycle
- worker orchestration
- configuration loading from YAML
- tracing
- health aggregation
- runtime status reporting
- HTTP control endpoints
- logging configuration
Business applications depend on plba as a package and implement only their own business behavior.
Architecture
Current PLBA architecture is built around one core idea:
- the runtime manages a set of application workers
A worker is any runtime-managed active component with a unified lifecycle:
start()stop(force=False)health()status()
This means PLBA does not require separate platform categories like source and consumer.
If an application needs polling, queue processing, listening, scheduled work, or another active loop, it is implemented as a worker.
Main runtime model
- application creates
RuntimeManager - runtime loads configuration
- runtime applies logging configuration
- application module registers workers and supporting services
- runtime starts all workers
- workers execute business-related loops or processing
- runtime aggregates health and status
- runtime stops workers gracefully or forcefully
Core concepts
ApplicationModule
File: application.py
Describes a business application to the runtime.
Responsibilities:
- provide module name
- register workers
- register queues if needed
- register handlers if needed
- register health contributors
- compose application-specific objects
ApplicationModule does not run the application itself.
It only declares how the application is assembled.
Worker
File: worker.py
The main runtime-managed contract.
Responsibilities:
- start its own execution
- stop gracefully or forcefully
- report health
- report runtime status
This is the main extension point for business applications.
TaskQueue
File: queue.py
Optional queue abstraction.
Use it when application workers need buffered or decoupled processing.
PLBA does not force every application to use a queue. Queue is one supported pattern, not the foundation of the whole platform.
TaskHandler
File: tasks.py
Optional unit of business processing for one task.
Useful when a worker follows queue-driven logic:
- worker takes a task
- handler executes business logic
TraceService
File: service.py
Platform trace service.
Responsibilities:
- create trace contexts
- resume trace from task metadata
- write context records
- write trace messages
Business code should use it as a platform service and should not implement its own tracing infrastructure.
HealthRegistry
File: registry.py
Aggregates application health.
PLBA uses three health states:
ok— all critical parts workdegraded— application still works, but there is a problemunhealthy— application should not be considered operational
Runtime status
File: types.py
Status is separate from health.
Current runtime states:
startingidlebusystoppingstopped
Status is used for operational lifecycle decisions such as graceful shutdown.
ControlPlaneService
Files:
Provides control and observability endpoints.
Currently supported:
- health access
- runtime start action
- runtime stop action
- runtime status action
ConfigurationManager
Files:
Loads and merges configuration.
Current built-in source:
- YAML file provider
LogManager
File: manager.py
Applies logging configuration from config.
Current expectation:
- logging config lives in the
logsection of YAML
Available platform services
PLBA currently provides these reusable services.
1. Runtime lifecycle
Service:
RuntimeManager
What it gives:
- startup orchestration
- worker registration and startup
- graceful stop with timeout
- force stop
- status snapshot
Example use:
- start
mail_order_bot - stop it after active email processing is drained
2. Worker supervision
Service:
WorkerSupervisor
What it gives:
- unified worker orchestration
- aggregated worker statuses
- aggregated worker health
- stop coordination
Example use:
- run one polling worker and three processing workers in the same application
3. Queue support
Services:
TaskQueueInMemoryTaskQueueQueueWorker
What it gives:
- buffered processing
- decoupling between task production and task consumption
- worker concurrency for task handling
Example use:
- worker A polls IMAP and pushes tasks to queue
- worker B processes queued email tasks with concurrency
3
4. Configuration
Services:
ConfigurationManagerFileConfigProviderConfigFileLoader
What it gives:
- YAML config loading
- config merging
- access to platform and application config
Example use:
- load
platformsection for runtime - load
mail_order_botsection for app-specific config
5. Tracing
Services:
TraceServiceTraceTransportNoOpTraceTransport
What it gives:
- trace context creation
- trace propagation through task metadata
- trace messages for processing steps
Example use:
- polling worker creates trace when it discovers a mail
- processing worker resumes trace and writes business steps
6. Health
Services:
HealthRegistryWorkerHealth
What it gives:
- per-worker health
- aggregated application health
- critical vs non-critical component handling
Example use:
- email processing workers are critical
- optional diagnostic worker may be non-critical
7. Status
Services:
WorkerStatus- runtime aggregated state
What it gives:
- current activity visibility
- ability to stop application only after in-flight work is completed
Example use:
- stop application only after processing workers become
idleorstopped
8. HTTP control
Services:
ControlPlaneServiceHttpControlChannel
What it gives:
- HTTP health/status/actions
- operational integration point
Example use:
- inspect current health from orchestration
- request graceful stop remotely
Public package API
Public namespace is plba.
Main imports for external applications:
from plba import ApplicationModule, QueueWorker, RuntimeManager, create_runtime
from plba.contracts import Task, TaskHandler, TaskQueue, Worker, WorkerHealth, WorkerStatus
from plba.queue import InMemoryTaskQueue
from plba.tracing import TraceService
Example application pattern
Minimal queue-based application:
from plba import ApplicationModule, QueueWorker, Task, TaskHandler, create_runtime
from plba.queue import InMemoryTaskQueue
class ExampleHandler(TaskHandler):
def handle(self, task: Task) -> None:
print(task.payload)
class ExampleModule(ApplicationModule):
@property
def name(self) -> str:
return "example"
def register(self, registry) -> None:
queue = InMemoryTaskQueue()
traces = registry.services.get("traces")
queue.publish(Task(name="incoming", payload={"hello": "world"}))
registry.add_queue("incoming", queue)
registry.add_worker(QueueWorker("example-worker", queue, ExampleHandler(), traces))
runtime = create_runtime(
ExampleModule(),
config_path="config.yml",
enable_http_control=False,
)
runtime.start()
Building business applications on PLBA
These are the current rules for building business applications correctly.
1. Keep platform and business concerns separate
PLBA owns:
- lifecycle
- worker management
- logging
- trace infrastructure
- health aggregation
- HTTP control
- config loading
Business application owns:
- business workflows
- domain services
- application-specific config schema
- business task payloads
- business error semantics
2. Build app behavior from workers
A business application should be described as a small set of workers.
Typical examples:
- polling worker
- processing worker
- reconciliation worker
Do not introduce new worker types at platform level unless there is clear need for custom runtime behavior.
3. Use queues only when they help
Queue is optional.
Use queue when:
- one worker discovers work
- another worker processes it
- buffering or decoupling helps
- concurrency is needed
Do not force queue into applications that do not need it.
4. Keep business logic out of worker lifecycle code
Worker should orchestrate execution. Business rules should live in dedicated services and handlers.
Good:
- worker gets config
- worker calls domain service
- worker reports trace and status
Bad:
- worker contains all parsing, decision logic, integration rules, and persistence rules in one class
5. Use trace as a platform service
Business application should:
- create meaningful trace steps
- propagate trace through task metadata if queue is used
- record business-relevant processing milestones
Business application should not:
- implement its own trace store
- control trace transport directly unless explicitly needed
6. Read config through PLBA
Business application should not read YAML directly.
Recommended flow:
- PLBA loads config
- application reads only its own config section
- application converts it to typed app config object
- services receive typed config object
7. Distinguish health from status
Use health for:
- is application operational?
Use status for:
- what is application doing right now?
This is important for graceful stop:
- health may still be
ok - status may be
busy
8. Design workers for graceful stop
Workers should support:
- stop accepting new work
- finish current in-flight work when possible
- report
busy,idle,stopping,stopped
This allows runtime to stop application safely.
Recommended repository model
PLBA is intended to live in its own repository as a reusable package.
Recommended setup:
- repository
plba: platform package only - repository
mail_order_bot: business application depending onplba - repository
service_b: business application depending onplba
Example: mail_order_bot
Simple first version of mail_order_bot on PLBA:
MailPollingWorker, concurrency1EmailProcessingWorker, concurrency3- shared
InMemoryTaskQueue - domain services for mail parsing and order processing
Flow:
- polling worker checks IMAP
- polling worker pushes email tasks into queue
- processing workers consume tasks
- processing workers execute domain logic
- runtime aggregates health and status
This keeps mail_order_bot small, explicit, and aligned with current PLBA architecture.