Architecture Guide¶
Guide Maps¶
graph TD
app["MonitoringApplication"] --> runtime["MonitoringRuntime"]
runtime --> aggregate["MonitoringPolicy aggregate"]
aggregate --> policies["Evaluation policies"]
aggregate --> events["Domain events"]
events --> projections["ActiveRuleIndex and IncidentLedger"]
runtime --> sink["Incident sink"]
runtime --> repo["Repository and unit of work"]
flowchart LR
command["Learner command"] --> app["Application facade"]
app --> domain["Domain model"]
domain --> event["Events emitted"]
event --> views["Read models updated"]
views --> review["Snapshot, tests, and review surfaces"]
This capstone is intentionally small, but its shape is strict. The domain is not buried inside infrastructure, and the runtime does not quietly absorb domain rules. The goal is to show an object-oriented design where ownership stays legible under change.
Ownership boundaries¶
application.pyis the learner-facing use-case surface.model.pyis the authoritative home of rule lifecycle and evaluation ownership.policies.pyisolates replaceable evaluation behavior.runtime.pycoordinates sources, sinks, projections, and units of work.repository.pyexpresses persistence intent and rollback semantics.read_models.pyandprojections.pyremain downstream of events.
Why this shape matters¶
The aggregate should stay authoritative for domain change. The runtime should stay thin enough that replacing a source or sink does not change the rules of the domain. The projections should stay derived so read concerns do not mutate authoritative state.
Dependency direction¶
| Surface | May depend on | Should not depend on |
|---|---|---|
application.py |
use-case inputs, runtime coordination, aggregate-facing operations | projection internals as a source of truth |
model.py |
domain values, events, policy abstractions | runtime adapters, sinks, or learner-facing command flow |
policies.py |
rule-evaluation inputs and domain values | repository or projection mechanics |
runtime.py |
aggregate operations, repositories, projections, sinks, and sources | projection state as an authority for domain decisions |
read_models.py and projections.py |
emitted events and display needs | aggregate mutation paths |
repository.py |
storage semantics and rollback coordination | hidden lifecycle or evaluation rules |
Review routes for architecture questions¶
- Use
make inspectwhen you want the derived state bundle before opening code. - Use
make tourwhen you want the learner-facing scenario route through the architecture. - Use
make verify-reportwhen you need executable evidence alongside that review. - Use
EXTENSION_GUIDE.mdwhen the architecture question is really a change-placement question. - Use
EVENT_FLOW_GUIDE.mdwhen the main question is how aggregate decisions become read-model state. - Use
CHANGE_RECIPES.mdwhen the architecture question has already become an edit plan. - Use
RUNTIME_GUIDE.mdwhen the main confusion is which orchestration belongs in the runtime at all.
Architecture questions for review¶
- What would break if rule activation lived in the runtime instead of the aggregate?
- What would become harder to trust if the read models were updated directly?
- Which extension should modify
policies.pywithout forcing a rewrite ofmodel.py?
Change placement¶
| If the change is... | Start in | Why |
|---|---|---|
| a new rule lifecycle constraint | model.py |
lifecycle authority belongs to the aggregate |
| a new evaluation mode | policies.py |
variation should stay replaceable instead of widening the aggregate |
| a new sink or source integration | runtime.py |
orchestration and adapters stay outside domain ownership |
| a new read model | projections.py or read_models.py |
derived views should stay downstream of events |
| a persistence or rollback detail | repository.py |
storage mechanics should adapt to the domain, not redefine it |
Anti-patterns this architecture rejects¶
- runtime code deciding domain lifecycle transitions
- projections mutating authoritative state
- persistence concerns leaking into the aggregate's core rules
- evaluation variability implemented as condition ladders spread across multiple files
Boundary drift review¶
Ask these during review before changing the code:
- does this proposal make the runtime smarter than the aggregate about rule truth
- does this proposal force a read model to become authoritative
- does this proposal hide a domain rule inside persistence or adapter code
- does this proposal widen several files because ownership was not settled first