drizzle-multitenant 1.0.4 → 1.0.6

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,385 @@
1
+ # Proposal: Melhorias Identificadas no PrimeSys-v2
2
+
3
+ > **Status**: Proposta
4
+ > **Origem**: Integração com PrimeSys-v2
5
+ > **Data**: 2024-12-23
6
+
7
+ ## Contexto
8
+
9
+ Durante a integração do `drizzle-multitenant` no projeto PrimeSys-v2 (multi-tenant SaaS para gestão industrial), foram identificadas melhorias que beneficiariam o pacote e outros usuários.
10
+
11
+ ---
12
+
13
+ ## 1. CLI: Flag `--mark-applied`
14
+
15
+ ### Problema
16
+
17
+ Projetos que já têm migrations aplicadas (via scripts legados) precisam sincronizar o tracking sem re-executar as migrations.
18
+
19
+ ### Solução
20
+
21
+ Adicionar flag `--mark-applied` ao comando `migrate`:
22
+
23
+ ```bash
24
+ # Marca migrations como aplicadas sem executar SQL
25
+ npx drizzle-multitenant migrate --all --mark-applied
26
+
27
+ # Para tenant específico
28
+ npx drizzle-multitenant migrate --tenant=abc --mark-applied
29
+ ```
30
+
31
+ ### Implementação
32
+
33
+ ```typescript
34
+ // src/cli/commands/migrate.ts
35
+ .option('--mark-applied', 'Mark migrations as applied without executing SQL')
36
+
37
+ // No handler
38
+ if (options.markApplied) {
39
+ await migrator.markAllAsApplied(tenantIds);
40
+ } else {
41
+ await migrator.migrateAll({ concurrency, dryRun });
42
+ }
43
+ ```
44
+
45
+ ### Benefício
46
+
47
+ - Facilita migração de projetos existentes
48
+ - Útil para sincronizar ambientes de staging/produção
49
+ - Evita necessidade de scripts manuais
50
+
51
+ ---
52
+
53
+ ## 2. CLI: Comando `sync`
54
+
55
+ ### Problema
56
+
57
+ Detectar e corrigir divergências entre migrations em disco e tracking no banco.
58
+
59
+ ### Solução
60
+
61
+ Novo comando `sync` com opções:
62
+
63
+ ```bash
64
+ # Mostrar divergências
65
+ npx drizzle-multitenant sync --status
66
+
67
+ # Marcar migrations faltantes como aplicadas
68
+ npx drizzle-multitenant sync --mark-missing
69
+
70
+ # Remover registros órfãos (migrations que não existem mais em disco)
71
+ npx drizzle-multitenant sync --clean-orphans
72
+ ```
73
+
74
+ ### Casos de Uso
75
+
76
+ 1. **Migration renomeada**: Arquivo renomeado, mas registro antigo no banco
77
+ 2. **Migration deletada**: Arquivo removido, registro ainda existe
78
+ 3. **Ambiente dessincronizado**: Migrations aplicadas manualmente
79
+
80
+ ---
81
+
82
+ ## 3. Compatibilidade com Tabelas de Tracking Legadas
83
+
84
+ ### Problema
85
+
86
+ Projetos podem usar estruturas de tabela diferentes:
87
+ - Hash-based: `id, hash, created_at`
88
+ - Name-based: `id, name, applied_at` (padrão drizzle-multitenant)
89
+
90
+ ### Solução
91
+
92
+ Suportar múltiplos formatos ou migração automática:
93
+
94
+ ```typescript
95
+ // tenant.config.ts
96
+ migrations: {
97
+ tenantFolder: "./drizzle/tenant",
98
+ tenantDiscovery: discoverTenants,
99
+ migrationsTable: "__drizzle_migrations",
100
+
101
+ // NOVO: Formato da tabela
102
+ tableFormat: "name", // "name" | "hash" | "auto-detect"
103
+
104
+ // NOVO: Migrar formato automaticamente
105
+ autoMigrateFormat: true,
106
+ }
107
+ ```
108
+
109
+ ### Alternativa
110
+
111
+ Script de migração incluído no pacote:
112
+
113
+ ```bash
114
+ npx drizzle-multitenant migrate-tracking-format --from=hash --to=name
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 4. Health Check API
120
+
121
+ ### Problema
122
+
123
+ Aplicações precisam verificar saúde dos pools para load balancers e monitoring.
124
+
125
+ ### Solução
126
+
127
+ Método `healthCheck()` no TenantManager:
128
+
129
+ ```typescript
130
+ const manager = createTenantManager(config);
131
+
132
+ const health = await manager.healthCheck();
133
+ // {
134
+ // healthy: true,
135
+ // pools: [
136
+ // { tenantId: 'abc', status: 'ok', connections: 5, idle: 3 },
137
+ // { tenantId: 'def', status: 'degraded', connections: 1, idle: 0 },
138
+ // ],
139
+ // sharedDb: { status: 'ok', connections: 10 },
140
+ // timestamp: '2024-12-23T10:30:00Z'
141
+ // }
142
+
143
+ // Endpoint para load balancers
144
+ app.get('/health/db', async (req, res) => {
145
+ const health = await manager.healthCheck();
146
+ res.status(health.healthy ? 200 : 503).json(health);
147
+ });
148
+ ```
149
+
150
+ ### Já no Roadmap
151
+
152
+ Previsto para v1.1.0 (Resiliência e Observabilidade).
153
+
154
+ ---
155
+
156
+ ## 5. Métricas Prometheus
157
+
158
+ ### Problema
159
+
160
+ Monitoramento de pools e queries é essencial para produção.
161
+
162
+ ### Solução
163
+
164
+ Exportar métricas no formato Prometheus:
165
+
166
+ ```typescript
167
+ // Configuração
168
+ const config = defineConfig({
169
+ metrics: {
170
+ enabled: true,
171
+ prefix: 'drizzle_multitenant',
172
+ },
173
+ });
174
+
175
+ // Endpoint
176
+ app.get('/metrics', (req, res) => {
177
+ res.set('Content-Type', 'text/plain');
178
+ res.send(manager.getPrometheusMetrics());
179
+ });
180
+ ```
181
+
182
+ **Métricas sugeridas:**
183
+ ```
184
+ drizzle_multitenant_pool_count 15
185
+ drizzle_multitenant_pool_connections_active{tenant="abc"} 3
186
+ drizzle_multitenant_pool_connections_idle{tenant="abc"} 7
187
+ drizzle_multitenant_pool_evictions_total 42
188
+ drizzle_multitenant_query_duration_seconds_bucket{le="0.1"} 1024
189
+ ```
190
+
191
+ ### Já no Roadmap
192
+
193
+ Previsto para v1.1.0 (Resiliência e Observabilidade).
194
+
195
+ ---
196
+
197
+ ## 6. NestJS: `@TenantId()` Parameter Decorator
198
+
199
+ ### Problema
200
+
201
+ Extrair `tenantId` do request requer código boilerplate:
202
+
203
+ ```typescript
204
+ @Get()
205
+ async getData(@Param('empresaId') empresaId: string) {
206
+ return this.service.getData(empresaId);
207
+ }
208
+ ```
209
+
210
+ ### Solução
211
+
212
+ Decorator que extrai automaticamente baseado na config:
213
+
214
+ ```typescript
215
+ import { TenantId } from 'drizzle-multitenant/nestjs';
216
+
217
+ @Get()
218
+ async getData(@TenantId() tenantId: string) {
219
+ return this.service.getData(tenantId);
220
+ }
221
+ ```
222
+
223
+ ### Implementação
224
+
225
+ ```typescript
226
+ // src/integrations/nestjs/decorators/tenant-id.decorator.ts
227
+ export const TenantId = createParamDecorator(
228
+ (data: unknown, ctx: ExecutionContext): string => {
229
+ const request = ctx.switchToHttp().getRequest();
230
+ // Usa extractTenantId configurado no TenantModule
231
+ return request.tenantId; // Setado pelo middleware
232
+ },
233
+ );
234
+ ```
235
+
236
+ ---
237
+
238
+ ## 7. Pool Warmup
239
+
240
+ ### Problema
241
+
242
+ Primeiro request para um tenant tem latência maior (cold start do pool).
243
+
244
+ ### Solução
245
+
246
+ API para pré-aquecer pools:
247
+
248
+ ```typescript
249
+ // Warmup de tenants específicos
250
+ await manager.warmup(['tenant-1', 'tenant-2', 'tenant-3']);
251
+
252
+ // Warmup de todos (usar com cuidado)
253
+ const tenants = await discoverTenants();
254
+ await manager.warmup(tenants.slice(0, 20)); // Top 20 mais ativos
255
+
256
+ // No bootstrap da aplicação NestJS
257
+ @Injectable()
258
+ export class WarmupService implements OnApplicationBootstrap {
259
+ constructor(@InjectTenantManager() private manager: TenantManager) {}
260
+
261
+ async onApplicationBootstrap() {
262
+ const topTenants = await this.getTopTenants();
263
+ await this.manager.warmup(topTenants);
264
+ }
265
+ }
266
+ ```
267
+
268
+ ---
269
+
270
+ ## 8. Debug Mode Aprimorado
271
+
272
+ ### Problema
273
+
274
+ Debugar queries multi-tenant é difícil sem contexto.
275
+
276
+ ### Solução
277
+
278
+ Modo debug que loga queries com tenant context:
279
+
280
+ ```typescript
281
+ const config = defineConfig({
282
+ debug: {
283
+ enabled: process.env.NODE_ENV === 'development',
284
+ logQueries: true,
285
+ logPoolEvents: true,
286
+ slowQueryThreshold: 1000, // ms
287
+ },
288
+ });
289
+
290
+ // Output
291
+ // [drizzle-multitenant] tenant=abc query="SELECT * FROM produtos" duration=45ms
292
+ // [drizzle-multitenant] tenant=abc SLOW_QUERY query="SELECT..." duration=1523ms
293
+ ```
294
+
295
+ ---
296
+
297
+ ## 9. Retry Logic para Conexões
298
+
299
+ ### Problema
300
+
301
+ Conexões podem falhar temporariamente (network issues, DB restart).
302
+
303
+ ### Solução
304
+
305
+ Retry automático com backoff exponencial:
306
+
307
+ ```typescript
308
+ const config = defineConfig({
309
+ connection: {
310
+ url: process.env.DATABASE_URL!,
311
+ retry: {
312
+ maxAttempts: 3,
313
+ initialDelayMs: 100,
314
+ maxDelayMs: 5000,
315
+ backoffMultiplier: 2,
316
+ },
317
+ },
318
+ });
319
+ ```
320
+
321
+ ### Já no Roadmap
322
+
323
+ Previsto para v1.1.0 (Resiliência e Observabilidade).
324
+
325
+ ---
326
+
327
+ ## 10. Cross-Schema Query Improvements
328
+
329
+ ### Problema
330
+
331
+ Queries que juntam tenant + shared tables são verbosas.
332
+
333
+ ### Solução Atual (v1.0)
334
+
335
+ ```typescript
336
+ const query = createCrossSchemaQuery({
337
+ tenantDb: tenants.getDb('tenant-123'),
338
+ sharedDb: tenants.getSharedDb(),
339
+ tenantSchema: 'tenant_123',
340
+ sharedSchema: 'public',
341
+ });
342
+ ```
343
+
344
+ ### Solução Proposta
345
+
346
+ Helper mais simples:
347
+
348
+ ```typescript
349
+ // Usando TenantDbFactory
350
+ const db = this.dbFactory.getDb(tenantId);
351
+
352
+ // Novo: withShared() helper
353
+ const result = await db
354
+ .withShared(this.sharedDb)
355
+ .select({
356
+ pedidoId: pedido.id,
357
+ workflowNome: workflowStep.nome, // da tabela public
358
+ })
359
+ .from(pedido)
360
+ .leftJoin(workflowStep, eq(pedido.workflowStepId, workflowStep.id));
361
+ ```
362
+
363
+ ---
364
+
365
+ ## Priorização Sugerida
366
+
367
+ | Melhoria | Esforço | Impacto | Prioridade |
368
+ |----------|---------|---------|------------|
369
+ | `--mark-applied` flag | 2h | Alto | P0 |
370
+ | `@TenantId()` decorator | 1h | Médio | P1 |
371
+ | Pool warmup | 2h | Médio | P1 |
372
+ | Health check API | 4h | Alto | P1 |
373
+ | Debug mode | 3h | Médio | P2 |
374
+ | Retry logic | 4h | Alto | P2 |
375
+ | Métricas Prometheus | 6h | Alto | P2 |
376
+ | Sync command | 4h | Médio | P2 |
377
+ | Tabela legada compat | 4h | Baixo | P3 |
378
+ | Cross-schema helper | 6h | Médio | P3 |
379
+
380
+ ---
381
+
382
+ ## Referências
383
+
384
+ - [PrimeSys-v2 Migration Proposal](../../PrimeSys-v2/proposals/backlog/drizzle-multitenant-migration.md)
385
+ - [drizzle-multitenant Roadmap](../roadmap.md)