Skip to content

Ørbs & Modules

An Ørb is the top-level application boundary in Orbital. It defines the namespace and structural context in which data, schemas, and processing rules exist.

An Ørb does not represent domain data itself. Instead, it scopes and constrains how data is defined, stored, and interpreted.

An Ørb is an abstraction on top of one or more microservices. It represents a logical grouping of features and data that is exposed to the platform as a single unit.

Ørbs do not replace services, rather they define how service capabilities are combined, scoped, and presented within Orbital.

Consider two Ørbs responsible for account management:

  • Access — Back Office administrator accounts
  • Users — Customer user accounts

Both Ørbs rely on the same underlying services:

  • the Core Service
  • an Auth Service

The Auth Service exposes a shared set of handlers:

  • /createInvite
  • /createAccount
  • /login
  • /logout

From the perspective of Orbital, these handlers are exposed through Ørb-specific endpoints derived from each Ørb’s configuration:

Access Ørb

  • /access/createInvite
  • /access/createAccount
  • /access/login
  • /access/logout

Users Ørb

  • /users/createInvite
  • /users/createAccount
  • /users/login
  • /users/logout

Although the underlying business logic is shared, each Ørb remains isolated. Data is stored in Ørb-dedicated streams, logs are produced in the context of the Ørb, and consumers operate exclusively on Ørb streams, without knowledge of the underlying services.

Ørbs have no direct knowledge of any other Ørb, with the exception of the Registry, which act as a runtime bridge between them.

Ørbs are classified by how they are implemented:

  • Standard Ørbs rely only on Orbital core capabilities
  • Custom Ørbs require one or more dedicated services to provide domain-specific logic.

This classification does not affect the structural definition of an Ørb. For the platform, they are just Ørbs.

An Ørb is responsible for:

  • defining a namespace for API endpoints, schemas, and entries
  • declaring which Modules exist
  • scoping Schemas and Entries and it’s other Messages to a single application context
  • isolating data and behavior from other Ørbs

There is no implicit sharing of data or state between Ørbs.

The hierarchy of which Ørb, Module, Schema a Message belongs to is derived from the NATS subject:

Entries

Subject: games.catalog.entries.game.cq9k2x4m5n6o7p8q
└─┬─┘ └──┬──┘ └──┬──┘ └┬─┘ └───────┬───────┘
orb module role schemaId id

Schemas

Subject: games.catalog.schemas.game
└─┬─┘ └──┬──┘ └──┬──┘ └┬─┘
orb module role id

From a Jetstream perspective, all Messages are managed identically.


A Module is a container that declares which category of Messages it allows.

  • each Module allows exactly one Message category
  • all Schemas within a Module must conform to that category
  • Entries created from those Schemas inherit the same category
  • how Schemas are created (manually or generated by services) is Module-specific

Module types are used to declare a specific Message payload structure. For example, the payload for Messages in a content Module must conform to a very specific content schema, and the payload for rules is following a completely different schema.

  • config — Managed Messages
  • content — Managed Messages
  • rules — Managed Messages
  • settings — Declarative Messages
  • transactions — Transactional Messages

All Messages in a Module Type conforms to exactly one Message category. This ensures lifecycle semantics are explicit and mechanically enforced.

flowchart TD

  subgraph Messages["Messages"]
    direction TB

    %% Level 1
    subgraph MessageCategory["Message Category"]
      direction LR
      declarative["declarative"]
      managed["managed"]
      transactional["transactional"]
    end

    %% Level 2
    subgraph ModuleType["Module Type"]
      direction LR
      settings["settings"]
      config["config"]
      content["content"]
      rules["rules"]
      transactions["transactions"]
    end

    %% Level 3 (renamed)
    subgraph MessageRole["Message Role"]
      direction LR
      settings_entries["entries"]

      config_schemas["schemas"]
      config_entries["entries"]

      content_schemas["schemas"]
      content_entries["entries"]

      rules_entries["entries"]

      transactions_schema["schema"]
      transactions_entries["entries"]
    end

    %% Category -> Module Type
    declarative --> settings
    managed --> config
    managed --> content
    managed --> rules
    transactional --> transactions

    %% Module Type -> Message Role
    settings --> settings_entries

    config --> config_schemas
    config --> config_entries

    content --> content_schemas
    content --> content_entries

    rules --> rules_entries

    transactions --> transactions_schema
    transactions --> transactions_entries
  end