Перенес workflow
This commit is contained in:
@@ -1,111 +1,50 @@
|
||||
# Architectural Constraints
|
||||
|
||||
**`docs/adr/0001-new-runtime.md`**
|
||||
```md
|
||||
# ADR 0001: Create a new runtime project instead of evolving the legacy ConfigManager model
|
||||
## Main constraints
|
||||
|
||||
## Status
|
||||
1. `Worker` is the primary runtime abstraction.
|
||||
2. `ApplicationModule` assembles the application and registers workers.
|
||||
3. Business behavior should live outside worker lifecycle code.
|
||||
4. The platform should avoid queue-centric abstractions as first-class architecture.
|
||||
5. Utility components are allowed only when they remain optional and transparent.
|
||||
|
||||
Accepted
|
||||
## Worker constraints
|
||||
|
||||
## Context
|
||||
Worker should own:
|
||||
- thread/process lifecycle
|
||||
- execution strategy
|
||||
- graceful stop
|
||||
- runtime state
|
||||
- health state
|
||||
|
||||
The previous generation of the application runtime was centered around a timer-driven execution model:
|
||||
- a manager loaded configuration
|
||||
- a worker loop periodically called `execute()`
|
||||
- applications placed most operational logic behind that entry point
|
||||
Worker should not own:
|
||||
- large business workflows
|
||||
- domain rules
|
||||
- parsing plus persistence plus integration logic in one class
|
||||
|
||||
That model is adequate for simple periodic jobs, but it does not match the direction of the new platform.
|
||||
## Business-code constraints
|
||||
|
||||
The new platform must support:
|
||||
- task sources
|
||||
- queue-driven processing
|
||||
- multiple parallel workers
|
||||
- trace propagation across producer/consumer boundaries
|
||||
- richer lifecycle and status management
|
||||
- health aggregation
|
||||
- future admin web interface
|
||||
- future authentication and user management
|
||||
Business routines and services should own:
|
||||
- business decisions
|
||||
- external integration calls
|
||||
- persistence
|
||||
- domain validation
|
||||
|
||||
These are platform concerns, not business concerns.
|
||||
They should not own:
|
||||
- platform lifecycle
|
||||
- worker supervision
|
||||
- runtime status management
|
||||
|
||||
We also want business applications to describe only business functionality and rely on the runtime for infrastructure behavior.
|
||||
## API constraints
|
||||
|
||||
## Decision
|
||||
Public API should stay small and clear.
|
||||
|
||||
We will create a new runtime project instead of implementing a `V3` directly inside the current legacy ConfigManager codebase.
|
||||
|
||||
The new runtime will be built around a platform-oriented model with explicit concepts such as:
|
||||
- `RuntimeManager`
|
||||
Prefer:
|
||||
- `ApplicationModule`
|
||||
- `TaskSource`
|
||||
- `TaskQueue`
|
||||
- `WorkerSupervisor`
|
||||
- `TaskHandler`
|
||||
- `TraceService`
|
||||
- `HealthRegistry`
|
||||
- `Worker`
|
||||
- runtime services
|
||||
- utility queue
|
||||
|
||||
The old execute-centered model is treated as a previous-generation design and is not the architectural basis of the new runtime.
|
||||
|
||||
## Rationale
|
||||
|
||||
Creating a new runtime project gives us:
|
||||
- freedom to design the correct abstractions from the start
|
||||
- no pressure to preserve legacy contracts
|
||||
- cleaner boundaries between platform and business logic
|
||||
- simpler documentation and tests
|
||||
- lower long-term complexity than mixing old and new models in one codebase
|
||||
|
||||
If we built this as `V3` inside the old project, we would likely inherit:
|
||||
- compatibility constraints
|
||||
- mixed abstractions
|
||||
- transitional adapters
|
||||
- conceptual confusion between periodic execution and event/queue processing
|
||||
|
||||
The expected long-term cost of such coupling is higher than creating a clean new runtime.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- the platform can be modeled cleanly
|
||||
- business applications can integrate through explicit contracts
|
||||
- new runtime capabilities can be added without legacy pressure
|
||||
- mail_order_bot can become the first pilot application on the new runtime
|
||||
|
||||
### Negative
|
||||
|
||||
- some existing capabilities will need to be reintroduced in the new project
|
||||
- there will be a temporary period with both legacy and new runtime lines
|
||||
- migration requires explicit planning
|
||||
|
||||
## Initial migration target
|
||||
|
||||
The first application on the new runtime will be `mail_order_bot`.
|
||||
|
||||
Initial runtime design for that application:
|
||||
- IMAP polling source
|
||||
- in-memory queue
|
||||
- parallel workers
|
||||
- business handler for email processing
|
||||
- message marked as read only after successful processing
|
||||
|
||||
Later:
|
||||
- swap IMAP polling source for IMAP IDLE source
|
||||
- keep the queue and worker model unchanged
|
||||
|
||||
## What we intentionally do not carry over
|
||||
|
||||
We do not keep the old architecture as the central organizing principle:
|
||||
- no `execute()`-centric application model
|
||||
- no timer-loop as the main abstraction
|
||||
- no implicit mixing of lifecycle and business processing
|
||||
|
||||
## Follow-up
|
||||
|
||||
Next design work should define:
|
||||
- core platform contracts
|
||||
- package structure
|
||||
- runtime lifecycle
|
||||
- queue and worker interfaces
|
||||
- config model split between platform and application
|
||||
- pilot integration for `mail_order_bot`
|
||||
Avoid:
|
||||
- legacy abstractions preserved only for compatibility
|
||||
- specialized platform roles without strong need
|
||||
|
||||
@@ -1,116 +1,62 @@
|
||||
# Mail Order Bot Migration Plan
|
||||
|
||||
## Purpose
|
||||
## Goal
|
||||
|
||||
This document describes how `mail_order_bot` will be adapted to the new runtime as the first pilot business application.
|
||||
Migrate `mail_order_bot` to the new PLBA model:
|
||||
- application assembled by `ApplicationModule`
|
||||
- runtime execution owned by `Worker`
|
||||
- business behavior implemented in routines/services
|
||||
|
||||
## Scope
|
||||
## Target structure
|
||||
|
||||
This is not a full migration specification for all future applications.
|
||||
It is a practical first use case to validate the runtime architecture.
|
||||
### Application module
|
||||
|
||||
## Current model
|
||||
|
||||
The current application flow is tightly coupled:
|
||||
- the manager checks IMAP
|
||||
- unread emails are fetched
|
||||
- emails are processed synchronously
|
||||
- messages are marked as read after processing
|
||||
|
||||
Polling and processing happen in one execution path.
|
||||
|
||||
## Target model
|
||||
|
||||
The new runtime-based flow should be:
|
||||
|
||||
1. a mail source detects new tasks
|
||||
2. tasks are published to a queue
|
||||
3. workers consume tasks in parallel
|
||||
4. a domain handler processes each email
|
||||
5. successful tasks lead to `mark_as_read`
|
||||
6. failed tasks remain retriable
|
||||
|
||||
## Phase 1
|
||||
|
||||
### Source
|
||||
- IMAP polling source
|
||||
|
||||
### Queue
|
||||
- in-memory task queue
|
||||
`MailOrderBotModule` should:
|
||||
- build domain services
|
||||
- build routines
|
||||
- register workers
|
||||
- register optional health contributors
|
||||
|
||||
### Workers
|
||||
- 2 to 4 parallel workers initially
|
||||
|
||||
### Handler
|
||||
- domain email processing handler built around the current processing logic
|
||||
Initial workers:
|
||||
- `MailPollingWorker`
|
||||
- `EmailProcessingWorker`
|
||||
- optional `ReconciliationWorker`
|
||||
|
||||
### Delivery semantics
|
||||
- email is marked as read only after successful processing
|
||||
- unread state acts as the first safety mechanism against message loss
|
||||
Each worker should own only runtime behavior:
|
||||
- start/stop
|
||||
- execution loop
|
||||
- thread model
|
||||
- health/status
|
||||
|
||||
## Why in-memory queue is acceptable at first
|
||||
### Routines
|
||||
|
||||
For the first phase:
|
||||
- infrastructure complexity stays low
|
||||
- the runtime contracts can be tested quickly
|
||||
- unread emails in IMAP provide a simple recovery path after crashes
|
||||
Initial routines:
|
||||
- `MailPollingRoutine`
|
||||
- `EmailProcessingRoutine`
|
||||
- `ReconciliationRoutine`
|
||||
|
||||
This allows us to validate the runtime architecture before adopting an external broker.
|
||||
Each routine should own business behavior:
|
||||
- fetch messages
|
||||
- parse message payload
|
||||
- call domain services
|
||||
- persist changes
|
||||
- report business failures upward
|
||||
|
||||
## Phase 2
|
||||
## Migration steps
|
||||
|
||||
Replace:
|
||||
- IMAP polling source
|
||||
1. Extract business logic from current worker-like components into routines/services.
|
||||
2. Implement thin workers that call those routines.
|
||||
3. Register workers from `MailOrderBotModule`.
|
||||
4. Use runtime health and status only through worker state.
|
||||
5. Keep any local queue only as an implementation detail if it still helps.
|
||||
|
||||
With:
|
||||
- IMAP IDLE source
|
||||
## Optional queue usage
|
||||
|
||||
The queue, workers, and handler should remain unchanged.
|
||||
If email processing still benefits from buffering, `InMemoryTaskQueue` may be used inside the application.
|
||||
|
||||
This is an explicit architectural goal:
|
||||
source replacement without redesigning the processing pipeline.
|
||||
|
||||
## Domain responsibilities that remain inside mail_order_bot
|
||||
|
||||
The runtime should not own:
|
||||
- email parsing rules
|
||||
- client resolution logic
|
||||
- attachment processing rules
|
||||
- order creation logic
|
||||
- client-specific behavior
|
||||
|
||||
These remain in the business application.
|
||||
|
||||
## Platform responsibilities used by mail_order_bot
|
||||
|
||||
The new runtime should provide:
|
||||
- lifecycle
|
||||
- configuration
|
||||
- queue abstraction
|
||||
- worker orchestration
|
||||
- tracing
|
||||
- health checks
|
||||
- status/control APIs
|
||||
|
||||
## Migration boundaries
|
||||
|
||||
### Move into runtime
|
||||
- source orchestration
|
||||
- worker supervision
|
||||
- queue management
|
||||
- trace provisioning
|
||||
- health aggregation
|
||||
|
||||
### Keep in mail_order_bot
|
||||
- email business handler
|
||||
- mail domain services
|
||||
- business pipeline
|
||||
- business-specific config validation beyond platform-level schema
|
||||
|
||||
## Success criteria
|
||||
|
||||
The migration is successful when:
|
||||
- mail polling is no longer tightly coupled to processing
|
||||
- workers can process emails in parallel
|
||||
- business logic is not moved into the runtime
|
||||
- replacing polling with IDLE is localized to the source layer
|
||||
Important:
|
||||
- it should remain an app-level detail
|
||||
- it should not define the platform contract
|
||||
- the main architecture should still be described through workers and routines
|
||||
|
||||
@@ -1,469 +1,134 @@
|
||||
# PLBA
|
||||
# PLBA Requirements
|
||||
|
||||
`PLBA` is a reusable platform runtime for business applications.
|
||||
## Goal
|
||||
|
||||
It solves platform concerns that should not live inside domain code:
|
||||
- application lifecycle
|
||||
`PLBA` is a reusable runtime for business applications.
|
||||
|
||||
The platform owns:
|
||||
- lifecycle
|
||||
- worker orchestration
|
||||
- configuration loading from YAML
|
||||
- tracing
|
||||
- health aggregation
|
||||
- runtime status reporting
|
||||
- HTTP control endpoints
|
||||
- configuration loading
|
||||
- logging configuration
|
||||
- health aggregation
|
||||
- tracing
|
||||
- control endpoints
|
||||
|
||||
Business applications depend on `plba` as a package and implement only their own business behavior.
|
||||
Business applications own:
|
||||
- business workflows
|
||||
- domain services
|
||||
- app-specific configuration schema
|
||||
- business error semantics
|
||||
|
||||
## Architecture
|
||||
## Core runtime model
|
||||
|
||||
Current PLBA architecture is built around one core idea:
|
||||
- the runtime manages a set of application workers
|
||||
The platform is built around workers.
|
||||
|
||||
A worker is any runtime-managed active component with a unified lifecycle:
|
||||
1. application defines an `ApplicationModule`
|
||||
2. module registers workers
|
||||
3. runtime starts workers
|
||||
4. workers execute business activity
|
||||
5. runtime aggregates status and health
|
||||
6. runtime stops workers gracefully
|
||||
|
||||
## Contracts
|
||||
|
||||
### `ApplicationModule`
|
||||
|
||||
Responsibilities:
|
||||
- provide module name
|
||||
- assemble application components
|
||||
- register workers
|
||||
- register optional health contributors
|
||||
|
||||
### `Worker`
|
||||
|
||||
Main runtime contract.
|
||||
|
||||
Responsibilities:
|
||||
- `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.
|
||||
The worker owns execution mechanics:
|
||||
- single-run or loop
|
||||
- thread model
|
||||
- stop conditions
|
||||
- runtime status
|
||||
- health interpretation
|
||||
|
||||
### Main runtime model
|
||||
### `Routine`
|
||||
|
||||
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.
|
||||
`Routine` is an application pattern, not a PLBA contract.
|
||||
|
||||
Responsibilities:
|
||||
- provide module name
|
||||
- register workers
|
||||
- register queues if needed
|
||||
- register handlers if needed
|
||||
- register health contributors
|
||||
- compose application-specific objects
|
||||
- execute business behavior
|
||||
- call domain services
|
||||
- apply business rules
|
||||
- talk to external integrations
|
||||
|
||||
`ApplicationModule` does not run the application itself.
|
||||
It only declares how the application is assembled.
|
||||
Recommended rule:
|
||||
- worker orchestrates
|
||||
- routine executes business behavior
|
||||
|
||||
### `Worker`
|
||||
## Architectural rules
|
||||
|
||||
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.
|
||||
### 1. Keep lifecycle and business behavior separate
|
||||
|
||||
Good:
|
||||
- worker gets config
|
||||
- worker calls domain service
|
||||
- worker reports trace and status
|
||||
- worker manages threading, loop, stop flags, health, status
|
||||
- routine contains business logic
|
||||
|
||||
Bad:
|
||||
- worker contains all parsing, decision logic, integration rules, and persistence rules in one class
|
||||
- worker mixes thread management, retry policy, parsing, persistence, integration rules, and domain decisions in one class
|
||||
|
||||
### 5. Use trace as a platform service
|
||||
### 2. Treat `Worker` as the main extension point
|
||||
|
||||
Business application should:
|
||||
- create meaningful trace steps
|
||||
- propagate trace through task metadata if queue is used
|
||||
- record business-relevant processing milestones
|
||||
Do not center the platform around queues, handlers, sources, or other specialized runtime categories.
|
||||
|
||||
Business application should not:
|
||||
- implement its own trace store
|
||||
- control trace transport directly unless explicitly needed
|
||||
### 3. Keep `Routine` out of the platform contract set
|
||||
|
||||
### 6. Read config through PLBA
|
||||
At the current stage PLBA should not force a universal `Routine` interface.
|
||||
|
||||
Business application should not read YAML directly.
|
||||
Applications may use:
|
||||
- `run()`
|
||||
- `run_once()`
|
||||
- `poll()`
|
||||
- `sync_window()`
|
||||
|
||||
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
|
||||
The exact business API belongs to the application.
|
||||
|
||||
### 7. Distinguish health from status
|
||||
### 4. Health is computed by the worker
|
||||
|
||||
Use `health` for:
|
||||
- is application operational?
|
||||
Routine should not directly mutate platform health state.
|
||||
|
||||
Use `status` for:
|
||||
- what is application doing right now?
|
||||
Instead:
|
||||
- routine succeeds
|
||||
- routine returns outcome
|
||||
- or routine raises typed exceptions
|
||||
|
||||
This is important for graceful stop:
|
||||
- health may still be `ok`
|
||||
- status may be `busy`
|
||||
Worker interprets the outcome into:
|
||||
- `ok`
|
||||
- `degraded`
|
||||
- `unhealthy`
|
||||
|
||||
### 8. Design workers for graceful stop
|
||||
### 5. `InMemoryTaskQueue` is utility-only
|
||||
|
||||
Workers should support:
|
||||
- stop accepting new work
|
||||
- finish current in-flight work when possible
|
||||
- report `busy`, `idle`, `stopping`, `stopped`
|
||||
`InMemoryTaskQueue` may stay as a reusable component for business applications.
|
||||
|
||||
This allows runtime to stop application safely.
|
||||
It is:
|
||||
- optional
|
||||
- local
|
||||
- not part of the main runtime contract model
|
||||
|
||||
## Recommended repository model
|
||||
## Public package direction
|
||||
|
||||
PLBA is intended to live in its own repository as a reusable package.
|
||||
Public namespace `plba` should expose:
|
||||
- application/runtime contracts
|
||||
- tracing
|
||||
- health
|
||||
- config
|
||||
- control plane
|
||||
- utility queue
|
||||
|
||||
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.
|
||||
It should not expose queue-centric runtime abstractions as primary architecture.
|
||||
|
||||
148
requirements/application_guidelines.md
Normal file
148
requirements/application_guidelines.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Application Guidelines
|
||||
|
||||
## Purpose
|
||||
|
||||
This document defines the default rules for building business applications on top of `plba`.
|
||||
|
||||
The goal is to keep applications:
|
||||
- explicit
|
||||
- small
|
||||
- easy to debug
|
||||
- free from platform legacy artifacts
|
||||
|
||||
## Main model
|
||||
|
||||
Build every application around this chain:
|
||||
|
||||
`ApplicationModule` -> `Worker` -> business `Routine`
|
||||
|
||||
Meaning:
|
||||
- `ApplicationModule` assembles the application
|
||||
- `Worker` owns runtime execution and lifecycle
|
||||
- `Routine` owns business behavior
|
||||
|
||||
`Routine` is an application pattern, not a mandatory platform contract.
|
||||
|
||||
## Rules
|
||||
|
||||
### 1. Assemble the app in `ApplicationModule`
|
||||
|
||||
`ApplicationModule` should:
|
||||
- create application services
|
||||
- create routines
|
||||
- create workers
|
||||
- register workers
|
||||
- register optional health contributors
|
||||
|
||||
`ApplicationModule` should not:
|
||||
- execute business logic itself
|
||||
- contain runtime loops
|
||||
|
||||
### 2. Treat `Worker` as the only primary runtime abstraction
|
||||
|
||||
`Worker` is the core runtime contract of the platform.
|
||||
|
||||
Worker should own:
|
||||
- `start()`
|
||||
- `stop(force=False)`
|
||||
- `health()`
|
||||
- `status()`
|
||||
- thread ownership
|
||||
- execution strategy
|
||||
- graceful shutdown
|
||||
|
||||
Worker should not own:
|
||||
- large business flows
|
||||
- domain decisions
|
||||
- parsing, persistence, and integration rules all mixed together
|
||||
|
||||
### 3. Keep one worker focused on one business activity
|
||||
|
||||
Default recommendation:
|
||||
- one worker -> one routine
|
||||
|
||||
If a process has multiple distinct behaviors:
|
||||
- split it into multiple workers
|
||||
- or compose several services behind one focused routine
|
||||
|
||||
Do not make a worker a container for unrelated business scenarios.
|
||||
|
||||
### 4. Put business logic into routines and services
|
||||
|
||||
Routine should contain:
|
||||
- business flow steps
|
||||
- domain service calls
|
||||
- business validation
|
||||
- integration calls
|
||||
- persistence orchestration
|
||||
|
||||
If the routine becomes too large:
|
||||
- split business logic into dedicated services
|
||||
- keep routine as a thin application-level orchestrator
|
||||
|
||||
### 5. Let the worker define the run model
|
||||
|
||||
The worker decides:
|
||||
- single-run or loop
|
||||
- one thread or multiple threads
|
||||
- interval between iterations
|
||||
- batch or long-running mode
|
||||
- stop conditions
|
||||
|
||||
The routine does not decide lifecycle strategy.
|
||||
|
||||
### 6. Let the worker compute health
|
||||
|
||||
Routine should not directly set platform health state.
|
||||
|
||||
Instead:
|
||||
- routine completes successfully
|
||||
- or returns outcome information
|
||||
- or raises typed exceptions
|
||||
|
||||
Then worker interprets that into:
|
||||
- `ok`
|
||||
- `degraded`
|
||||
- `unhealthy`
|
||||
|
||||
### 7. Use queues only as optional app-level utilities
|
||||
|
||||
`InMemoryTaskQueue` may be used inside an application when buffering helps.
|
||||
|
||||
But:
|
||||
- queue is not a core platform concept
|
||||
- queue usage should stay a local implementation choice
|
||||
- the app should still be described through workers and routines
|
||||
|
||||
### 8. Keep tracing vocabulary neutral
|
||||
|
||||
Use tracing to describe operations and execution context, not legacy architectural roles.
|
||||
|
||||
Prefer terms like:
|
||||
- operation
|
||||
- worker
|
||||
- routine
|
||||
- metadata
|
||||
- step
|
||||
|
||||
Avoid making trace terminology define the application architecture.
|
||||
|
||||
### 9. Keep classes small and responsibilities clear
|
||||
|
||||
Preferred shape:
|
||||
- thin `ApplicationModule`
|
||||
- thin `Worker`
|
||||
- focused `Routine`
|
||||
- dedicated domain services
|
||||
|
||||
If a class grows too much, split it by responsibility instead of adding more platform abstractions.
|
||||
|
||||
## Checklist
|
||||
|
||||
Before adding a new application component, check:
|
||||
|
||||
1. Is this runtime behavior or business behavior?
|
||||
2. If runtime behavior, should it live in a `Worker`?
|
||||
3. If business behavior, should it live in a `Routine` or service?
|
||||
4. Does this component stay small and single-purpose?
|
||||
5. Am I adding a queue because it is useful, or because of old mental models?
|
||||
@@ -2,247 +2,130 @@
|
||||
|
||||
## Overview
|
||||
|
||||
The runtime is built as a platform layer for business applications.
|
||||
|
||||
It consists of four logical layers:
|
||||
PLBA consists of four logical layers:
|
||||
- platform core
|
||||
- platform contracts
|
||||
- infrastructure adapters
|
||||
- infrastructure services
|
||||
- business applications
|
||||
|
||||
The runtime is centered on `Worker`.
|
||||
|
||||
## Layers
|
||||
|
||||
### Platform core
|
||||
|
||||
The core contains long-lived runtime services:
|
||||
Core services:
|
||||
- `RuntimeManager`
|
||||
- `ConfigurationManager`
|
||||
- `WorkerSupervisor`
|
||||
- `TraceService`
|
||||
- `HealthRegistry`
|
||||
- `ControlPlaneService`
|
||||
- `ServiceContainer`
|
||||
|
||||
The core is responsible for orchestration, not domain behavior.
|
||||
|
||||
### Platform contracts
|
||||
|
||||
Contracts define how business applications integrate with the runtime.
|
||||
|
||||
Main contracts:
|
||||
- `ApplicationModule`
|
||||
- `TaskSource`
|
||||
- `TaskQueue`
|
||||
- `Worker`
|
||||
- `TaskHandler`
|
||||
- `ConfigProvider`
|
||||
- `HealthContributor`
|
||||
- `TraceFactory`
|
||||
|
||||
These contracts must remain domain-agnostic.
|
||||
|
||||
### Infrastructure adapters
|
||||
|
||||
Adapters implement concrete runtime capabilities:
|
||||
- in-memory queue
|
||||
- Redis queue
|
||||
- file config loader
|
||||
- database config loader
|
||||
- polling source
|
||||
- IMAP IDLE source
|
||||
- HTTP control plane
|
||||
- trace transport adapters
|
||||
|
||||
Adapters may change between applications and deployments.
|
||||
|
||||
### Business applications
|
||||
|
||||
Applications are built on top of the contracts and adapters.
|
||||
|
||||
Examples:
|
||||
- `mail_order_bot`
|
||||
- future event-driven business services
|
||||
|
||||
Applications contain:
|
||||
- domain models
|
||||
- domain handlers
|
||||
- application-specific configuration schema
|
||||
- source/handler composition
|
||||
|
||||
## Core runtime components
|
||||
|
||||
### RuntimeManager
|
||||
|
||||
The main platform facade.
|
||||
|
||||
Responsibilities:
|
||||
- bootstrap runtime
|
||||
- initialize services
|
||||
- register application modules
|
||||
- start and stop all runtime-managed components
|
||||
- expose status
|
||||
- coordinate graceful shutdown
|
||||
- wire core services
|
||||
- register modules
|
||||
- start and stop workers
|
||||
- expose runtime snapshot
|
||||
|
||||
### ConfigurationManager
|
||||
### Platform contracts
|
||||
|
||||
Responsibilities:
|
||||
- load configuration
|
||||
- validate configuration
|
||||
- publish config updates
|
||||
- provide typed config access
|
||||
- notify subscribers on reload
|
||||
Main contracts:
|
||||
- `ApplicationModule`
|
||||
- `Worker`
|
||||
- `ConfigProvider`
|
||||
- `HealthContributor`
|
||||
- tracing contracts
|
||||
|
||||
Configuration should be divided into:
|
||||
- platform config
|
||||
- application config
|
||||
- environment/runtime overrides
|
||||
These contracts must remain domain-agnostic.
|
||||
|
||||
### WorkerSupervisor
|
||||
### Infrastructure services
|
||||
|
||||
Responsibilities:
|
||||
- register worker definitions
|
||||
- start worker pools
|
||||
- monitor worker health
|
||||
- restart failed workers when appropriate
|
||||
- manage parallelism and backpressure
|
||||
- expose worker-level status
|
||||
Platform services include:
|
||||
- tracing
|
||||
- health registry
|
||||
- logging manager
|
||||
- control plane
|
||||
- config providers
|
||||
- `InMemoryTaskQueue` as optional utility
|
||||
|
||||
### TraceService
|
||||
### Business applications
|
||||
|
||||
Responsibilities:
|
||||
- create traces for operations
|
||||
- propagate trace context across source -> queue -> worker -> handler boundaries
|
||||
- provide trace factories to applications
|
||||
- remain transport-agnostic
|
||||
Applications define:
|
||||
- routines
|
||||
- domain services
|
||||
- custom worker implementations
|
||||
- typed app config
|
||||
|
||||
### HealthRegistry
|
||||
|
||||
Responsibilities:
|
||||
- collect health from registered contributors
|
||||
- aggregate health into liveness/readiness/status views
|
||||
- expose structured runtime health
|
||||
|
||||
### ControlPlaneService
|
||||
|
||||
Responsibilities:
|
||||
- control endpoints
|
||||
- runtime state visibility
|
||||
- administrative actions
|
||||
- later authentication and user/session-aware access
|
||||
|
||||
## Main runtime model
|
||||
|
||||
The runtime should operate on this conceptual flow:
|
||||
## Runtime flow
|
||||
|
||||
1. runtime starts
|
||||
2. configuration is loaded
|
||||
3. services are initialized
|
||||
4. application modules register sources, queues, handlers, and workers
|
||||
5. task sources start producing tasks
|
||||
6. tasks are published into queues
|
||||
7. workers consume tasks
|
||||
8. handlers execute business logic
|
||||
9. traces and health are updated throughout the flow
|
||||
10. runtime stops gracefully on request
|
||||
3. core services become available
|
||||
4. application modules register workers
|
||||
5. workers start execution
|
||||
6. workers call business routines
|
||||
7. runtime aggregates health and status
|
||||
8. runtime stops workers on request
|
||||
|
||||
## Contracts
|
||||
## Worker model
|
||||
|
||||
### ApplicationModule
|
||||
Worker is responsible for runtime behavior:
|
||||
- execution strategy
|
||||
- thread ownership
|
||||
- graceful shutdown
|
||||
- runtime status
|
||||
- health interpretation
|
||||
|
||||
Describes a business application to the runtime.
|
||||
Routine is responsible for business behavior:
|
||||
- business decisions
|
||||
- domain orchestration
|
||||
- persistence and integrations
|
||||
|
||||
Responsibilities:
|
||||
- register domain services
|
||||
- register task sources
|
||||
- register queues
|
||||
- register worker pools
|
||||
- register handlers
|
||||
- declare config requirements
|
||||
- optionally register health contributors
|
||||
Recommended shape:
|
||||
|
||||
### TaskSource
|
||||
```python
|
||||
class SomeWorker(Worker):
|
||||
def __init__(self, routine) -> None:
|
||||
self._routine = routine
|
||||
|
||||
Produces tasks into queues.
|
||||
def start(self) -> None:
|
||||
...
|
||||
|
||||
Examples:
|
||||
- IMAP polling source
|
||||
- IMAP IDLE source
|
||||
- webhook source
|
||||
- scheduled source
|
||||
def stop(self, force: bool = False) -> None:
|
||||
...
|
||||
|
||||
Responsibilities:
|
||||
- start
|
||||
- stop
|
||||
- publish tasks
|
||||
- expose source status
|
||||
def health(self) -> WorkerHealth:
|
||||
...
|
||||
|
||||
### TaskQueue
|
||||
def status(self) -> WorkerStatus:
|
||||
...
|
||||
```
|
||||
|
||||
A queue abstraction.
|
||||
## Design rules
|
||||
|
||||
Expected operations:
|
||||
- `publish(task)`
|
||||
- `consume()`
|
||||
- `ack(task)`
|
||||
- `nack(task, retry_delay=None)`
|
||||
- `stats()`
|
||||
### 1. Runtime should not know business semantics
|
||||
|
||||
The first implementation may be in-memory, but the interface should support future backends.
|
||||
PLBA knows:
|
||||
- worker started
|
||||
- worker stopped
|
||||
- routine succeeded
|
||||
- routine failed
|
||||
|
||||
### Worker
|
||||
PLBA does not know:
|
||||
- what the business operation means
|
||||
- which domain decision was made
|
||||
|
||||
Consumes tasks from a queue and passes them to a handler.
|
||||
### 2. Queue is not a core architecture primitive
|
||||
|
||||
Responsibilities:
|
||||
- obtain task from queue
|
||||
- open or resume trace context
|
||||
- call business handler
|
||||
- ack or nack the task
|
||||
- expose worker state
|
||||
Queues may exist inside applications as implementation details.
|
||||
|
||||
### TaskHandler
|
||||
They must not define the platform mental model.
|
||||
|
||||
Executes business logic for one task.
|
||||
### 3. Keep components small
|
||||
|
||||
The runtime should not know what the handler does.
|
||||
It only knows that a task is processed.
|
||||
Prefer:
|
||||
- thin workers
|
||||
- focused routines
|
||||
- dedicated domain services
|
||||
|
||||
## Mail Order Bot as first application
|
||||
|
||||
### Phase 1
|
||||
|
||||
- source: IMAP polling
|
||||
- queue: in-memory queue
|
||||
- workers: parallel email processing workers
|
||||
- handler: domain email processing handler
|
||||
- mark message as read only after successful processing
|
||||
|
||||
### Phase 2
|
||||
|
||||
- source changes from polling to IMAP IDLE
|
||||
- queue and workers remain the same
|
||||
|
||||
This demonstrates one of the architectural goals:
|
||||
the source can change without redesigning the rest of the processing pipeline.
|
||||
|
||||
## Suggested package structure
|
||||
|
||||
```text
|
||||
src/
|
||||
app_runtime/
|
||||
core/
|
||||
contracts/
|
||||
config/
|
||||
workers/
|
||||
queue/
|
||||
tracing/
|
||||
health/
|
||||
control/
|
||||
container/
|
||||
adapters/
|
||||
mail_order_bot_app/
|
||||
module/
|
||||
sources/
|
||||
handlers/
|
||||
services/
|
||||
domain/
|
||||
Avoid large platform abstractions that exist only for hypothetical reuse.
|
||||
|
||||
@@ -1,119 +1,38 @@
|
||||
# Vision
|
||||
|
||||
## Purpose
|
||||
## Product vision
|
||||
|
||||
This project provides a reusable runtime platform for business applications.
|
||||
PLBA should be a transparent runtime for business applications.
|
||||
|
||||
The runtime exists to solve service and infrastructure concerns that are needed by many applications but do not belong to business logic:
|
||||
- start and stop
|
||||
- status and lifecycle
|
||||
- configuration
|
||||
- worker execution
|
||||
- trace propagation
|
||||
- health checks
|
||||
- control and administration
|
||||
- later authentication and user management
|
||||
The desired feeling of the platform:
|
||||
- simple to read
|
||||
- explicit in behavior
|
||||
- small number of core concepts
|
||||
- easy to debug
|
||||
- no architectural legacy artifacts
|
||||
|
||||
The runtime should allow a business application to focus only on domain behavior.
|
||||
## Core concepts
|
||||
|
||||
## Vision statement
|
||||
The platform should be understandable through three ideas:
|
||||
- `ApplicationModule` assembles the app
|
||||
- `Worker` owns lifecycle and execution
|
||||
- business behavior lives in application routines and services
|
||||
|
||||
We build a platform where business applications are assembled from small domain modules, while the runtime consistently provides lifecycle, workers, tracing, configuration, and control-plane capabilities.
|
||||
## Non-goals
|
||||
|
||||
## Problem
|
||||
PLBA should not become:
|
||||
- a framework of many specialized runtime roles
|
||||
- a queue-centric architecture by default
|
||||
- a compatibility shell for legacy abstractions
|
||||
- a place where business logic hides inside infrastructure classes
|
||||
|
||||
In the previous generation, the execution model was centered around a single `execute()` entry point called by a timer loop.
|
||||
## Utility components
|
||||
|
||||
That model works for simple periodic jobs, but it becomes too narrow when we need:
|
||||
- queue-driven processing
|
||||
- multiple concurrent workers
|
||||
- independent task sources
|
||||
- richer health semantics
|
||||
- trace propagation across producer and consumer boundaries
|
||||
- reusable runtime patterns across different applications
|
||||
- future admin and authentication capabilities
|
||||
Some utility components may still exist, for example `InMemoryTaskQueue`.
|
||||
|
||||
The old model couples orchestration and execution too tightly.
|
||||
They are acceptable when they stay:
|
||||
- optional
|
||||
- local
|
||||
- implementation-oriented
|
||||
|
||||
## Desired future state
|
||||
|
||||
The new runtime should provide:
|
||||
- a reusable lifecycle and orchestration core
|
||||
- a clean contract for business applications
|
||||
- support for sources, queues, workers, and handlers
|
||||
- explicit separation between platform and domain
|
||||
- trace and health as first-class platform services
|
||||
- the ability to evolve into an admin platform
|
||||
|
||||
## Design principles
|
||||
|
||||
### 1. Platform vs domain separation
|
||||
|
||||
The runtime owns platform concerns.
|
||||
Applications own domain concerns.
|
||||
|
||||
The runtime must not contain business rules such as:
|
||||
- email parsing policies
|
||||
- order creation logic
|
||||
- invoice decisions
|
||||
- client-specific rules
|
||||
|
||||
### 2. Composition over inheritance
|
||||
|
||||
Applications should be composed by registering modules and services in the runtime, not by inheriting a timer-driven class and overriding one method.
|
||||
|
||||
### 3. Explicit task model
|
||||
|
||||
Applications should model processing as:
|
||||
- task source
|
||||
- task queue
|
||||
- worker
|
||||
- handler
|
||||
|
||||
This is more scalable than one monolithic execute loop.
|
||||
|
||||
### 4. Parallelism as a first-class concern
|
||||
|
||||
The runtime should supervise worker pools and concurrency safely instead of leaving this to ad hoc application code.
|
||||
|
||||
### 5. Observability by default
|
||||
|
||||
Trace, logs, metrics, and health should be available as platform services from the start.
|
||||
|
||||
### 6. Evolvability
|
||||
|
||||
The runtime should be able to support:
|
||||
- different queue backends
|
||||
- different task sources
|
||||
- different control planes
|
||||
- different admin capabilities
|
||||
without forcing business applications to change their domain code.
|
||||
|
||||
## First target use case
|
||||
|
||||
The first business application is `mail_order_bot`.
|
||||
|
||||
Its business domain is:
|
||||
- fetching incoming mail
|
||||
- processing attachments
|
||||
- executing order-related pipelines
|
||||
|
||||
Its platform/runtime needs are:
|
||||
- lifecycle management
|
||||
- polling or IMAP IDLE source
|
||||
- queueing
|
||||
- worker pools
|
||||
- tracing
|
||||
- health checks
|
||||
- future admin API
|
||||
|
||||
This makes it a good pilot for the new runtime.
|
||||
|
||||
## Success criteria
|
||||
|
||||
We consider the runtime direction successful if:
|
||||
- `mail_order_bot` business logic can run on top of it without leaking infrastructure details into domain code
|
||||
- the runtime can manage concurrent workers
|
||||
- the runtime can support a queue-based flow
|
||||
- the runtime can expose status and health
|
||||
- the runtime can later host admin/auth features without redesigning the core
|
||||
They should not redefine the main platform model.
|
||||
|
||||
Reference in New Issue
Block a user