@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,624 @@
1
+ # Padrão de Criação de Módulos - API v2
2
+
3
+ > Guia completo para criar novos módulos/entidades no sistema (ex: Branch, Lead, City)
4
+
5
+ ## Arquitetura Completa de um Módulo
6
+
7
+ Um módulo completo possui **4 componentes principais**:
8
+
9
+ | Componente | Localização | Responsabilidade |
10
+ |------------|-------------|------------------|
11
+ | **NPM Package** | `/horizon-npm-packages/horizon-modules/{entity}/domain-data/` | Schema base (TypeScript + Zod) |
12
+ | **Database** | `/apps/api/database/tables/{entity}/` | SQL puro (CREATE, índices, FTS) |
13
+ | **API Module** | `/apps/api/src/modules/{entity}/` | Controllers, Services, Mappers |
14
+ | **API Routes** | `/apps/api/src/app/api/{entity}/` | Endpoints Next.js |
15
+
16
+ ---
17
+
18
+ ## Estrutura de Pastas Completa
19
+
20
+ ### NPM Package (`@horizon-domains/{entity}-model`)
21
+
22
+ ```
23
+ horizon-npm-packages/horizon-modules/{entity}/domain-data/
24
+ ├── package.json # @horizon-domains/{entity}-model
25
+ ├── tsconfig.json # Configuração TypeScript
26
+ ├── tsup.config.ts # Build config (CJS, ESM, DTS)
27
+ ├── vitest.config.ts # Testes
28
+ └── src/
29
+ ├── index.ts # Exports principais
30
+ └── schemas/
31
+ ├── types.ts # Tipos base (EntitySchema, FieldSchema, etc.)
32
+ ├── horizon-{entity}-schema-base.ts # Schema TypeScript
33
+ └── horizon-{entity}-schema-base.zod.ts # Schema Zod para validação
34
+ ```
35
+
36
+ ### Database (SQL)
37
+
38
+ ```
39
+ apps/api/database/tables/{entity}/
40
+ ├── create.sql # CREATE TABLE + índices
41
+ ├── generated-columns.sql # FTS tsvector, campos derivados
42
+ └── drop.sql # DROP TABLE (desenvolvimento)
43
+ ```
44
+
45
+ ### API Module
46
+
47
+ ```
48
+ apps/api/src/modules/{entity}/
49
+ ├── schemas/ # Config/Schemas (renomeado de config/)
50
+ │ ├── entity-schema.ts # Schema TypeScript (SSOT local)
51
+ │ ├── entity-schema.zod.ts # Validação Zod
52
+ │ └── search-schema.ts # Schema de busca (se aplicável)
53
+
54
+ ├── controllers/
55
+ │ ├── find.controller.ts # POST busca única
56
+ │ ├── search.controller.ts # POST busca avançada
57
+ │ ├── sync.controller.ts # PUT upsert / DELETE
58
+ │ └── schemas.controller.ts # GET schemas JSON
59
+
60
+ ├── services/
61
+ │ ├── find.service.ts # Lógica de busca única
62
+ │ ├── search.service.ts # Lógica de busca múltipla
63
+ │ └── sync.service.ts # Lógica de sync/upsert/delete
64
+
65
+ ├── mappers/
66
+ │ ├── computedFields/ # Campos computados
67
+ │ │ ├── index.ts # Orquestra computações
68
+ │ │ └── buildSlug.ts # Gera slug (exemplo)
69
+ │ │
70
+ │ └── dataModifier/ # Regras de negócio
71
+ │ └── index.ts # Transformações cliente-específicas
72
+
73
+ └── lib/ # Funções auxiliares (opcional)
74
+ ```
75
+
76
+ ### API Routes
77
+
78
+ ```
79
+ apps/api/src/app/api/{entity}/
80
+ ├── find/
81
+ │ └── route.ts # POST /api/{entity}/find
82
+ ├── search/
83
+ │ └── route.ts # POST /api/{entity}/search
84
+ ├── sync/
85
+ │ └── route.ts # PUT/DELETE /api/{entity}/sync
86
+ └── schemas/
87
+ ├── entity/
88
+ │ └── route.ts # GET /api/{entity}/schemas/entity
89
+ └── search/
90
+ └── route.ts # GET /api/{entity}/schemas/search
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Passo a Passo para Criar Novo Módulo
96
+
97
+ ### Passo 1: Criar NPM Package
98
+
99
+ **1.1 Criar estrutura de diretórios:**
100
+ ```bash
101
+ mkdir -p /horizon-npm-packages/horizon-modules/{entity}/domain-data/src/schemas
102
+ ```
103
+
104
+ **1.2 Criar `package.json`:**
105
+ ```json
106
+ {
107
+ "name": "@horizon-domains/{entity}-model",
108
+ "version": "1.0.0",
109
+ "description": "Schema definitions for {Entity}",
110
+ "main": "dist/index.js",
111
+ "module": "dist/index.mjs",
112
+ "types": "dist/index.d.ts",
113
+ "exports": {
114
+ ".": {
115
+ "import": "./dist/index.mjs",
116
+ "require": "./dist/index.js",
117
+ "types": "./dist/index.d.ts"
118
+ }
119
+ },
120
+ "scripts": {
121
+ "build": "tsup",
122
+ "test": "vitest"
123
+ },
124
+ "dependencies": {
125
+ "zod": "^3.24.2"
126
+ },
127
+ "devDependencies": {
128
+ "tsup": "^8.0.0",
129
+ "typescript": "^5.0.0",
130
+ "vitest": "^1.0.0"
131
+ }
132
+ }
133
+ ```
134
+
135
+ **1.3 Criar `tsconfig.json`:**
136
+ ```json
137
+ {
138
+ "compilerOptions": {
139
+ "target": "ES2020",
140
+ "module": "ESNext",
141
+ "moduleResolution": "bundler",
142
+ "declaration": true,
143
+ "declarationMap": true,
144
+ "strict": true,
145
+ "esModuleInterop": true,
146
+ "skipLibCheck": true,
147
+ "outDir": "./dist"
148
+ },
149
+ "include": ["src/**/*"],
150
+ "exclude": ["node_modules", "dist"]
151
+ }
152
+ ```
153
+
154
+ **1.4 Criar `tsup.config.ts`:**
155
+ ```typescript
156
+ import { defineConfig } from "tsup";
157
+
158
+ export default defineConfig({
159
+ entry: ["src/index.ts"],
160
+ format: ["cjs", "esm"],
161
+ dts: true,
162
+ clean: true,
163
+ sourcemap: true,
164
+ });
165
+ ```
166
+
167
+ **1.5 Criar `src/schemas/types.ts`:**
168
+ ```typescript
169
+ // Reexportar tipos base do property-model ou definir localmente
170
+ export interface FieldSchema {
171
+ type: "string" | "number" | "boolean" | "date" | "array" | "object" | "json";
172
+ required?: boolean;
173
+ description?: string;
174
+ ui?: UIMetadata;
175
+ }
176
+
177
+ export interface UIMetadata {
178
+ label?: string;
179
+ placeholder?: string;
180
+ inputType?: string;
181
+ }
182
+
183
+ export interface EntitySchema {
184
+ name: string;
185
+ description?: string;
186
+ fields: Record<string, FieldSchema>;
187
+ }
188
+ ```
189
+
190
+ **1.6 Criar `src/schemas/horizon-{entity}-schema-base.ts`:**
191
+ ```typescript
192
+ import type { EntitySchema } from "./types";
193
+
194
+ export const horizon{Entity}SchemaBase: EntitySchema = {
195
+ name: "{Entity}",
196
+ description: "Schema base para {Entity}",
197
+ fields: {
198
+ key: { type: "string", required: true, description: "Identificador único" },
199
+ name: { type: "string", required: true, description: "Nome" },
200
+ slug: { type: "string", required: false, description: "URL amigável" },
201
+ // Adicione campos específicos aqui
202
+ }
203
+ };
204
+ ```
205
+
206
+ **1.7 Criar `src/schemas/horizon-{entity}-schema-base.zod.ts`:**
207
+ ```typescript
208
+ import { z } from "zod";
209
+
210
+ export const Horizon{Entity}SchemaBaseZod = z.object({
211
+ key: z.string().min(1),
212
+ name: z.string().min(1),
213
+ slug: z.string().optional(),
214
+ // Adicione validações Zod aqui
215
+ });
216
+
217
+ // Versão passthrough (aceita campos extras)
218
+ export const Horizon{Entity}SchemaBaseZodPassthrough = Horizon{Entity}SchemaBaseZod.passthrough();
219
+
220
+ export type Horizon{Entity}SchemaBaseType = z.infer<typeof Horizon{Entity}SchemaBaseZod>;
221
+ ```
222
+
223
+ **1.8 Criar `src/index.ts`:**
224
+ ```typescript
225
+ // Schema TypeScript
226
+ export { horizon{Entity}SchemaBase } from "./schemas/horizon-{entity}-schema-base";
227
+
228
+ // Types
229
+ export type { EntitySchema, FieldSchema, UIMetadata } from "./schemas/types";
230
+
231
+ // Schema Zod
232
+ export {
233
+ Horizon{Entity}SchemaBaseZod,
234
+ Horizon{Entity}SchemaBaseZodPassthrough,
235
+ type Horizon{Entity}SchemaBaseType,
236
+ } from "./schemas/horizon-{entity}-schema-base.zod";
237
+ ```
238
+
239
+ **1.9 Build do pacote:**
240
+ ```bash
241
+ cd /horizon-npm-packages/horizon-modules/{entity}/domain-data
242
+ pnpm install
243
+ pnpm build
244
+ ```
245
+
246
+ ---
247
+
248
+ ### Passo 2: Criar Tabela SQL
249
+
250
+ **2.1 Criar `database/tables/{entity}/create.sql`:**
251
+ ```sql
252
+ -- =============================================
253
+ -- TABELA: {entity}
254
+ -- =============================================
255
+
256
+ CREATE TABLE IF NOT EXISTS {entity} (
257
+ -- Sistema
258
+ id SERIAL PRIMARY KEY,
259
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
260
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
261
+
262
+ -- Identificação (obrigatórios)
263
+ key VARCHAR(255) UNIQUE NOT NULL,
264
+ name VARCHAR(500) NOT NULL,
265
+ slug VARCHAR(600),
266
+
267
+ -- Campos específicos
268
+ -- Adicione campos aqui
269
+
270
+ -- Full-text search
271
+ search_tsvector TSVECTOR
272
+ );
273
+
274
+ -- Índices
275
+ CREATE INDEX IF NOT EXISTS idx_{entity}_key ON {entity}(key);
276
+ CREATE INDEX IF NOT EXISTS idx_{entity}_slug ON {entity}(slug);
277
+ CREATE INDEX IF NOT EXISTS idx_{entity}_search ON {entity} USING GIN(search_tsvector);
278
+ ```
279
+
280
+ **2.2 Criar `database/tables/{entity}/generated-columns.sql`:**
281
+ ```sql
282
+ -- =============================================
283
+ -- GENERATED COLUMNS: {entity}
284
+ -- Full-text search e campos derivados
285
+ -- =============================================
286
+
287
+ -- Função para atualizar tsvector
288
+ CREATE OR REPLACE FUNCTION update_{entity}_search_tsvector()
289
+ RETURNS TRIGGER AS $$
290
+ BEGIN
291
+ NEW.search_tsvector :=
292
+ setweight(to_tsvector('portuguese', COALESCE(NEW.name, '')), 'A');
293
+ -- Adicione mais campos conforme necessário
294
+ RETURN NEW;
295
+ END;
296
+ $$ LANGUAGE plpgsql;
297
+
298
+ -- Trigger para atualização automática
299
+ DROP TRIGGER IF EXISTS trigger_update_{entity}_search ON {entity};
300
+ CREATE TRIGGER trigger_update_{entity}_search
301
+ BEFORE INSERT OR UPDATE ON {entity}
302
+ FOR EACH ROW
303
+ EXECUTE FUNCTION update_{entity}_search_tsvector();
304
+ ```
305
+
306
+ **2.3 Criar `database/tables/{entity}/drop.sql`:**
307
+ ```sql
308
+ -- DROP (apenas desenvolvimento)
309
+ DROP TABLE IF EXISTS {entity} CASCADE;
310
+ DROP FUNCTION IF EXISTS update_{entity}_search_tsvector();
311
+ ```
312
+
313
+ **2.4 Executar no banco e sincronizar Prisma:**
314
+ ```bash
315
+ # Executar SQL no banco (psql ou DBeaver)
316
+ psql -d your_db -f database/tables/{entity}/create.sql
317
+ psql -d your_db -f database/tables/{entity}/generated-columns.sql
318
+
319
+ # Sincronizar Prisma (pull do banco)
320
+ pnpm prisma db pull
321
+ pnpm prisma generate
322
+ ```
323
+
324
+ ---
325
+
326
+ ### Passo 3: Criar Módulo na API
327
+
328
+ **3.1 Criar estrutura de pastas:**
329
+ ```bash
330
+ mkdir -p src/modules/{entity}/{schemas,controllers,services,mappers/computedFields,mappers/dataModifier}
331
+ ```
332
+
333
+ **3.2 Criar `schemas/entity-schema.ts`:**
334
+ ```typescript
335
+ /**
336
+ * Entity Schema do {Entity} - SSOT Local
337
+ */
338
+
339
+ // Importa do NPM Package (ou define localmente)
340
+ // import { horizon{Entity}SchemaBase } from '@horizon-domains/{entity}-model';
341
+
342
+ export const {entity}EntitySchema = {
343
+ name: "{Entity}",
344
+ fields: {
345
+ key: { type: "string", required: true },
346
+ name: { type: "string", required: true },
347
+ slug: { type: "string", required: false },
348
+ // ...campos
349
+ },
350
+ // Override local se necessário
351
+ displayOverrides: {}
352
+ };
353
+ ```
354
+
355
+ **3.3 Criar `mappers/computedFields/index.ts`:**
356
+ ```typescript
357
+ /**
358
+ * 🔄 Computed Fields do {Entity}
359
+ */
360
+ import { buildSlug } from "./buildSlug";
361
+
362
+ export function compute{Entity}Fields(data: any, config?: any): any {
363
+ const enriched = { ...data };
364
+
365
+ // 1. Gerar slug
366
+ if (!enriched.slug && enriched.name) {
367
+ enriched.slug = buildSlug(enriched);
368
+ }
369
+
370
+ // 2. Timestamps
371
+ if (!enriched.created_at) {
372
+ enriched.created_at = new Date();
373
+ }
374
+ enriched.updated_at = new Date();
375
+
376
+ return enriched;
377
+ }
378
+ ```
379
+
380
+ **3.4 Criar `mappers/computedFields/buildSlug.ts`:**
381
+ ```typescript
382
+ /**
383
+ * Build Slug
384
+ */
385
+ export function buildSlug(data: any): string {
386
+ if (!data.name) return data.key || "{entity}";
387
+
388
+ const baseSlug = data.name
389
+ .toLowerCase()
390
+ .normalize("NFD")
391
+ .replace(/[\u0300-\u036f]/g, "") // Remove acentos
392
+ .replace(/[^a-z0-9]+/g, "-") // Substitui especiais
393
+ .replace(/^-+|-+$/g, "") // Remove - bordas
394
+ .substring(0, 50);
395
+
396
+ return data.key ? `${baseSlug}-${data.key}` : baseSlug;
397
+ }
398
+ ```
399
+
400
+ **3.5 Criar `mappers/dataModifier/index.ts`:**
401
+ ```typescript
402
+ /**
403
+ * Data Modifier do {Entity}
404
+ * Aplica regras de negócio (passthrough por padrão)
405
+ */
406
+ export function modify{Entity}Data<T extends Record<string, any>>(data: T): T {
407
+ // Passthrough - adicione regras conforme necessidade
408
+ return data;
409
+ }
410
+ ```
411
+
412
+ **3.6 Criar `services/sync.service.ts`:**
413
+ ```typescript
414
+ import { prisma } from '@/core/prisma';
415
+ import { modify{Entity}Data } from '../mappers/dataModifier';
416
+ import { compute{Entity}Fields } from '../mappers/computedFields';
417
+
418
+ export interface UpsertResponse {
419
+ inserted: number;
420
+ updated: number;
421
+ keys: string[];
422
+ }
423
+
424
+ export async function upsert{Entity}sService(
425
+ items: any[],
426
+ config?: any
427
+ ): Promise<UpsertResponse> {
428
+ if (!Array.isArray(items) || items.length === 0) {
429
+ return { inserted: 0, updated: 0, keys: [] };
430
+ }
431
+
432
+ const prepared: any[] = [];
433
+ const keys: string[] = [];
434
+
435
+ for (const item of items) {
436
+ const modified = modify{Entity}Data(item);
437
+ const enriched = compute{Entity}Fields(modified, config);
438
+ prepared.push(enriched);
439
+ keys.push(item.key);
440
+ }
441
+
442
+ const result = await prisma.$transaction(async (tx) => {
443
+ const deleteResult = await tx.{entity}.deleteMany({
444
+ where: { key: { in: keys } }
445
+ });
446
+
447
+ const insertResult = await tx.{entity}.createMany({
448
+ data: prepared,
449
+ skipDuplicates: true
450
+ });
451
+
452
+ return { deleted: deleteResult.count, inserted: insertResult.count };
453
+ });
454
+
455
+ return { inserted: result.inserted, updated: result.inserted, keys };
456
+ }
457
+
458
+ export async function delete{Entity}sService(keys: string[]): Promise<any> {
459
+ if (!Array.isArray(keys) || keys.length === 0) {
460
+ return { deleted: 0, keys: [] };
461
+ }
462
+
463
+ const result = await prisma.{entity}.deleteMany({
464
+ where: { key: { in: keys } }
465
+ });
466
+
467
+ return { deleted: result.count, keys };
468
+ }
469
+ ```
470
+
471
+ **3.7 Criar Controllers e Routes:**
472
+
473
+ Seguir o mesmo padrão dos módulos existentes (property, broker).
474
+ Exportar handlers nos arquivos route.ts.
475
+
476
+ ---
477
+
478
+ ### Passo 4: Testar Endpoints
479
+
480
+ ```bash
481
+ # 1. Inserir dados (Sync)
482
+ curl -X PUT http://localhost:3000/api/{entity}/sync \
483
+ -H "Content-Type: application/json" \
484
+ -d '{ "{entity}s": [{ "key": "test-001", "name": "Test" }] }'
485
+
486
+ # 2. Buscar por critério (Find)
487
+ curl -X POST http://localhost:3000/api/{entity}/find \
488
+ -H "Content-Type: application/json" \
489
+ -d '{ "criteria": { "key": "test-001" } }'
490
+
491
+ # 3. Busca avançada (Search)
492
+ curl -X POST http://localhost:3000/api/{entity}/search \
493
+ -H "Content-Type: application/json" \
494
+ -d '{ "list": { "page": 1, "limit": 10 } }'
495
+
496
+ # 4. Deletar (Sync DELETE)
497
+ curl -X DELETE http://localhost:3000/api/{entity}/sync \
498
+ -H "Content-Type: application/json" \
499
+ -d '{ "keys": ["test-001"] }'
500
+ ```
501
+
502
+ ---
503
+
504
+ ## 📌 Checklist de Validação
505
+
506
+ ### NPM Package
507
+ - [ ] `package.json` com nome `@horizon-domains/{entity}-model`
508
+ - [ ] Schema TypeScript criado (`horizon-{entity}-schema-base.ts`)
509
+ - [ ] Schema Zod criado (`horizon-{entity}-schema-base.zod.ts`)
510
+ - [ ] `src/index.ts` exportando tudo
511
+ - [ ] Build funciona (`pnpm build`)
512
+
513
+ ### Database
514
+ - [ ] `create.sql` com tabela e índices
515
+ - [ ] `generated-columns.sql` com FTS tsvector
516
+ - [ ] SQL executado no banco
517
+ - [ ] Prisma sincronizado (`prisma db pull && prisma generate`)
518
+
519
+ ### API Module
520
+ - [ ] Estrutura de pastas criada (`schemas/`, `controllers/`, `services/`, `mappers/`)
521
+ - [ ] `entity-schema.ts` criado
522
+ - [ ] `computedFields/` com `index.ts` e `buildSlug.ts`
523
+ - [ ] `dataModifier/index.ts` criado (passthrough)
524
+ - [ ] Services implementados (`sync`, `find`, `search`)
525
+ - [ ] Controllers implementados com validação Zod
526
+ - [ ] Routes configuradas em `/app/api/{entity}/`
527
+
528
+ ### Testes
529
+ - [ ] PUT sync funciona (inserir)
530
+ - [ ] POST find funciona (buscar)
531
+ - [ ] POST search funciona (listar)
532
+ - [ ] DELETE sync funciona (deletar)
533
+ - [ ] Slug é gerado automaticamente
534
+
535
+ ---
536
+
537
+ ## Arquitetura WEB - Estrutura de Módulos no Frontend
538
+
539
+ Um módulo no frontend (`/apps/web/src/modules/{entity}/`) segue estrutura padronizada:
540
+
541
+ ### **Estrutura Completa WEB**
542
+
543
+ ```
544
+ apps/web/src/modules/{entity}/
545
+ ├── core/ # Lógica core do módulo
546
+ │ ├── module.config.ts # Configuração do módulo
547
+ │ ├── item/ # Tudo relacionado a UM item
548
+ │ │ ├── index.ts # Exports
549
+ │ │ ├── {entity}-url-builder.ts # Builder de URLs
550
+ │ │ ├── enrichMapper.ts # Mapper para enriquecer dados
551
+ │ │ ├── schemas/ # Schemas do item
552
+ │ │ │ ├── index.ts # Exports dos schemas
553
+ │ │ │ ├── schema.ts # Schema TypeScript (SSOT)
554
+ │ │ │ ├── schema.generated.json # Schema gerado da API
555
+ │ │ │ ├── base-schema.json # Schema base (campos)
556
+ │ │ │ ├── display-layer-schema.json # Overrides de UI
557
+ │ │ │ └── computed-schema.json # Campos computados
558
+ │ │ └── computed-fields/ # Campos computados runtime
559
+ │ │ ├── index.ts
560
+ │ │ ├── {entity}-url.ts # Gerador de URL
561
+ │ │ └── assembleComputedData.ts
562
+ │ ├── list/ # Tudo relacionado a LISTA (se houver)
563
+ │ │ └── ...
564
+ │ ├── libs/ # Bibliotecas específicas (se houver)
565
+ │ │ └── ...
566
+ │ └── services/ # Services do módulo
567
+ │ └── {Entity}Service.ts
568
+
569
+ └── features/ # Features/páginas do módulo
570
+ ├── item-display/ # Página individual
571
+ │ └── page-display/
572
+ │ ├── components/
573
+ │ └── data/
574
+ └── search-list/ # Listagem com busca (se houver)
575
+ ├── components/
576
+ ├── config/
577
+ └── hooks/
578
+ ```
579
+
580
+ ### **Arquivos Obrigatórios por Módulo**
581
+
582
+ | Arquivo | Descrição |
583
+ |---------|-----------|
584
+ | `core/item/index.ts` | Exportações do item |
585
+ | `core/item/schemas/index.ts` | Exportações dos schemas |
586
+ | `core/item/schemas/schema.ts` | Schema TypeScript |
587
+ | `core/item/schemas/schema.generated.json` | Schema da API |
588
+ | `core/item/enrichMapper.ts` | Mapper de enriquecimento |
589
+ | `core/item/{entity}-url-builder.ts` | Construtor de URLs |
590
+
591
+ ### **Comparação Property vs Broker**
592
+
593
+ | Aspecto | Property (Completo) | Broker (Simplificado) |
594
+ |---------|---------------------|----------------------|
595
+ | `core/item/schemas/` | Completo | Completo |
596
+ | `core/item/computed-fields/` | Sim | Sim |
597
+ | `core/libs/` | 7 módulos | Não precisa |
598
+ | `core/list/` | Sim | Não precisa |
599
+ | `features/search-list/` | 20+ componentes | Fora do escopo |
600
+
601
+ ---
602
+
603
+ ## Referências
604
+
605
+ | Módulo | Descrição | Complexidade |
606
+ |--------|-----------|--------------|
607
+ | **Property** | Módulo completo de imóveis | Alta (com CRM, rules, computed) |
608
+ | **Broker** | Módulo simplificado de corretores | Baixa (passthrough) |
609
+
610
+ Use o **Broker** como template para módulos simples.
611
+ Use o **Property** como referência para módulos complexos.
612
+
613
+ ---
614
+
615
+ ## Convenções
616
+
617
+ | Elemento | Convenção | Exemplo |
618
+ |----------|-----------|---------|
619
+ | Types/Interfaces | PascalCase | `BrokerEntity` |
620
+ | Funções | camelCase | `findBrokerService` |
621
+ | Arquivos | kebab-case | `entity-schema.ts` |
622
+ | Tabelas SQL | snake_case | `broker` |
623
+ | NPM Package | kebab-case | `@horizon-domains/broker-model` |
624
+ | Pastas | kebab-case | `computed-fields/` ou `computedFields/` |