Data-First Design
In Nebula, data is the application. Instead of building services around business logic and hoping the data layer keeps up, you model your entire domain as composable components on lightweight entities. The data model drives everything — storage, processing, real-time updates, and access control.
Entities and Components
An entity in Nebula is deliberately simple: an ID, creation metadata, and an owner. That's it. All meaningful data lives in components — typed JSON objects attached directly to the entity's database record.
This means no joins. No lookup tables. When you query an entity, you get everything it carries in a single read.
Each entity can have at most one instance of any given component type. This single-component dimensionality keeps the data model flat and predictable. If an Account entity has a Customer component, there's exactly one — not a list, not a nullable reference to another table.
The Relationship Graph
Since each entity can only have one instance of each component, Nebula supports a relationship graph to model more complex structures. Any entity can have multiple parents and multiple children. These relationships are semantic — it's up to you to decide what parent/child means in your domain.
An Account entity might have Claim entities as children and be a child of an Organization entity. A Claim might link to both an Account (parent) and multiple ServiceLine entities (children). The graph is flexible and doesn't impose hierarchy.
This is fundamentally different from relational foreign keys. Relationships in Nebula are first-class: you can query by them, subscribe to changes in them, and use them in access control rules.
Why This Matters
Traditional applications accumulate complexity through schema migrations, ORM configurations, and ever-growing join queries. Adding a new data dimension means altering tables, updating models, and hoping nothing breaks downstream.
In Nebula, adding new data is additive by nature. Want to track denial opportunities on existing Account entities? Register a DenialOpportunity component and start attaching it. No migration. No schema change. No existing code breaks — components that don't know about DenialOpportunity simply ignore it.
This composability extends to queries. Because components live directly on entities, you can query across component boundaries without joins:
No joins. No repository patterns. The query describes the composition you're interested in, and Nebula returns matching entities with all their components.
The data-first approach means your Nebula application grows by adding components — not by redesigning tables or refactoring services. This is the foundation that Behaviors and Orchestration build on. See Relationship Patterns for how links extend this model into queryable entity graphs.