@venizia/ignis-docs 0.0.2 → 0.0.4-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/README.md +1 -1
- package/package.json +4 -2
- package/wiki/best-practices/api-usage-examples.md +591 -0
- package/wiki/best-practices/architectural-patterns.md +415 -0
- package/wiki/best-practices/architecture-decisions.md +488 -0
- package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +647 -182
- package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
- package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
- package/wiki/best-practices/data-modeling.md +376 -0
- package/wiki/best-practices/deployment-strategies.md +698 -0
- package/wiki/best-practices/index.md +27 -0
- package/wiki/best-practices/performance-optimization.md +196 -0
- package/wiki/best-practices/security-guidelines.md +218 -0
- package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
- package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +1 -1
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
- package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +86 -0
- package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
- package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
- package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
- package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
- package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
- package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
- package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
- package/wiki/changelogs/index.md +8 -1
- package/wiki/changelogs/planned-schema-migrator.md +2 -10
- package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
- package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
- package/wiki/guides/core-concepts/components-guide.md +509 -0
- package/wiki/guides/core-concepts/components.md +122 -0
- package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
- package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
- package/wiki/guides/core-concepts/persistent/index.md +119 -0
- package/wiki/guides/core-concepts/persistent/models.md +241 -0
- package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
- package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
- package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
- package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
- package/wiki/guides/get-started/philosophy.md +682 -0
- package/wiki/guides/get-started/setup.md +157 -0
- package/wiki/guides/index.md +89 -0
- package/wiki/guides/reference/glossary.md +243 -0
- package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
- package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
- package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
- package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
- package/wiki/guides/tutorials/realtime-chat.md +1261 -0
- package/wiki/guides/tutorials/testing.md +723 -0
- package/wiki/index.md +176 -37
- package/wiki/references/base/application.md +27 -0
- package/wiki/references/base/bootstrapping.md +30 -26
- package/wiki/references/base/components.md +532 -31
- package/wiki/references/base/controllers.md +136 -38
- package/wiki/references/base/datasources.md +108 -5
- package/wiki/references/base/dependency-injection.md +39 -3
- package/wiki/references/base/filter-system/application-usage.md +224 -0
- package/wiki/references/base/filter-system/array-operators.md +132 -0
- package/wiki/references/base/filter-system/comparison-operators.md +109 -0
- package/wiki/references/base/filter-system/default-filter.md +428 -0
- package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
- package/wiki/references/base/filter-system/index.md +127 -0
- package/wiki/references/base/filter-system/json-filtering.md +197 -0
- package/wiki/references/base/filter-system/list-operators.md +71 -0
- package/wiki/references/base/filter-system/logical-operators.md +156 -0
- package/wiki/references/base/filter-system/null-operators.md +58 -0
- package/wiki/references/base/filter-system/pattern-matching.md +108 -0
- package/wiki/references/base/filter-system/quick-reference.md +431 -0
- package/wiki/references/base/filter-system/range-operators.md +63 -0
- package/wiki/references/base/filter-system/tips.md +190 -0
- package/wiki/references/base/filter-system/use-cases.md +452 -0
- package/wiki/references/base/index.md +90 -0
- package/wiki/references/base/middlewares.md +602 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +732 -0
- package/wiki/references/base/repositories/advanced.md +555 -0
- package/wiki/references/base/repositories/index.md +228 -0
- package/wiki/references/base/repositories/mixins.md +331 -0
- package/wiki/references/base/repositories/relations.md +486 -0
- package/wiki/references/base/repositories.md +40 -549
- package/wiki/references/base/services.md +28 -4
- package/wiki/references/components/authentication.md +22 -2
- package/wiki/references/components/health-check.md +12 -0
- package/wiki/references/components/index.md +23 -0
- package/wiki/references/components/mail.md +687 -0
- package/wiki/references/components/request-tracker.md +16 -0
- package/wiki/references/components/socket-io.md +18 -0
- package/wiki/references/components/static-asset.md +14 -26
- package/wiki/references/components/swagger.md +17 -0
- package/wiki/references/configuration/environment-variables.md +427 -0
- package/wiki/references/configuration/index.md +73 -0
- package/wiki/references/helpers/cron.md +14 -0
- package/wiki/references/helpers/crypto.md +15 -0
- package/wiki/references/helpers/env.md +16 -0
- package/wiki/references/helpers/error.md +17 -0
- package/wiki/references/helpers/index.md +15 -0
- package/wiki/references/helpers/inversion.md +24 -4
- package/wiki/references/helpers/logger.md +19 -0
- package/wiki/references/helpers/network.md +11 -0
- package/wiki/references/helpers/queue.md +19 -0
- package/wiki/references/helpers/redis.md +21 -0
- package/wiki/references/helpers/socket-io.md +24 -5
- package/wiki/references/helpers/storage.md +18 -10
- package/wiki/references/helpers/testing.md +18 -0
- package/wiki/references/helpers/types.md +167 -0
- package/wiki/references/helpers/uid.md +167 -0
- package/wiki/references/helpers/worker-thread.md +16 -0
- package/wiki/references/index.md +177 -0
- package/wiki/references/quick-reference.md +634 -0
- package/wiki/references/src-details/boot.md +3 -3
- package/wiki/references/src-details/dev-configs.md +0 -4
- package/wiki/references/src-details/docs.md +2 -2
- package/wiki/references/src-details/index.md +86 -0
- package/wiki/references/src-details/inversion.md +1 -6
- package/wiki/references/src-details/mcp-server.md +3 -15
- package/wiki/references/utilities/index.md +86 -10
- package/wiki/references/utilities/jsx.md +577 -0
- package/wiki/references/utilities/request.md +0 -2
- package/wiki/references/utilities/statuses.md +740 -0
- package/wiki/changelogs/planned-transaction-support.md +0 -216
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
- package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
- package/wiki/get-started/best-practices/data-modeling.md +0 -177
- package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
- package/wiki/get-started/best-practices/performance-optimization.md +0 -88
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/components.md +0 -98
- package/wiki/get-started/core-concepts/persistent.md +0 -543
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- package/wiki/get-started/prerequisites.md +0 -113
package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md}
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The Application class orchestrates your application's configuration, lifecycle, and resource registration (components, controllers, services).
|
|
4
4
|
|
|
5
|
-
> **Deep Dive:** See [Application Reference](
|
|
5
|
+
> **Deep Dive:** See [Application Reference](../../../references/base/application.md) for technical details.
|
|
6
6
|
|
|
7
7
|
## Creating an Application
|
|
8
8
|
|
|
@@ -42,13 +42,10 @@ export class Application extends BaseApplication {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
preConfigure(): ValueOrPromise<void> {
|
|
45
|
-
//
|
|
45
|
+
// Register your resources
|
|
46
46
|
this.dataSource(MyDataSource);
|
|
47
47
|
this.service(MyService);
|
|
48
48
|
this.controller(MyController);
|
|
49
|
-
|
|
50
|
-
// Or use boot system for auto-discovery (recommended for larger apps)
|
|
51
|
-
// See Bootstrapping section below
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
postConfigure(): ValueOrPromise<void> {
|
|
@@ -78,109 +75,39 @@ The `BaseApplication` class provides several **overridable hook methods** that a
|
|
|
78
75
|
| :--- | :--- |
|
|
79
76
|
| `getAppInfo()` | **Required.** Return application metadata, usually from `package.json`. Used for OpenAPI docs. |
|
|
80
77
|
| `staticConfigure()` | Configure static file serving. |
|
|
81
|
-
| `preConfigure()` | **Most Important Hook.** Set up application resources like components, controllers, services, and datasources. Can be skipped if using
|
|
78
|
+
| `preConfigure()` | **Most Important Hook.** Set up application resources like components, controllers, services, and datasources. Can be skipped if using [Bootstrapping](./bootstrapping). |
|
|
82
79
|
| `postConfigure()` | Perform actions *after* all resources have been configured and instantiated. |
|
|
83
80
|
| `setupMiddlewares()`| Add custom application-level middlewares to the Hono instance. |
|
|
84
81
|
|
|
85
|
-
##
|
|
86
|
-
|
|
87
|
-
The boot system provides automatic artifact discovery and loading, eliminating manual registration. When enabled, it scans your project directory and automatically loads controllers, services, repositories, and datasources.
|
|
88
|
-
|
|
89
|
-
> **Detailed Guide:** See [Bootstrapping Concepts](./bootstrapping.md) for complete documentation.
|
|
90
|
-
|
|
91
|
-
### Enabling Boot System
|
|
92
|
-
|
|
93
|
-
Add `bootOptions` to your application config:
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
export const appConfigs: IApplicationConfigs = {
|
|
97
|
-
host: process.env.APP_ENV_SERVER_HOST,
|
|
98
|
-
port: +(process.env.APP_ENV_SERVER_PORT ?? 3000),
|
|
99
|
-
// Enable boot system
|
|
100
|
-
bootOptions: {
|
|
101
|
-
datasources: { dirs: ['datasources'] },
|
|
102
|
-
repositories: { dirs: ['repositories'] },
|
|
103
|
-
services: { dirs: ['services'] },
|
|
104
|
-
controllers: { dirs: ['controllers'] }
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
With boot enabled, you can skip manual registration in `preConfigure()`:
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
export class Application extends BaseApplication {
|
|
113
|
-
// No need to register artifacts manually!
|
|
114
|
-
// Boot system handles it automatically
|
|
115
|
-
|
|
116
|
-
preConfigure(): ValueOrPromise<void> {
|
|
117
|
-
// Only register things that need custom configuration
|
|
118
|
-
// Everything else is auto-discovered
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Boot vs Manual Registration
|
|
124
|
-
|
|
125
|
-
| Approach | Use Case | Pros | Cons |
|
|
126
|
-
|----------|----------|------|------|
|
|
127
|
-
| **Boot System** | Apps with 10+ artifacts per type | Auto-discovery, scalable, clean code | Requires file naming conventions |
|
|
128
|
-
| **Manual Registration** | Small apps (< 5 artifacts) | Fine-grained control, explicit | Tedious, maintenance burden |
|
|
129
|
-
|
|
130
|
-
### Project Structure for Boot
|
|
82
|
+
## Lifecycle Diagram
|
|
131
83
|
|
|
132
|
-
|
|
84
|
+
This diagram shows the sequence of operations during application startup.
|
|
133
85
|
|
|
134
86
|
```
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
│
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
direction TB
|
|
160
|
-
B --> B0["boot() - if bootOptions configured"];
|
|
161
|
-
B0 --> B1["printStartUpInfo"];
|
|
162
|
-
B1 --> B2["validateEnvs"];
|
|
163
|
-
B2 --> B3["registerDefaultMiddlewares"];
|
|
164
|
-
B3 --> B4["staticConfigure()"];
|
|
165
|
-
B4 --> B5["preConfigure()"];
|
|
166
|
-
B5 --> B6["registerDataSources"];
|
|
167
|
-
B6 --> B7["registerComponents"];
|
|
168
|
-
B7 --> B8["registerControllers"];
|
|
169
|
-
B8 --> B9["postConfigure()"];
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
B9 --> C["setupMiddlewares()"];
|
|
173
|
-
C --> D["Mount Root Router"];
|
|
174
|
-
D --> E["Start HTTP Server"];
|
|
175
|
-
|
|
176
|
-
subgraph "Overridable Hooks"
|
|
177
|
-
style B4 fill:#ffc0cb,stroke:#333
|
|
178
|
-
style B5 fill:#ffc0cb,stroke:#333
|
|
179
|
-
style B9 fill:#ffc0cb,stroke:#333
|
|
180
|
-
style C fill:#ffc0cb,stroke:#333
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
classDef default fill:#fff,stroke:#333,stroke-width:2px;
|
|
87
|
+
start()
|
|
88
|
+
│
|
|
89
|
+
▼
|
|
90
|
+
initialize()
|
|
91
|
+
│
|
|
92
|
+
├─► boot() (if bootOptions configured)
|
|
93
|
+
├─► printStartUpInfo
|
|
94
|
+
├─► validateEnvs
|
|
95
|
+
├─► registerDefaultMiddlewares
|
|
96
|
+
├─► staticConfigure() ← Override hook
|
|
97
|
+
├─► preConfigure() ← Override hook
|
|
98
|
+
├─► registerDataSources
|
|
99
|
+
├─► registerComponents
|
|
100
|
+
├─► registerControllers
|
|
101
|
+
└─► postConfigure() ← Override hook
|
|
102
|
+
│
|
|
103
|
+
▼
|
|
104
|
+
setupMiddlewares() ← Override hook
|
|
105
|
+
│
|
|
106
|
+
▼
|
|
107
|
+
Mount Root Router
|
|
108
|
+
│
|
|
109
|
+
▼
|
|
110
|
+
Start HTTP Server
|
|
184
111
|
```
|
|
185
112
|
|
|
186
113
|
## Configuration
|
|
@@ -197,7 +124,7 @@ Application configuration is passed to the `BaseApplication` constructor via an
|
|
|
197
124
|
| `path.isStrict`| `boolean`| `true` | If `true`, the router is strict about trailing slashes. |
|
|
198
125
|
| `debug.showRoutes`| `boolean`| `false`| If `true`, prints all registered routes to the console on startup. |
|
|
199
126
|
| `favicon` | `string` | `'🔥'` | An emoji to be used as the application's favicon. |
|
|
200
|
-
| `bootOptions` | `IBootOptions` | `undefined` | Enable auto-discovery of artifacts. See [Bootstrapping](./bootstrapping
|
|
127
|
+
| `bootOptions` | `IBootOptions` | `undefined` | Enable auto-discovery of artifacts. See [Bootstrapping](./bootstrapping). |
|
|
201
128
|
|
|
202
129
|
### Example Configuration
|
|
203
130
|
|
|
@@ -234,4 +161,20 @@ Register resources in `preConfigure()` to tell the DI container about your class
|
|
|
234
161
|
4. Controllers (depend on Services)
|
|
235
162
|
5. Components last
|
|
236
163
|
|
|
237
|
-
> **Deep Dive:** See [Dependency Injection](
|
|
164
|
+
> **Deep Dive:** See [Dependency Injection](../dependency-injection.md) for how registration and injection work together.
|
|
165
|
+
|
|
166
|
+
## See Also
|
|
167
|
+
|
|
168
|
+
- **Related Concepts:**
|
|
169
|
+
- [Bootstrapping](./bootstrapping) - Auto-discovery of artifacts
|
|
170
|
+
- [Controllers](/guides/core-concepts/controllers) - Creating HTTP endpoints
|
|
171
|
+
- [Services](/guides/core-concepts/services) - Business logic layer
|
|
172
|
+
- [Dependency Injection](/guides/core-concepts/dependency-injection) - How DI works in IGNIS
|
|
173
|
+
|
|
174
|
+
- **References:**
|
|
175
|
+
- [BaseApplication API](/references/base/application) - Complete API reference
|
|
176
|
+
- [Environment Variables](/references/configuration/environment-variables) - Configuration management
|
|
177
|
+
|
|
178
|
+
- **Tutorials:**
|
|
179
|
+
- [5-Minute Quickstart](/guides/get-started/5-minute-quickstart) - Create your first app
|
|
180
|
+
- [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Complete application example
|
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
# Creating Components Guide
|
|
2
|
+
|
|
3
|
+
This guide walks you through creating your own components step-by-step.
|
|
4
|
+
|
|
5
|
+
## What Problem Do Components Solve?
|
|
6
|
+
|
|
7
|
+
Imagine you're building multiple applications that all need the same features: authentication, health checks, file uploads. Without components, you'd copy-paste code between projects:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// ❌ Without Components - Copy-paste everywhere
|
|
11
|
+
export class Application extends BaseApplication {
|
|
12
|
+
preConfigure() {
|
|
13
|
+
// Auth feature - copied to every project
|
|
14
|
+
this.service(TokenService);
|
|
15
|
+
this.service(AuthService);
|
|
16
|
+
this.controller(AuthController);
|
|
17
|
+
this.bind({ key: 'auth.secret' }).toValue(process.env.JWT_SECRET);
|
|
18
|
+
|
|
19
|
+
// Health check - copied to every project
|
|
20
|
+
this.controller(HealthController);
|
|
21
|
+
this.service(HealthService);
|
|
22
|
+
|
|
23
|
+
// Your actual app code...
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Components solve this** by packaging related functionality into reusable, plug-and-play modules:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// ✅ With Components - Clean and reusable
|
|
32
|
+
export class Application extends BaseApplication {
|
|
33
|
+
preConfigure() {
|
|
34
|
+
this.component(AuthenticateComponent); // All auth features in one line
|
|
35
|
+
this.component(HealthCheckComponent); // Health check ready to go
|
|
36
|
+
|
|
37
|
+
// Your actual app code...
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Think of Components as "Feature Plugins"
|
|
43
|
+
|
|
44
|
+
A Component is a **self-contained feature package** that bundles:
|
|
45
|
+
|
|
46
|
+
| What It Bundles | Example |
|
|
47
|
+
|-----------------|---------|
|
|
48
|
+
| Services | `TokenService`, `AuthService` |
|
|
49
|
+
| Controllers | `AuthController` with login/logout endpoints |
|
|
50
|
+
| Repositories | `UserRepository` for auth data |
|
|
51
|
+
| Configuration | Default settings, binding keys |
|
|
52
|
+
| Middlewares | JWT validation middleware |
|
|
53
|
+
|
|
54
|
+
When you register a component, all of these get added to your application automatically.
|
|
55
|
+
|
|
56
|
+
## When Should You Create a Component?
|
|
57
|
+
|
|
58
|
+
| Scenario | Use Component? | Why |
|
|
59
|
+
|----------|----------------|-----|
|
|
60
|
+
| Feature used in **one** project only | ❌ No | Just register services/controllers directly |
|
|
61
|
+
| Feature **shared across projects** | ✅ Yes | Package once, reuse everywhere |
|
|
62
|
+
| Feature with **multiple related parts** | ✅ Yes | Keep related code together |
|
|
63
|
+
| Building a **library/package** | ✅ Yes | Easy distribution and installation |
|
|
64
|
+
| **Configurable feature** with options | ✅ Yes | Components handle configuration elegantly |
|
|
65
|
+
|
|
66
|
+
## Creating Your First Component
|
|
67
|
+
|
|
68
|
+
### Step 1: Identify What to Bundle
|
|
69
|
+
|
|
70
|
+
Let's create a `NotificationComponent` that provides:
|
|
71
|
+
- A service to send notifications
|
|
72
|
+
- A controller with REST endpoints
|
|
73
|
+
- Configuration options
|
|
74
|
+
|
|
75
|
+
### Step 2: Create the Component Class
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// src/components/notification/component.ts
|
|
79
|
+
import {
|
|
80
|
+
BaseApplication,
|
|
81
|
+
BaseComponent,
|
|
82
|
+
CoreBindings,
|
|
83
|
+
inject,
|
|
84
|
+
ValueOrPromise,
|
|
85
|
+
} from '@venizia/ignis';
|
|
86
|
+
import { NotificationService } from './services';
|
|
87
|
+
import { NotificationController } from './controllers';
|
|
88
|
+
|
|
89
|
+
export class NotificationComponent extends BaseComponent {
|
|
90
|
+
constructor(
|
|
91
|
+
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
|
|
92
|
+
private _application: BaseApplication,
|
|
93
|
+
) {
|
|
94
|
+
super({
|
|
95
|
+
scope: NotificationComponent.name,
|
|
96
|
+
initDefault: { enable: true, container: _application },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
override binding(): ValueOrPromise<void> {
|
|
101
|
+
// Register all component resources with the application
|
|
102
|
+
this._application.service(NotificationService);
|
|
103
|
+
this._application.controller(NotificationController);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Step 3: Create the Service
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// src/components/notification/services/service.ts
|
|
112
|
+
import { BaseService } from '@venizia/ignis';
|
|
113
|
+
|
|
114
|
+
export class NotificationService extends BaseService {
|
|
115
|
+
constructor() {
|
|
116
|
+
super({ scope: NotificationService.name });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async send(opts: { userId: string; message: string }) {
|
|
120
|
+
// Send notification logic
|
|
121
|
+
console.log(`Sending to ${opts.userId}: ${opts.message}`);
|
|
122
|
+
return { success: true };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Step 4: Create the Controller
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// src/components/notification/controllers/controller.ts
|
|
131
|
+
import {
|
|
132
|
+
BaseController,
|
|
133
|
+
controller,
|
|
134
|
+
post,
|
|
135
|
+
inject,
|
|
136
|
+
HTTP,
|
|
137
|
+
jsonContent,
|
|
138
|
+
jsonResponse,
|
|
139
|
+
TRouteContext,
|
|
140
|
+
} from '@venizia/ignis';
|
|
141
|
+
import { z } from '@hono/zod-openapi';
|
|
142
|
+
import { NotificationService } from '../services';
|
|
143
|
+
|
|
144
|
+
const NotificationRoutes = {
|
|
145
|
+
SEND: {
|
|
146
|
+
method: HTTP.Methods.POST,
|
|
147
|
+
path: '/send',
|
|
148
|
+
request: {
|
|
149
|
+
body: jsonContent({
|
|
150
|
+
schema: z.object({
|
|
151
|
+
userId: z.string(),
|
|
152
|
+
message: z.string(),
|
|
153
|
+
}),
|
|
154
|
+
}),
|
|
155
|
+
},
|
|
156
|
+
responses: jsonResponse({
|
|
157
|
+
schema: z.object({ success: z.boolean() }),
|
|
158
|
+
}),
|
|
159
|
+
},
|
|
160
|
+
} as const;
|
|
161
|
+
|
|
162
|
+
@controller({ path: '/notifications' })
|
|
163
|
+
export class NotificationController extends BaseController {
|
|
164
|
+
constructor(
|
|
165
|
+
@inject({ key: 'services.NotificationService' })
|
|
166
|
+
private _notificationService: NotificationService,
|
|
167
|
+
) {
|
|
168
|
+
super({ scope: NotificationController.name, path: '/notifications' });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@post({ configs: NotificationRoutes.SEND })
|
|
172
|
+
async send(c: TRouteContext<typeof NotificationRoutes.SEND>) {
|
|
173
|
+
const body = c.req.valid('json');
|
|
174
|
+
const result = await this._notificationService.send({
|
|
175
|
+
userId: body.userId,
|
|
176
|
+
message: body.message,
|
|
177
|
+
});
|
|
178
|
+
return c.json(result, HTTP.ResultCodes.RS_2.Ok);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Step 5: Use the Component
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// src/application.ts
|
|
187
|
+
import { NotificationComponent } from './components/notification';
|
|
188
|
+
|
|
189
|
+
export class Application extends BaseApplication {
|
|
190
|
+
preConfigure() {
|
|
191
|
+
this.component(NotificationComponent);
|
|
192
|
+
// That's it! /notifications/send endpoint is now available
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Adding Configuration Options
|
|
198
|
+
|
|
199
|
+
Components become powerful when they accept options. Here's how:
|
|
200
|
+
|
|
201
|
+
### Step 1: Define Types and Binding Keys in `common/`
|
|
202
|
+
|
|
203
|
+
**Types go in `types.ts`:**
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// src/components/notification/common/types.ts
|
|
207
|
+
export interface INotificationOptions {
|
|
208
|
+
provider: 'email' | 'sms' | 'push';
|
|
209
|
+
defaultFrom?: string;
|
|
210
|
+
retryCount?: number;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface INotificationPayload {
|
|
214
|
+
userId: string;
|
|
215
|
+
message: string;
|
|
216
|
+
channel?: string;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Binding keys go in `keys.ts`:**
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// src/components/notification/common/keys.ts
|
|
224
|
+
export const NotificationBindingKeys = {
|
|
225
|
+
OPTIONS: 'components.notification.options',
|
|
226
|
+
SERVICE: 'services.NotificationService',
|
|
227
|
+
CONTROLLER: 'controllers.NotificationController',
|
|
228
|
+
} as const;
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Barrel export in `index.ts`:**
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// src/components/notification/common/index.ts
|
|
235
|
+
export * from './types';
|
|
236
|
+
export * from './keys';
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Step 2: Use Options in Service
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// src/components/notification/services/service.ts
|
|
243
|
+
import { BaseService, inject } from '@venizia/ignis';
|
|
244
|
+
import { INotificationOptions, NotificationBindingKeys } from '../common';
|
|
245
|
+
|
|
246
|
+
export class NotificationService extends BaseService {
|
|
247
|
+
constructor(
|
|
248
|
+
@inject({ key: NotificationBindingKeys.OPTIONS })
|
|
249
|
+
private _options: INotificationOptions,
|
|
250
|
+
) {
|
|
251
|
+
super({ scope: NotificationService.name });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async send(opts: { userId: string; message: string }) {
|
|
255
|
+
console.log(`Sending via ${this._options.provider}: ${opts.message}`);
|
|
256
|
+
// Use this._options.retryCount, this._options.defaultFrom, etc.
|
|
257
|
+
return { success: true };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Step 3: Provide Default Options in Component
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
// src/components/notification/component.ts
|
|
266
|
+
import { NotificationBindingKeys, INotificationOptions } from './common';
|
|
267
|
+
|
|
268
|
+
export class NotificationComponent extends BaseComponent {
|
|
269
|
+
constructor(
|
|
270
|
+
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
|
|
271
|
+
private _application: BaseApplication,
|
|
272
|
+
) {
|
|
273
|
+
super({
|
|
274
|
+
scope: NotificationComponent.name,
|
|
275
|
+
initDefault: { enable: true, container: _application },
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
override binding(): ValueOrPromise<void> {
|
|
280
|
+
// Provide default options (can be overridden by user)
|
|
281
|
+
if (!this._application.isBound({ key: NotificationBindingKeys.OPTIONS })) {
|
|
282
|
+
this._application.bind<INotificationOptions>({ key: NotificationBindingKeys.OPTIONS })
|
|
283
|
+
.toValue({
|
|
284
|
+
provider: 'email',
|
|
285
|
+
defaultFrom: 'noreply@example.com',
|
|
286
|
+
retryCount: 3,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
this._application.service(NotificationService);
|
|
291
|
+
this._application.controller(NotificationController);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Step 4: Users Can Override Options
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
// In user's application.ts
|
|
300
|
+
export class Application extends BaseApplication {
|
|
301
|
+
preConfigure() {
|
|
302
|
+
// Override options BEFORE registering component
|
|
303
|
+
this.bind<INotificationOptions>({ key: NotificationBindingKeys.OPTIONS })
|
|
304
|
+
.toValue({
|
|
305
|
+
provider: 'sms', // Use SMS instead of email
|
|
306
|
+
retryCount: 5, // More retries
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
this.component(NotificationComponent);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Component Lifecycle
|
|
315
|
+
|
|
316
|
+
```
|
|
317
|
+
Application.preConfigure()
|
|
318
|
+
│
|
|
319
|
+
▼
|
|
320
|
+
this.component(MyComponent)
|
|
321
|
+
│
|
|
322
|
+
▼
|
|
323
|
+
┌────────────────────────────────┐
|
|
324
|
+
│ 1. Constructor called │
|
|
325
|
+
│ - Inject application │
|
|
326
|
+
│ - Set up component scope │
|
|
327
|
+
└────────────────────────────────┘
|
|
328
|
+
│
|
|
329
|
+
▼
|
|
330
|
+
┌────────────────────────────────┐
|
|
331
|
+
│ 2. binding() called │
|
|
332
|
+
│ - Register services │
|
|
333
|
+
│ - Register controllers │
|
|
334
|
+
│ - Register repositories │
|
|
335
|
+
│ - Bind default options │
|
|
336
|
+
└────────────────────────────────┘
|
|
337
|
+
│
|
|
338
|
+
▼
|
|
339
|
+
Component resources are now
|
|
340
|
+
available in the application
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Component Directory Structure
|
|
344
|
+
|
|
345
|
+
Organize your component files following IGNIS conventions:
|
|
346
|
+
|
|
347
|
+
**Simple Component:**
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
src/components/notification/
|
|
351
|
+
├── index.ts # Barrel exports
|
|
352
|
+
├── component.ts # IoC binding setup
|
|
353
|
+
├── controllers/
|
|
354
|
+
│ ├── index.ts # Barrel exports for controllers
|
|
355
|
+
│ └── controller.ts # Route handlers (or notification.controller.ts)
|
|
356
|
+
├── services/
|
|
357
|
+
│ ├── index.ts # Barrel exports for services
|
|
358
|
+
│ └── service.ts # Business logic (or notification.service.ts)
|
|
359
|
+
└── common/
|
|
360
|
+
├── index.ts # Barrel exports for common
|
|
361
|
+
├── keys.ts # Binding key constants
|
|
362
|
+
├── types.ts # Interfaces and types
|
|
363
|
+
└── rest-paths.ts # Route path constants (optional)
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Complex Component (with multiple sub-features):**
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
src/components/auth/
|
|
370
|
+
├── index.ts
|
|
371
|
+
├── component.ts
|
|
372
|
+
├── controllers/
|
|
373
|
+
│ ├── index.ts
|
|
374
|
+
│ ├── auth.controller.ts
|
|
375
|
+
│ └── session.controller.ts
|
|
376
|
+
├── services/
|
|
377
|
+
│ ├── index.ts
|
|
378
|
+
│ ├── token.service.ts
|
|
379
|
+
│ └── session.service.ts
|
|
380
|
+
├── strategies/
|
|
381
|
+
│ ├── index.ts
|
|
382
|
+
│ ├── jwt.strategy.ts
|
|
383
|
+
│ └── basic.strategy.ts
|
|
384
|
+
├── common/
|
|
385
|
+
│ ├── index.ts
|
|
386
|
+
│ ├── keys.ts
|
|
387
|
+
│ └── types.ts
|
|
388
|
+
└── models/
|
|
389
|
+
├── entities/
|
|
390
|
+
└── requests/
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Barrel exports at every level:**
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
// src/components/notification/index.ts
|
|
397
|
+
export * from './common';
|
|
398
|
+
export * from './component';
|
|
399
|
+
export * from './controllers';
|
|
400
|
+
export * from './services';
|
|
401
|
+
|
|
402
|
+
// src/components/notification/controllers/index.ts
|
|
403
|
+
export * from './controller';
|
|
404
|
+
|
|
405
|
+
// src/components/notification/services/index.ts
|
|
406
|
+
export * from './service';
|
|
407
|
+
|
|
408
|
+
// src/components/notification/common/index.ts
|
|
409
|
+
export * from './keys';
|
|
410
|
+
export * from './types';
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**File naming:**
|
|
414
|
+
- Use folders: `controllers/`, `services/`, `common/`
|
|
415
|
+
- Single file in folder: `controller.ts`, `service.ts`
|
|
416
|
+
- Multiple files in folder: `auth.controller.ts`, `token.service.ts`
|
|
417
|
+
- Types and keys always in `common/` folder
|
|
418
|
+
|
|
419
|
+
## Quick Reference
|
|
420
|
+
|
|
421
|
+
### Minimal Component Template
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
import { BaseApplication, BaseComponent, CoreBindings, inject, ValueOrPromise } from '@venizia/ignis';
|
|
425
|
+
|
|
426
|
+
export class MyComponent extends BaseComponent {
|
|
427
|
+
constructor(
|
|
428
|
+
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
|
|
429
|
+
private _application: BaseApplication,
|
|
430
|
+
) {
|
|
431
|
+
super({
|
|
432
|
+
scope: MyComponent.name,
|
|
433
|
+
initDefault: { enable: true, container: _application },
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
override binding(): ValueOrPromise<void> {
|
|
438
|
+
// Register your services, controllers, etc.
|
|
439
|
+
this._application.service(MyService);
|
|
440
|
+
this._application.controller(MyController);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Component with Options Template
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
import { BaseApplication, BaseComponent, CoreBindings, inject, ValueOrPromise } from '@venizia/ignis';
|
|
449
|
+
|
|
450
|
+
// 1. Define options interface
|
|
451
|
+
export interface IMyComponentOptions {
|
|
452
|
+
enabled: boolean;
|
|
453
|
+
config: string;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// 2. Define binding keys
|
|
457
|
+
export const MyComponentKeys = {
|
|
458
|
+
OPTIONS: 'components.my.options',
|
|
459
|
+
} as const;
|
|
460
|
+
|
|
461
|
+
// 3. Create component
|
|
462
|
+
export class MyComponent extends BaseComponent {
|
|
463
|
+
constructor(
|
|
464
|
+
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
|
|
465
|
+
private _application: BaseApplication,
|
|
466
|
+
) {
|
|
467
|
+
super({
|
|
468
|
+
scope: MyComponent.name,
|
|
469
|
+
initDefault: { enable: true, container: _application },
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
override binding(): ValueOrPromise<void> {
|
|
474
|
+
// Provide defaults if not already bound
|
|
475
|
+
if (!this._application.isBound({ key: MyComponentKeys.OPTIONS })) {
|
|
476
|
+
this._application.bind<IMyComponentOptions>({ key: MyComponentKeys.OPTIONS })
|
|
477
|
+
.toValue({ enabled: true, config: 'default' });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
this._application.service(MyService);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Summary
|
|
486
|
+
|
|
487
|
+
| Concept | Description |
|
|
488
|
+
|---------|-------------|
|
|
489
|
+
| **Component** | A reusable package that bundles services, controllers, and configuration |
|
|
490
|
+
| **When to use** | Shared features, multi-part features, distributable packages |
|
|
491
|
+
| **Key method** | `binding()` - register all resources here |
|
|
492
|
+
| **Configuration** | Use binding keys + `isBound()` check for overridable options |
|
|
493
|
+
| **Registration** | `this.component(MyComponent)` in `preConfigure()` |
|
|
494
|
+
|
|
495
|
+
## See Also
|
|
496
|
+
|
|
497
|
+
- **Related Concepts:**
|
|
498
|
+
- [Components Overview](/guides/core-concepts/components) - What components are
|
|
499
|
+
- [Application](/guides/core-concepts/application/) - Registering components
|
|
500
|
+
- [Dependency Injection](/guides/core-concepts/dependency-injection) - Component bindings
|
|
501
|
+
|
|
502
|
+
- **References:**
|
|
503
|
+
- [BaseComponent API](/references/base/components) - Complete API reference
|
|
504
|
+
- [Authentication Component](/references/components/authentication) - Real-world component example
|
|
505
|
+
- [Health Check Component](/references/components/health-check) - Simple component example
|
|
506
|
+
|
|
507
|
+
- **Best Practices:**
|
|
508
|
+
- [Architectural Patterns](/best-practices/architectural-patterns) - Component architecture patterns
|
|
509
|
+
- [Code Style Standards](/best-practices/code-style-standards) - Component coding standards
|