Technical Memo: Components, Phases, Systems, and Events in ecs-js
Purpose
Record the intended separation of responsibilities between components, phases, systems, and events.
This memo is architectural doctrine for ecs-js usage. It is not an implementation patch by itself.
The central decision:
Components carry truth.
Phases advance truth.
Systems process rules.
Events describe what happened.
Extensions attach observers/adapters.
Declared Purpose of Components
Components represent durable simulation state.
They are the facts the world owns.
Examples:
Position
Vitality
Inventory
Equipment
ActiveEffects
Threat
Score
Posture
SleepState
Projectile
Faction
ItemInfo
A component should be used when a fact must survive beyond the current reaction moment.
Use components for:
Persistent world facts
Entity state
Rule inputs
Rule outputs
State that must be queryable
State that must be serializable
State that participates in tick-based progression
Avoid using events as a substitute for component state.
If a later system must know something during a phase, prefer durable state, changed markers, phase ordering, or transient rule components over event-listener side effects.
Declared Purpose of Phases
Phases represent ordered passage through simulation time.
They are the coarse lifecycle of a tick.
Examples:
ai
intents
movement
combat
effects
cleanup
presentation
Use phases when ordering matters.
A phase answers:
What kind of simulation work is happening now?
What systems are allowed to mutate state now?
What durable state should already exist by this point?
What durable state will later phases consume?
Phases are the primary mechanism for deterministic system-to-system rule progression.
Declared Purpose of Systems
Systems are rule processors.
They read components, write components, create/destroy entities, and emit post-fact events when useful.
Systems should not call other systems directly.
Systems communicate through:
Component state
Changed markers
Phase ordering
Explicit queries
Occasional emitted events for observation/presentation
A system is the right place for simulation truth changes.
Examples:
MovementSystem mutates Position.
CombatSystem mutates Vitality.
EffectSystem mutates ActiveEffects.
ThreatSystem mutates Threat.
ScoreSystem mutates Score.
CleanupSystem destroys dead or expired entities.
Declared Purpose of Events
Events are runtime-only descriptions of things that happened.
They are not durable state.
They are not serialized.
They are not rule truth.
They are best used as presentation / observation exhaust from the simulation.
Events are ideal for:
Rendering
Display
Audio
VFX
UI notifications
Floating text
Debug tracing
Telemetry
External observers
Presentation bridges
Examples:
EntityMoved
DamageApplied
AttackDodged
AttackParried
ItemPickedUp
ActorDied
SpellCast
TileScorched
ProjectileFired
SoundEmitted
StatusFloated
Events answer:
What just happened?
Where did it happen?
Who caused it?
What might a display/audio/VFX/debug layer want to know?
Events should generally be emitted after the rule outcome has been determined.
Rule Boundary
Events can describe rule outcomes.
Events should not determine rule outcomes.
This means event listeners should not usually be where core simulation facts are produced.
Suspicious event usage:
Damage resolution
Turn order
AI state mutation
Inventory truth
Persistent status effects
Score truth
Map mutation
Combat modifiers
Rule vetoes
Good event usage:
Play hit sound
Show damage number
Spawn blood effect
Shake screen
Show dodge text
Log debug trace
Notify UI
Animate projectile path
No Mutable Rule Hooks
Avoid mutable event hooks such as:
world.emit("beforeHit", mutableContext);
world.emit("hit", mutableContext);
That pattern lets rules escape the phase model.
If a combat outcome needs staged modification, model that inside rules space:
AttackIntent
CombatResolution
DamageApplication
Aftermath
Then process those through named systems/phases.
Example staged model:
combat:start
Create CombatResolution from AttackIntent
combat:accuracy
Resolve miss / dodge / parry
combat:modifiers
Apply posture, weapon, affix, blindness, shrine, coating, status modifiers
combat:damage
Commit Vitality changes
combat:aftermath
Apply secondary rule outcomes
presentation
Emit DamageApplied, AttackDodged, ActorDied, etc.
The event is the receipt, not the mechanism.
System-to-System Communication Doctrine
Use this decision table:
Hard order dependency
Use phases / before / after
Persistent data dependency
Use components
Transient rule staging
Use transient components or explicit phase-local records
Post-fact observation
Use events
Display/audio/VFX/UI/debug bridge
Use events consumed by world extensions
Runtime setup
Use world extensions
Do not let event listeners become hidden systems.
A listener that mutates simulation truth is probably a system trying to escape scheduling.
Presentation Boundary
A clean frame looks like this:
Simulation phases
Mutate durable state.
Emit descriptive events.
Presentation bridge
Consumes events.
Produces rendering/audio/VFX/UI effects.
Does not mutate simulation truth.
Example:
movement phase
MovementSystem mutates Position.
MovementSystem emits EntityMoved.
effects phase
TileStepEffectSystem reads Position / tile state.
TrapSystem reads Position / trap state.
AutoPickupSystem reads Position / pickup state.
presentation phase or external adapter
Renderer consumes EntityMoved.
Audio consumes EntityMoved.
VFX consumes EntityMoved.
UI consumes EntityMoved.
Relationship to World Extensions
A world extension is runtime setup around the world.
Extensions are appropriate for:
Render event bridge
Audio event bridge
VFX event bridge
UI event bridge
Debug event log
Telemetry bridge
Script router
External adapter
Diagnostics
Extensions may install event listeners.
Extensions should be treated with suspicion if they mutate rule truth through event listeners.
If an extension contains real simulation logic, consider converting it into a system and assigning it to a phase.
Compact Rule
Components are memory.
Phases are time.
Systems are law.
Events are traces.
Extensions are attachments.
Acceptance Criteria for Future Design Work
When adding new behavior, classify it first:
Is it durable truth?
Component.
Is it ordered rule progression?
System in a phase.
Is it a temporary rule-resolution artifact?
Transient component or explicit staged record.
Is it presentation, sound, VFX, UI, debug, telemetry, or observation?
Event.
Is it runtime wiring around a world?
Extension.
If the answer is unclear, default toward components + phases for simulation rules, and events for presentation.
Technical Memo: Components, Phases, Systems, and Events in
ecs-jsPurpose
Record the intended separation of responsibilities between components, phases, systems, and events.
This memo is architectural doctrine for
ecs-jsusage. It is not an implementation patch by itself.The central decision:
Declared Purpose of Components
Components represent durable simulation state.
They are the facts the world owns.
Examples:
A component should be used when a fact must survive beyond the current reaction moment.
Use components for:
Avoid using events as a substitute for component state.
If a later system must know something during a phase, prefer durable state, changed markers, phase ordering, or transient rule components over event-listener side effects.
Declared Purpose of Phases
Phases represent ordered passage through simulation time.
They are the coarse lifecycle of a tick.
Examples:
Use phases when ordering matters.
A phase answers:
Phases are the primary mechanism for deterministic system-to-system rule progression.
Declared Purpose of Systems
Systems are rule processors.
They read components, write components, create/destroy entities, and emit post-fact events when useful.
Systems should not call other systems directly.
Systems communicate through:
A system is the right place for simulation truth changes.
Examples:
Declared Purpose of Events
Events are runtime-only descriptions of things that happened.
They are not durable state.
They are not serialized.
They are not rule truth.
They are best used as presentation / observation exhaust from the simulation.
Events are ideal for:
Examples:
Events answer:
Events should generally be emitted after the rule outcome has been determined.
Rule Boundary
Events can describe rule outcomes.
Events should not determine rule outcomes.
This means event listeners should not usually be where core simulation facts are produced.
Suspicious event usage:
Good event usage:
No Mutable Rule Hooks
Avoid mutable event hooks such as:
That pattern lets rules escape the phase model.
If a combat outcome needs staged modification, model that inside rules space:
Then process those through named systems/phases.
Example staged model:
combat:start Create CombatResolution from AttackIntent combat:accuracy Resolve miss / dodge / parry combat:modifiers Apply posture, weapon, affix, blindness, shrine, coating, status modifiers combat:damage Commit Vitality changes combat:aftermath Apply secondary rule outcomes presentation Emit DamageApplied, AttackDodged, ActorDied, etc.The event is the receipt, not the mechanism.
System-to-System Communication Doctrine
Use this decision table:
Hard order dependency Use phases / before / after Persistent data dependency Use components Transient rule staging Use transient components or explicit phase-local records Post-fact observation Use events Display/audio/VFX/UI/debug bridge Use events consumed by world extensions Runtime setup Use world extensionsDo not let event listeners become hidden systems.
A listener that mutates simulation truth is probably a system trying to escape scheduling.
Presentation Boundary
A clean frame looks like this:
Simulation phases Mutate durable state. Emit descriptive events. Presentation bridge Consumes events. Produces rendering/audio/VFX/UI effects. Does not mutate simulation truth.Example:
movement phase MovementSystem mutates Position. MovementSystem emits EntityMoved. effects phase TileStepEffectSystem reads Position / tile state. TrapSystem reads Position / trap state. AutoPickupSystem reads Position / pickup state. presentation phase or external adapter Renderer consumes EntityMoved. Audio consumes EntityMoved. VFX consumes EntityMoved. UI consumes EntityMoved.Relationship to World Extensions
A world extension is runtime setup around the world.
Extensions are appropriate for:
Extensions may install event listeners.
Extensions should be treated with suspicion if they mutate rule truth through event listeners.
If an extension contains real simulation logic, consider converting it into a system and assigning it to a phase.
Compact Rule
Acceptance Criteria for Future Design Work
When adding new behavior, classify it first:
Is it durable truth? Component. Is it ordered rule progression? System in a phase. Is it a temporary rule-resolution artifact? Transient component or explicit staged record. Is it presentation, sound, VFX, UI, debug, telemetry, or observation? Event. Is it runtime wiring around a world? Extension.If the answer is unclear, default toward components + phases for simulation rules, and events for presentation.