> For the complete documentation index, see [llms.txt](https://navixy.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://navixy.com/docs/analytics/pt-br/iot-query/schema-overview/bronze-layer/bdr.md).

# Esquema BDR

BDR organiza dispositivos e entidades de negócio em ativos, com Campos customizados, relacionamentos e trilhas de auditoria. Navegue pela definição do esquema e pelas descrições das tabelas principais

{% hint style="warning" %}
**Este esquema está atualmente em desenvolvimento.** BDR (Business Data Repository) organiza os dados em torno de ativos: uma combinação unificada de um dispositivo e uma entidade de negócio, como um veículo e seu motorista, ou um dispositivo e seus sensores. O IoT Query lê do BDR para expor esses dados para análises. Se você tiver interesse em acesso antecipado ou tiver dúvidas sobre essa funcionalidade, entre em contato <iotquery@navixy.com>.
{% endhint %}

O BDR fornece uma estrutura abrangente para gerenciar estruturas organizacionais, atores, dispositivos, ativos e seus relacionamentos em ambientes multi-tenant. Desenvolvido sobre PostgreSQL 18 com as `ltree` e `pg_trgm` extensões, o esquema oferece suporte a organizações hierárquicas, definições de campos customizados para qualquer tipo de entidade, controle de acesso por função baseado em ator com restrições em nível de objeto, bloqueio otimista para edições concorrentes e trilhas de auditoria completas com rastreamento de alterações em nível de campo. Todas as entidades podem ser estendidas sem modificações no esquema, localizadas para implantações internacionais e vinculadas por meio de relacionamentos polimórficos flexíveis.

O esquema aborda cenários complexos de gerenciamento de dados, incluindo hierarquias de ativos da frota em diferentes níveis organizacionais, plataformas SaaS multi-tenant que exigem isolamento de dados, operações orientadas por conformidade com requisitos detalhados de auditoria e sistemas que precisam de modelos de dados dinâmicos adaptáveis por meio de campos customizados em vez de migrações de banco de dados.

{% hint style="info" %}
O diagrama interativo do esquema de dados do BDR está disponível em **dbdiagram.io**: <https://dbdiagram.io/d/Navixy-Repo-data-schema-68ad788c1e7a611967a0930e>
{% endhint %}

Encontre os detalhes do esquema BDR abaixo.

{% code title="esquema de dados do BDR" expandable="true" %}

```sql
// ============================================
// Schema do BDR (Business Data Repository) - Jornada do Cliente
// PostgreSQL 18 com as extensões ltree, pg_trgm
// Versão: 2.2
// ============================================

// ============================================
// TABELAS DE REFERÊNCIA BASE (hierarquia ci_base - herança de tabela única)
// ============================================

Table ci_base {
  id uuid [primary key, note: '4-char entity type code embedded in UUID bytes 5-8, extracted via uuid_type_code()']
  code text [not null, note: 'Código legível por máquina, must start with a letter']
  title_en text [not null, note: 'Ordenação sem distinção de maiúsculas e minúsculas (ICU)']
  description_en text
  order int [not null, default: 0]
  is_system boolean [not null, default: false]
  discriminator text [not null, note: 'Preenchido automaticamente a partir do id via trg_ci_set_discriminator']
  catalog_id uuid [note: 'Catálogo pai para itens de catálogo do usuário. NULL para catálogos do sistema e para as próprias definições de catálogo']
  organization_id uuid
  parent_id uuid
  path ltree [note: 'Mantido automaticamente pelo trigger quando is_hierarchical = true']
  is_hierarchical boolean [not null, default: false]
  extra jsonb [not null, default: `{}`]
  version int [not null, default: 1, note: 'Bloqueio otimista']
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz

  índices {
    (id) [name: 'idx_ci_active', note: 'partial: WHERE deleted_at IS NULL']
    (parent_id) [name: 'idx_ci_parent']
    (path) [type: gist, name: 'idx_ci_path_gist']
    (catalog_id) [name: 'idx_ci_catalog']
    (organization_id) [name: 'idx_ci_org']
    (discriminator) [name: 'idx_ci_discriminator']
    (title_en) [name: 'idx_ci_title_en']
    (discriminator, code) [unique, name: 'uq_ci_code_discriminator']
    (catalog_id, code) [unique, name: 'uq_ci_code_catalog']
  }
}

Tabela ci_module {
  id uuid [primary key]
}

Table ci_catalog_category {
  id uuid [primary key]
}

Table ci_country {
  id uuid [primary key]
}

Tabela ci_role {
  id uuid [primary key]
}

Tabela ci_entity_type {
  id uuid [primary key]
  uuid_discriminator char(4) [not null, note: 'código de 4 caracteres incorporado nos UUIDs de entidades']
  is_customizable boolean [not null, default: true, note: 'Se as entidades deste tipo oferecem suporte a Campos customizados']
}

Table ci_device_status {
  id uuid [primary key]
}

Table ci_permission_scope {
  id uuid [primary key]
  module_id uuid [not null]
  entity_type_id uuid [not null]
  category text [not null]
}

Table ci_device_vendor {
  id uuid [primary key, note: 'Fabricantes/fornecedores de dispositivos']
}

Table ci_device_model {
  id uuid [primary key]
  vendor_id uuid [not null]
}

Table ci_device_type {
  id uuid [primary key]
}

Tabela ci_asset_type {
  id uuid [chave primária, note: 'Hierárquico via ci_base parent_id/path']
}

Tabela ci_geo_object_type {
  id uuid [primary key]
}

Tabela ci_schedule_type {
  id uuid [primary key]
}

Tabela ci_asset_group_type {
  id uuid [primary key]
}

Tabela asset_group_type_to_asset_type_relation {
  id uuid [primary key]
  grupo_type_id uuid [não nulo]
  asset_type_id uuid [not null]
  max_items int [nota: 'Máximo de ativos deste tipo no Grupo. NULL = ilimitado']

  índices {
    (group_type_id) [name: 'idx_agt_at_rel_group']
    (asset_type_id) [nome: 'idx_agt_at_rel_asset']
    (group_type_id, asset_type_id) [único]
  }
}

Tabela ci_device_relation_type {
  id uuid [primary key]
}

Tabela ci_tag {
  id uuid [primary key]
  entity_type_id uuid [note: 'Tipo de entidade aplicável. NULL = Etiqueta universal']
}

Table ci_user_catalog_item {
  id uuid [chave primária, nota: 'Elementos de catálogos definidos pelo usuário']
}

Table ci_catalog {
  id uuid [primary key, note: 'Definições de catálogo. Catálogos são, por si só, itens de catálogo']
  organization_id uuid [not null]
  module_id uuid [not null]
  category_id uuid
  fields_schema jsonb [note: 'Esquema JSON para validação de item.extra e interface do usuário']

  índices {
    (organization_id) [name: 'idx_ci_catalog_org']
    (module_id) [name: 'idx_ci_catalog_module']
  }
}

Table ci_custom_field_definition {
  id uuid [primary key]
  owner_catalog_item_id uuid [not null, note: 'Proprietário: entity_type (system) ou um tipo específico, como asset_type']
  target_entity_type_id uuid [not null]
  field_type text [not null, note: 'STRING, TEXT, NUMBER, BOOLEAN, DATE, DATETIME, GEOJSON, SCHEDULE, OPTIONS, DEVICE, REFERENCE, CATALOG, Etiqueta']
  is_required boolean [not null, default: false]
  params jsonb [note: 'Parâmetros específicos do tipo (min/max, opções, ref, etc.)']

  índices {
    (owner_catalog_item_id) [name: 'idx_ci_cfd_owner']
    (target_entity_type_id) [name: 'idx_ci_cfd_target']
  }
}

// ============================================
// ENTIDADES PRINCIPAIS
// ============================================

Organização da tabela {
  id uuid [primary key]
  parent_id uuid
  caminho ltree
  texto de código [não nulo]
  title_en text [not null]
  external_id text [note: 'Identificador do sistema externo para integrações']
  is_active boolean [not null, default: true]
  is_dealer boolean [not null, default: false, note: 'Se a organização pode criar organizações filhas']
  version int [not null, default: 1]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (parent_id) [name: 'idx_org_parent']
    (path) [type: gist, name: 'idx_org_path_gist']
    (code) [unique, name: 'uq_org_code']
    (external_id) [name: 'idx_org_external_id']
    (title_en) [name: 'idx_org_title', note: 'O DDL de origem aponta para uma coluna title inexistente, corrigido aqui para title_en']
  }
}

Table actor {
  id uuid [primary key, note: 'Entidade abstrata para usuários e integrações. Base para ACL']
  actor_type text [not null, note: 'USER, INTEGRATION, or SYSTEM']
  version int [not null, default: 1]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (actor_type) [name: 'idx_actor_type']
  }
}

Table user {
  id uuid [primary key]
  identity_provider text [not null, default: 'keycloak']
  identity_provider_id uuid [not null]
  full_name text [not null]
  external_id text
  is_active boolean [not null, default: true]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (identity_provider, identity_provider_id) [unique, name: 'uq_user_idp']
    (external_id) [name: 'idx_user_external_id']
  }
}

Table member {
  id uuid [primary key, note: 'Associação de um usuário a uma organização']
  user_id uuid [not null]
  organization_id uuid [not null]
  is_active boolean [not null, default: true]
  custom_fields_data jsonb [not null, default: `{}`, note: 'Campos customizados do membro (cargo, departamento, etc.)']
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  assigned_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (user_id) [name: 'idx_member_user']
    (organization_id) [name: 'idx_member_org']
    (user_id, organization_id) [unique, name: 'uq_member_user_org']
  }
}

Table integration {
  id uuid [primary key, note: 'Ator de integração de sistema externo']
  name text [not null]
  credential_ref text [note: 'Referência para credenciais em um cofre seguro']
  is_active boolean [not null, default: true]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid
}

// ============================================
// CONTROLE DE ACESSO (ACL)
// ============================================

Table actor_role {
  id uuid [primary key]
  actor_id uuid [not null]
  role_id uuid [não nulo]
  assigned_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  assigned_by uuid
  expire_date timestamptz [nota: 'NULL = permanente']

  índices {
    (actor_id) [name: 'idx_actor_role_actor']
    (role_id) [name: 'idx_actor_role_role']
    (actor_id, role_id) [único]
  }
}

Table acl_role_permission {
  id uuid [primary key]
  role_id uuid [não nulo]
  permission_scope_id uuid [not null]
  target_entity_id uuid [nota: 'Entidade específica. NULL = todas as entidades do tipo. Polimórfico, sem FK']
  actions int [not null, note: 'Máscara de bits: READ=1, CREATE=2, UPDATE=4, DELETE=8']
  granted_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  granted_by uuid

  índices {
    (role_id) [nome: 'idx_acl_role_perm_role']
    (permission_scope_id) [name: 'idx_acl_role_perm_scope']
    (role_id, permission_scope_id, target_entity_id) [único]
  }
}

Table acl_user_scope {
  id uuid [primary key, note: 'Filtro de whitelist. Vazio = acesso total da Função. Preenchido = interseção com funções']
  actor_id uuid [not null]
  permission_scope_id uuid [not null]
  target_entity_id uuid [not null]
  actions int [not null]

  índices {
    (actor_id, permission_scope_id) [name: 'idx_acl_user_scope_actor']
    (actor_id, permission_scope_id, target_entity_id) [unique]
  }
}

// ============================================
// ENTIDADES DE NEGÓCIO
// ============================================

Tabela device {
  id uuid [primary key]
  organization_id uuid [not null]
  device_type_id uuid [not null]
  model_id uuid [note: 'Opcional']
  status_id uuid [not null]
  title text [not null]
  custom_fields_data jsonb [not null, default: `{}`]
  version int [not null, default: 1]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (organization_id) [name: 'idx_device_org']
    (device_type_id) [name: 'idx_device_type']
    (model_id) [name: 'idx_device_model']
    (status_id) [name: 'idx_device_status']
    (title) [type: gin, name: 'idx_device_title_trgm', note: 'gin_trgm_ops for fuzzy search']
  }
}

Tabela device_identifier {
  id uuid [primary key]
  device_id uuid [not null]
  type text [not null, note: 'UUID, IMEI, MEID_HEX, MEID_DEC, MAC_ADDRESS, SERIAL_NUMBER, CUSTOM']
  value text [not null]
  namespace text [note: 'Escopo de unicidade. NULL = global']
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]

  índices {
    (device_id) [name: 'idx_device_identifier_device']
    (value) [name: 'idx_device_identifier_value']
    (type, value) [unique, name: 'uq_device_identifier_global', note: 'parcial: WHERE namespace IS NULL']
    (type, value, namespace) [unique, name: 'uq_device_identifier_namespaced', note: 'parcial: WHERE namespace IS NOT NULL']
  }
}

Table device_relation {
  id uuid [primary key]
  first_id uuid [not null]
  second_id uuid [not null]
  relation_type_id uuid [not null, note: 'Restrição de verificação: first_id <> second_id']

  índices {
    (first_id) [name: 'idx_device_relation_first']
    (second_id) [name: 'idx_device_relation_second']
    (first_id, second_id, relation_type_id) [unique]
  }
}

Table inventory {
  id uuid [primary key]
  organization_id uuid [not null]
  title text [not null]
  texto de código [não nulo]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (organization_id) [name: 'idx_inventory_org']
    (organization_id, code) [unique, name: 'uq_inventory_org_code']
  }
}

Table device_inventory_relation {
  id uuid [primary key]
  device_id uuid [not null]
  inventory_id uuid [not null]
  assigned_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  assigned_by uuid
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (device_id) [unique, name: 'uq_device_inventory_active', note: 'parcial: WHERE deleted_at IS NULL - um inventário ativo por dispositivo']
    (inventory_id) [name: 'idx_device_inventory_rel_inventory']
  }
}

Tabela Ativo {
  id uuid [primary key]
  organization_id uuid [not null]
  asset_type_id uuid [not null]
  title text [not null]
  custom_fields_data jsonb [not null, default: `{}`]
  version int [not null, default: 1]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (organization_id) [nome: 'idx_asset_org']
    (asset_type_id) [nome: 'idx_asset_type']
    (título) [tipo: gin, nome: 'idx_asset_title_trgm']
  }
}

Table ativo_grupo {
  id uuid [primary key]
  organization_id uuid [not null]
  grupo_type_id uuid [não nulo]
  title_en text [not null]
  texto colorido
  version int [not null, default: 1]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (organization_id) [name: 'idx_asset_group_org']
    (group_type_id) [name: 'idx_asset_group_type']
  }
}

Tabela asset_group_item {
  id uuid [primary key]
  group_id uuid [não nulo]
  Ativo_id uuid [not null]
  attached_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  detached_at timestamptz [nota: 'NULL = atualmente anexado']

  índices {
    (group_id) [nome: 'idx_asset_group_item_group']
    (asset_id) [nome: 'idx_asset_group_item_asset']
    (Grupo_id, Ativo_id, detached_at) [único]
  }
}

Tabela geo_object {
  id uuid [chave primária, observação: 'Geocercas, pontos de interesse, rotas']
  organization_id uuid [not null]
  geo_object_type_id uuid [não nulo]
  title text [not null]
  custom_fields_data jsonb [not null, default: `{}`, note: 'Inclui geometria geojson']
  version int [not null, default: 1]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (organization_id) [name: 'idx_geo_object_org']
    (geo_object_type_id) [nome: 'idx_geo_object_type']
    (título) [tipo: gin, nome: 'idx_geo_object_title_trgm']
  }
}

Agendamento da tabela {
  id uuid [primary key]
  organization_id uuid [not null]
  schedule_type_id uuid [not null]
  title text [not null]
  custom_fields_data jsonb [not null, default: `{}`, note: 'Os dados de agendamento ficam aqui']
  version int [not null, default: 1]
  created_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  updated_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]
  deleted_at timestamptz
  deleted_by uuid

  índices {
    (organization_id) [name: 'idx_schedule_org']
    (schedule_type_id) [name: 'idx_schedule_type']
    (title) [type: gin, name: 'idx_schedule_title_trgm']
  }
}

// ============================================
// LOCALIZAÇÃO
// ============================================

Table i18n_text {
  entity_id uuid [pk]
  field_code text [pk]
  locale text [pk]
  value text [not null]

  índices {
    (entity_id) [name: 'idx_i18n_entity']
    (locale) [name: 'idx_i18n_locale']
    (value) [type: gin, name: 'idx_i18n_text_trgm']
  }
}

// ============================================
// Campos customizados - VALORES (tabelas de cache, uma por field_type)
// ============================================

Table custom_field_value_text {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  value text [not null]

  índices {
    (field_definition_id, value) [name: 'idx_cfv_text_value']
  }
}

Tabela custom_field_value_number {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  value numeric [not null]
}

Tabela custom_field_value_boolean {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  value boolean [not null]
}

Tabela custom_field_value_date {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  value date [not null]
}

Tabela custom_field_value_datetime {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  value timestamptz [not null]
}

Tabela custom_field_value_geojson {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  value jsonb [not null]
}

Tabela custom_field_value_schedule {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  value jsonb [not null]
}

Tabela custom_field_value_option {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  ref_item_id uuid [not null, note: 'Referências ci_user_catalog_item']
}

Tabela custom_field_value_device {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  ref_device_id uuid [not null]
}

Tabela custom_field_value_entity {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  ref_entity_id uuid [not null, note: 'Referência polimórfica, sem FK']
}

Tabela custom_field_value_catalog {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  ref_item_id uuid [not null, note: 'Referências ci_base']
}

Tabela custom_field_value_tag {
  entity_id uuid [pk]
  field_definition_id uuid [pk]
  value_index smallint [pk, default: 0]
  ref_tag_id uuid [não nulo]
}

// ============================================
// AUDITORIA
// ============================================

Enum source_type {
  WEB
  MÓVEL
  API
  INTERNO
  Integração
}

Table audit_event {
  id uuid [not null]
  organization_id uuid
  event_category text [not null, nota: 'auth or domain']
  actor_id uuid
  ip_address inet
  texto user_agent
  source_type source_type [not null, default: 'API']
  source_name texto [nota: 'Nome do aplicativo de origem']
  trace_id uuid [note: 'ID de rastreamento distribuído para correlação de logs']
  aggregate_type text [note: 'Tipo de entidade: dispositivo, Ativo, usuário, etc.']
  aggregate_id uuid [note: 'Polymorphic, no FK']
  event_type text [not null, nota: 'CREATED, UPDATED, DELETED, RESTORED, ROLE_ASSIGNED, etc.']
  event_data jsonb [nota: 'Carga útil com delta de changed_fields']
  occurred_at timestamptz [not null, default: `CURRENT_TIMESTAMP`]

  Observação: 'Particionado POR RANGE (occurred_at), partições mensais, criado automaticamente via create_audit_partition_if_needed()'

  índices {
    (id, occurred_at) [pk]
    (actor_id, occurred_at) [name: 'idx_audit_event_actor']
    (aggregate_type, aggregate_id, occurred_at) [name: 'idx_audit_event_aggregate']
    (event_category, occurred_at) [name: 'idx_audit_event_category']
    (event_type, occurred_at) [name: 'idx_audit_event_type']
    (organization_id, occurred_at) [name: 'idx_audit_event_org']
    (trace_id) [name: 'idx_audit_event_trace']
  }
}

// ============================================
// RELACIONAMENTOS
// ============================================

Ref: ci_base.catalog_id > ci_catalog.id
Ref: ci_base.organization_id > organization.id
Ref.: ci_base.parent_id > ci_base.id

Referência: ci_module.id - ci_base.id
Ref.: ci_catalog_category.id - ci_base.id
Ref: ci_country.id - ci_base.id
Ref: ci_role.id - ci_base.id
Ref: ci_entity_type.id - ci_base.id
Ref: ci_device_status.id - ci_base.id
Ref.: ci_permission_scope.id - ci_base.id
Ref.: ci_device_vendor.id - ci_base.id
Ref: ci_device_model.id - ci_base.id
Ref: ci_device_type.id - ci_base.id
Ref: Ativo ci_asset_type.id - ci_base.id
Ref: ci_geo_object_type.id - ci_base.id
Ref: ci_schedule_type.id - ci_base.id
Ref: Grupo ci_asset_group_type.id - ci_base.id
Ref: ci_device_relation_type.id - ci_base.id
Ref: Etiqueta ci_tag.id - ci_base.id
Ref: ci_user_catalog_item.id - ci_base.id
Ref.: ci_catalog.id - ci_base.id
Ref.: ci_custom_field_definition.id - ci_base.id

Ref.: ci_device_model.vendor_id > ci_device_vendor.id
Ref: ci_permission_scope.module_id > ci_module.id
Ref: ci_permission_scope.entity_type_id > ci_entity_type.id
Ref: asset_group_type_to_asset_type_relation.group_type_id > ci_asset_group_type.id
Ref: asset_group_type_to_asset_type_relation.asset_type_id > ci_asset_type.id
Ref: ci_tag.entity_type_id > ci_entity_type.id
Ref: ci_catalog.organization_id > organization.id
Ref: ci_catalog.module_id > ci_module.id
Ref: ci_catalog.category_id > ci_catalog_category.id
Ref: ci_custom_field_definition.owner_catalog_item_id > ci_base.id
Ref.: ci_custom_field_definition.target_entity_type_id > ci_entity_type.id

Ref: organization.parent_id > organization.id
Ref: organization.deleted_by > actor.id

Ref: actor.deleted_by > actor.id

Ref: user.id - actor.id
Ref: user.deleted_by > actor.id

Ref: member.user_id > user.id
Ref: member.organization_id > organization.id
Ref: member.deleted_by > actor.id

Ref.: integration.id - actor.id
Ref: integration.deleted_by > actor.id

Ref: actor_role.actor_id > actor.id
Ref: actor_role.role_id > ci_role.id
Ref: actor_role.assigned_by > actor.id

Ref: acl_role_permission.role_id > ci_role.id
Ref: acl_role_permission.permission_scope_id > ci_permission_scope.id
Ref: acl_role_permission.granted_by > actor.id

Ref: acl_user_scope.actor_id > actor.id
Ref.: acl_user_scope.permission_scope_id > ci_permission_scope.id

Ref: device.organization_id > organization.id
Ref: device.device_type_id > ci_device_type.id
Ref.: device.model_id > ci_device_model.id
Ref: device.status_id > ci_device_status.id
Ref: device.deleted_by > actor.id

Ref: device_identifier.device_id > device.id

Ref: device_relation.first_id > device.id
Ref.: device_relation.second_id > device.id
Ref: device_relation.relation_type_id > ci_device_relation_type.id

Ref: inventory.organization_id > organization.id
Ref: inventory.deleted_by > actor.id

Ref: device_inventory_relation.device_id > device.id
Ref.: device_inventory_relation.inventory_id > inventory.id
Ref: device_inventory_relation.assigned_by > actor.id
Ref: device_inventory_relation.deleted_by > actor.id

Ref: Ativo.organization_id > organization.id
Ref: asset.asset_type_id > ci_asset_type.id
Ref: Ativo.deleted_by > actor.id

Ref: asset_group.organization_id > organization.id
Ref: asset_group.group_type_id > ci_asset_group_type.id
Ref: asset_group.deleted_by > actor.id

Ref: asset_group_item.group_id > asset_group.id
Ref: asset_group_item.asset_id > asset.id

Ref: geo_object.organization_id > organization.id
Ref: geo_object.geo_object_type_id > ci_geo_object_type.id
Ref.: geo_objeto.deleted_by > actor.id

Ref: schedule.organization_id > organization.id
Ref: schedule.schedule_type_id > ci_schedule_type.id
Ref: schedule.deleted_by > actor.id

Ref: custom_field_value_text.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_number.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_boolean.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_date.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_datetime.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_geojson.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_schedule.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_option.field_definition_id > ci_custom_field_definition.id
Referência: custom_field_value_option.ref_item_id > ci_user_catalog_item.id
Ref: custom_field_value_device.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_device.ref_device_id > device.id
Ref: custom_field_value_entity.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_catalog.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_catalog.ref_item_id > ci_base.id
Ref: custom_field_value_tag.field_definition_id > ci_custom_field_definition.id
Ref: custom_field_value_tag.ref_tag_id > ci_tag.id

Ref: audit_event.organization_id > organization.id
Ref: audit_event.actor_id > actor.id
```

{% endcode %}

## Frequência de atualização

Os dados no BDR são sincronizados em tempo real com os sistemas de origem. As atualizações ocorrem imediatamente à medida que as mudanças acontecem, com trilhas de auditoria registrando todas as modificações para conformidade e análise histórica.

## `ci_base`

BDR usa uma **herança de tabela única** padrão para todos os dados de referência por meio da `ci_base` tabela. Esse design consolida dicionários do sistema, classificações e itens de referência definidos pelo usuário em uma estrutura unificada, proporcionando consistência e flexibilidade em todo o esquema.

**Arquitetura:**

O `ci_base` tabela serve como a base para todos os dados de referência, usando um `discriminador` campo para identificar o tipo específico de referência. Cada tipo de referência tem uma tabela correspondente (como `ci_device_type`, `ci_asset_type`) que compartilha o mesmo `id` como `ci_base`, criando uma relação de herança segura quanto ao tipo. O `discriminador` não é definido manualmente. Um gatilho o deriva automaticamente a partir do código de tipo de 4 caracteres incorporado no UUID da entidade, e ele falha na inserção se o código de tipo do UUID não for reconhecido.

Hierarquias (por exemplo, tipos de Ativo aninhados ou Etiquetas) são tratadas diretamente em `ci_base` por meio de seu próprio `parent_id`/`path` colunas em vez de por meio de tabelas de categorias separadas. Uma única `is_hierarchical` indicador ativa isso por item de catálogo.

**Como as entidades de negócio se conectam a ci\_base:**

Todas as entidades de negócio no BDR fazem referência a `ci_base` subtipos para definir sua classificação e comportamento:

* `organization` e `user`/`membro` são *não* digitados por `ci_base`, já que organizações e usuários são entidades centrais por si só, não classificadas por um item do catálogo
* `dispositivo` → referencia `ci_device_type`, opcionalmente `ci_device_model` (que por sua vez referencia `ci_device_vendor`), e `ci_device_status`
* `Ativo` → referencia `ci_asset_type`
* `Ativo_Grupo` → referencia `ci_asset_group_type`, com os tipos de membro permitidos restritos por meio de `relação do tipo de grupo de Ativo para o tipo de Ativo`
* `geo_objeto` → referencia `ci_geo_object_type`
* `agendamento` → referencia `ci_schedule_type`
* `inventário` não possui um tipo de item de catálogo próprio

**Categorias de tipos de referência:**

| Categoria                           | Tabelas                                                                                                              | Finalidade                                                                                               |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| **Configuração do sistema**         | `ci_module`, `ci_country`, `ci_Função`                                                                               | Defina módulos do sistema, referências geográficas e funções de usuário                                  |
| **Definições de tipos de entidade** | `ci_entity_type`, `ci_device_type`, `ci_asset_type`, `ci_geo_object_type`, `ci_schedule_type`, `ci_asset_group_type` | Classifique todas as entidades de negócios por tipo                                                      |
| **Catálogo de dispositivos**        | `ci_device_vendor`, `ci_device_model`, `ci_device_status`                                                            | Modele dispositivos por fabricante, modelo e status atual                                                |
| **Controle de acesso**              | `ci_permission_scope`                                                                                                | Defina quais permissões podem ser concedidas (conectadas a `ci_module` e `ci_entity_type`)               |
| **Relacionamentos**                 | `ci_device_relation_type`                                                                                            | Defina tipos de relacionamentos entre dispositivos (mestre-escravo, backup, etc.)                        |
| **Categorização**                   | `ci_etiqueta`, `ci_catalog_category`, `ci_user_catalog_item`                                                         | Ative marcação flexível e catálogos definidos pelo usuário                                               |
| **Catálogo / Campos customizados**  | `ci_catalog`, `ci_custom_field_definition`                                                                           | Catálogo de catálogos e metadados de campos personalizados, ambos modelados como itens de catálogo em si |

<details>

<summary><strong>Exemplos de padrões de consulta</strong></summary>

```sql
-- Obter todos os tipos de dispositivo de uma organização (sistema + personalizado)
SELECT cb.id, cb.code, cb.title_en, cb.is_system
FROM bdr.ci_base cb
JOIN bdr.ci_device_type dt ON dt.id = cb.id
WHERE cb.discriminator = 'device_type'
  AND (cb.is_system = true OR cb.organization_id = $org_id)
  AND cb.deleted_at IS NULL;

-- Obter modelos de dispositivo com seu fornecedor
SELECT
  cb.code as model_code,
  cb.title_en as model_name,
  vendor_cb.title_en as vendor_name
FROM bdr.ci_base cb
JOIN bdr.ci_device_model dm ON dm.id = cb.id
JOIN bdr.ci_device_vendor v ON v.id = dm.vendor_id
JOIN bdr.ci_base vendor_cb ON vendor_cb.id = v.id
WHERE cb.discriminator = 'device_model'
  AND cb.deleted_at IS NULL;

-- Obter estrutura hierárquica de etiquetas
SELECT cb.id, cb.code, cb.title_en, cb.path, cb.parent_id
FROM bdr.ci_base cb
JOIN bdr.ci_tag t ON t.id = cb.id
ONDE cb.discriminator = 'tag'
  AND cb.deleted_at IS NULL
ORDER BY cb.path;
```

</details>

## Principais tabelas por categoria

As tabelas no BDR são organizadas em categorias funcionais. As descrições abaixo resumem as tabelas mais importantes por sua finalidade de negócio.

<details>

<summary><code>organization</code></summary>

**Finalidade:** Gestão hierárquica da organização

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code>, <code>parent_id</code>, <code>path</code>, <code>code</code>, <code>title_en</code>, <code>is_active</code>, <code>is_dealer</code>, <code>version</code>, <code>deleted_at</code></td></tr><tr><td><strong>Indexação</strong></td><td>índice GiST em <code>path</code> para consultas hierárquicas, índice exclusivo em <code>code</code>, índice em <code>parent_id</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>Usa ltree para hierarquias de vários níveis. <code>is_dealer</code> concede o direito de criar organizações filhas. <code>version</code> oferece suporte a bloqueio otimista</td></tr></tbody></table>

</details>

<details>

<summary><code>ator</code></summary>

**Finalidade:** Abstração compartilhada por todo principal do ACL: usuários, integrações e futuros atores do sistema

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code>, <code>actor_type</code> (<code>USUÁRIO</code>, <code>Integração</code>, <code>SISTEMA</code>), <code>version</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>Todas as tabelas de ACL (<code>actor_role</code>, <code>acl_role_permission</code>, <code>acl_user_scope</code>) e cada <code>deleted_by</code> chave da coluna desativada <code>actor_id</code>, não <code>user_id</code>, então as integrações também podem ter funções e excluir registros</td></tr></tbody></table>

</details>

<details>

<summary><code>user</code> / <code>membro</code></summary>

**Finalidade:** Contas de usuário e suas afiliações à organização

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>user</code>: <code>id</code> (compartilhado com <code>ator</code>), <code>identity_provider</code>, <code>identity_provider_id</code>, <code>full_name</code>. <code>membro</code>: <code>user_id</code>, <code>organization_id</code>, <code>custom_fields_data</code></td></tr><tr><td><strong>Indexação</strong></td><td>Índice exclusivo em (<code>identity_provider</code>, <code>identity_provider_id</code>). Índice exclusivo em (<code>user_id</code>, <code>organization_id</code>) em <code>membro</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>A associação de um usuário à organização é uma linha separada em <code>membro</code>, então um usuário pode pertencer a várias organizações. Os usuários se autenticam por meio de um provedor externo de identidade (Keycloak por padrão)</td></tr></tbody></table>

</details>

<details>

<summary><code>integração</code></summary>

**Finalidade:** Sistemas externos que atuam na plataforma (por exemplo, por meio de Chaves de API)

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code> (compartilhado com <code>ator</code>), <code>nome</code>, <code>credential_ref</code>, <code>is_active</code></td></tr><tr><td><strong>Notas especiais</strong></td><td><code>credential_ref</code> aponta para um cofre externo seguro, então nenhuma credencial é armazenada nesta tabela</td></tr></tbody></table>

</details>

<details>

<summary><code>dispositivo</code> / <code>device_identifier</code></summary>

**Finalidade:** Dispositivos físicos de rastreamento e seus identificadores de hardware

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>dispositivo</code>: <code>id</code>, <code>organization_id</code>, <code>device_type_id</code>, <code>model_id</code>, <code>status_id</code>, <code>title</code>, <code>custom_fields_data</code>, <code>version</code>. <code>device_identifier</code>: <code>device_id</code>, <code>tipo</code>, <code>valor</code>, <code>namespace</code></td></tr><tr><td><strong>Indexação</strong></td><td>Índice trigram GIN em <code>title</code> para busca difusa. Índice exclusivo em (<code>tipo</code>, <code>valor</code>) para identificadores globais, ou (<code>tipo</code>, <code>valor</code>, <code>namespace</code>) quando houver namespace</td></tr><tr><td><strong>Notas especiais</strong></td><td>Um dispositivo pode carregar vários identificadores (IMEI, MAC, número de série etc.) em vez de uma única coluna de ID de hardware. Campos customizados ficam diretamente em <code>custom_fields_data</code> na linha do dispositivo</td></tr></tbody></table>

</details>

<details>

<summary><code>Ativo</code></summary>

**Finalidade:** Ativos físicos ou virtuais

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code>, <code>organization_id</code>, <code>Ativo_type_id</code>, <code>title</code>, <code>custom_fields_data</code>, <code>version</code></td></tr><tr><td><strong>Indexação</strong></td><td>Índices em <code>organization_id</code> e <code>Ativo_type_id</code>. índice de trigramas GIN em <code>title</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>Campos customizados são armazenados diretamente na linha, não por meio de uma tabela separada de entidade personalizável</td></tr></tbody></table>

</details>

<details>

<summary><code>inventário</code> / <code>device_inventory_relation</code></summary>

**Finalidade:** Registros de inventário e de armazém, e atribuição de dispositivos ao inventário

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>inventário</code>: <code>id</code>, <code>organization_id</code>, <code>title</code>, <code>code</code>. <code>device_inventory_relation</code>: <code>device_id</code>, <code>inventory_id</code>, <code>assigned_at</code>, <code>deleted_at</code></td></tr><tr><td><strong>Indexação</strong></td><td>Índice exclusivo em (<code>organization_id</code>, <code>code</code>). Um índice único parcial garante que um dispositivo tenha no máximo uma atribuição de inventário ativa</td></tr><tr><td><strong>Notas especiais</strong></td><td>A atribuição pode ser excluída logicamente, portanto o histórico do inventário de dispositivos é preservado</td></tr></tbody></table>

</details>

<details>

<summary><code>geo_objeto</code></summary>

**Finalidade:** Geocercas, pontos de interesse e rotas

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code>, <code>organization_id</code>, <code>geo_object_type_id</code>, <code>title</code>, <code>custom_fields_data</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>Geometria (anteriormente uma dedicada <code>geojson</code> coluna) agora é armazenada como um campo personalizado dentro de <code>custom_fields_data</code></td></tr></tbody></table>

</details>

<details>

<summary><code>agendamento</code></summary>

**Finalidade:** Agendamentos reutilizáveis referenciados por outras entidades

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code>, <code>organization_id</code>, <code>schedule_type_id</code>, <code>title</code>, <code>custom_fields_data</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>Os dados de agendamento em si (regras de recorrência, janelas de tempo) são armazenados em <code>custom_fields_data</code></td></tr></tbody></table>

</details>

<details>

<summary><code>Ativo_Grupo</code></summary>

**Finalidade:** Agrupamento de Ativo com Monitor histórico

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code>, <code>organization_id</code>, <code>group_type_id</code>, <code>title_en</code>, <code>cor</code></td></tr><tr><td><strong>Relacionamentos</strong></td><td><code>FROM bdr.asset_group AS ag JOIN bdr.asset_group_item AS agi ON agi.group_id = ag.id JOIN bdr.asset AS a ON a.id = agi.asset_id WHERE agi.detached_at IS NULL</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>Associação baseada em tempo via <code>ativo_grupo_item</code>, consulte os membros atuais com <code>WHERE detached_at IS NULL</code>. Tipos de membros permitidos por tipo de Grupo são restringidos por meio de <code>relação do tipo de grupo de Ativo para o tipo de Ativo</code></td></tr></tbody></table>

</details>

<details>

<summary><code>ci_custom_field_definition</code></summary>

**Finalidade:** Definições de campos personalizados e metadados, modelados como um item de catálogo

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code> (compartilhado com <code>ci_base</code>), <code>Proprietário_catalog_item_id</code>, <code>target_entity_type_id</code>, <code>field_type</code>, <code>is_required</code>, <code>params</code></td></tr><tr><td><strong>Conteúdo</strong></td><td>12 tipos de campo: <code>STRING</code>, <code>TEXT</code>, <code>NUMBER</code>, <code>BOOLEAN</code>, <code>DATE</code>, <code>DATETIME</code>, <code>GeoJSON</code>, <code>Agendamento</code>, <code>Opções</code>, <code>Dispositivo</code>, <code>Referência</code>, <code>Catálogo</code>, <code>Etiqueta</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>Os valores são armazenados tanto como a fonte da verdade (<code>custom_fields_data</code> JSONB na entidade proprietária) e, por tipo, em um <code>custom_field_value_*</code> tabela de cache para filtragem/ordenação. <code>Opções</code> é um wrapper em torno de <code>Catálogo</code>, e cada <code>Opções</code> o campo recebe seu próprio catálogo oculto de usuários internamente</td></tr></tbody></table>

</details>

<details>

<summary><code>acl_role_permission</code></summary>

**Finalidade:** Gerenciamento de permissões baseado em Função

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code>, <code>id da Função</code>, <code>permission_scope_id</code>, <code>target_entity_id</code>, <code>ações</code></td></tr><tr><td><strong>Conteúdo</strong></td><td>Bitmask de ação (READ=1, CREATE=2, UPDATE=4, DELETE=8), permissões específicas do alvo ou abrangentes do tipo de entidade</td></tr><tr><td><strong>Relacionamentos</strong></td><td><code>FROM bdr.actor_role AS ar JOIN bdr.acl_role_permission AS rp ON rp.role_id = ar.role_id WHERE ar.actor_id = $actor_id</code></td></tr><tr><td><strong>Notas especiais</strong></td><td>Permissões efetivas = permissões da Função ∩ <code>acl_user_scope</code>. De um ator <code>acl_user_scope</code> é um filtro de lista de permissões: vazio significa acesso irrestrito à Função, preenchido significa que o acesso é restringido aos objetos listados</td></tr></tbody></table>

</details>

<details>

<summary><code>audit_event</code></summary>

**Finalidade:** Registro de auditoria unificado para todas as alterações do sistema

<table><thead><tr><th width="139">Atributo</th><th>Detalhes</th></tr></thead><tbody><tr><td><strong>Campos principais</strong></td><td><code>id</code>, <code>organization_id</code>, <code>event_category</code>, <code>actor_id</code>, <code>source_type</code>, <code>Rastreamento_id</code>, <code>aggregate_type</code>, <code>aggregate_id</code>, <code>event_type</code>, <code>event_data</code>, <code>occurred_at</code></td></tr><tr><td><strong>Indexação</strong></td><td>Índices em (<code>actor_id</code>, <code>occurred_at</code>), (<code>aggregate_type</code>, <code>aggregate_id</code>, <code>occurred_at</code>), (<code>event_category</code>, <code>occurred_at</code>), (<code>organization_id</code>, <code>occurred_at</code>)</td></tr><tr><td><strong>Notas especiais</strong></td><td>Particionado fisicamente por <code>occurred_at</code> (partições de intervalo mensais, criadas automaticamente). <code>event_category</code> é <code>auth</code> (LOGIN, LOGOUT, FAILED_LOGIN, PASSWORD_RESET, SESSION_EXPIRED) ou <code>domain</code> (CREATED, UPDATED, DELETED, RESTORED, ROLE_ASSIGNED, ROLE_REVOKED, PERMISSION_GRANTED, PERMISSION_REVOKED, LINKED, UNLINKED, ATTACHED, DETACHED). <code>source_type</code> e <code>Rastreamento_id</code> oferece suporte a rastreamento distribuído, e a tabela armazena deltas de alterações no nível de campo em <code>event_data</code> JSONB</td></tr></tbody></table>

</details>

## Relacionamentos de dados

BDR implementa padrões sofisticados de relacionamento para modelagem de dados flexível:

**Estruturas hierárquicas**

* Organizações usam caminhos ltree para consultas de árvore eficientes
* Itens de referência (`ci_base`) oferecem suporte a hierarquias opcionais por meio de suas próprias `parent_id`/`path`, controlado por `is_hierarchical`
* Manutenção automática do caminho via gatilhos de banco de dados

**Padrões de herança**

* Herança de ID: `ci_base` → tabelas do tipo referência (`ci_device_type`, `ci_asset_type`, etc.), e `ator` → `user` e `integração`
* Campos customizados são vinculados por entidade via uma `custom_fields_data` coluna JSONB diretamente em cada tabela de negócio (`dispositivo`, `Ativo`, `geo_objeto`, `agendamento`), em vez de por meio de uma tabela de entidade base compartilhada
* Discriminação de tipo por meio do `discriminador` campo em `ci_base`, derivado automaticamente do UUID da entidade

**Bloqueio otimista**

Entidades de negócios e `ci_base` as linhas carregam um `version` inteiro. As atualizações usam `SET version = version + 1 WHERE id = $id AND version = $expected_version`. Zero linhas afetadas sinaliza um conflito de versão, permitindo que os clientes detectem e resolvam edições concorrentes sem bloqueios no nível do banco de dados. `audit_event` e outras tabelas append-only ou operadas em massa não usam esse padrão.

**Relacionamentos polimórficos**

Algumas tabelas usam referências polimórficas sem restrições de chave estrangeira para máxima flexibilidade:

* `acl_role_permission.target_entity_id` e `acl_user_scope.target_entity_id` → qualquer entidade de negócio
* `audit_event.aggregate_id` → qualquer entidade de negócio, pareada com `aggregate_type`
* `custom_field_value_entity.ref_entity_id` → qualquer entidade de negócio

Essas relações são validadas no nível da aplicação.

## Informações adicionais

### Validação de dados

BDR garante a integridade dos dados por meio de vários mecanismos:

**Restrições de banco de dados**

* Restrições UNIQUE com suporte a exclusão lógica (índices parciais WHERE `deleted_at` IS NULL)
* Restrições CHECK (por exemplo, `device_relation` garante `first_id` ≠ `second_id`, e `ci_base.code` deve começar com uma letra)
* Restrições NOT NULL em campos obrigatórios
* Valores DEFAULT para timestamps, booleanos e colunas JSONB

**Validação no nível da aplicação**

* Validação do tipo de entidade para referências polimórficas
* Validação do catálogo para referências de campos personalizados
* Validação do tipo de campo personalizado
* Verificações de versão de bloqueio otimista na atualização

### Otimização de consultas

As tabelas são organizadas com estratégias específicas de indexação:

**Índices padrão:**

* Todas as chaves estrangeiras têm índices dedicados
* Índices baseados em tempo em `created_at`, `updated_at`, `deleted_at`
* Índices compostos para colunas frequentemente associadas

**Índices especializados:**

* Índices GiST em caminhos ltree para consultas hierárquicas
* Índices únicos parciais que oferecem suporte à exclusão lógica
* trigrama GIN (`pg_trgm`) índices para busca difusa em `title`/`title_en` e texto traduzido
* Índices de valores de campos personalizados para filtragem e ordenação
* Índices de eventos de auditoria por tempo + entidade para consultas eficientes

**Considerações de desempenho:**

* Pooling de conexões recomendado (PgBouncer)
* Manutenção regular do VACUUM para tabelas grandes
* `audit_event` é particionada fisicamente por mês, portanto partições mais antigas podem ser desanexadas ou arquivadas independentemente
* Views materializadas para cálculos complexos de controle de acesso


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://navixy.com/docs/analytics/pt-br/iot-query/schema-overview/bronze-layer/bdr.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
