UNPKG

@horizon-apps/domain-schema-core

Version:

Core domain schema utilities for Horizon Platform - Schema generators, data enrichers, converters and specifications

818 lines (689 loc) 22.6 kB
# 🏗️ Formato de Schema Horizon - Especificação Completa ## 📋 Resumo Executivo Este documento define o formato padrão para campos de schema no sistema Horizon. É um formato **minimalista, inteligente e pragmático** projetado para suportar 90+ campos sem redundância. ### Princípios Base - **Minimalista**: Apenas 3-7 propriedades por campo em média - **Inferência Inteligente**: Sistema deduz configurações automaticamente - **Separação Clara**: Validação frontend Constraints de banco - **Zero Redundância**: Nunca repetir informação --- ## 🚀 Quick Start ### Campo Básico (apenas 4 propriedades) ```javascript { "key": "valor_venda", "type": "Number", "label": "Valor de Venda", "origin": "horizon-base/property" } ``` ### Campo Completo (seguindo ordem padrão) ```javascript { "key": "dormitorios", "label": "Dormitórios", "description": "Número de dormitórios do imóvel", "type": "Number", "format": "count", "validation": { "min": 0, "max": 20 }, "filterable": true, "composedLabel": "{{value}} dormitório{{p:s}}", "icon": "bedroom", "placeholder": "Ex: 3", "categories": ["dependencias", "principais"], "origin": "horizon-base/property" } ``` --- ## 🏗️ Ordem Padrão de Propriedades **SEMPRE siga esta ordem ao definir campos:** ### 1. **IDENTIFICAÇÃO BÁSICA** 1. `key` - Identificador único 2. `label` - Nome para UI 3. `description` - Texto de ajuda/helper para formulários 4. `enum` - Valores possíveis 5. `type` - Tipo de dado ### 2. **FORMATAÇÃO BÁSICA** 6. `format` - Como formatar na exibição 7. `unit` - Unidade de medida ### 3. **VALIDAÇÃO E BANCO (backend)** 8. `validation` - Regras de validação 9. `db` - Configurações específicas do banco ### 4. **COMPORTAMENTO DE BUSCA** 10. `searchable` - Pesquisável por texto 11. `filterable` - Aparece nos filtros 12. `sortable` - Pode ordenar ### 5. **RELAÇÕES E DEPENDÊNCIAS** 13. `parent` - Campo pai hierárquico 14. `conditions` - Condições de visibilidade ### 6. **DISPLAY/UI (camada frontend)** 15. `value` - Valor atual do campo (adicionado na camada de display) 16. `valueLabel` - Valor formatado para exibição (adicionado na camada de display) 17. `composedLabel` - Template de exibição 18. `icon` - Ícone semântico ### 7. **FORMULÁRIOS (específico para inputs)** 19. `mask` - Máscara/validação específica 20. `placeholder` - Texto de exemplo no input ### 8. **CATEGORIZAÇÃO** 21. `categories` - Categorias múltiplas ### 9. **AUDITORIA** 22. `origin` - Origem do campo 23. `modifiedBy` - Histórico de modificações --- ## 📝 **LISTA COMPLETA DE METADADOS (23 total)** **TODOS os metadados definidos na especificação:** 1. **`key`** - Identificador único 2. **`label`** - Nome para UI 3. **`description`** - Texto de ajuda/helper para formulários 4. **`enum`** - Valores possíveis 5. **`type`** - Tipo de dado 6. **`format`** - Como formatar na exibição 7. **`unit`** - Unidade de medida 8. **`validation`** - Regras de validação 9. **`db`** - Configurações específicas do banco 10. **`searchable`** - Pesquisável por texto 11. **`filterable`** - Aparece nos filtros 12. **`sortable`** - Pode ordenar 13. **`parent`** - Campo pai hierárquico 14. **`conditions`** - Condições de visibilidade 15. **`value`** - Valor atual do campo *(adicionado na camada de display)* 16. **`valueLabel`** - Valor formatado para exibição *(adicionado na camada de display)* 17. **`composedLabel`** - Template de exibição 18. **`icon`** - Ícone semântico 19. **`mask`** - Máscara/validação específica 20. **`placeholder`** - Texto de exemplo no input 21. **`categories`** - Categorias múltiplas 22. **`origin`** - Origem do campo 23. **`modifiedBy`** - Histórico de modificações **Exemplo seguindo a ordem:** ```javascript { "key": "valor_venda", "label": "Valor de Venda", "description": "Preço de venda do imóvel em Reais", "type": "Number", "format": "currency", "unit": "BRL", "validation": { "min": 0, "max": 999999999.99, "precision": 2 }, "filterable": true, "placeholder": "R$ 0,00", "categories": ["valores", "principais"], "origin": "horizon-base/property" } ``` --- ## 📚 Referência Completa ### 1. CAMPOS OBRIGATÓRIOS | Campo | Tipo | Descrição | Exemplo | |-------|------|-----------|---------| | `key` | String | Identificador único (snake_case) | `"valor_venda"` | | `label` | String | Nome para UI | `"Valor de Venda"` | | `type` | String | Tipo Prisma | `"String"`, `"Number"`, `"String[]"` | | `origin` | String | Origem do campo | `"horizon-base/property"` | ### 2. FORMATAÇÃO E DISPLAY | Campo | Tipo | Descrição | Quando Usar | |-------|------|-----------|-------------| | `format` | String | Como formatar na exibição | Quando muda a forma de mostrar | | `unit` | String | Unidade de medida | Com format currency/area | | `mask` | String | Máscara/validação específica | Para CPF, phone, email | | ~~`icon`~~ | ~~String~~ | ~~Ícone semântico~~ | **REMOVIDO** - Será adicionado na camada de display | | ~~`composedLabel`~~ | ~~String~~ | ~~Template de exibição~~ | **REMOVIDO** - Será adicionado na camada de display | #### Formats Disponíveis ```javascript "currency" // 1000 → R$ 1.000,00 "date" // 2024-01-01 → 01/01/2024 "area" // 100 → 100 m² "distance" // 1000 → 1 km "percent" // 0.15 → 15% "count" // Com pluralização automática "geo-point" // Campo de geolocalização (coordenadas lat/lng) ``` #### Units Disponíveis ```javascript // Moedas "BRL", "USD", "EUR" // Área "m2", "km2", "hectare", "ft2" // Distância "m", "km", "mi" ``` #### Masks Disponíveis **🤖 INFERÊNCIA AUTOMÁTICA:** Quando `format` + `unit` estão presentes, o `mask` é **automaticamente inferido**: ```javascript // INFERÊNCIA AUTOMÁTICA (não precisa definir mask) format: "currency" + unit: "BRL" mask: "currency-brl" // R$ 1.000,00 format: "currency" + unit: "USD" mask: "currency-usd" // $ 1,000.00 format: "percent" mask: "percent" // 15% format: "date" mask: "date" // DD/MM/YYYY format: "area" + unit: "m2" mask: "area" // 100 m² format: "distance" + unit: "km" mask: "distance" // 1,5 km ``` **📝 MASKS EXPLÍCITOS:** Use apenas para casos específicos sem format/unit equivalente: ```javascript "cpf" // 000.000.000-00 "cnpj" // 00.000.000/0000-00 "cep" // 00000-000 "phone" // (00) 00000-0000 "email" // Validação de email "url" // Validação de URL ``` ### 3. CATEGORIZAÇÃO E UI | Campo | Tipo | Descrição | Exemplo | |-------|------|-----------|---------| | `categories` | String[] | Categorias múltiplas | `["valores", "principais"]` | | `searchable` | Boolean | Pesquisável por texto | `true` | | `filterable` | Boolean | Aparece nos filtros | `true` | | `sortable` | Boolean | Pode ordenar | `true` | #### Categories Comuns ```javascript "valores" // Campos monetários "localizacao" // Endereço e geolocalização "dependencias" // Cômodos e estrutura "identificacao" // Títulos e referências "comercial" // Info comerciais "principais" // Destaque no frontend "caracteristicas" // Amenidades "sistema" // Campos internos "seo" // SEO e marketing "corretor" // Info do corretor "condominio" // Info do condomínio ``` ### 4. ENUMERAÇÃO | Campo | Tipo | Descrição | Formato | |-------|------|-----------|---------| | `enum` | Object | Valores possíveis | `{ "chave": "Label" }` | ```javascript // Exemplo "enum": { "venda": "Venda", "aluguel": "Aluguel", "temporada": "Temporada" } ``` ### 5. RELAÇÕES E DEPENDÊNCIAS | Campo | Tipo | Descrição | Uso | |-------|------|-----------|-----| | `parent` | String | Campo pai hierárquico | `"tipo"` | | `conditions` | String[] | Condições de visibilidade | `["operacao:venda"]` | #### Conditions - Operadores Disponíveis | Categoria | Operador | Descrição | Exemplo | |-----------|----------|-----------|---------| | **Comparação** | `equals` | Igual a | `"tipo.equals:apartamento"` | | | `not` | Diferente de | `"tipo.not:rural"` | | | `in` | Está na lista | `"finalidade.in:residencial,comercial"` | | | `notIn` | Não está na lista | `"status.notIn:vendido,alugado"` | | **Strings** | `contains` | Contém (padrão) | `"operacao:venda"` | | | `startsWith` | Começa com | `"titulo.startsWith:Casa"` | | | `endsWith` | Termina com | `"titulo.endsWith:Centro"` | | **Números** | `gt` | Maior que | `"valor.gt:100000"` | | | `gte` | Maior ou igual | `"valor.gte:100000"` | | | `lt` | Menor que | `"valor.lt:500000"` | | | `lte` | Menor ou igual | `"valor.lte:500000"` | | **Existência** | `exists` | Campo preenchido/vazio | `"condominio.exists:true"` | ```javascript // Exemplos práticos "conditions": [ "operacao:venda", // operacao contém "venda" (padrão) "tipo.not:rural", // tipo diferente de "rural" "valor.gte:100000", // valor >= 100000 "condominio.exists:true", // condominio preenchido "finalidade.in:residencial,comercial" // finalidade é residencial OU comercial ] ``` ### 6. VALIDAÇÃO (Frontend/Zod) | Campo | Tipo | Descrição | Exemplo | |-------|------|-----------|---------| | `validation` | Object | Regras de validação | Ver abaixo | ```javascript "validation": { "required": true, // Obrigatório "min": 0, // Valor mínimo (números) "max": 999999999.99, // Valor máximo (números) "minLength": 3, // Tamanho mínimo (strings) "maxLength": 255, // Tamanho máximo (strings) "precision": 2 // Casas decimais (infere decimal no DB) } ``` **Importante**: NÃO incluir validações já cobertas por `mask` (cpf, email, etc) ### 7. BANCO DE DADOS | Campo | Tipo | Descrição | Quando Usar | |-------|------|-----------|-------------| | `db` | Object | Configurações do banco | Quando diferente do padrão | ```javascript "db": { "type": "decimal(12,2)", // Tipo específico "unique": true, // Constraint única "index": true, // Criar índice "default": "now()", // Valor padrão "primary": true, // Chave primária "autoincrement": true, // Auto incremento // CONFIGURAÇÃO PARA GEOLOCALIZAÇÃO "geoSource": ["latitude", "longitude"] // Campos fonte para coordenadas } ``` #### Tipos de DB Especiais ```javascript "text" // Texto longo "decimal(12,2)" // Decimal com precisão "timestamp" // Data/hora "uuid" // UUID "jsonb" // JSON binário (Postgres) ``` #### Tipos de Index ```javascript true // Índice simples (btree) false // Sem índice "fulltext" // Busca de texto (para descriptions) "hash" // Hash index "gin" // GIN index (Postgres) "gist" // GiST index (para dados geográficos PostGIS) ``` ### 8. AUDITORIA | Campo | Tipo | Descrição | Exemplo | |-------|------|-----------|---------| | `origin` | String | Onde foi criado | `"horizon-base/property"` | | `modifiedBy` | String[] | Quem modificou | `["jetimob", "crm-sync"]` | --- ## 📋 Campos Removidos da Especificação Base ### `icon` e `composedLabel` - Camada de Display **Decisão**: Os campos `icon` e `composedLabel` foram **removidos do schema base** e serão adicionados **manualmente na camada de display** apenas quando necessário. **Justificativa**: - Apenas poucos campos principais precisam de ícones (dormitórios, banheiros, vagas, área) - `composedLabel` é específico da UI e pode variar por contexto - Mantém o schema base focado nos dados essenciais - Camada de display terá mais flexibilidade para customizações **Implementação**: ```javascript // Schema base (limpo) { "key": "dormitorios", "type": "Number", "label": "Dormitórios", "format": "count" } // Camada de display (quando necessário) const displayConfig = { "dormitorios": { "icon": "bedroom", "composedLabel": "{{value}} dormitório{{p:s}}" } } ``` --- ## 🧠 Sistema de Inferência Inteligente O sistema deduz automaticamente para reduzir redundância: ### Inferência de Tipo de Banco ```javascript // String + maxLength → varchar(N) { "validation": { "maxLength": 100 } } // Gera: db.type = "varchar(100)" // String sem limite → varchar(255) { "type": "String" } // Gera: db.type = "varchar(255)" // String longo + searchable → text + fulltext { "type": "String", "searchable": true } // Gera: db.type = "text", db.index = "fulltext" // Number + precision → decimal { "validation": { "precision": 2, "max": 999999.99 } } // Gera: db.type = "decimal(8,2)" // Format + padrões → decimal automático { "format": "currency" } // Gera: db.type = "decimal(12,2)" { "format": "area" } // Gera: db.type = "decimal(8,2)" { "format": "percent" } // Gera: db.type = "decimal(5,4)" ``` ### Inferência de Índices ```javascript // POR PADRÃO: TODOS OS CAMPOS SÃO INDEXADOS (db.index = true) // Apenas explicitamos exceções: { "db": { "index": false } } // Campo SEM índice { "db": { "index": "gin" } } // Índice GIN (arrays/JSONB) { "db": { "index": "fulltext" } } // Índice de texto completo { "db": { "unique": true } } // Unique constraint (já inclui index) // Casos especiais automáticos: { "type": "String[]", "searchable": true } // Gera: db.index = "gin" { "type": "Json", "searchable": true } // Gera: db.index = "gin" ``` ### Inferência de Mask Automática ```javascript // Format + Unit → Mask automático (frontend) { "format": "currency", "unit": "BRL" } // Gera: mask = "currency-brl" { "format": "percent" } // Gera: mask = "percent" { "format": "date" } // Gera: mask = "date" { "format": "area", "unit": "m2" } // Gera: mask = "area" // Override manual quando necessário { "format": "currency", "unit": "BRL", "mask": false } // Desabilita mask { "format": "currency", "unit": "BRL", "mask": "custom" } // Override ``` ### Inferência de Validação ```javascript // Mask explícito (apenas casos específicos) { "mask": "cpf" } // Frontend: formatação + validação visual { "mask": "email" } // Frontend: validação de formato { "mask": "cep" } // Frontend: formatação CEP // Precision para Zod (backend) { "validation": { "precision": 2 } } // Gera: z.number().multipleOf(0.01) { "validation": { "precision": 3 } } // Gera: z.number().multipleOf(0.001) ``` ### Inferência para Geolocalização ```javascript // Format geo-point + geoSource → configurações espaciais automáticas { "format": "geo-point", "db": { "geoSource": ["lat", "lng"] } } // Sistema infere baseado no ORM/banco: // - PostgreSQL/PostGIS: geography(Point, 4326) + índice gist // - MySQL: POINT + índice spatial // - MongoDB: 2dsphere index // - Coluna gerada ou trigger conforme suporte do banco ``` ### Cálculo Automático de Decimal ```javascript // Sistema calcula precisão automaticamente: validation.max = 999999.99 + precision = 2 decimal(8,2) validation.max = 999999999.99 + precision = 2 decimal(12,2) // Exemplos práticos: { "validation": { "max": 999999.99, "precision": 2 } } // decimal(8,2) { "validation": { "max": 999999999.99, "precision": 2 } } // decimal(12,2) { "validation": { "max": 180.0, "precision": 8 } } // decimal(11,8) - lat/lng ``` --- ## 📖 Biblioteca de Exemplos ### Campos Simples #### Campo Básico ```javascript { "key": "reference", "type": "String", "label": "Referência", "origin": "horizon-base/property" } ``` #### Campo com Validação ```javascript { "key": "email", "type": "String", "label": "E-mail", "mask": "email", "validation": { "required": true, "maxLength": 255 }, "origin": "horizon-base/property" } ``` #### Campo com Formatação e Precision ```javascript { "key": "valor_venda", "type": "Number", "label": "Valor de Venda", "format": "currency", "unit": "BRL", "categories": ["valores"], "validation": { "required": true, "min": 0, "max": 999999999.99, "precision": 2 }, "origin": "horizon-base/property" // Sistema infere automaticamente: // - db.type = "decimal(12,2)", db.index = true // - mask = "currency-brl" (frontend) } ``` ### Campos com Enum ```javascript { "key": "operacao", "type": "String[]", "label": "Operação", "enum": { "venda": "Venda", "aluguel": "Aluguel", "temporada": "Temporada" }, "categories": ["comercial"], "filterable": true, "origin": "horizon-base/property" } ``` ### Campos com Hierarquia ```javascript { "key": "subtipo", "type": "String", "label": "Subtipo", "parent": "tipo", "categories": ["estrutura"], "filterable": true, "origin": "horizon-base/property" } ``` ### Campos com Condições #### Condição Simples ```javascript { "key": "valor_aluguel", "type": "Number", "label": "Valor do Aluguel", "format": "currency", "unit": "BRL", "conditions": ["operacao:aluguel"], "categories": ["valores"], "filterable": true, "origin": "horizon-base/property" } ``` #### Condições Múltiplas ```javascript { "key": "andar", "type": "Number", "label": "Andar", "conditions": [ "tipo.equals:apartamento", "operacao:venda", "valor.gte:200000" ], "categories": ["estrutura"], "validation": { "min": 1, "max": 50 }, "origin": "horizon-base/property" } ``` ### Campos com Banco Customizado #### Campo Único ```javascript { "key": "cpf", "type": "String", "label": "CPF", "mask": "cpf", "db": { "unique": true }, "validation": { "required": true, "maxLength": 14 }, "origin": "horizon-base/property" } ``` #### Campo com Fulltext ```javascript { "key": "description", "type": "String", "label": "Descrição", "db": { "type": "text", "index": "fulltext" }, "categories": ["identificacao"], "searchable": true, "validation": { "required": true }, "origin": "horizon-base/property" } ``` ### Campos Complexos #### Campo Principal Completo ```javascript { "key": "dormitorios", "type": "Number", "label": "Dormitórios", "icon": "bedroom", "categories": ["dependencias", "principais"], "format": "count", "composedLabel": "{{value}} dormitório{{p:s}}", "filterable": true, "sortable": true, "validation": { "min": 0, "max": 20 }, "origin": "horizon-base/property" // Sistema infere: mask = "count" (frontend) } ``` #### Campo com Index Especial ```javascript { "key": "description", "type": "String", "label": "Descrição", "categories": ["identificacao"], "searchable": true, "validation": { "required": true }, "origin": "horizon-base/property" // Sistema infere: db.type = "text", db.index = "fulltext" } ``` #### Campo Sem Index ```javascript { "key": "endereco_numero", "type": "String", "label": "Número", "categories": ["localizacao"], "db": {"index": false}, // Explicitamente SEM índice "origin": "horizon-base/property" } ``` #### Campo com Precision ```javascript { "key": "latitude", "type": "Number", "label": "Latitude", "categories": ["localizacao"], "validation": { "min": -90.0, "max": 90.0, "precision": 8 }, "origin": "horizon-base/property" // Sistema infere: db.type = "decimal(10,8)" } ``` #### Array com Index GIN ```javascript { "key": "caracteristicas", "type": "String[]", "label": "Características", "categories": ["caracteristicas"], "searchable": true, "origin": "horizon-base/property" // Sistema infere: db.type = "jsonb", db.index = "gin" } ``` #### Campo de Geolocalização ```javascript { "key": "location", "type": "String", "label": "Localização", "format": "geo-point", "db": { "geoSource": ["latitude", "longitude"] }, "filterable": true, "origin": "horizon-base/property" // Sistema infere todo o resto baseado no ORM/banco: // - Tipo apropriado (geography, geometry, point, etc) // - Índice espacial apropriado // - Coluna gerada ou trigger conforme o banco } ``` --- ## ⚠️ Anti-Patterns ### Redundância ```javascript // ERRADO - Repetindo informação { "db": { "type": "varchar(255)" }, "validation": { "maxLength": 255 } } // CERTO - Sistema infere { "validation": { "maxLength": 255 } } ``` ### Format Desnecessário ```javascript // ERRADO - Type já diz tudo { "type": "String", "format": "text" } // CERTO - Sem format { "type": "String" } ``` ### Validação Duplicada ```javascript // ERRADO - Mask já valida { "mask": "cpf", "validation": { "cpf": true } } // CERTO - Mask cuida da validação { "mask": "cpf" } ``` --- ## 📏 Guia de Decisão Rápido ### Quando usar cada campo? | Se você quer... | Use... | Exemplo | |------------------|---------|----------| | Formatar exibição | `format` + `unit` | `"format": "currency", "unit": "BRL"` | | Validar/mascarar | `mask` | `"mask": "cpf"` | | Múltiplas categorias | `categories` | `"categories": ["valores", "principais"]` | | Hierarquia pai-filho | `parent` | `"parent": "tipo"` | | Condições de visibilidade | `conditions` | `"conditions": ["operacao:venda"]` | | Constraints de banco | `db` | `"db": { "unique": true }` | | Validação frontend | `validation` | `"validation": { "required": true }` | | Rastrear origem | `origin` + `modifiedBy` | `"origin": "jetimob/import"` | ### Quantas propriedades usar? - **Mínimo**: 4 (key, type, label, origin) - **Média ideal**: 5-7 propriedades - **Máximo aceitável**: 10-12 (casos muito complexos) --- ## 🎯 Regras de Ouro 1. **Comece simples**: key, type, label, origin 2. **Adicione só quando necessário**: Cada propriedade deve ter propósito claro 3. **Deixe o sistema inferir**: Não especifique o que pode ser automatizado 4. **Separe responsabilidades**: Frontend Banco 5. **Zero redundância**: Nunca repetir informação 6. **Documentes mudanças**: Use `modifiedBy` para auditoria 7. **🤖 Mask automático**: Quando `format` + `unit` existem, `mask` é inferido automaticamente 8. **📝 Mask explícito**: Use apenas para casos específicos (CPF, CEP, telefone, email, URL) --- Este formato é a **base definitiva** para todos os schemas do sistema Horizon. Deve ser seguido consistentemente para garantir interoperabilidade, manutenibilidade e escalabilidade do sistema.