470 lines
11 KiB
Markdown
470 lines
11 KiB
Markdown
# 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
|
|
|
|
1. application creates `RuntimeManager`
|
|
2. runtime loads configuration
|
|
3. runtime applies logging configuration
|
|
4. application module registers workers and supporting services
|
|
5. runtime starts all workers
|
|
6. workers execute business-related loops or processing
|
|
7. runtime aggregates health and status
|
|
8. runtime stops workers gracefully or forcefully
|
|
|
|
## Core concepts
|
|
|
|
### `ApplicationModule`
|
|
|
|
File: [application.py](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/contracts/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](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/contracts/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](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/contracts/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](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/contracts/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](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/tracing/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](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/health/registry.py)
|
|
|
|
Aggregates application health.
|
|
|
|
PLBA uses three health states:
|
|
- `ok` — all critical parts work
|
|
- `degraded` — application still works, but there is a problem
|
|
- `unhealthy` — application should not be considered operational
|
|
|
|
### Runtime status
|
|
|
|
File: [types.py](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/core/types.py)
|
|
|
|
Status is separate from health.
|
|
|
|
Current runtime states:
|
|
- `starting`
|
|
- `idle`
|
|
- `busy`
|
|
- `stopping`
|
|
- `stopped`
|
|
|
|
Status is used for operational lifecycle decisions such as graceful shutdown.
|
|
|
|
### `ControlPlaneService`
|
|
|
|
Files:
|
|
- [service.py](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/control/service.py)
|
|
- [http_channel.py](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/control/http_channel.py)
|
|
|
|
Provides control and observability endpoints.
|
|
|
|
Currently supported:
|
|
- health access
|
|
- runtime start action
|
|
- runtime stop action
|
|
- runtime status action
|
|
|
|
### `ConfigurationManager`
|
|
|
|
Files:
|
|
- [configuration.py](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/core/configuration.py)
|
|
- [file_loader.py](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/config/file_loader.py)
|
|
- [providers.py](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/config/providers.py)
|
|
|
|
Loads and merges configuration.
|
|
|
|
Current built-in source:
|
|
- YAML file provider
|
|
|
|
### `LogManager`
|
|
|
|
File: [manager.py](/Users/alex/Dev_projects_v2/apps/plba/src/app_runtime/logging/manager.py)
|
|
|
|
Applies logging configuration from config.
|
|
|
|
Current expectation:
|
|
- logging config lives in the `log` section 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:
|
|
- `TaskQueue`
|
|
- `InMemoryTaskQueue`
|
|
- `QueueWorker`
|
|
|
|
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:
|
|
- `ConfigurationManager`
|
|
- `FileConfigProvider`
|
|
- `ConfigFileLoader`
|
|
|
|
What it gives:
|
|
- YAML config loading
|
|
- config merging
|
|
- access to platform and application config
|
|
|
|
Example use:
|
|
- load `platform` section for runtime
|
|
- load `mail_order_bot` section for app-specific config
|
|
|
|
### 5. Tracing
|
|
|
|
Services:
|
|
- `TraceService`
|
|
- `TraceTransport`
|
|
- `NoOpTraceTransport`
|
|
|
|
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:
|
|
- `HealthRegistry`
|
|
- `WorkerHealth`
|
|
|
|
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 `idle` or `stopped`
|
|
|
|
### 8. HTTP control
|
|
|
|
Services:
|
|
- `ControlPlaneService`
|
|
- `HttpControlChannel`
|
|
|
|
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:
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
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 on `plba`
|
|
- repository `service_b`: business application depending on `plba`
|
|
|
|
## Example: `mail_order_bot`
|
|
|
|
Simple first version of `mail_order_bot` on PLBA:
|
|
- `MailPollingWorker`, concurrency `1`
|
|
- `EmailProcessingWorker`, concurrency `3`
|
|
- shared `InMemoryTaskQueue`
|
|
- domain services for mail parsing and order processing
|
|
|
|
Flow:
|
|
1. polling worker checks IMAP
|
|
2. polling worker pushes email tasks into queue
|
|
3. processing workers consume tasks
|
|
4. processing workers execute domain logic
|
|
5. runtime aggregates health and status
|
|
|
|
This keeps `mail_order_bot` small, explicit, and aligned with current PLBA architecture.
|