149 lines
3.5 KiB
Markdown
149 lines
3.5 KiB
Markdown
# 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?
|