@horizon-framework/api-nextjs-docs 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,200 @@
1
+ # Batch Totals API - Menu Dinâmico com Performance
2
+
3
+ ## **Problema Resolvido**
4
+
5
+ **Cenário:** Menu de navegação em site imobiliário
6
+ ```
7
+ IMÓVEIS
8
+ ├── Apartamento 2 quartos (845) ← precisa do total
9
+ ├── Apartamento 3 quartos (723)
10
+ ├── Casa 2 quartos (400)
11
+ ├── Casa 3 quartos (169)
12
+ └── ... mais 12 categorias
13
+ ```
14
+
15
+ **Solução Ruim:** 16 requests HTTP separados
16
+ **Solução Inteligente:** 1 request com array de queries
17
+
18
+ ## **Performance**
19
+
20
+ | Abordagem | HTTP Calls | Tempo | Eficiência |
21
+ |-----------|------------|-------|------------|
22
+ | **Múltiplos requests** | 16 | ~2.3s | Lento |
23
+ | **Batch Totals** | 1 | ~250ms | 90% mais rápido |
24
+
25
+ ## **API Specification**
26
+
27
+ ### **Endpoint:**
28
+ ```
29
+ POST /api/property/batch-totals
30
+ ```
31
+
32
+ ### **Request Format:**
33
+ ```json
34
+ {
35
+ "queries": [
36
+ {
37
+ "key": "apartamento_2q",
38
+ "filters": {
39
+ "tipo": "Apartamento",
40
+ "dormitorios": 2
41
+ },
42
+ "meta": { "total": true }
43
+ },
44
+ {
45
+ "key": "casa_3q",
46
+ "filters": {
47
+ "tipo": "Casa",
48
+ "dormitorios": 3
49
+ },
50
+ "meta": { "total": true }
51
+ }
52
+ ]
53
+ }
54
+ ```
55
+
56
+ ### **Response Format:**
57
+ ```json
58
+ {
59
+ "results": {
60
+ "apartamento_2q": { "total": 845 },
61
+ "casa_3q": { "total": 169 }
62
+ },
63
+ "metadata": {
64
+ "executionTime": 245,
65
+ "queriesProcessed": 2,
66
+ "parallelExecution": true
67
+ }
68
+ }
69
+ ```
70
+
71
+ ## **Implementação Técnica**
72
+
73
+ ### **Arquitetura:**
74
+ ```
75
+ Controller → Service → Promise.all() → advancedSearch() × N
76
+ ```
77
+
78
+ ### **Características:**
79
+ - **Execução paralela** com `Promise.all()`
80
+ - **Keys únicas** para cada query
81
+ - **Filtros flexíveis** (qualquer combinação)
82
+ - **Error handling** individual por query
83
+ - **Performance logs** automáticos
84
+
85
+ ## **Casos de Uso**
86
+
87
+ ### **1. Menu de Navegação**
88
+ ```javascript
89
+ const menuQueries = [
90
+ { key: "apt_2q", filters: { tipo: "Apartamento", dormitorios: 2 } },
91
+ { key: "apt_3q", filters: { tipo: "Apartamento", dormitorios: 3 } },
92
+ { key: "casa_2q", filters: { tipo: "Casa", dormitorios: 2 } }
93
+ ];
94
+ ```
95
+
96
+ ### **2. Dashboard com Métricas**
97
+ ```javascript
98
+ const dashboardQueries = [
99
+ { key: "total_ativos", filters: { status: "ativo" } },
100
+ { key: "total_vendidos", filters: { status: "vendido" } },
101
+ { key: "total_premium", filters: { destaque: true } }
102
+ ];
103
+ ```
104
+
105
+ ### **3. Filtros Dinâmicos**
106
+ ```javascript
107
+ const filterCounts = [
108
+ { key: "por_cidade", filters: { endereco_cidade: "Florianópolis" } },
109
+ { key: "por_preco", filters: { valor_venda: { gte: 500000 } } },
110
+ { key: "com_piscina", filters: { caracteristicas: { contains: "Piscina" } } }
111
+ ];
112
+ ```
113
+
114
+ ## **Frontend Integration**
115
+
116
+ ### **Next.js Static Props:**
117
+ ```typescript
118
+ export async function getStaticProps() {
119
+ const response = await fetch('/api/property/batch-totals', {
120
+ method: 'POST',
121
+ headers: { 'Content-Type': 'application/json' },
122
+ body: JSON.stringify({ queries: MENU_QUERIES })
123
+ });
124
+
125
+ const { results } = await response.json();
126
+
127
+ return {
128
+ props: { menuCounters: results },
129
+ revalidate: 7200 // Cache 2h
130
+ };
131
+ }
132
+ ```
133
+
134
+ ### **SWR Client-Side:**
135
+ ```typescript
136
+ import useSWR from 'swr';
137
+
138
+ const { data: counters } = useSWR(
139
+ 'menu-counters',
140
+ () => batchTotals(MENU_QUERIES),
141
+ { refreshInterval: 300000 } // 5min
142
+ );
143
+ ```
144
+
145
+ ## **Limitações & Considerações**
146
+
147
+ ### **Limites:**
148
+ - **Máximo:** 20 queries por request
149
+ - **Timeout:** 30 segundos total
150
+ - **Cache:** Recomendado 1-4 horas
151
+
152
+ ### **Error Handling:**
153
+ ```json
154
+ {
155
+ "results": {
156
+ "valid_query": { "total": 845 },
157
+ "invalid_query": { "error": "Campo inválido", "total": 0 }
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### **Performance Tips:**
163
+ - Cache no cliente (SWR/React Query)
164
+ - Cache no servidor (Redis)
165
+ - Revalidação com webhooks
166
+ - Não usar para queries complexas
167
+
168
+ ## **Exemplo Completo**
169
+
170
+ ### **Request Real:**
171
+ ```bash
172
+ curl -X POST http://localhost:5000/api/property/batch-totals \
173
+ -H "Content-Type: application/json" \
174
+ -d '{
175
+ "queries": [
176
+ {
177
+ "key": "apartamento_2q",
178
+ "filters": { "tipo": "Apartamento", "dormitorios": 2 },
179
+ "meta": { "total": true }
180
+ }
181
+ ]
182
+ }'
183
+ ```
184
+
185
+ ### **Response Real:**
186
+ ```json
187
+ {
188
+ "results": {
189
+ "apartamento_2q": { "total": 845 }
190
+ },
191
+ "metadata": {
192
+ "executionTime": 156,
193
+ "queriesProcessed": 1
194
+ }
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ **Batch Totals API - Solução definitiva para menus dinâmicos com performance!**
@@ -0,0 +1,328 @@
1
+ # Domínio e Entidades — Modelos de Dados Ponta a Ponta
2
+
3
+ > Arquitetura de dados dos módulos Horizon: como os modelos de entidades são definidos, transformados e consumidos em cada camada da aplicação — do CRM externo ao pixel na tela.
4
+ > Genérico para qualquer módulo (property, broker, etc.) e qualquer CRM.
5
+
6
+ ---
7
+
8
+ ## CONCEITO
9
+
10
+ O Horizon trabalha com **módulos de domínio**. Cada módulo (property, broker, etc.) segue a mesma arquitetura de dados em 3 camadas:
11
+
12
+ ### As 17 camadas do fluxo de dados
13
+
14
+ **ROOT (genérica, qualquer entidade):**
15
+ 1. DB System — id, created_at, updated_at, first_synced_at, sync_raw_data
16
+ 2. Generated Columns SQL — fts_vector (fulltext), geom (PostGIS)
17
+ 3. Relations — relações virtualizadas (ex: broker → Broker.key)
18
+
19
+ **HORIZON BASE (universal):**
20
+ 4. Campos base do domain model (ex: 45 campos property)
21
+ 5. Computed base — derivados diretos da base (seo_slug_gen, thumbnails, endereco_completo)
22
+
23
+ **CRM (muda por integração):**
24
+ 6. Extras CRM — campos específicos do CRM no banco
25
+ 7. Transformações CRM — dataModifier rules + computed customizados
26
+
27
+ **SYNC (entrada):**
28
+ 8. PUT /api/[entity]/sync — recebe dados do conversor
29
+ 9. Validação + Transformações — Zod + rules + computed (pipeline)
30
+
31
+ **PRISMA (ponte):**
32
+ 10. db pull → patch-relations → generate
33
+
34
+ **SCHEMA (SSOT):**
35
+ 11. entity-schema.ts + API endpoint
36
+
37
+ **FRONTEND:**
38
+ 12. prebuild → schema.generated.json
39
+ 13. Computed Frontend — URL + flags layout (assembleComputedData)
40
+ 14. fields-lists.ts — seções customizadas de atributos
41
+ 15. select-fields.config.ts — campos do card
42
+
43
+ **BUSCA:**
44
+ 16. Search schema + config — filtros, facets, ranges, sort
45
+ 17. Filtros extras por CRM
46
+
47
+ Cada módulo no backend e frontend segue a mesma estrutura:
48
+ ```
49
+ [modulo]/
50
+ ├── core/ → Serviços, schemas, libs (FUNDAÇÃO)
51
+ ├── features/ → Features completas e autônomas
52
+ └── customs/ → Customizações do site
53
+ ```
54
+
55
+ ---
56
+
57
+ ## FILOSOFIA HORIZON — DOMAIN MODELS
58
+
59
+ O Horizon define um **modelo de domínio base** para cada entidade, publicado como pacote npm `@horizon-domains/[entidade]-model`. Este modelo é o contrato que TODOS os CRMs devem cumprir.
60
+
61
+ ### Pacotes de domínio existentes
62
+
63
+ | Pacote | Entidade | Campos base | Local |
64
+ |---|---|---|---|
65
+ | `@horizon-domains/property-model` | Imóvel | 45 campos | `horizon-modules/property/domain-data` |
66
+ | `@horizon-domains/broker-model` | Corretor | ~20 campos | `horizon-modules/broker/domain-data` |
67
+
68
+ ### Outros módulos Horizon (sem domínio de CRM)
69
+
70
+ | Módulo | Função |
71
+ |---|---|
72
+ | `horizon-modules/blog` | Blog |
73
+ | `horizon-modules/cidade-e-bairros` | Cidades e bairros |
74
+ | `horizon-modules/condo` | Condomínios |
75
+ | `horizon-modules/instagram-advanced` | Instagram avançado |
76
+
77
+ ### Como funciona
78
+
79
+ 1. **Campos base** — O domain model define campos OBRIGATÓRIOS que toda entidade deve ter (ex: property tem 45 campos: reference, title, description, tipo, operacao, dormitorios, etc.). Todo CRM PRECISA preencher esses campos.
80
+
81
+ 2. **Campos extras do CRM** — Cada CRM tem campos ADICIONAIS além da base. Esses excedentes são normalizados pelo pacote conversor seguindo regras de normalização:
82
+ - Ponto → underscore: `insurance.letter` → `insurance_letter`
83
+ - Hífen → underscore: `air-conditioner` → `air_conditioner`
84
+ - Objetos aninhados → prefixo: `features.room` → `features_room`
85
+ - Booleans agrupados → array TEXT[]: `features`, `security`, `recreation`, `rural`
86
+ - Tudo snake_case minúsculo
87
+
88
+ 3. **Schema final** — O entity-schema do site combina campos base (do domain model) + campos extras (do CRM) numa entidade única.
89
+
90
+ ```
91
+ @horizon-domains/property-model (45 campos base)
92
+ + @horizon-integrations/[crm] (campos extras normalizados + enum labels)
93
+ = Entity Schema do site (base + extras + computed + relações)
94
+ ```
95
+
96
+ 4. **Enum labels** — Campos com valores fixos (enums) devem ter o mapa `enum: { VALOR: "Label PT-BR" }` definido **no pacote CRM**, não no site. O banco armazena a chave técnica (SOLD, RENTED, etc.) e o frontend traduz pra label legível via enricher usando o `enum` do schema.
97
+
98
+ **REGRA:** Valores no banco = chaves técnicas (inglês). Labels de exibição = vêm do `enum` no schema (que vem do pacote CRM). NUNCA traduzir no conversor — isso quebraria filtros e facets.
99
+
100
+ ---
101
+
102
+ ## CAMADA 1 — PACOTE CONVERSOR (npm)
103
+
104
+ Cada CRM tem um pacote npm `@horizon-integrations/[crm]` que:
105
+
106
+ | Exporta | Função |
107
+ |---|---|
108
+ | Conversor | Transforma dados crus do CRM → formato Horizon padronizado (snake_case) |
109
+ | Schema Zod | Validação dos dados convertidos |
110
+ | EntitySchema | Schema da entidade com metadados (tipo, label, categoria, source tracking) |
111
+ | FICHA_TECNICA_INTEGRACAO.md | Diretrizes completas: campos, tipos SQL, badges, filtros, transformações |
112
+
113
+ **Base Horizon:** 45 campos padronizados que existem em QUALQUER CRM (reference, title, description, operacao, tipo, dormitorios, etc.). Definidos em `@horizon-domains/property-model`.
114
+
115
+ **Extras CRM:** Campos específicos que cada CRM adiciona além da base (variam por CRM).
116
+
117
+ ---
118
+
119
+ ## CAMADA 2 — API (apps/api)
120
+
121
+ ### Sync (entrada de dados)
122
+ ```
123
+ PUT /api/property/sync
124
+ → source-schema.zod.ts (validação Zod do pacote CRM)
125
+ → dataModifier/rules.ts (transformações Etapa 3: subtipo, tags→campos)
126
+ → computedFields/index.ts (campos calculados: slug, thumbnails, endereço, flags)
127
+ → Prisma INSERT/UPSERT → PostgreSQL
128
+ ```
129
+
130
+ **Autenticação:** Header `x-api-key` com `API_SECRET_KEY` do `.env`
131
+ **Batching:** Máximo ~50 imóveis por request (timeout Prisma = 5s)
132
+
133
+ ### Banco PostgreSQL
134
+ - **create.sql** — Seções ordenadas: DB system → Horizon base (45) → Extras CRM → Computed → Etapa 3
135
+ - **generated-columns.sql** — `fts_vector` (busca textual), `geom` (geoespacial)
136
+ - **relations.ts** — Relações virtualizadas (injetadas via `pnpm db:patch-relations`)
137
+ - **Índices** — B-tree (filtros), GIN (arrays + FTS), GIST (geom)
138
+
139
+ ### Entity Schema (SSOT)
140
+ - **entity-schema.ts** — Define TODOS os campos da entidade com:
141
+ - `type` (String, Number, Boolean, Json, Relation)
142
+ - `categories` (ficha-tecnica, localizacao, media, etc.)
143
+ - `ui` (label, description, icon, displayTemplate)
144
+ - `relation` (model, field, labelField, displayTemplate, foreignKey)
145
+ - `audit` (origin: crm-base, crm-extra, computed, local-relation)
146
+ - **Display Overrides** — Templates como `"{{valueLabel}} quarto{{p:s}}"`, ícones customizados
147
+ - **Exposto via:** `GET /api/property/schemas/entity`
148
+
149
+ ### Search e Facets
150
+ - **`POST /api/property/search`** — Busca multipart (filters, facets, fts, geom, list, map)
151
+ - **`POST /api/property/batch-search`** — Múltiplas buscas em 1 request
152
+ - **Facets** — Sistema automático de contagens agrupadas (terms, range, histogram)
153
+ - **Enricher de relação** — Transforma key → label via lookup no modelo relacionado
154
+
155
+ ---
156
+
157
+ ## CAMADA 3 — FRONTEND (apps/web)
158
+
159
+ ### Schema no Frontend
160
+ - **Geração:** `npx tsx scripts/generate-schema.ts` (API precisa estar rodando)
161
+ - **Output:** `schema.generated.json` no módulo
162
+ - **REGRA:** Sempre regenerar quando alterar entity-schema.ts na API
163
+
164
+ ### Data Display Enricher
165
+ - **Função:** Transforma dados crus da API → formato de exibição
166
+ - **Processo:** Lê `schema.generated.json` → aplica labels, formatos, ícones, templates → retorna objeto enriquecido
167
+ - **Cada campo vira:** `{ key, value, valueLabel, label, type, icon, ... }`
168
+
169
+ ### PropertyService (core)
170
+ Singleton `propertyService` com métodos:
171
+
172
+ | Método | Uso |
173
+ |---|---|
174
+ | `search(request)` | Busca com filtros, facets, paginação |
175
+ | `batchSearch(queries)` | Múltiplas buscas em 1 request (contagens, navegação) |
176
+ | `find(criteria)` | Busca simples por critério |
177
+ | `getDistinct(fields)` | Valores distintos (server-side, requer API_SECRET) |
178
+ | `getEntitySchema()` | Schema SSOT da entidade |
179
+
180
+ **Usos além de busca:** Contagens dinâmicas em cards, links, navegações, vitrines. Sempre usar com cache.
181
+
182
+ ### Fields Lists (core)
183
+ `fields-lists.ts` — Funções getter que filtram e organizam campos pra cada contexto de exibição:
184
+ - Cards: tarjas, badges, destaques com ícone, valores principais
185
+ - Página: seções da ficha (principal, características, segurança, lazer, dimensões, rural, etc.)
186
+ - Valores: caixa lateral (preços, taxas, flags comerciais)
187
+
188
+ ### Select Fields (core)
189
+ `select-fields.config.ts` — Define quais campos buscar da API por contexto:
190
+ - `full` — Todos os campos + relações
191
+ - `cardList` — Campos necessários pro card (tarjas, badges, destaques, endereço, preço)
192
+ - `mapList` — Apenas id, reference, lat, lng (pins do mapa)
193
+
194
+ ---
195
+
196
+ ## FLUXO DE ALTERAÇÃO
197
+
198
+ ### Alterar entity-schema.ts (displayOverrides, labels, etc.)
199
+ 1. Alterar arquivo na API
200
+ 2. Reiniciar API (Prisma Client cacheado em memória)
201
+ 3. Regenerar schema: `cd apps/web && npx tsx scripts/generate-schema.ts`
202
+
203
+ ### Alterar create.sql (colunas do banco)
204
+ 1. `db:drop` → `db:create` → `prisma db pull` → `db:patch-relations` → `prisma generate`
205
+ 2. Reiniciar API
206
+ 3. Re-sincronizar dados
207
+
208
+ ### Trocar CRM
209
+ Ver checklist completa em `AGENTE_CONFIGURACAO_CRM.md`
210
+
211
+ ---
212
+
213
+ ## DATABASE (apps/api/database/)
214
+
215
+ Sistema de gestão do banco PostgreSQL:
216
+
217
+ ```
218
+ database/
219
+ ├── tables/
220
+ │ ├── property/
221
+ │ │ ├── create.sql → Schema da tabela (CREATE TABLE)
222
+ │ │ └── generated-columns.sql → Colunas geradas (fts_vector, geom)
223
+ │ ├── broker/
224
+ │ │ └── create.sql
225
+ │ └── branch/
226
+ │ └── create.sql
227
+ ├── shared/ → SQL compartilhado entre tabelas
228
+ ├── relations.ts → Definição de relações Prisma (injetadas)
229
+ └── scripts/
230
+ ├── db-setup.ts → Setup inicial do banco
231
+ ├── table-create.ts → Cria tabela a partir do create.sql
232
+ ├── table-drop.ts → Dropa tabela
233
+ ├── patch-relations.ts → Injeta relações no schema Prisma
234
+ └── sync-prisma.ts → Sincroniza Prisma com banco
235
+ ```
236
+
237
+ ### Comandos de banco (via pnpm)
238
+ | Comando | Função |
239
+ |---|---|
240
+ | `pnpm db:create [tabela]` | Cria tabela a partir do create.sql |
241
+ | `pnpm db:drop [tabela]` | Dropa tabela |
242
+ | `pnpm db:patch-relations` | Injeta relações no schema.prisma |
243
+ | `npx prisma db pull` | Puxa schema do banco pro Prisma |
244
+ | `npx prisma generate` | Gera Prisma Client |
245
+
246
+ ### Ordem obrigatória ao recriar tabela
247
+ ```
248
+ db:drop → db:create → prisma db pull → db:patch-relations → prisma generate → REINICIAR API
249
+ ```
250
+ **ORDEM IMPORTA:** `pull` ANTES de `patch-relations` (pull sobrescreve relações).
251
+
252
+ ---
253
+
254
+ ## MÓDULOS BACKEND (apps/api/src/modules/)
255
+
256
+ Cada módulo segue a mesma estrutura:
257
+
258
+ ```
259
+ modules/[modulo]/
260
+ ├── controllers/ → HTTP handlers (Next.js API routes)
261
+ ├── services/ → Lógica de negócio
262
+ ├── schemas/
263
+ │ ├── source-schema.zod.ts → Validação de entrada (Zod do CRM)
264
+ │ └── entity-schema.ts → Schema SSOT (tipo, label, icon, relações)
265
+ ├── mappers/
266
+ │ ├── dataModifier/rules.ts → Transformações na entrada
267
+ │ └── computedFields/index.ts → Campos calculados
268
+ └── __tests__/ → Testes
269
+ ```
270
+
271
+ Módulos existentes:
272
+
273
+ | Módulo | Entidade | Controllers | Pacote de domínio |
274
+ |---|---|---|---|
275
+ | property | Imóvel | search, sync, batch-search, schemas | `@horizon-domains/property-model` |
276
+ | broker | Corretor | search, sync, schemas | `@horizon-domains/broker-model` |
277
+
278
+ ---
279
+
280
+ ## LIB DE PESQUISA AVANÇADA (apps/api/src/lib/advancedSearch/)
281
+
282
+ Sistema **100% genérico** de busca avançada usado por todos os módulos. Zero hardcodes de domínio.
283
+
284
+ ```
285
+ advancedSearch/
286
+ ├── index.ts → Entry point (advancedSearch function)
287
+ ├── types.ts → Types do SearchRequest/SearchResult
288
+ ├── executeList.ts → Executa query de listagem (paginação offset/cursor)
289
+ ├── executeMap.ts → Executa query de mapa (todos os pontos)
290
+ ├── executeTotal.ts → Executa contagem total
291
+ ├── extractIds.ts → Extrai IDs de resultados
292
+ ├── helpers.ts → Helpers de filtros, FTS, geom
293
+ ├── facets/ → Sistema de facetas
294
+ │ ├── index.ts
295
+ │ ├── facets.service.ts → Orquestra execução de facetas
296
+ │ ├── types.ts → Types de facetas
297
+ │ ├── enrichers/
298
+ │ │ └── relation.ts → Enricher de relações (key → label lookup)
299
+ │ └── tests/
300
+ └── docs/ → Documentação interna completa
301
+ └── facets/
302
+ ├── README.md → Índice
303
+ ├── facets-system-complete.md → DOC PRINCIPAL E ATUALIZADA
304
+ ├── facets-objectives.md → Visão original
305
+ └── facets-implementation-checklist.md
306
+ ```
307
+
308
+ ### Tipos de faceta suportados
309
+ | Tipo | Uso | Exemplo |
310
+ |---|---|---|
311
+ | `terms` | Contagem por valor distinto | tipo, cidade, bairro, features[] |
312
+ | `range` | Buckets customizados ou automáticos | valor_venda (faixas de preço) |
313
+ | `histogram` | Buckets de distribuição visual | area_privativa (equal-depth) |
314
+
315
+ ### Enricher de relação
316
+ Transforma automaticamente chaves estrangeiras em labels legíveis:
317
+ - Detecta relação no entity-schema → faz lookup no modelo Prisma → retorna `{value, label, count}`
318
+ - Exemplo: facet `broker` → `{value: "elvis-ghisi", label: "Elvis Ghisi", count: 1}`
319
+
320
+ ### Documentação interna
321
+ Para detalhes completos do sistema de facetas, consultar:
322
+ `apps/api/src/lib/advancedSearch/docs/facets/facets-system-complete.md`
323
+
324
+ ---
325
+
326
+ ## MÓDULOS EXISTENTES
327
+
328
+ Cada módulo segue a mesma arquitetura descrita acima. Os conceitos de entity-schema, enricher, select fields e fields lists se aplicam a todos.