@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.
- 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 +406 -17
- 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 +2 -2
- 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 +6 -0
- package/wiki/changelogs/planned-schema-migrator.md +0 -8
- 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/{get-started → guides}/core-concepts/components.md +24 -17
- 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 +31 -26
- package/wiki/references/base/components.md +24 -7
- package/wiki/references/base/controllers.md +50 -20
- package/wiki/references/base/datasources.md +30 -0
- 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 +604 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +731 -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 -635
- 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 +14 -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 +16 -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/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 -97
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/persistent.md +0 -539
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- package/wiki/get-started/prerequisites.md +0 -113
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# Architectural Patterns
|
|
2
|
+
|
|
3
|
+
Ignis promotes separation of concerns, dependency injection, and modularity for scalable, maintainable applications.
|
|
4
|
+
|
|
5
|
+
> **Deep Dive:** See [Core Framework Reference](../references/src-details/core.md) for implementation details.
|
|
6
|
+
|
|
7
|
+
## 1. Layered Architecture
|
|
8
|
+
|
|
9
|
+
Each layer has a single responsibility. Ignis supports **two architectural approaches**:
|
|
10
|
+
|
|
11
|
+
```mermaid
|
|
12
|
+
graph TD
|
|
13
|
+
Client[Client/API Consumer]
|
|
14
|
+
|
|
15
|
+
Client -->|HTTP Request| Controller[Controllers]
|
|
16
|
+
|
|
17
|
+
Controller -->|Simple CRUD| Repo[Repositories]
|
|
18
|
+
Controller -->|Complex Logic| Service[Services]
|
|
19
|
+
|
|
20
|
+
Service --> Repo
|
|
21
|
+
|
|
22
|
+
Repo --> DataSource[DataSources]
|
|
23
|
+
DataSource --> DB[(Database)]
|
|
24
|
+
|
|
25
|
+
style Service fill:#e1f5ff
|
|
26
|
+
style Repo fill:#fff4e1
|
|
27
|
+
style Controller fill:#ffe1f5
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Layer | Responsibility | Example |
|
|
31
|
+
|-------|---------------|---------|
|
|
32
|
+
| **Controllers** | Handle HTTP - parse requests, validate, format responses | `ConfigurationController` (uses `ControllerFactory`) |
|
|
33
|
+
| **Services** | Business logic - orchestrate operations | `AuthenticationService` (auth logic) |
|
|
34
|
+
| **Repositories** | Data access - CRUD operations | `ConfigurationRepository` (extends `DefaultCRUDRepository`) |
|
|
35
|
+
| **DataSources** | Database connections | `PostgresDataSource` (connects to PostgreSQL) |
|
|
36
|
+
| **Models** | Data structure - Drizzle schemas + Entity classes | `Configuration`, `User` models |
|
|
37
|
+
|
|
38
|
+
**Key Principle - Two Approaches:**
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Simple CRUD (no business logic):
|
|
42
|
+
┌────────────┐
|
|
43
|
+
│ Controller │──────────────┐
|
|
44
|
+
└────────────┘ │
|
|
45
|
+
▼
|
|
46
|
+
┌──────────────┐
|
|
47
|
+
│ Repository │
|
|
48
|
+
└──────────────┘
|
|
49
|
+
│
|
|
50
|
+
▼
|
|
51
|
+
Database
|
|
52
|
+
|
|
53
|
+
Complex Logic (validation, orchestration):
|
|
54
|
+
┌────────────┐
|
|
55
|
+
│ Controller │────┐
|
|
56
|
+
└────────────┘ │
|
|
57
|
+
▼
|
|
58
|
+
┌─────────┐
|
|
59
|
+
│ Service │
|
|
60
|
+
└─────────┘
|
|
61
|
+
│
|
|
62
|
+
▼
|
|
63
|
+
┌──────────────┐
|
|
64
|
+
│ Repository │
|
|
65
|
+
└──────────────┘
|
|
66
|
+
│
|
|
67
|
+
▼
|
|
68
|
+
Database
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**When to use each:**
|
|
72
|
+
- **Controller → Repository** - Simple CRUD (list, get by ID, create, update, delete)
|
|
73
|
+
- **Controller → Service → Repository** - Business logic, validation, orchestrating multiple repositories
|
|
74
|
+
|
|
75
|
+
## 2. Dependency Injection (DI)
|
|
76
|
+
|
|
77
|
+
Classes declare dependencies in their constructor - the framework automatically provides them at runtime.
|
|
78
|
+
|
|
79
|
+
**Benefits:**
|
|
80
|
+
- Loosely coupled code
|
|
81
|
+
- Easy to test (mock dependencies)
|
|
82
|
+
- Easy to swap implementations
|
|
83
|
+
|
|
84
|
+
**Example:**
|
|
85
|
+
```typescript
|
|
86
|
+
@controller({ path: BASE_PATH })
|
|
87
|
+
export class ConfigurationController extends _Controller {
|
|
88
|
+
constructor(
|
|
89
|
+
// The @inject decorator tells the container to provide
|
|
90
|
+
// an instance of ConfigurationRepository here.
|
|
91
|
+
@inject({
|
|
92
|
+
key: BindingKeys.build({
|
|
93
|
+
namespace: BindingNamespaces.REPOSITORY,
|
|
94
|
+
key: ConfigurationRepository.name,
|
|
95
|
+
}),
|
|
96
|
+
})
|
|
97
|
+
repository: ConfigurationRepository,
|
|
98
|
+
) {
|
|
99
|
+
super(repository);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 3. Component-Based Modularity
|
|
105
|
+
|
|
106
|
+
Components bundle a group of related, reusable, and pluggable features into self-contained modules. A single component can encapsulate multiple providers, services, controllers, and repositories, essentially functioning as a mini-application that can be easily "plugged in" to any Ignis project.
|
|
107
|
+
|
|
108
|
+
**Built-in Components:**
|
|
109
|
+
- `AuthenticateComponent` - JWT authentication
|
|
110
|
+
- `SwaggerComponent` - OpenAPI documentation
|
|
111
|
+
- `HealthCheckComponent` - Health check endpoint
|
|
112
|
+
- `RequestTrackerComponent` - Request logging
|
|
113
|
+
|
|
114
|
+
**Example:**
|
|
115
|
+
```typescript
|
|
116
|
+
// src/application.ts
|
|
117
|
+
|
|
118
|
+
export class Application extends BaseApplication {
|
|
119
|
+
// ...
|
|
120
|
+
preConfigure(): ValueOrPromise<void> {
|
|
121
|
+
// ...
|
|
122
|
+
// Registering components plugs their functionality into the application.
|
|
123
|
+
this.component(HealthCheckComponent);
|
|
124
|
+
this.component(SwaggerComponent);
|
|
125
|
+
// ...
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
This architecture keeps the main `Application` class clean and focused on high-level assembly, while the details of each feature are neatly encapsulated within their respective components.
|
|
130
|
+
|
|
131
|
+
## 4. Custom Components
|
|
132
|
+
|
|
133
|
+
You can encapsulate your own logic or third-party integrations (like Socket.IO, Redis, specific Cron jobs) into reusable Components.
|
|
134
|
+
|
|
135
|
+
**Structure of a Component:**
|
|
136
|
+
1. Extend `BaseComponent`.
|
|
137
|
+
2. Define default `bindings` (optional configuration/options).
|
|
138
|
+
3. Implement `binding()` to register services, providers, or attach logic to the application.
|
|
139
|
+
|
|
140
|
+
**Example (`SocketIOComponent`):**
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { BaseComponent, inject, CoreBindings, Binding } from '@venizia/ignis';
|
|
144
|
+
|
|
145
|
+
export class MySocketComponent extends BaseComponent {
|
|
146
|
+
constructor(
|
|
147
|
+
@inject({ key: CoreBindings.APPLICATION_INSTANCE }) private application: BaseApplication,
|
|
148
|
+
) {
|
|
149
|
+
super({
|
|
150
|
+
scope: MySocketComponent.name,
|
|
151
|
+
// Automatically register bindings when component is loaded
|
|
152
|
+
initDefault: { enable: true, container: application },
|
|
153
|
+
bindings: {
|
|
154
|
+
// Define default configuration binding
|
|
155
|
+
'my.socket.options': Binding.bind({ key: 'my.socket.options' }).toValue({ port: 8080 }),
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// The binding method is called during application startup (preConfigure)
|
|
161
|
+
override binding(): void {
|
|
162
|
+
const options = this.application.get({ key: 'my.socket.options' });
|
|
163
|
+
|
|
164
|
+
this.logger.info('Initializing Socket.IO with options: %j', options);
|
|
165
|
+
|
|
166
|
+
// Perform setup logic, register other services, etc.
|
|
167
|
+
// this.application.bind(...).toValue(...);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## 5. Application Lifecycle Hooks
|
|
173
|
+
|
|
174
|
+
Ignis applications follow a predictable startup sequence with hooks for customization:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
178
|
+
│ initialize() │
|
|
179
|
+
├─────────────────────────────────────────────────────────────┤
|
|
180
|
+
│ 1. printStartUpInfo() - Log startup configuration │
|
|
181
|
+
│ 2. validateEnvs() - Validate APP_ENV_* variables │
|
|
182
|
+
│ 3. registerDefaultMiddlewares() - Error handlers, favicon │
|
|
183
|
+
│ 4. staticConfigure() - Configure static file serving │
|
|
184
|
+
│ │
|
|
185
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
186
|
+
│ │ 5. preConfigure() ← YOUR CODE HERE │ │
|
|
187
|
+
│ │ - Register DataSources │ │
|
|
188
|
+
│ │ - Register Repositories │ │
|
|
189
|
+
│ │ - Register Services │ │
|
|
190
|
+
│ │ - Register Controllers │ │
|
|
191
|
+
│ │ - Register Components │ │
|
|
192
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
193
|
+
│ │
|
|
194
|
+
│ 6. registerDataSources() - Initialize DB connections │
|
|
195
|
+
│ 7. registerComponents() - Configure all components │
|
|
196
|
+
│ 8. registerControllers() - Mount routes to router │
|
|
197
|
+
│ │
|
|
198
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
199
|
+
│ │ 9. postConfigure() ← YOUR CODE HERE │ │
|
|
200
|
+
│ │ - Seed data │ │
|
|
201
|
+
│ │ - Start background jobs │ │
|
|
202
|
+
│ │ - Custom initialization │ │
|
|
203
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
204
|
+
└─────────────────────────────────────────────────────────────┘
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Lifecycle Methods:**
|
|
208
|
+
|
|
209
|
+
| Method | When | Purpose |
|
|
210
|
+
|--------|------|---------|
|
|
211
|
+
| `staticConfigure()` | Before DI registration | Configure static file serving |
|
|
212
|
+
| `preConfigure()` | Before auto-registration | Register all bindings (datasources, repos, services, controllers, components) |
|
|
213
|
+
| `postConfigure()` | After everything is registered | Seed data, start jobs, custom logic |
|
|
214
|
+
|
|
215
|
+
**Example:**
|
|
216
|
+
```typescript
|
|
217
|
+
export class Application extends BaseApplication {
|
|
218
|
+
// Called before automatic registration
|
|
219
|
+
async preConfigure(): Promise<void> {
|
|
220
|
+
// DataSources (order matters - first)
|
|
221
|
+
this.dataSource(PostgresDataSource);
|
|
222
|
+
|
|
223
|
+
// Repositories
|
|
224
|
+
this.repository(UserRepository);
|
|
225
|
+
this.repository(OrderRepository);
|
|
226
|
+
|
|
227
|
+
// Services
|
|
228
|
+
this.service(AuthService);
|
|
229
|
+
this.service(EmailService);
|
|
230
|
+
|
|
231
|
+
// Controllers
|
|
232
|
+
this.controller(UserController);
|
|
233
|
+
this.controller(OrderController);
|
|
234
|
+
|
|
235
|
+
// Components
|
|
236
|
+
this.component(AuthenticateComponent);
|
|
237
|
+
this.component(SwaggerComponent);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Called after all registrations complete
|
|
241
|
+
async postConfigure(): Promise<void> {
|
|
242
|
+
// Access registered services
|
|
243
|
+
const userRepo = this.get<UserRepository>({
|
|
244
|
+
key: BindingKeys.build({
|
|
245
|
+
namespace: BindingNamespaces.REPOSITORY,
|
|
246
|
+
key: UserRepository.name,
|
|
247
|
+
}),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Seed initial data
|
|
251
|
+
const adminExists = await userRepo.findOne({
|
|
252
|
+
filter: { where: { role: 'admin' } },
|
|
253
|
+
});
|
|
254
|
+
if (!adminExists.data) {
|
|
255
|
+
await userRepo.create({ data: { name: 'Admin', role: 'admin' } });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Configure static file serving
|
|
260
|
+
staticConfigure(): void {
|
|
261
|
+
this.static({ restPath: '/public/*', folderPath: './public' });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
> [!WARNING]
|
|
267
|
+
> Do not register new datasources, components, or controllers in `postConfigure()`. They will not be automatically initialized. Use `preConfigure()` for all registrations.
|
|
268
|
+
|
|
269
|
+
## 6. Mixin Pattern
|
|
270
|
+
|
|
271
|
+
Mixins enable class composition without deep inheritance hierarchies. Ignis uses mixins to add capabilities to the `BaseApplication` class.
|
|
272
|
+
|
|
273
|
+
**How Mixins Work:**
|
|
274
|
+
```typescript
|
|
275
|
+
// A mixin is a function that takes a class and returns an extended class
|
|
276
|
+
const ServiceMixin = <T extends TMixinTarget<AbstractApplication>>(baseClass: T) => {
|
|
277
|
+
return class extends baseClass {
|
|
278
|
+
service<Base extends IService>(ctor: TClass<Base>): Binding<Base> {
|
|
279
|
+
return this.bind<Base>({
|
|
280
|
+
key: BindingKeys.build({
|
|
281
|
+
namespace: BindingNamespaces.SERVICE,
|
|
282
|
+
key: ctor.name,
|
|
283
|
+
}),
|
|
284
|
+
}).toClass(ctor);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Available Mixins:**
|
|
291
|
+
|
|
292
|
+
| Mixin | Methods Added | Purpose |
|
|
293
|
+
|-------|---------------|---------|
|
|
294
|
+
| `ServiceMixin` | `service()` | Register service classes |
|
|
295
|
+
| `RepositoryMixin` | `repository()`, `dataSource()`, `registerDataSources()` | Register data layer |
|
|
296
|
+
| `ControllerMixin` | `controller()` | Register HTTP controllers |
|
|
297
|
+
| `ComponentMixin` | `component()` | Register modular components |
|
|
298
|
+
|
|
299
|
+
**Composing Mixins:**
|
|
300
|
+
```typescript
|
|
301
|
+
// The framework composes mixins like this:
|
|
302
|
+
class AbstractApplication extends ComponentMixin(
|
|
303
|
+
ControllerMixin(
|
|
304
|
+
ServiceMixin(
|
|
305
|
+
RepositoryMixin(BaseClass)
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
) {
|
|
309
|
+
// Now has: service(), repository(), dataSource(), controller(), component()
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Why Mixins?**
|
|
314
|
+
- Avoid "diamond inheritance" problems
|
|
315
|
+
- Add capabilities selectively
|
|
316
|
+
- Keep base classes focused
|
|
317
|
+
- Enable code reuse across unrelated classes
|
|
318
|
+
|
|
319
|
+
## 7. Controller Factory Pattern
|
|
320
|
+
|
|
321
|
+
`ControllerFactory.defineCrudController()` generates a complete CRUD controller from an entity definition. This reduces boilerplate while maintaining full customization.
|
|
322
|
+
|
|
323
|
+
**Basic Usage:**
|
|
324
|
+
```typescript
|
|
325
|
+
const _Controller = ControllerFactory.defineCrudController({
|
|
326
|
+
entity: () => User,
|
|
327
|
+
repository: { name: UserRepository.name },
|
|
328
|
+
controller: {
|
|
329
|
+
name: 'UserController',
|
|
330
|
+
basePath: '/users',
|
|
331
|
+
isStrict: true, // Enable strict validation
|
|
332
|
+
defaultLimit: 50, // Default pagination limit
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
@controller({ path: '/users' })
|
|
337
|
+
export class UserController extends _Controller {
|
|
338
|
+
constructor(
|
|
339
|
+
@inject({ key: BindingKeys.build({ namespace: BindingNamespaces.REPOSITORY, key: UserRepository.name }) })
|
|
340
|
+
repository: UserRepository,
|
|
341
|
+
) {
|
|
342
|
+
super(repository);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Per-Route Authentication:**
|
|
348
|
+
```typescript
|
|
349
|
+
const _Controller = ControllerFactory.defineCrudController({
|
|
350
|
+
entity: () => User,
|
|
351
|
+
repository: { name: UserRepository.name },
|
|
352
|
+
controller: { name: 'UserController', basePath: '/users' },
|
|
353
|
+
|
|
354
|
+
// Apply JWT to all routes by default
|
|
355
|
+
authStrategies: [Authentication.STRATEGY_JWT],
|
|
356
|
+
|
|
357
|
+
// Override per-route
|
|
358
|
+
routes: {
|
|
359
|
+
// Public read endpoints
|
|
360
|
+
find: { skipAuth: true },
|
|
361
|
+
findById: { skipAuth: true },
|
|
362
|
+
count: { skipAuth: true },
|
|
363
|
+
|
|
364
|
+
// Protected write endpoints (use controller-level auth)
|
|
365
|
+
create: {},
|
|
366
|
+
updateById: {},
|
|
367
|
+
deleteById: {},
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Custom Schemas Per Route:**
|
|
373
|
+
```typescript
|
|
374
|
+
const _Controller = ControllerFactory.defineCrudController({
|
|
375
|
+
entity: () => User,
|
|
376
|
+
repository: { name: UserRepository.name },
|
|
377
|
+
controller: { name: 'UserController', basePath: '/users' },
|
|
378
|
+
|
|
379
|
+
routes: {
|
|
380
|
+
// Custom request body schema for create
|
|
381
|
+
create: {
|
|
382
|
+
authStrategies: [Authentication.STRATEGY_JWT],
|
|
383
|
+
requestBody: z.object({
|
|
384
|
+
email: z.string().email(),
|
|
385
|
+
name: z.string().min(2),
|
|
386
|
+
// Exclude sensitive fields from client input
|
|
387
|
+
}),
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
// Custom response schema
|
|
391
|
+
find: {
|
|
392
|
+
skipAuth: true,
|
|
393
|
+
schema: z.array(z.object({
|
|
394
|
+
id: z.string(),
|
|
395
|
+
name: z.string(),
|
|
396
|
+
// Exclude internal fields from response
|
|
397
|
+
})),
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Generated Routes:**
|
|
404
|
+
|
|
405
|
+
| Route | Method | Path | Description |
|
|
406
|
+
|-------|--------|------|-------------|
|
|
407
|
+
| `count` | GET | `/count` | Count records matching filter |
|
|
408
|
+
| `find` | GET | `/` | List records with filter |
|
|
409
|
+
| `findById` | GET | `/:id` | Get single record |
|
|
410
|
+
| `findOne` | GET | `/find-one` | Get first matching record |
|
|
411
|
+
| `create` | POST | `/` | Create new record |
|
|
412
|
+
| `updateById` | PATCH | `/:id` | Update record by ID |
|
|
413
|
+
| `updateBy` | PATCH | `/` | Bulk update by filter |
|
|
414
|
+
| `deleteById` | DELETE | `/:id` | Delete by ID |
|
|
415
|
+
| `deleteBy` | DELETE | `/` | Bulk delete by filter |
|