# What Is Spatiotemporal Composability in Cordis? A Deep Dive into the Meta-Framework

> Explore spatiotemporal composability in Cordis, its core architectural principle for composing functionality across spatial and temporal boundaries. Learn about component trees and dynamic plugin lifecycles.

- Repository: [Cordiverse/cordis](https://github.com/cordiverse/cordis)
- Tags: deep-dive
- Published: 2026-08-24

---

**Spatiotemporal composability is Cordis's core architectural principle that enables developers to compose functionality across both spatial boundaries (hierarchical component trees) and temporal boundaries (dynamic plugin lifecycles and runtime updates).**

Cordis (maintained by the cordiverse organization) implements **spatiotemporal composability** as its foundational paradigm. This meta-framework allows you to build complex applications from small, independent plugins that can be registered in specific scopes and safely updated while the system runs without breaking existing functionality.

## The Two Dimensions of Spatiotemporal Composability

Spatiotemporal composability operates across two distinct but complementary axes. **Spatial composition** refers to how functionality is organized across different logical "rooms" of an application—such as UI modules, micro-frontends, or server sub-systems. **Temporal composition** handles the evolution of the system as plugins are added, removed, or hot-reloaded during runtime.

According to the Cordis source code, these dimensions are not treated as separate concerns but as unified properties of the application context. The framework guarantees that service lookups respect both the current *where* (position in the context hierarchy) and *when* (current version of the service implementation).

## Spatial Composition via the Registry

The **spatial** dimension enables independent application pieces to be registered locally and later queried from any descendant context without tight coupling. In [`packages/core/src/registry.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/registry.ts), the `Registry` class creates a hierarchical map of services scoped to specific contexts.

### Hierarchical Service Registration

The `Registry` maintains parent-child relationships that allow service lookup to bubble up through the context tree. When you register a service in a parent context, all child contexts can retrieve it, but children can also override registrations locally without affecting siblings or ancestors.

```typescript
import { createContext } from '@cordis/core'

// Create a root context (the "space" for the whole app)
const root = createContext()

// Register a service in that spatial scope
root.registry.add('logger', console)

// Retrieve the service from a child context (spatial composition)
const child = root.createChild()
const logger = child.registry.get('logger')
logger.info('Hello from the child context!')

```

In [`packages/core/src/context.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/context.ts), the `Context` class exposes the `registry` property that enables this spatial scoping, allowing services to be placed exactly where they are needed in the component hierarchy.

## Temporal Composition with Fiber and Events

The **temporal** dimension allows new behavior to be introduced after the application has started and ensures old behavior can be cleanly disposed of without memory leaks or dangling references. This is implemented primarily through the `Fiber` class in [`packages/core/src/fiber.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/fiber.ts).

### Managing Async Lifecycles with Fiber

The `Fiber` class tracks the lifecycle of asynchronous tasks and manages disposables—resources that need cleanup when a component unmounts or updates. When you register a disposable via `fiber.disposable()`, Cordis ensures it executes when that temporal boundary ends, preventing resource leaks during hot-reloads.

```typescript
// Dynamically replace the logger at runtime (temporal composition)
child.fiber.disposable(() => {
  // Clean up old logger when the fiber ends
  console.log('Disposing old logger')
})

child.registry.set('logger', {
  info(msg: string) { console.error('[New]', msg) },
})

// New lookups see the updated implementation
const newLogger = child.registry.get('logger')
newLogger.info('This uses the new logger')

```

### Cross-Time Communication via Events

For communication that spans temporal boundaries, [`packages/core/src/events.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/events.ts) provides an `EventEmitter` that works with fibers. This publish/subscribe bus functions correctly even as listeners are added or removed at runtime, ensuring events reach only active, relevant contexts.

```typescript
import { EventEmitter } from '@cordis/core'

// Create an emitter attached to a context's fiber
const emitter = new EventEmitter(child.fiber)

// Listener added now
emitter.on('data', payload => console.log('Received', payload))

// Later, after async work, emit an event (temporal)
setTimeout(() => emitter.emit('data', { id: 42 }), 1000)

```

## Unifying Space and Time in Context

The `Context` class in [`packages/core/src/context.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/context.ts) unifies both dimensions by carrying references to both the `Registry` (spatial) and `Fiber` (temporal). This design allows a service to be scoped to a specific subtree while simultaneously being updated temporally—such as hot-replacing a command for a specific UI region without affecting other regions.

When you invoke `createContext()`, you instantiate a node in the spatial hierarchy that also contains its own temporal boundaries. The `Context` API exposes methods like `createChild()` for spatial extension and manages the underlying fiber for lifecycle control. The base `Service` class in [`packages/core/src/service.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/service.ts) extends this pattern, providing an abstract foundation for services that support both registration and runtime override.

## Core Implementation Files

- **[`packages/core/src/context.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/context.ts)** — Central entry point holding `Registry` and `Fiber` references for a given scope.
- **[`packages/core/src/registry.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/registry.ts)** — Hierarchical service registry enabling spatial lookup and dynamic service updates.
- **[`packages/core/src/fiber.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/fiber.ts)** — Manages async lifecycle boundaries, disposables, and temporal cleanup.
- **[`packages/core/src/events.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/events.ts)** — Event-bus implementation that respects fiber lifecycles for temporal event handling.
- **[`packages/core/src/service.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/service.ts)** — Abstract base class for services supporting registration and temporal override.

## Summary

- **Spatiotemporal composability** combines spatial scoping (hierarchical component trees) with temporal lifecycle management (runtime plugin updates).
- The **`Registry`** system in [`packages/core/src/registry.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/registry.ts) enables spatial composition by allowing services to be registered at specific hierarchy levels and inherited by descendants.
- The **`Fiber`** system in [`packages/core/src/fiber.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/fiber.ts) enables temporal composition by tracking async task lifecycles and managing resource disposal during dynamic updates.
- The **`Context`** class unifies both dimensions, exposing a concise API for registration, lookup, and disposal across space and time.
- Cordis supports hot-reloading and dynamic plugin replacement without breaking existing functionality or leaking resources.

## Frequently Asked Questions

### How does spatiotemporal composability differ from standard dependency injection?

Standard dependency injection typically focuses on wiring dependencies at startup time within a static spatial hierarchy. Spatiotemporal composability extends this by adding the temporal dimension—services can be added, removed, or replaced after the application has started, and the framework manages the cleanup of old implementations through fiber disposables.

### Can services be updated at runtime without restarting the application?

Yes. Cordis supports runtime updates through the `Registry.set()` method combined with `Fiber` disposables. When you replace a service registration, new lookups immediately receive the updated implementation, while the fiber ensures previous instances are properly disposed of via registered cleanup callbacks.

### What is the relationship between Context and Fiber in Cordis?

Each `Context` instance contains a reference to a `Fiber` that manages the temporal lifecycle of that specific spatial scope. The context handles *where* services exist in the hierarchy (spatial), while the fiber handles *when* they are active and how they are cleaned up (temporal).

### How does the Registry handle service resolution across parent and child contexts?

The `Registry` implements hierarchical lookup logic where `get()` calls first check the local context, then traverse up through parent contexts until finding a match. This allows children to inherit services from ancestors while maintaining the ability to override registrations locally without affecting other branches of the tree.