@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.
- package/docs/BATCH_TOTALS_API.md +200 -0
- package/docs/DOMINIO_ENTIDADES.md +328 -0
- package/docs/MODULE_CREATION_PATTERN.md +624 -0
- package/docs/SEARCH_ENGINE_API.md +1038 -0
- package/docs/SSOT_SPECIFICATION_V2.md +333 -0
- package/docs/SYNC_API_PATTERN.md +268 -0
- package/package.json +10 -0
|
@@ -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.
|