# IDs

Every entity in the Lavanda platform is addressed by an **ID**. Across our services these IDs are
backed by a mix of underlying datatypes:

- **BigInt** — sequential 64-bit integers, common on older records in the PMS.
- **NanoId** — short, URL-safe, randomly generated strings (e.g. `V1StGXR8_Z5jdHi6B-myT`).
- **UUIDv4** — 128-bit random identifiers (e.g. `9f290d93-3d97-4b3f-8a1e-2b6c4d5e6f70`).
- **Other schemes** — additional formats may be introduced over time as new domains and services
  come online.

## The ID value is stable — the datatype is not

An ID, once assigned to an entity, **will not change**. The record you fetched yesterday keeps the
same identifier tomorrow.

What **is** subject to change is the *datatype* used to represent that identifier. As entities are
migrated between services, or as domains are re-modelled, an entity that started life with a
**BigInt** ID may later be represented as a **NanoId** or a **UUIDv4**. This can happen at any time
and without a breaking-change announcement, because from the consumer's perspective the identifier
is still just an opaque token.

This is also why every identifier field in our GraphQL schema uses the `ID` scalar rather than
`String` or `Int` — the `ID` scalar is intentionally opaque and serialises as a string on the wire.

## Model every ID as a string

For forwards compatibility, **all API and webhook consumers must model IDs as strings.**

- **Do** store IDs in `string` / `text` columns and string-typed fields.
- **Do** treat an ID as an opaque token: compare it for equality, pass it back verbatim in
  subsequent requests, and use it as a map key.
- **Don't** parse an ID into an integer, assume it is numeric, or assume a fixed length or format.
- **Don't** rely on IDs being sequential, sortable, or monotonically increasing — NanoIds and
  UUIDs carry no ordering guarantees.
- **Don't** generate, derive, or validate IDs on the client side against a specific format.

A consumer that stores a BigInt ID in a numeric column will break the moment that entity's ID
becomes a NanoId or UUID. A consumer that treats every ID as an opaque string keeps working through
any future datatype migration with no changes required.

## Summary

| Aspect | Guarantee |
| --- | --- |
| The ID **value** | Stable — never changes for a given entity |
| The ID **datatype** | May change at any time (BigInt → NanoId → UUIDv4 → …) |
| How to **model** it | Always as an opaque **string** |
