2.0 KiB
ADR-002: Event Bus & Cross-Layer Communication Pattern
Status: Accepted Date: 2026-04-27 Engine: Unity 2022.3.62f3c1 GDD Requirements Addressed: All L0→L1 communication from systems-index.md
Context
Architecture requires L0 to never call L1 or L2. All upward communication must go through an event mechanism. Need to choose: C# built-in event vs. a dedicated EventBus class. This decision affects every L0 module's interface design and memory safety.
Decision
Use C# built-in event + Action<T>. No dedicated EventBus class.
Rationale:
- L0 has a fixed set of 10 modules — direct
eventdeclarations are manageable without a central registry - C#
eventprovides compile-time type safety; parameter mismatches are caught at build time, not runtime - Zero external dependencies — no third-party messaging library
- Each L0 module's events are part of its public API contract, visible and self-documenting for external developers
Subscription lifecycle:
L1/L2 subscribe to L0 events in OnEnable()
L1/L2 unsubscribe in OnDisable()
(NOT OnDestroy — avoids lost subscriptions on disable/re-enable cycles)
Naming convention: On[What] — e.g., OnFormChanged, OnEnergyChanged, OnWaveStart
Parameter convention: Use lightweight structs or primitive types. Never pass mutable L0 internal object references to subscribers (prevents L1 from mutating L0 state).
Hot path exception: Attack resolution and damage calculation use per-frame method calls (CombatLogic.ResolveAttacks()), not events. Events are reserved for low-frequency state transition notifications.
Consequences
- Each L0 module's event surface is explicitly visible, making inter-system communication self-documenting
- No centralized message registry reduces coupling
- L1 developers MUST follow OnEnable/OnDisable subscription discipline — leaks or null refs otherwise
- If system count grows beyond ~20, a refactor to a dedicated EventBus may be warranted