# PLBA `PLBA` (`Platform Runtime for Business Applications`) - runtime для бизнес-приложений. Платформа берет на себя инфраструктурные обязанности: - lifecycle приложения - запуск и остановку воркеров - health/status - tracing - logging - control plane - загрузку конфигурации Бизнес-приложение на базе `plba` собирается вокруг трех уровней: - `ApplicationModule` собирает приложение и регистрирует воркеры - `Worker` управляет исполнением и lifecycle - `Routine` реализует бизнес-функцию `Routine` не является контрактом `plba`. Это рекомендуемый архитектурный паттерн для прикладного кода. Правила построения приложений на платформе собраны отдельно в [application_guidelines.md](/Users/alex/Dev_projects_v2/apps/plba/requirements/application_guidelines.md). ## Runtime model 1. приложение объявляет `ApplicationModule` 2. модуль регистрирует один или несколько `Worker` 3. `RuntimeManager` запускает все воркеры 4. каждый `Worker` запускает свою бизнес-активность 5. runtime агрегирует health и status 6. runtime останавливает воркеры graceful или forcefully ## Main contracts ### `ApplicationModule` Описывает, из чего состоит приложение. Ответственность: - дать имя модуля - зарегистрировать воркеры - зарегистрировать health contributors при необходимости - собрать прикладные зависимости ### `Worker` Главный runtime-контракт платформы. Контракт: - `name` - `critical` - `start()` - `stop(force=False)` - `health()` - `status()` `Worker` отвечает только за runtime-поведение: - как запускается бизнес-активность - в одном потоке или нескольких - single-run или loop - graceful shutdown - интерпретацию ошибок в `health/status` ### `Routine` Рекомендуемый application-level паттерн. `Routine` описывает бизнес-функцию: - что читать - какие сервисы вызывать - какие бизнес-решения принимать - что сохранять или отправлять наружу Обычно воркер получает одну routine через конструктор и вызывает ее в `start()` или во внутренних helper-методах. ## Minimal example ```python from threading import Event, Lock, Thread from time import sleep from plba import ( ApplicationModule, Worker, WorkerHealth, WorkerStatus, create_runtime, ) class OrdersRoutine: def __init__(self, service) -> None: self._service = service def run(self) -> None: self._service.process_new_orders() class OrdersWorker(Worker): def __init__(self, routine: OrdersRoutine, interval: float = 1.0) -> None: self._routine = routine self._interval = interval self._thread: Thread | None = None self._stop_requested = Event() self._lock = Lock() self._in_flight = 0 self._failures = 0 @property def name(self) -> str: return "orders-worker" @property def critical(self) -> bool: return True def start(self) -> None: if self._thread and self._thread.is_alive(): return self._stop_requested.clear() self._thread = Thread(target=self._run_loop, daemon=True) self._thread.start() def stop(self, force: bool = False) -> None: del force self._stop_requested.set() def health(self) -> WorkerHealth: if self._failures > 0: return WorkerHealth(self.name, "degraded", self.critical) return WorkerHealth(self.name, "ok", self.critical) def status(self) -> WorkerStatus: alive = self._thread is not None and self._thread.is_alive() state = "busy" if self._in_flight else "idle" if not alive: state = "stopped" elif self._stop_requested.is_set(): state = "stopping" return WorkerStatus(name=self.name, state=state, in_flight=self._in_flight) def _run_loop(self) -> None: while not self._stop_requested.is_set(): with self._lock: self._in_flight += 1 try: self._routine.run() except Exception: self._failures += 1 finally: with self._lock: self._in_flight -= 1 sleep(self._interval) class OrdersModule(ApplicationModule): @property def name(self) -> str: return "orders" def register(self, registry) -> None: service = OrderService() routine = OrdersRoutine(service) registry.add_worker(OrdersWorker(routine)) runtime = create_runtime(OrdersModule(), config_path="config.yml") runtime.start() ``` ## Health and status Практика такая: - `Routine` выполняет бизнес-работу - `Worker` ловит ее ошибки - `Worker` интерпретирует outcome в `health()` и `status()` То есть routine не выставляет health напрямую. ## In-memory queue `InMemoryTaskQueue` остается в платформе как простой in-memory utility. Это не базовый платформенный контракт и не обязательный паттерн архитектуры. Ее можно использовать в прикладном коде как локальный буфер между компонентами, если это действительно помогает. ```python from plba import InMemoryTaskQueue queue = InMemoryTaskQueue[str]() queue.put("payload") item = queue.get(timeout=0.1) ``` ## Public API Основные публичные сущности: - `ApplicationModule` - `Worker` - `WorkerHealth` - `WorkerStatus` - `RuntimeManager` - `WorkerSupervisor` - `TraceService` - `HealthRegistry` - `InMemoryTaskQueue` - `create_runtime(...)`