Architecture
Goals
PSChristmasTree keeps a simple product scope (animated terminal tree) while applying production-grade engineering practices:
- deterministic configuration normalization
- clear separation of concerns between orchestration, domain logic and terminal I/O
- testable logic in pure or near-pure helpers
- predictable release and CI quality gates
Runtime Layers
1. Public command layer
Public commands in the module surface a stable API:
Show-PSChristmasTreeSet-PSChristmasTreeConfigGet-PSChristmasTreeConfigReset-PSChristmasTreeConfig
These functions are orchestration-only entry points and should avoid embedding deep business logic.
2. Configuration domain layer
Configuration is represented as a structured v3 schema:
LanguageTreeDecorationsColorsDisplayAudioMessages
Core responsibilities:
ConvertTo-PSChristmasTreeConfig: schema defaults + validation + migration from legacy flat shapeResolve-PSChristmasTreeEffectiveConfig: precedence merge (runtime > saved > defaults)Read-PSChristmasTreeConfig/Write-PSChristmasTreeConfig: persistence boundary
This layer defines invariants for downstream rendering.
3. Render preparation layer
Render data preparation is intentionally separated from terminal side effects:
Get-PSChristmasTreeDecoratedTreeGet-PSChristmasTreeMessageRenderModel
These helpers transform domain objects into display-ready payloads and are unit-tested directly.
4. Terminal I/O layer
Invoke-PSChristmasTreeRenderLoop is the side-effect boundary:
- cursor and foreground color lifecycle
- terminal clear/write calls
- resize-aware re-centering
- optional asynchronous carol playback with explicit Start/Wait/Stop session ownership
The render loop delegates preparation logic to the render helpers to keep I/O code focused and reviewable.
The render loop owns the carol session. It waits for playback after normal rendering and stops it on errors or interruption. The score is shared by Windows playback and the development-time WAV generator; macOS and Linux play the packaged WAV through native or available system players.
Key Invariants
- Invalid colors are filtered during normalization.
Decorations.Mode = Defaultalways clears customMap.Colorseffective list is always non-empty at runtime.- Runtime arguments always override saved configuration.
- Terminal state is restored in
finallyblocks.
Testing Strategy
The test suite combines:
- configuration and migration tests
- command orchestration tests with mocks
- render-engine helper tests
- module packaging/export contract checks
CI runs Pester across the entire Tests folder and fails fast on any failed block.
Release and CI Posture
- deterministic dependency versions for build tooling
- explicit validation of tag/version consistency before release and publish
- static analysis as a required quality gate
- GitHub Pages documentation deployment via workflow artifacts
Why This Matters
This architecture keeps the project approachable while making engineering quality visible:
- each layer has a clear reason to change
- core behavior is testable without a real terminal
- regressions are easier to isolate
- contributors can extend features without rewriting orchestration