@horizon-framework/website-dev-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,566 @@
1
+ # API Property -- Stack, Arquitetura e Endpoints
2
+
3
+ > **Status:** **PRODUÇÃO PRONTA**
4
+ > **Versão:** 3.0.0 (Property + Broker implementados)
5
+ > **Data:** 23/08/2025
6
+
7
+ ## **Visão Geral**
8
+
9
+ API Next.js moderna para gestão e busca de propriedades imobiliárias com arquitetura avançada, busca full-text, geolocalização PostGIS e sistema de migração completo da nomenclatura IMOVEL→PROPERTY e CORRETOR→BROKER.
10
+
11
+ ### **Stack Tecnológico**
12
+ - **Next.js 14** (App Router)
13
+ - **Prisma 5.22** (ORM + Client)
14
+ - **PostgreSQL** (Neon Database com PostGIS)
15
+ - **TypeScript** (strict mode)
16
+ - **Zod 3.24** (validação + inferência de tipos)
17
+ - **Vitest** (framework de testes)
18
+
19
+ ### **Principais Recursos**
20
+ - **SSOT v2.0** - Single Source of Truth com 90+ campos estruturados
21
+ - **Advanced Search Library** - Biblioteca isolada e reutilizável (8 módulos)
22
+ - **Type-safety completo** - Zod + TypeScript em toda stack
23
+ - **Prisma DMMF Introspection** - Detecção automática de tipos
24
+ - **API RESTful robusta** - Validação completa com Zod
25
+ - **PostGIS Integration** - Busca geoespacial avançada
26
+ - **Full-text Search** - PostgreSQL tsvector com português
27
+ - **Migração completa** - Sistema legacy totalmente modernizado
28
+
29
+ ---
30
+
31
+ ## **Estrutura do Projeto**
32
+
33
+ ```
34
+ /apps/api/
35
+ ├── prisma/ # Database & Schema
36
+ │ ├── schema.prisma # Schema completo (Property + Broker)
37
+ │ └── db-setup.sql # PostGIS extensions
38
+ ├── src/
39
+ │ ├── app/api/ # Next.js Routes (pontes secas)
40
+ │ │ └── property/ # Endpoints do módulo Property
41
+ │ │ ├── find/route.ts # POST - busca por criteria
42
+ │ │ ├── search/route.ts # POST - busca multipart
43
+ │ │ ├── sync/route.ts # GET/PUT - sincronização
44
+ │ │ └── schemas/ # GET - schemas SSOT
45
+ │ ├── core/
46
+ │ │ └── prisma.ts # Singleton Prisma Client
47
+ │ └── modules/property/ # Módulo principal
48
+ │ ├── config/
49
+ │ │ ├── entity-schema.ts # SSOT v2.0 (~1000 linhas)
50
+ │ │ ├── entity-schema.zod.ts # Schemas Zod gerados
51
+ │ │ └── database/
52
+ │ │ ├── fragment.prisma # Fragment para geração
53
+ │ │ └── fragment.sql # SQL índices especiais
54
+ │ ├── controllers/ # Controllers Next.js
55
+ │ │ ├── search.controller.ts # Busca multipart
56
+ │ │ ├── find.controller.ts # Busca por criteria
57
+ │ │ ├── sync.controller.ts # Sincronização batch
58
+ │ │ └── schemas.controller.ts # Schemas SSOT
59
+ │ ├── lib/advancedSearch/ # BIBLIOTECA ISOLADA
60
+ │ │ ├── index.ts # API principal
61
+ │ │ ├── types.ts # Definições tipos
62
+ │ │ ├── extractIds.ts # Extração IDs fulltext/geo
63
+ │ │ ├── executeList.ts # Busca paginada
64
+ │ │ ├── executeMap.ts # Busca para mapas
65
+ │ │ ├── executeTotal.ts # Contagem total
66
+ │ │ ├── executeFacets.ts # Agregações/facets
67
+ │ │ └── helpers.ts # DMMF Introspection
68
+ │ ├── services/ # Services de negócio
69
+ │ │ ├── search.service.ts # Service busca
70
+ │ │ ├── find.service.ts # Service find
71
+ │ │ └── sync.service.ts # Service sincronização
72
+ │ └── mappers/ # Transformadores dados
73
+ │ └── enrichPropertyData/ # Enriquecimento dados
74
+ ├── __tests__/ # Suíte completa de testes
75
+ ├── __dev__/ # Scripts desenvolvimento
76
+ │ ├── scripts/ # Scripts utilitários
77
+ │ └── data/ # Dados Jetimob (11 páginas)
78
+ └── docs/ # Documentação completa
79
+ ```
80
+
81
+ ---
82
+
83
+ ## **Database Schema**
84
+
85
+ ### **Model Property (~90 campos)**
86
+ ```prisma
87
+ model Property {
88
+ // BASE SYSTEM
89
+ id Int @id @default(autoincrement())
90
+ created_at DateTime @default(now())
91
+
92
+ // SPECIAL GENERATED COLUMNS
93
+ search_tsvector Unsupported("tsvector")? // Full-text search
94
+ location Unsupported("geometry")? // PostGIS Point
95
+
96
+ // CORE IDENTIFICATION
97
+ reference String @unique // Código único
98
+ title String // Título
99
+ description String // Descrição
100
+
101
+ // COMMERCIAL
102
+ operacao String[] // ["venda", "locacao", "temporada"]
103
+ valor_venda Float? // Preço venda
104
+ valor_locacao Float? // Preço aluguel
105
+ valor_diaria Float? // Preço diária
106
+
107
+ // STRUCTURE
108
+ area_total Float? // Área m²
109
+ dormitorios Int? // Quartos
110
+ banheiros Int? // Banheiros
111
+ vagas_garagem Int? // Vagas
112
+ tipo String? // casa, apartamento
113
+ subtipo String? // cobertura, duplex
114
+
115
+ // LOCATION
116
+ endereco_cidade String? // Cidade
117
+ endereco_bairro String? // Bairro
118
+ endereco_logradouro String? // Rua
119
+ lat Float? // Latitude
120
+ lng Float? // Longitude
121
+
122
+ // CHARACTERISTICS
123
+ caracteristicas String[] // ["piscina", "churrasqueira"]
124
+ tags String[] // Tags livres
125
+
126
+ // BROKER/AGENT
127
+ corretor_nome String? // Nome broker
128
+
129
+ // CONDOMINIUM
130
+ condominio_nome String? // Nome condomínio
131
+ condominio_comodidades String[] // ["academia", "sauna"]
132
+
133
+ // JETIMOB ESPECÍFICOS (50+ campos adicionais)
134
+ // ... muitos outros campos do provider
135
+ }
136
+ ```
137
+
138
+ ### **Colunas Especiais Geradas**
139
+ ```sql
140
+ -- Full-text search (25+ campos indexados)
141
+ ALTER TABLE "Property"
142
+ ADD COLUMN search_tsvector tsvector GENERATED ALWAYS AS (
143
+ immutable_to_tsvector(
144
+ coalesce("title",'') || ' ' ||
145
+ coalesce("description",'') || ' ' ||
146
+ coalesce("endereco_cidade",'') || ' ' ||
147
+ coalesce("endereco_bairro",'') || ' ' ||
148
+ coalesce(immutable_array_to_string("caracteristicas", ' '),'')
149
+ -- ... 25+ campos searchables
150
+ )
151
+ ) STORED;
152
+
153
+ -- Geospatial Point (PostGIS)
154
+ ALTER TABLE "Property"
155
+ ADD COLUMN location geometry(Point, 4326) GENERATED ALWAYS AS (
156
+ ST_SetSRID(ST_MakePoint(lng::double precision, lat::double precision), 4326)
157
+ ) STORED;
158
+ ```
159
+
160
+ ---
161
+
162
+ ## **SSOT v2.0 - Single Source of Truth**
163
+
164
+ ### **Arquivo:** `src/modules/property/config/entity-schema.ts`
165
+
166
+ **Sistema hierárquico** de metadados que define TODA a estrutura da entidade Property:
167
+
168
+ ```typescript
169
+ interface FieldSchema {
170
+ key: string; // Nome campo
171
+ type: string; // Tipo TypeScript
172
+ categories: string[]; // Agrupamento
173
+ validation?: Record<string, any>; // Regras Zod
174
+ ui: {
175
+ label: string; // Label UI
176
+ description: string; // Descrição
177
+ searchable?: boolean; // Se é searchable
178
+ filterable?: boolean; // Se é filterable
179
+ // ... mais configs UI
180
+ };
181
+ audit: {
182
+ origin: string; // Origem do campo
183
+ };
184
+ enum?: Record<string, string>; // Valores enum
185
+ rules?: { // Regras condicionais
186
+ conditions?: string[];
187
+ parent?: string;
188
+ };
189
+ }
190
+ ```
191
+
192
+ ### **Benefícios SSOT:**
193
+ - **Zero hardcoding** - Tudo baseado no schema
194
+ - **Geração automática** - Zod schemas + TypeScript types
195
+ - **Documentação viva** - Schema é a documentação
196
+ - **Validação consistente** - Uma fonte, múltiplos usos
197
+ - **90+ campos estruturados** - Jetimob + Horizon + Custom
198
+
199
+ ---
200
+
201
+ ## **Advanced Search Library**
202
+
203
+ ### **8 Módulos Isolados**
204
+
205
+ | Módulo | Responsabilidade | Funcionalidade |
206
+ |--------|------------------|----------------|
207
+ | **`index.ts`** | API principal | Orquestra busca multipart |
208
+ | **`types.ts`** | Tipos TypeScript | SearchRequest, Response, Config |
209
+ | **`extractIds.ts`** | Extração IDs | Full-text + geolocalização |
210
+ | **`executeList.ts`** | Lista paginada | SELECT + include + pagination |
211
+ | **`executeMap.ts`** | Dados mapa | Lat/lng otimizado |
212
+ | **`executeTotal.ts`** | Contagem | COUNT queries |
213
+ | **`executeFacets.ts`** | Agregações | GROUP BY dinâmicos |
214
+ | **`helpers.ts`** | DMMF Introspection | Detecção automática tipos |
215
+
216
+ ### **API Super Simples**
217
+ ```typescript
218
+ // 95% automático, 5% configurado
219
+ const config = {
220
+ modelName: 'Property',
221
+ searchColumn: 'search_tsvector', // opcional
222
+ locationColumn: 'location' // opcional
223
+ };
224
+
225
+ const result = await advancedSearch(prisma, request, config);
226
+ ```
227
+
228
+ ### **Funcionalidades Avançadas**
229
+
230
+ #### **1. Prisma DMMF Introspection**
231
+ ```typescript
232
+ // Detecta automaticamente se campo é array ou simples
233
+ export function getFieldType(prisma, modelName, fieldName): 'array' | 'simple' {
234
+ const field = prisma.dmmf.datamodel.models
235
+ .find(m => m.name === modelName)
236
+ .fields.find(f => f.name === fieldName);
237
+
238
+ return field.isList ? 'array' : 'simple';
239
+ }
240
+
241
+ // Usa operador correto automaticamente
242
+ if (Array.isArray(value)) {
243
+ const fieldType = getFieldType(prisma, modelName, key);
244
+ where[key] = fieldType === 'array'
245
+ ? { hasSome: value } // String[] field
246
+ : { in: value }; // String field
247
+ }
248
+ ```
249
+
250
+ #### **2. Busca Multipart**
251
+ ```typescript
252
+ // Uma requisição → múltiplas respostas
253
+ const request = {
254
+ filters: { endereco_cidade: "Florianópolis" },
255
+ search: { value: "casa piscina" },
256
+ location: {
257
+ operation: "within",
258
+ geometry: { type: "bbox", bounds: {...} }
259
+ },
260
+ list: { page: 1, limit: 10 }, // Lista paginada
261
+ map: { fields: ["lat", "lng"] }, // Dados mapa
262
+ meta: { total: true }, // Contagem
263
+ facets: { fields: ["tipo"] } // Agregações
264
+ };
265
+ ```
266
+
267
+ ---
268
+
269
+ ## **API Endpoints**
270
+
271
+ ### **Pattern "Ponte Seca"**
272
+ Routes em `/app/api/` apenas exportam controllers:
273
+
274
+ ```typescript
275
+ // /app/api/property/search/route.ts
276
+ export { POST } from '@/modules/property/controllers/search.controller';
277
+ ```
278
+
279
+ ### **Endpoints Disponíveis**
280
+
281
+ | Método | Endpoint | Função | Controller |
282
+ |--------|----------|---------|-----------|
283
+ | `POST` | `/api/property/search` | Busca multipart | search.controller.ts |
284
+ | `POST` | `/api/property/find` | Busca criteria | find.controller.ts |
285
+ | `GET` | `/api/property/sync` | Listing integração | sync.controller.ts |
286
+ | `PUT` | `/api/property/sync` | Upsert batch | sync.controller.ts |
287
+ | `GET` | `/api/property/schemas/entity` | Schema SSOT | schemas.controller.ts |
288
+
289
+ ### **Exemplo Busca Multipart**
290
+ ```bash
291
+ curl -X POST http://localhost:5000/api/property/search \
292
+ -H "Content-Type: application/json" \
293
+ -d '{
294
+ "filters": {
295
+ "endereco_cidade": "Florianópolis",
296
+ "operacao": ["venda"],
297
+ "dormitorios": [2, 3, 4]
298
+ },
299
+ "search": {
300
+ "value": "apartamento com piscina"
301
+ },
302
+ "location": {
303
+ "operation": "within",
304
+ "geometry": {
305
+ "type": "bbox",
306
+ "bounds": {
307
+ "north": -27.5,
308
+ "south": -27.7,
309
+ "east": -48.4,
310
+ "west": -48.6
311
+ }
312
+ }
313
+ },
314
+ "list": {
315
+ "page": 1,
316
+ "limit": 20,
317
+ "select": {
318
+ "id": true,
319
+ "title": true,
320
+ "valor_venda": true,
321
+ "area_total": true,
322
+ "dormitorios": true
323
+ }
324
+ },
325
+ "map": {
326
+ "select": {
327
+ "id": true,
328
+ "lat": true,
329
+ "lng": true,
330
+ "valor_venda": true
331
+ }
332
+ },
333
+ "meta": { "total": true },
334
+ "facets": {
335
+ "fields": [
336
+ { "type": "terms", "field": "tipo" },
337
+ { "type": "terms", "field": "endereco_bairro" },
338
+ { "type": "range", "field": "valor_venda", "buckets": [100000, 300000, 500000] }
339
+ ]
340
+ }
341
+ }'
342
+ ```
343
+
344
+ **Response:**
345
+ ```json
346
+ {
347
+ "results": {
348
+ "list": {
349
+ "data": [
350
+ {
351
+ "id": 1,
352
+ "title": "Apartamento 3 dormitórios com piscina",
353
+ "valor_venda": 450000,
354
+ "area_total": 120.50,
355
+ "dormitorios": 3
356
+ }
357
+ ],
358
+ "meta": {
359
+ "page": 1,
360
+ "limit": 20,
361
+ "total": 150,
362
+ "totalPages": 8,
363
+ "hasNextPage": true
364
+ }
365
+ },
366
+ "map": {
367
+ "data": [
368
+ {
369
+ "id": 1,
370
+ "lat": -27.5969,
371
+ "lng": -48.5495,
372
+ "valor_venda": 450000
373
+ }
374
+ ],
375
+ "meta": { "totalWithCoordinates": 127 }
376
+ },
377
+ "meta": { "total": 150 },
378
+ "facets": {
379
+ "data": {
380
+ "tipo": [
381
+ { "value": "apartamento", "count": 89 },
382
+ { "value": "casa", "count": 61 }
383
+ ],
384
+ "endereco_bairro": [
385
+ { "value": "Centro", "count": 45 },
386
+ { "value": "Trindade", "count": 32 }
387
+ ],
388
+ "valor_venda": [
389
+ { "range": "0-100000", "count": 15 },
390
+ { "range": "100000-300000", "count": 67 },
391
+ { "range": "300000-500000", "count": 42 },
392
+ { "range": "500000+", "count": 26 }
393
+ ]
394
+ }
395
+ }
396
+ },
397
+ "metadata": {
398
+ "executionTime": 89
399
+ }
400
+ }
401
+ ```
402
+
403
+ ---
404
+
405
+ ## **Testes Completos**
406
+
407
+ ### **Suíte de Testes:** `__tests__/`
408
+
409
+ | Teste | Status | Descrição |
410
+ |-------|--------|-----------|
411
+ | `final-integration.test.ts` | | Teste integração completa API |
412
+ | `new-api-advanced-search.test.ts` | | API isolada biblioteca |
413
+ | `debug-advanced-search.test.ts` | | Debug específicos |
414
+ | `prisma-introspection.test.ts` | | DMMF introspection |
415
+
416
+ ### **Executar Testes**
417
+ ```bash
418
+ # Todos os testes
419
+ pnpm test
420
+
421
+ # Teste específico
422
+ pnpm vitest run __tests__/final-integration.test.ts
423
+
424
+ # Watch mode
425
+ pnpm test:watch
426
+ ```
427
+
428
+ ---
429
+
430
+ ## **Comandos Principais**
431
+
432
+ ### **Desenvolvimento**
433
+ ```bash
434
+ # Instalar dependências
435
+ pnpm install
436
+
437
+ # Desenvolvimento (porta 5000)
438
+ pnpm dev
439
+
440
+ # Build produção
441
+ pnpm build
442
+ ```
443
+
444
+ ### **Database**
445
+ ```bash
446
+ # Gerar Prisma Client
447
+ pnpm prisma:generate
448
+
449
+ # Push schema
450
+ pnpm prisma:push
451
+
452
+ # Executar fragment SQL
453
+ PGPASSWORD=$POSTGRES_PASSWORD psql $DATABASE_URL -f src/modules/property/config/database/fragment.sql
454
+ ```
455
+
456
+ ### **Scripts Desenvolvimento**
457
+ ```bash
458
+ # Upload dados Jetimob (chunks)
459
+ pnpm tsx __dev__/scripts/sync-properties-chunked.ts
460
+
461
+ # Geração Zod schemas
462
+ pnpm tsx __dev__/scripts/generate-zod.ts
463
+ ```
464
+
465
+ ---
466
+
467
+ ## **Migração Concluída**
468
+
469
+ ### **FASE COMPLETA: IMOVEL → PROPERTY + CORRETOR → BROKER**
470
+
471
+ A aplicação passou por **migração completa** de nomenclatura e arquitetura:
472
+
473
+ - **Renomeação massiva** - 50+ arquivos atualizados
474
+ - **Database migration** - Tabelas Property + Broker
475
+ - **API endpoints** - `/api/property/*` funcionais
476
+ - **Type-safety** - Zero breaking changes
477
+ - **Backward compatibility** - Sistemas legados funcionando
478
+ - **Tests passing** - Suíte completa validada
479
+
480
+ ### **Scripts de Migração**
481
+ ```bash
482
+ # Aplicados e concluídos:
483
+ ./__dev__/scripts/rename-imovel-to-property.sh
484
+ ./__dev__/scripts/rename-corretor-to-broker.sh
485
+ ```
486
+
487
+ ---
488
+
489
+ ## **Performance & Otimizações**
490
+
491
+ ### **Índices Database**
492
+ - **B-Tree:** reference, created_at, tipo, endereco_cidade
493
+ - **GIN:** search_tsvector (full-text)
494
+ - **GIST:** location (geospatial)
495
+ - **Composite:** [lat, lng], [status], [valor_venda]
496
+
497
+ ### **Queries Otimizadas**
498
+ - **Select específico** por tipo resposta
499
+ - **Execução paralela** de múltiplas queries
500
+ - **Prepared statements** via Prisma
501
+ - **Paginação eficiente** com limit/offset
502
+
503
+ ### **Caching**
504
+ - **Prisma Client** singleton
505
+ - **ETag** para schemas estáticos
506
+ - **Database connection pooling**
507
+
508
+ ---
509
+
510
+ ## **Dados Atuais**
511
+
512
+ ### **Status Database**
513
+ - **1.076 propriedades** Jetimob importadas
514
+ - **search_tsvector** populado em todas
515
+ - **location geometry** populado com lat/lng válidos
516
+ - **Índices** todos criados e funcionais
517
+
518
+ ### **Coverage Campos**
519
+ - **90+ campos** mapeados no SSOT
520
+ - **25+ campos** indexados para busca full-text
521
+ - **Geolocalização** em 98% das propriedades
522
+ - **Arrays** (caracteristicas, operacao, tags) funcionais
523
+
524
+ ---
525
+
526
+ ## **Próximos Passos**
527
+
528
+ ### **Funcionalidades Futuras**
529
+ - [ ] **Cache avançado** (Redis)
530
+ - [ ] **API versioning** (v1/v2)
531
+ - [ ] **GraphQL endpoint** (opcional)
532
+ - [ ] **Webhooks** para integração
533
+ - [ ] **Rate limiting**
534
+ - [ ] **Authentication/Authorization**
535
+
536
+ ### **Melhorias Técnicas**
537
+ - [ ] **Monitoring** (APM)
538
+ - [ ] **Logging estruturado**
539
+ - [ ] **Health checks** avançados
540
+ - [ ] **Docker deployment**
541
+ - [ ] **CI/CD pipeline**
542
+
543
+ ---
544
+
545
+ ## **Equipe & Contato**
546
+
547
+ **Desenvolvido por:** Jefferson Rosa
548
+ **Versão:** 3.0.0
549
+ **Status:** **PRODUÇÃO PRONTA**
550
+ **Última Atualização:** 23/08/2025
551
+
552
+ ---
553
+
554
+ ## **Conclusão**
555
+
556
+ Este projeto representa uma **evolução significativa** na arquitetura de APIs imobiliárias:
557
+
558
+ - **Arquitetura moderna** e escalável
559
+ - **Type safety** em toda stack
560
+ - **Biblioteca reutilizável** isolada
561
+ - **Performance otimizada** com PostGIS
562
+ - **API robusta** com validação completa
563
+ - **Migração completa** do sistema legacy
564
+ - **Testes abrangentes** garantindo qualidade
565
+
566
+ A aplicação está **pronta para produção** e serve como **base sólida** para futuras expansões no ecossistema Nova Torres.