first_commit
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# Architecture Principles
|
||||
|
||||
## Standalone Subsystems
|
||||
|
||||
Each class should behave like a small standalone subsystem. It should own one clear responsibility, expose a narrow public interface, and avoid hidden dependencies on the internals of other classes.
|
||||
|
||||
Good subsystem boundaries:
|
||||
- accept explicit inputs
|
||||
- return explicit outputs
|
||||
- keep internal state private
|
||||
- avoid reaching into global state
|
||||
- avoid performing unrelated work
|
||||
- can be tested with recorded or fixture data
|
||||
|
||||
Examples:
|
||||
- a weather client fetches forecast data
|
||||
- a weather parser converts API payloads into forecast points
|
||||
- a weather builder normalizes external forecast records for storage
|
||||
- a storage class persists records
|
||||
- Gibil makes decisions from snapshots
|
||||
|
||||
## Data Models Between Subsystems
|
||||
|
||||
Subsystems should communicate through shared data models rather than through source-specific payloads.
|
||||
|
||||
For example:
|
||||
- Open-Meteo JSON should become `WeatherForecastRun`
|
||||
- Modbus register reads should become `Observation`
|
||||
- HASS entity state should become `Observation`
|
||||
- Gibil should reason from `Snapshot`
|
||||
|
||||
This keeps the edges messy and the core calm.
|
||||
|
||||
## Side Effects At The Edges
|
||||
|
||||
Network calls, database writes, MQTT publishes, and filesystem writes should live at clear boundaries.
|
||||
|
||||
Core reasoning classes should generally be pure or nearly pure:
|
||||
- input data in
|
||||
- answer out
|
||||
- no surprise I/O
|
||||
|
||||
Stateful classes are allowed, but their state should be deliberate and inspectable.
|
||||
|
||||
## Grow By Composition
|
||||
|
||||
Astrape should grow by connecting small subsystems together, not by building one large object that knows everything.
|
||||
|
||||
The desired shape is:
|
||||
|
||||
```text
|
||||
source client -> parser -> model -> storage -> query/snapshot -> Gibil -> publisher
|
||||
```
|
||||
|
||||
Each part should be replaceable without rewriting the others.
|
||||
|
||||
## Prefer Working Slices
|
||||
|
||||
Build one thin working path at a time. A thin slice may start with empty storage or recorded source data, but it should still follow the real subsystem boundaries.
|
||||
|
||||
For example, the weather slice can start with:
|
||||
|
||||
```text
|
||||
Open-Meteo forecast run -> WeatherBuilder -> clean forecast records
|
||||
```
|
||||
|
||||
Then grow into:
|
||||
|
||||
```text
|
||||
Open-Meteo -> parser -> WeatherBuilder -> TimescaleDB -> weather_predictor.py
|
||||
```
|
||||
Reference in New Issue
Block a user