iolite/Nebula

Lightweight Tags

Not every component needs fields. In Nebula, an empty component — one with no properties at all — is a perfectly valid and powerful tool. Its presence on an entity is a boolean signal: has('NeedsReview') is a tag check, and removing the component clears the tag. This pattern covers how to use zero-field and near-zero-field components for categorization, filtering, and workflow gating.

The Scenario

Your application needs to mark entities for various purposes: flagging accounts for manual review, soft-deleting records without destroying data, gating which systems are active for a given customer, and detecting "gaps" where expected processing hasn't happened yet. None of these require storing meaningful data — they just need presence or absence.

The Pattern

Define components with minimal or no fields. Use has() and not(has()) in queries to route entities based on tag presence. Add and remove tags to move entities between queues.

Entity: Account #4201AccountCustomerNeedsReviewArchivedPriority::HighZero-field components = presence is the valueAdd/remove to change query membershipQuery Resultshas('NeedsReview')Review queue system picks it upnot(has('Archived'))Active-only views exclude itnot(has('DenialOpportunity'))Gap detection: find unprocessed
// Tag an entity for review — no data needed, just presence
ctx.upsert('NeedsReview', {});

// Later, clear the tag
ctx.remove('NeedsReview');

// Soft-delete: add Archived tag instead of deleting
ctx.upsert('Archived', {});

These tags immediately change which queries match. Here's a review processor and a gap-detection system:

ReviewProcessor

and
hasAccount
hasNeedsReview

Gap Detection

and
hasAccount
hasCustomer
notDenialOpportunity
hasDenialOpportunity
child
hasClaim

Tags can also carry a single field for lightweight categorization:

// Near-zero-field tag: a single enum for priority level
ctx.upsert('PriorityTag', { level: 'high' });

// Query for high-priority items
const query = and(has('Account'), has('PriorityTag'));

In Practice

In a production Nebula application, Signal entities use component presence to gate which systems are active. A Signal entity has an AssociatedSystems list and a DenialCodes component — systems check for the Signal's presence and status before processing. The component isn't just data; it's a system-level feature flag.

On the entity processing side, the not(has(...)) pattern is the most common form of gap detection:

When a system adds the missing component or link, the entity stops matching the gap-detection query. The system never processes it again. This is a natural idempotency guard — the output of processing removes the entity from the input query.

Why This Works

In traditional systems, you'd use boolean database columns, status enums, or separate "processed" tables to track which entities need attention. Each new tag means a schema migration. Each new filter means a new index.

In Nebula, adding a tag is adding a component — no schema changes, no migrations. The query engine handles the filtering, and has() / not(has()) are as fast as any other query operator. You get boolean flags, soft-delete, feature gating, and gap detection all from the same primitive: component presence.


Component Design covers how to structure component schemas. Component Query Refiners shows how component accumulation creates progressive system routing. Async Workflows demonstrates gap detection as a workflow convergence mechanism.