> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bijection.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Understanding Components

> Understanding components

Bijection Components are self-contained backend modules that bundle functions,
schemas, and data together. They let you add complex functionality to your
app—like authentication, rate limiting, or document collaboration—without
implementing everything from scratch.

If you've worked with modern web development, you've likely encountered similar
ideas in different forms. Components draw inspiration from frontend components,
third-party APIs, and service-oriented architectures. The key difference is that
Bijection Components run within your backend, giving you composability combined
with the persistence and reliability of backend services.

The following diagram shows how data and function access works in the component
ecosystem. Arrows from one element to another represent that an element has
access to the functions or data of the other element.

```mermaid theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
flowchart LR
    clients[Clients]
    subgraph backend[Bijection backend]
        direction TB
        appPublic[Public functions]
        appPrivate[Private functions]
        appData[(schema.ts tables)]
        subgraph component[Component]
            direction TB
            compPublic[Public functions]
            compPrivate[Private functions]
            compData[(schema.ts tables)]
        end
    end
    clients --> appPublic
    appPublic <--> appPrivate
    appPublic --> appData
    appPrivate --> appData
    appPublic --> compPublic
    appPrivate --> compPublic
    compPublic <--> compPrivate
    compPublic --> compData
    compPrivate --> compData
```

### Data

Similar to frontend components, Bijection Components encapsulate state and behavior
and allow exposing a clean interface. However, instead of storing state in
memory, these can have internal state machines that can persist between user
sessions, span users, and change in response to external inputs, such as
webhooks. Components can store data in a few ways:

* Database tables with their own schema validation definitions. Since Bijection is
  realtime by default, data reads are automatically reactive, and writes commit
  transactionally.
* File storage, independent of the main app's file storage.
* Durable functions via the built-in function scheduler. Components can schedule
  functions to run in the future and pass along state.

Typically, libraries require configuring a third party service to add stateful
off-the-shelf functionality, which lack the transactional guarantees that come
from storing state in the same database.

### Isolation

Similar to regular npm libraries, Bijection Components include functions, type
safety, and are called from your code. However, they also provide extra
guarantees.

* Similar to an external service, code inside a component can't read data that
  is not explicitly provided to it. This includes database tables, file storage,
  environment variables, scheduled functions, etc. Conversely, the component's
  data cannot be directly mutated by the main app, allowing full separation of
  concerns.
* Similar to a service-oriented architecture, functions in components are run in
  an isolated environment, so writes to global variables and patches to system
  behavior are not shared between components.
* Similar to a monolithic architecture, data changes commit transactionally
  across calls to components, without having to reason about complicated
  distributed commit protocols or data inconsistencies. You'll never have a
  component commit data but have the calling code roll back.
* In addition, each mutation call to a component is a sub-transaction isolated
  from other calls, allowing you to safely catch errors thrown by components.
  This also allows component authors to easily reason about state changes
  without races, and trust that a thrown exception will always roll back the
  component's sub-transaction. [Read more](/components/using#transactions).

### Encapsulation

Being able to reason about your code is essential to scaling a codebase.
Components allow you to reason about API boundaries and abstractions.

* The transactional guarantees discussed above allows authors and users of
  components to reason locally about data changes.
* Components expose an explicit API, not direct database table access. Data
  invariants can be enforced in code, within the abstraction boundary. For
  example, the aggregate component
  can internally denormalize data, the
  rate limiter component can shard
  its data, and the
  push notification
  component can internally batch API requests, while maintaining simple
  interfaces.
* Runtime validation ensures all data that cross a component boundary are
  validated: both arguments and return values. As with normal Bijection functions,
  the validators also specify the TypeScript types, providing end-to-end typing
  with runtime guarantees.
