# Core Principles of the Flecs ECS Pattern in Equilibrium Engine

> Discover the core principles of the Flecs ECS pattern in Equilibrium Engine. Explore its world-centric architecture, explicit component registration, modular systems, declarative queries, and data-oriented design for efficient ...

- Repository: [Alexander/equilibriumengine](https://github.com/clibequilibrium/equilibriumengine)
- Tags: internals
- Published: 2026-02-27

---

**The Equilibrium Engine implements the Flecs ECS pattern through a world-centric architecture that emphasizes explicit component registration, modular system imports, declarative queries with change detection, and data-oriented design for cache-friendly iteration.**

The Equilibrium Engine leverages flecs—a fast, data-oriented Entity-Component-System library—to power its game architecture. Understanding how this engine applies the Flecs ECS pattern reveals a design built on strict separation of data and behavior, hierarchical relationships, and high-performance query filters. By examining the source code in `clibequilibrium/equilibriumengine`, we can identify the foundational concepts that govern entity lifecycle, component storage, and system execution.

## World-Centric Architecture and Explicit Registration

At the heart of the Equilibrium Engine's Flecs implementation lies the **world as the central container**. The engine creates a single `ecs_world_t` (aliased as `world_t` in [`equilibrium/world.h`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/world.h)) that owns all entities, components, and systems. All API calls route through this object, ensuring a unified state management context.

Before any component can be used, it must be explicitly registered with the world. The engine follows a two-phase registration pattern using `ECS_COMPONENT_DECLARE` in headers and `ECS_COMPONENT_DEFINE` in module implementation files. In [`equilibrium/components/transform.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/components/transform.c), components like `Position`, `Rotation`, and `Transform` are defined within the `TransformComponents` module import function, making them known to the world and enabling fast lookups.

```c
/* In transform.c */
ECS_COMPONENT_DECLARE(Position);
ECS_COMPONENT_DECLARE(Rotation);
ECS_COMPONENT_DECLARE(Transform);

void TransformComponentsImport(world_t *world) {
    ECS_MODULE(world, TransformComponents);
    
    ECS_COMPONENT_DEFINE(world, Position);
    ECS_COMPONENT_DEFINE(world, Rotation);
    ECS_COMPONENT_DEFINE(world, Transform);
}

```

## Modular Composition with Flecs Imports

The Equilibrium Engine isolates functionality through **Flecs modules** that are imported using the `ECS_IMPORT` macro. This pattern prevents circular dependencies and allows the engine to compose features on demand. For example, `TransformSystem` and `TransformComponents` are separate modules that can be imported independently based on the application's needs.

In [`equilibrium/systems/transform_system.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/systems/transform_system.c), the `TransformSystemImport` function registers both the component definitions and the systems that operate on them. This modular approach is visible in the directory structure, where simulation systems live in `equilibrium/engine-simulation/` while rendering systems reside in `equilibrium/systems/rendering/`, yet both operate on the same ECS world.

```c
world_t *world = world_create();

/* Import the component module so the world knows about Position, Transform, … */
ECS_IMPORT(world, TransformComponents);

/* Import the system that will compute world-space matrices */
ECS_IMPORT(world, TransformSystem);

```

## Declarative Query-Driven Systems

Systems in the Equilibrium Engine are defined by **declarative queries** rather than imperative entity iteration. Using the `ECS_SYSTEM` macro, developers specify filter expressions that select entities based on component presence, optionality (marked with `?`), and relationships. The `ApplyTransform` system in [`equilibrium/systems/transform_system.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/systems/transform_system.c) demonstrates this pattern by querying entities that have `Transform`, optionally have `Rotation` and `Scale`, and participate in hierarchical relationships.

The engine also leverages **query change detection** to avoid unnecessary computation. Systems can early-out when data remains static by calling `ecs_query_changed`, as implemented in the `ApplyTransform` system's early-exit logic at lines 15–18 of [`transform_system.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/transform_system.c).

```c
void ApplyTransform(ecs_iter_t *it) {
    /* Early exit if nothing changed */
    if (!ecs_query_changed(NULL, it)) {
        return;
    }
    
    Transform *t = ecs_field(it, Transform, 1);
    // ... processing logic
}

/* Registration with filter expression */
ECS_SYSTEM(world, ApplyTransform, EcsOnValidate,
    [out] Transform,
    [in] ?Transform(parent|cascade),
    [in] Position(self|up),
    [in] ?Rotation,
    [in] ?Scale);

```

## Data-Oriented Optimization Techniques

The Flecs ECS pattern in Equilibrium Engine adheres to strict **data-oriented design** principles where components are plain data structs and behavior lives exclusively in systems. Components like `Position` and `Transform` (defined in [`equilibrium/components/transform.h`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/components/transform.h) using `cglm` wrappers) contain no methods—only raw data fields.

To maximize performance, the engine utilizes **instanced queries** for cache-friendly iteration. By setting `.query.filter.instanced = true` in system definitions (visible at lines 89–90 of [`transform_system.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/transform_system.c)), flecs packs matching components tightly in memory, improving data locality during system execution. This is particularly critical for transform calculations that process large numbers of entities per frame.

## Hierarchical Relationships via Cascade Queries

The engine models parent-child transforms using **cascade queries** that leverage flecs relationship traversal. The `(parent|cascade)` syntax in query expressions tells flecs to walk the entity hierarchy and supply the parent's transform to child systems. This is implemented in the `ApplyTransform` system's component list, where `[in] ?Transform(parent|cascade)` receives the parent's transformation matrix if it exists, while `[in] Position(self|up)` accesses the entity's own position or searches upward in the hierarchy.

The `entity_create` macro in [`equilibrium/entity.h`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/entity.h) (lines 23–31) further simplifies entity spawning by encapsulating `ecs_new`, entity naming, component assignment, and tagging with the generic `Entity` component into a single, readable operation.

```c
/* Spawning a hierarchical entity */
entity_t child = entity_create(
    world,
    "ChildObject",
    Transform, {},
    Position, { .x = 1.0f, .y = 0.0f, .z = 0.0f });

entity_set_parent(child, parent_entity);

```

## Summary

- **World-centric design**: A single `world_t` instance in [`equilibrium/world.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/world.c) manages all ECS state, ensuring centralized entity and component lifecycle management.
- **Explicit registration**: Components require `ECS_COMPONENT_DECLARE` in headers and `ECS_COMPONENT_DEFINE` in modules (as seen in [`transform.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/transform.c)), establishing type safety and fast lookups.
- **Modular architecture**: The `ECS_IMPORT` macro enables clean separation between components, simulation systems, and rendering systems while sharing a single world instance.
- **Declarative systems**: `ECS_SYSTEM` macros with query filters determine iteration sets, with support for optional components (`?`) and relationship traversal (`parent|cascade`).
- **Performance optimization**: Instanced queries (`.instanced = true`) and change detection (`ecs_query_changed`) minimize CPU overhead and maximize cache locality.
- **Data-oriented approach**: Components are pure data structs (defined in [`transform.h`](https://github.com/clibequilibrium/equilibriumengine/blob/main/transform.h)) with behavior isolated in systems, following strict ECS principles.

## Frequently Asked Questions

### What is the difference between ECS_COMPONENT_DECLARE and ECS_COMPONENT_DEFINE?

**`ECS_COMPONENT_DECLARE`** is used in header files (such as [`equilibrium/components/transform.h`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/components/transform.h)) to declare the component type identifier, while **`ECS_COMPONENT_DEFINE`** is called within a module's import function (like `TransformComponentsImport` in [`transform.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/transform.c)) to register the component with a specific world instance. This separation allows headers to reference component types without requiring the full world context, while implementation files handle the actual registration logic that maps types to flecs internal storage.

### How does Equilibrium Engine handle parent-child relationships in Flecs?

The engine utilizes **cascade queries** with the `(parent|cascade)` relationship specifier in system definitions. In [`equilibrium/systems/transform_system.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/systems/transform_system.c), the `ApplyTransform` system declares `[in] ?Transform(parent|cascade)` to receive the parent's transform matrix when iterating child entities. This declarative approach lets flecs automatically traverse the entity hierarchy and supply parent data during system execution, eliminating manual tree-walking code while maintaining cache-friendly iteration patterns.

### What is the purpose of instanced queries in the Flecs ECS pattern?

**Instanced queries** (enabled by setting `.query.filter.instanced = true` in system definitions) instruct flecs to pack matching components into contiguous memory arrays during iteration. In the Equilibrium Engine, this is applied to the `ApplyTransform` system to ensure that `Transform`, `Position`, and `Rotation` components accessed via `ecs_field` reside in cache-friendly layouts. This optimization reduces memory latency and improves throughput when processing large numbers of entities, which is critical for real-time simulation and rendering loops.

### How does the engine separate simulation logic from rendering?

The Equilibrium Engine maintains **physical directory separation** while using a unified ECS world. Simulation systems reside in `equilibrium/engine-simulation/` and rendering systems in `equilibrium/systems/rendering/`, yet both import the same `world_t` instance created via `world_create()` in [`equilibrium/world.c`](https://github.com/clibequilibrium/equilibriumengine/blob/main/equilibrium/world.c). Because all systems register with the same world through `ECS_IMPORT`, simulation updates and rendering queries operate on consistent entity state without requiring manual synchronization glue code, allowing the renderer to access transform data immediately after simulation systems update it.