@venizia/ignis-docs 0.0.3 → 0.0.4-1

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.
Files changed (131) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -2
  3. package/wiki/best-practices/api-usage-examples.md +591 -0
  4. package/wiki/best-practices/architectural-patterns.md +415 -0
  5. package/wiki/best-practices/architecture-decisions.md +488 -0
  6. package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +406 -17
  7. package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
  8. package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
  9. package/wiki/best-practices/data-modeling.md +376 -0
  10. package/wiki/best-practices/deployment-strategies.md +698 -0
  11. package/wiki/best-practices/index.md +27 -0
  12. package/wiki/best-practices/performance-optimization.md +196 -0
  13. package/wiki/best-practices/security-guidelines.md +218 -0
  14. package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
  15. package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
  16. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
  17. package/wiki/changelogs/2025-12-17-refactor.md +1 -1
  18. package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
  19. package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
  20. package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +2 -2
  21. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  22. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  23. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  24. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  25. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  26. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  27. package/wiki/changelogs/index.md +6 -0
  28. package/wiki/changelogs/planned-schema-migrator.md +0 -8
  29. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  30. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  31. package/wiki/guides/core-concepts/components-guide.md +509 -0
  32. package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
  33. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  34. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  35. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  36. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  37. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  38. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  39. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  40. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  41. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  42. package/wiki/guides/get-started/philosophy.md +682 -0
  43. package/wiki/guides/get-started/setup.md +157 -0
  44. package/wiki/guides/index.md +89 -0
  45. package/wiki/guides/reference/glossary.md +243 -0
  46. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  47. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  48. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  49. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  50. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  51. package/wiki/guides/tutorials/testing.md +723 -0
  52. package/wiki/index.md +176 -37
  53. package/wiki/references/base/application.md +27 -0
  54. package/wiki/references/base/bootstrapping.md +31 -26
  55. package/wiki/references/base/components.md +24 -7
  56. package/wiki/references/base/controllers.md +50 -20
  57. package/wiki/references/base/datasources.md +30 -0
  58. package/wiki/references/base/dependency-injection.md +39 -3
  59. package/wiki/references/base/filter-system/application-usage.md +224 -0
  60. package/wiki/references/base/filter-system/array-operators.md +132 -0
  61. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  62. package/wiki/references/base/filter-system/default-filter.md +428 -0
  63. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  64. package/wiki/references/base/filter-system/index.md +127 -0
  65. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  66. package/wiki/references/base/filter-system/list-operators.md +71 -0
  67. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  68. package/wiki/references/base/filter-system/null-operators.md +58 -0
  69. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  70. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  71. package/wiki/references/base/filter-system/range-operators.md +63 -0
  72. package/wiki/references/base/filter-system/tips.md +190 -0
  73. package/wiki/references/base/filter-system/use-cases.md +452 -0
  74. package/wiki/references/base/index.md +90 -0
  75. package/wiki/references/base/middlewares.md +604 -0
  76. package/wiki/references/base/models.md +215 -23
  77. package/wiki/references/base/providers.md +731 -0
  78. package/wiki/references/base/repositories/advanced.md +555 -0
  79. package/wiki/references/base/repositories/index.md +228 -0
  80. package/wiki/references/base/repositories/mixins.md +331 -0
  81. package/wiki/references/base/repositories/relations.md +486 -0
  82. package/wiki/references/base/repositories.md +40 -635
  83. package/wiki/references/base/services.md +28 -4
  84. package/wiki/references/components/authentication.md +22 -2
  85. package/wiki/references/components/health-check.md +12 -0
  86. package/wiki/references/components/index.md +23 -0
  87. package/wiki/references/components/mail.md +687 -0
  88. package/wiki/references/components/request-tracker.md +16 -0
  89. package/wiki/references/components/socket-io.md +18 -0
  90. package/wiki/references/components/static-asset.md +14 -26
  91. package/wiki/references/components/swagger.md +17 -0
  92. package/wiki/references/configuration/environment-variables.md +427 -0
  93. package/wiki/references/configuration/index.md +73 -0
  94. package/wiki/references/helpers/cron.md +14 -0
  95. package/wiki/references/helpers/crypto.md +15 -0
  96. package/wiki/references/helpers/env.md +16 -0
  97. package/wiki/references/helpers/error.md +17 -0
  98. package/wiki/references/helpers/index.md +14 -0
  99. package/wiki/references/helpers/inversion.md +24 -4
  100. package/wiki/references/helpers/logger.md +19 -0
  101. package/wiki/references/helpers/network.md +11 -0
  102. package/wiki/references/helpers/queue.md +19 -0
  103. package/wiki/references/helpers/redis.md +21 -0
  104. package/wiki/references/helpers/socket-io.md +24 -5
  105. package/wiki/references/helpers/storage.md +18 -10
  106. package/wiki/references/helpers/testing.md +18 -0
  107. package/wiki/references/helpers/types.md +16 -0
  108. package/wiki/references/helpers/uid.md +167 -0
  109. package/wiki/references/helpers/worker-thread.md +16 -0
  110. package/wiki/references/index.md +177 -0
  111. package/wiki/references/quick-reference.md +634 -0
  112. package/wiki/references/src-details/boot.md +3 -3
  113. package/wiki/references/src-details/dev-configs.md +0 -4
  114. package/wiki/references/src-details/docs.md +2 -2
  115. package/wiki/references/src-details/index.md +86 -0
  116. package/wiki/references/src-details/inversion.md +1 -6
  117. package/wiki/references/src-details/mcp-server.md +3 -15
  118. package/wiki/references/utilities/index.md +86 -10
  119. package/wiki/references/utilities/jsx.md +577 -0
  120. package/wiki/references/utilities/request.md +0 -2
  121. package/wiki/references/utilities/statuses.md +740 -0
  122. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  123. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  124. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  125. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  126. package/wiki/get-started/best-practices/performance-optimization.md +0 -97
  127. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  128. package/wiki/get-started/core-concepts/persistent.md +0 -539
  129. package/wiki/get-started/index.md +0 -65
  130. package/wiki/get-started/philosophy.md +0 -296
  131. package/wiki/get-started/prerequisites.md +0 -113
@@ -0,0 +1,731 @@
1
+ ---
2
+ title: Providers Reference
3
+ description: Technical reference for the Provider pattern in IGNIS
4
+ difficulty: advanced
5
+ lastUpdated: 2026-01-03
6
+ ---
7
+
8
+ # Providers Reference
9
+
10
+ Providers implement the Factory pattern in IGNIS, allowing you to create and configure instances dynamically at runtime based on configuration or context. Unlike services that contain business logic, providers are factories that produce values, instances, or functions.
11
+
12
+ **Files:**
13
+ - `packages/core/src/base/providers/base.ts`
14
+
15
+ ## Prerequisites
16
+
17
+ Before reading this document, you should understand:
18
+ - [TypeScript basics](https://www.typescriptlang.org/docs/)
19
+ - [Dependency Injection in IGNIS](./dependency-injection.md)
20
+ - [Services](./services.md) - To understand the difference
21
+
22
+ ## Quick Reference
23
+
24
+ | Feature | Description |
25
+ |---------|-------------|
26
+ | **Purpose** | Factory pattern for runtime instance creation |
27
+ | **Base Class** | `BaseProvider<T>` |
28
+ | **Key Method** | `value(container: Container): T` |
29
+ | **Use Case** | Dynamic configuration, multi-strategy patterns, plugin systems |
30
+ | **Extends** | `BaseHelper` (provides logging) |
31
+ | **Implements** | `IProvider<T>` from `@venizia/ignis-inversion` |
32
+
33
+ ## What is a Provider?
34
+
35
+ A **Provider** is a class that implements the Factory pattern, responsible for creating and configuring instances based on runtime conditions, configuration, or context.
36
+
37
+ ### Core Characteristics
38
+
39
+ 1. **Factory Pattern**: Produces values, instances, or functions
40
+ 2. **Configuration-Driven**: Creates different implementations based on config
41
+ 3. **Deferred Creation**: Instances are created when needed, not at startup
42
+ 4. **Type-Safe**: Generic type `T` ensures type safety of produced values
43
+
44
+ ### Common Use Cases
45
+
46
+ - **Strategy Selection**: Choose between multiple implementations (e.g., email providers: Nodemailer, Mailgun, SendGrid)
47
+ - **Configuration-Based Instantiation**: Create instances with different configurations
48
+ - **Plugin Systems**: Load and configure plugins dynamically
49
+ - **Multi-Tenant**: Provide tenant-specific instances
50
+ - **Feature Flags**: Enable/disable features at runtime
51
+
52
+ ---
53
+
54
+ ## BaseProvider Class
55
+
56
+ Abstract base class for all providers in IGNIS.
57
+
58
+ ### Class Definition
59
+
60
+ ```typescript
61
+ import { Container } from '@/helpers/inversion';
62
+ import { BaseHelper } from '@venizia/ignis-helpers';
63
+ import { IProvider } from '@venizia/ignis-inversion';
64
+
65
+ export abstract class BaseProvider<T> extends BaseHelper implements IProvider<T> {
66
+ abstract value(container: Container): T;
67
+ }
68
+ ```
69
+
70
+ ### Generic Type Parameter
71
+
72
+ | Parameter | Description |
73
+ |-----------|-------------|
74
+ | `T` | The type of value this provider produces |
75
+
76
+ **Examples:**
77
+ - `BaseProvider<IMailTransport>` - Produces mail transport instances
78
+ - `BaseProvider<MiddlewareHandler>` - Produces Hono middleware
79
+ - `BaseProvider<(config: Config) => Service>` - Produces factory functions
80
+
81
+ ### Inheritance
82
+
83
+ - **Extends `BaseHelper`**: Provides scoped logging via `this.logger`
84
+ - **Implements `IProvider<T>`**: Enforces the `value(container: Container): T` contract
85
+
86
+ ### Abstract Method: `value(container: Container): T`
87
+
88
+ The `value` method is where you implement your factory logic.
89
+
90
+ **Parameters:**
91
+ | Parameter | Type | Description |
92
+ |-----------|------|-------------|
93
+ | `container` | `Container` | DI container instance for resolving dependencies |
94
+
95
+ **Returns:** `T` - The produced value, instance, or factory function
96
+
97
+ **Purpose:**
98
+ - Access to DI container allows resolving other dependencies
99
+ - Create and configure instances based on logic
100
+ - Return factories for deferred instantiation
101
+
102
+ ---
103
+
104
+ ## Provider vs Service
105
+
106
+ Understanding when to use Providers vs Services is crucial for proper architecture.
107
+
108
+ ### Comparison Table
109
+
110
+ | Aspect | Provider | Service |
111
+ |--------|----------|---------|
112
+ | **Purpose** | Create/configure instances | Contain business logic |
113
+ | **Pattern** | Factory pattern | Business logic layer |
114
+ | **Method** | `value(container)` returns factory | Business methods (CRUD, etc.) |
115
+ | **Lifecycle** | Creates instances on-demand | Single instance per DI scope |
116
+ | **Dependencies** | Produces configured instances | Uses repositories/other services |
117
+ | **Example** | `MailTransportProvider` | `UserService` |
118
+ | **Returns** | Values, instances, or functions | Business data/results |
119
+
120
+ ### When to Use Providers
121
+
122
+ Use providers when you need:
123
+
124
+ ```typescript
125
+ // ✅ Multiple implementations to choose from
126
+ class MailTransportProvider extends BaseProvider<TGetMailTransportFn> {
127
+ value(container: Container): TGetMailTransportFn {
128
+ return (options) => {
129
+ switch (options.provider) {
130
+ case 'nodemailer': return new NodemailerTransport(options);
131
+ case 'mailgun': return new MailgunTransport(options);
132
+ case 'sendgrid': return new SendGridTransport(options);
133
+ }
134
+ };
135
+ }
136
+ }
137
+
138
+ // ✅ Configuration-based instance creation
139
+ class DatabaseProvider extends BaseProvider<Database> {
140
+ value(container: Container): Database {
141
+ const config = container.get(ConfigService);
142
+ return new Database({
143
+ host: config.get('DB_HOST'),
144
+ port: config.get('DB_PORT'),
145
+ });
146
+ }
147
+ }
148
+
149
+ // ✅ Runtime factory functions
150
+ class CacheProvider extends BaseProvider<(key: string) => Cache> {
151
+ value(container: Container): (key: string) => Cache {
152
+ return (key: string) => new Cache({ namespace: key });
153
+ }
154
+ }
155
+ ```
156
+
157
+ ### When to Use Services
158
+
159
+ Use services when you need:
160
+
161
+ ```typescript
162
+ // ✅ Business logic
163
+ class UserService extends BaseService {
164
+ async createUser(data: CreateUserDto) {
165
+ // Validation, transformation, business rules
166
+ const user = await this.userRepository.create(data);
167
+ await this.emailService.sendWelcome(user.email);
168
+ return user;
169
+ }
170
+ }
171
+
172
+ // ✅ Orchestration between repositories
173
+ class OrderService extends BaseService {
174
+ async createOrder(items: CartItem[]) {
175
+ const order = await this.orderRepository.create(items);
176
+ await this.inventoryRepository.decrementStock(items);
177
+ await this.paymentService.charge(order.total);
178
+ return order;
179
+ }
180
+ }
181
+ ```
182
+
183
+ :::tip Quick Decision
184
+ - **Need to produce different implementations?** → Use a Provider
185
+ - **Need to implement business logic?** → Use a Service
186
+ :::
187
+
188
+ ---
189
+
190
+ ## Creating Custom Providers
191
+
192
+ ### Basic Provider
193
+
194
+ ```typescript
195
+ import { BaseProvider } from '@venizia/ignis';
196
+ import { Container } from '@venizia/ignis-inversion';
197
+ import { injectable } from '@venizia/ignis-inversion';
198
+
199
+ interface ILogger {
200
+ log(message: string): void;
201
+ }
202
+
203
+ class ConsoleLogger implements ILogger {
204
+ log(message: string) {
205
+ console.log(message);
206
+ }
207
+ }
208
+
209
+ class FileLogger implements ILogger {
210
+ constructor(private filePath: string) {}
211
+
212
+ log(message: string) {
213
+ // Write to file
214
+ }
215
+ }
216
+
217
+ @injectable()
218
+ export class LoggerProvider extends BaseProvider<ILogger> {
219
+ constructor() {
220
+ super({ scope: LoggerProvider.name });
221
+ }
222
+
223
+ value(container: Container): ILogger {
224
+ const env = process.env.NODE_ENV;
225
+
226
+ if (env === 'production') {
227
+ this.logger.info('[value] Creating FileLogger for production');
228
+ return new FileLogger('/var/log/app.log');
229
+ }
230
+
231
+ this.logger.info('[value] Creating ConsoleLogger for development');
232
+ return new ConsoleLogger();
233
+ }
234
+ }
235
+ ```
236
+
237
+ ### Factory Function Provider
238
+
239
+ Providers can return factory functions for deferred instantiation:
240
+
241
+ ```typescript
242
+ type TGetMailTransportFn = (options: MailOptions) => IMailTransport;
243
+
244
+ @injectable()
245
+ export class MailTransportProvider extends BaseProvider<TGetMailTransportFn> {
246
+ constructor() {
247
+ super({ scope: MailTransportProvider.name });
248
+ }
249
+
250
+ value(container: Container): TGetMailTransportFn {
251
+ // Return a factory function
252
+ return (options: MailOptions) => {
253
+ this.logger.info('[value] Creating mail transport: %s', options.provider);
254
+
255
+ switch (options.provider) {
256
+ case 'nodemailer':
257
+ return new NodemailerTransport(options.config);
258
+ case 'mailgun':
259
+ return new MailgunTransport(options.config);
260
+ default:
261
+ throw new Error(`Unknown provider: ${options.provider}`);
262
+ }
263
+ };
264
+ }
265
+ }
266
+
267
+ // Usage
268
+ const getTransport = app.get(MailTransportProvider).value(container);
269
+ const transport = getTransport({ provider: 'nodemailer', config: {...} });
270
+ ```
271
+
272
+ ### Provider with Dependency Injection
273
+
274
+ Access other dependencies through the container:
275
+
276
+ ```typescript
277
+ @injectable()
278
+ export class DatabaseProvider extends BaseProvider<Database> {
279
+ constructor() {
280
+ super({ scope: DatabaseProvider.name });
281
+ }
282
+
283
+ value(container: Container): Database {
284
+ // Resolve dependencies from container
285
+ const config = container.get(ConfigService);
286
+ const logger = container.get(LoggerService);
287
+
288
+ const database = new Database({
289
+ host: config.get('DB_HOST'),
290
+ port: config.get('DB_PORT'),
291
+ logger: logger,
292
+ });
293
+
294
+ this.logger.info('[value] Database instance created');
295
+ return database;
296
+ }
297
+ }
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Provider Lifecycle
303
+
304
+ Understanding the provider lifecycle helps you use them effectively.
305
+
306
+ ### Lifecycle Stages
307
+
308
+ ```mermaid
309
+ graph TD
310
+ A[Application Start] --> B[DI Container Scans Providers]
311
+ B --> C[Provider Instance Created]
312
+ C --> D[Provider Registered in Container]
313
+ D --> E[Application Calls provider.value]
314
+ E --> F[value Returns Factory/Instance]
315
+ F --> G[Consumer Uses Returned Value]
316
+ G --> H{Need Another Instance?}
317
+ H -->|Yes| E
318
+ H -->|No| I[End]
319
+ ```
320
+
321
+ ### Key Points
322
+
323
+ 1. **Provider Instance Created Once**: The provider class itself is instantiated once by the DI container
324
+ 2. **`value()` Called When Needed**: The `value(container)` method is called when the application needs the produced value
325
+ 3. **Factory vs Instance**: Providers can return:
326
+ - Direct instances (created each time `value()` is called)
327
+ - Factory functions (deferred creation)
328
+ - Singleton instances (same instance each time)
329
+
330
+ ### Example: Singleton vs Factory
331
+
332
+ ```typescript
333
+ // Singleton: Same instance every time
334
+ @injectable()
335
+ export class SingletonDatabaseProvider extends BaseProvider<Database> {
336
+ private instance?: Database;
337
+
338
+ value(container: Container): Database {
339
+ if (!this.instance) {
340
+ this.instance = new Database({...});
341
+ this.logger.info('[value] Database singleton created');
342
+ }
343
+ return this.instance;
344
+ }
345
+ }
346
+
347
+ // Factory: New instance every time
348
+ @injectable()
349
+ export class FactoryDatabaseProvider extends BaseProvider<Database> {
350
+ value(container: Container): Database {
351
+ this.logger.info('[value] Creating new Database instance');
352
+ return new Database({...});
353
+ }
354
+ }
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Real-World Examples
360
+
361
+ ### Example 1: Mail Transport Provider
362
+
363
+ From `packages/core/src/components/mail/providers/mail-transporter.provider.ts`:
364
+
365
+ ```typescript
366
+ type TGetMailTransportFn = (options: TMailOptions) => IMailTransport;
367
+
368
+ @injectable()
369
+ export class MailTransportProvider extends BaseProvider<TGetMailTransportFn> {
370
+ constructor() {
371
+ super({ scope: MailTransportProvider.name });
372
+ }
373
+
374
+ value(_container: Container): TGetMailTransportFn {
375
+ return (options: TMailOptions) => {
376
+ this.logger.info('[value] Creating mail transport: %s', options.provider);
377
+
378
+ switch (options.provider) {
379
+ case MailProviders.NODEMAILER:
380
+ return this.createNodemailerTransport(options);
381
+
382
+ case MailProviders.MAILGUN:
383
+ return this.createMailgunTransport(options);
384
+
385
+ case MailProviders.CUSTOM:
386
+ return this.createCustomTransport(options);
387
+
388
+ default:
389
+ throw new Error(`Unsupported provider: ${options.provider}`);
390
+ }
391
+ };
392
+ }
393
+
394
+ private createNodemailerTransport(options: INodemailerMailOptions) {
395
+ this.logger.info('[createNodemailerTransport] Initializing');
396
+ return new NodemailerTransportHelper(options.config);
397
+ }
398
+
399
+ private createMailgunTransport(options: IMailgunMailOptions) {
400
+ this.logger.info('[createMailgunTransport] Initializing');
401
+ return new MailgunTransportHelper(options.config);
402
+ }
403
+
404
+ private createCustomTransport(options: ICustomMailOptions) {
405
+ this.logger.info('[createCustomTransport] Using custom transport');
406
+ return options.config; // Already implements IMailTransport
407
+ }
408
+ }
409
+ ```
410
+
411
+ **Usage:**
412
+
413
+ ```typescript
414
+ // In your service or application setup
415
+ const getMailTransport = app.get(MailTransportProvider).value(container);
416
+
417
+ // Create Nodemailer transport
418
+ const nodemailerTransport = getMailTransport({
419
+ provider: MailProviders.NODEMAILER,
420
+ config: { /* nodemailer config */ }
421
+ });
422
+
423
+ // Create Mailgun transport
424
+ const mailgunTransport = getMailTransport({
425
+ provider: MailProviders.MAILGUN,
426
+ config: { /* mailgun config */ }
427
+ });
428
+ ```
429
+
430
+ ### Example 2: Queue Executor Provider
431
+
432
+ From `packages/core/src/components/mail/providers/mail-queue-executor.provider.ts`:
433
+
434
+ ```typescript
435
+ type TGetMailQueueExecutorFn = (config: IMailQueueExecutorConfig) => IMailQueueExecutor;
436
+
437
+ @injectable()
438
+ export class MailQueueExecutorProvider extends BaseProvider<TGetMailQueueExecutorFn> {
439
+ constructor() {
440
+ super({ scope: MailQueueExecutorProvider.name });
441
+ }
442
+
443
+ value(_container: Container): TGetMailQueueExecutorFn {
444
+ return (config: IMailQueueExecutorConfig) => {
445
+ this.logger.info('[value] Creating executor: %s', config.type);
446
+
447
+ switch (config.type) {
448
+ case MailQueueExecutorTypes.DIRECT:
449
+ return new DirectMailExecutorHelper();
450
+
451
+ case MailQueueExecutorTypes.INTERNAL_QUEUE:
452
+ return new InternalQueueMailExecutorHelper({
453
+ identifier: config.internalQueue.identifier,
454
+ });
455
+
456
+ case MailQueueExecutorTypes.BULLMQ:
457
+ return new BullMQMailExecutorHelper(config.bullmq);
458
+
459
+ default:
460
+ throw new Error(`Unknown type: ${config.type}`);
461
+ }
462
+ };
463
+ }
464
+ }
465
+ ```
466
+
467
+ ### Example 3: Middleware Provider
468
+
469
+ Providers can also produce middleware:
470
+
471
+ ```typescript
472
+ import { RequestSpyMiddleware } from '@venizia/ignis';
473
+
474
+ // RequestSpyMiddleware is a provider that produces Hono middleware
475
+ @injectable()
476
+ export class RequestSpyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
477
+ static readonly REQUEST_ID_KEY = 'requestId';
478
+
479
+ constructor() {
480
+ super({ scope: RequestSpyMiddleware.name });
481
+ }
482
+
483
+ value() {
484
+ return createMiddleware(async (context, next) => {
485
+ const requestId = context.get(RequestSpyMiddleware.REQUEST_ID_KEY);
486
+
487
+ this.logger.info('[spy][%s] START | path: %s', requestId, context.req.path);
488
+
489
+ await next();
490
+
491
+ this.logger.info('[spy][%s] DONE | path: %s', requestId, context.req.path);
492
+ });
493
+ }
494
+ }
495
+
496
+ // Usage
497
+ const requestSpy = new RequestSpyMiddleware();
498
+ app.use(requestSpy.value());
499
+ ```
500
+
501
+ ---
502
+
503
+ ## Common Patterns
504
+
505
+ ### Pattern 1: Configuration Validator
506
+
507
+ Validate configuration before creating instances:
508
+
509
+ ```typescript
510
+ @injectable()
511
+ export class S3StorageProvider extends BaseProvider<S3Storage> {
512
+ constructor() {
513
+ super({ scope: S3StorageProvider.name });
514
+ }
515
+
516
+ value(container: Container): S3Storage {
517
+ const config = container.get(ConfigService);
518
+
519
+ const accessKey = config.get('AWS_ACCESS_KEY');
520
+ const secretKey = config.get('AWS_SECRET_KEY');
521
+ const bucket = config.get('AWS_S3_BUCKET');
522
+
523
+ // Validate configuration
524
+ if (!accessKey || !secretKey || !bucket) {
525
+ throw new Error('S3 configuration incomplete');
526
+ }
527
+
528
+ this.logger.info('[value] Creating S3 storage for bucket: %s', bucket);
529
+
530
+ return new S3Storage({
531
+ accessKeyId: accessKey,
532
+ secretAccessKey: secretKey,
533
+ bucket: bucket,
534
+ });
535
+ }
536
+ }
537
+ ```
538
+
539
+ ### Pattern 2: Lazy Singleton
540
+
541
+ Create instance only once, lazily:
542
+
543
+ ```typescript
544
+ @injectable()
545
+ export class DatabaseConnectionProvider extends BaseProvider<DatabaseConnection> {
546
+ private connection?: DatabaseConnection;
547
+
548
+ value(container: Container): DatabaseConnection {
549
+ if (!this.connection) {
550
+ this.logger.info('[value] Creating database connection');
551
+ const config = container.get(ConfigService);
552
+ this.connection = new DatabaseConnection(config.get('DATABASE_URL'));
553
+ } else {
554
+ this.logger.debug('[value] Reusing existing connection');
555
+ }
556
+
557
+ return this.connection;
558
+ }
559
+ }
560
+ ```
561
+
562
+ ### Pattern 3: Environment-Based Strategy
563
+
564
+ Select implementation based on environment:
565
+
566
+ ```typescript
567
+ @injectable()
568
+ export class CacheProvider extends BaseProvider<ICache> {
569
+ value(container: Container): ICache {
570
+ const env = process.env.NODE_ENV;
571
+
572
+ if (env === 'test') {
573
+ this.logger.info('[value] Using InMemoryCache for testing');
574
+ return new InMemoryCache();
575
+ }
576
+
577
+ if (env === 'production') {
578
+ this.logger.info('[value] Using RedisCache for production');
579
+ const config = container.get(ConfigService);
580
+ return new RedisCache({
581
+ host: config.get('REDIS_HOST'),
582
+ port: config.get('REDIS_PORT'),
583
+ });
584
+ }
585
+
586
+ this.logger.info('[value] Using InMemoryCache for development');
587
+ return new InMemoryCache();
588
+ }
589
+ }
590
+ ```
591
+
592
+ ---
593
+
594
+ ## Common Pitfalls
595
+
596
+ ### Pitfall 1: Forgetting to Call `value()`
597
+
598
+ ```typescript
599
+ // ❌ Wrong: Getting the provider instance
600
+ const provider = app.get(MailTransportProvider);
601
+ const transport = provider({ provider: 'nodemailer' }); // Error!
602
+
603
+ // ✅ Correct: Call value() first
604
+ const provider = app.get(MailTransportProvider);
605
+ const getTransport = provider.value(container);
606
+ const transport = getTransport({ provider: 'nodemailer' });
607
+ ```
608
+
609
+ ### Pitfall 2: Creating Instances in Constructor
610
+
611
+ ```typescript
612
+ // ❌ Wrong: Creating instances in constructor
613
+ @injectable()
614
+ export class BadProvider extends BaseProvider<Database> {
615
+ private db: Database;
616
+
617
+ constructor() {
618
+ super({ scope: BadProvider.name });
619
+ this.db = new Database(); // Too early! Config might not be ready
620
+ }
621
+
622
+ value(container: Container): Database {
623
+ return this.db;
624
+ }
625
+ }
626
+
627
+ // ✅ Correct: Create in value() method
628
+ @injectable()
629
+ export class GoodProvider extends BaseProvider<Database> {
630
+ constructor() {
631
+ super({ scope: GoodProvider.name });
632
+ }
633
+
634
+ value(container: Container): Database {
635
+ const config = container.get(ConfigService);
636
+ return new Database(config.get('DATABASE_URL'));
637
+ }
638
+ }
639
+ ```
640
+
641
+ ### Pitfall 3: Not Handling Errors
642
+
643
+ ```typescript
644
+ // ❌ Wrong: No error handling
645
+ value(container: Container): IMailTransport {
646
+ return new MailTransport(config.get('MAIL_CONFIG')); // Might throw
647
+ }
648
+
649
+ // ✅ Correct: Validate and handle errors
650
+ value(container: Container): IMailTransport {
651
+ const config = container.get(ConfigService);
652
+ const mailConfig = config.get('MAIL_CONFIG');
653
+
654
+ if (!mailConfig) {
655
+ throw new Error('Mail configuration is missing');
656
+ }
657
+
658
+ try {
659
+ return new MailTransport(mailConfig);
660
+ } catch (error) {
661
+ this.logger.error('[value] Failed to create mail transport', error);
662
+ throw error;
663
+ }
664
+ }
665
+ ```
666
+
667
+ ---
668
+
669
+ ## Performance Considerations
670
+
671
+ ### Factory Functions vs Direct Instances
672
+
673
+ **Factory Functions** (Recommended for multiple instances):
674
+ ```typescript
675
+ // Returns a factory - no instance created until called
676
+ value(container: Container): () => Service {
677
+ return () => new Service();
678
+ }
679
+ ```
680
+
681
+ **Direct Instances** (Use for singletons):
682
+ ```typescript
683
+ // Creates instance immediately
684
+ value(container: Container): Service {
685
+ return new Service();
686
+ }
687
+ ```
688
+
689
+ ### Caching Strategies
690
+
691
+ ```typescript
692
+ // Cache expensive operations
693
+ @injectable()
694
+ export class ConfigProvider extends BaseProvider<Config> {
695
+ private cachedConfig?: Config;
696
+
697
+ value(container: Container): Config {
698
+ if (this.cachedConfig) {
699
+ return this.cachedConfig;
700
+ }
701
+
702
+ // Expensive operation: read files, parse, validate
703
+ this.cachedConfig = this.loadAndValidateConfig();
704
+ return this.cachedConfig;
705
+ }
706
+
707
+ private loadAndValidateConfig(): Config {
708
+ // ... expensive operations
709
+ }
710
+ }
711
+ ```
712
+
713
+ ---
714
+
715
+ ## See Also
716
+
717
+ - **Related References:**
718
+ - [Services](./services.md) - Business logic layer
719
+ - [Dependency Injection](./dependency-injection.md) - DI container and injection
720
+ - [Middlewares](./middlewares.md) - Middleware providers
721
+
722
+ - **Guides:**
723
+ - [Dependency Injection Guide](/guides/core-concepts/dependency-injection.md)
724
+ - [Building Services](/guides/core-concepts/services.md)
725
+
726
+ - **Best Practices:**
727
+ - [Architectural Patterns](/best-practices/architectural-patterns)
728
+
729
+ - **External Resources:**
730
+ - [Factory Pattern](https://refactoring.guru/design-patterns/factory-method)
731
+ - [Dependency Injection Pattern](https://en.wikipedia.org/wiki/Dependency_injection)