@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.
Files changed (134) 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 +647 -182
  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 +86 -0
  21. package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
  22. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  23. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  24. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  25. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  26. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  27. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  28. package/wiki/changelogs/index.md +8 -1
  29. package/wiki/changelogs/planned-schema-migrator.md +2 -10
  30. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  31. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  32. package/wiki/guides/core-concepts/components-guide.md +509 -0
  33. package/wiki/guides/core-concepts/components.md +122 -0
  34. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  35. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  36. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  37. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  38. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  39. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  40. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  41. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  42. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  43. package/wiki/guides/get-started/philosophy.md +682 -0
  44. package/wiki/guides/get-started/setup.md +157 -0
  45. package/wiki/guides/index.md +89 -0
  46. package/wiki/guides/reference/glossary.md +243 -0
  47. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  48. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  49. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  50. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  51. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  52. package/wiki/guides/tutorials/testing.md +723 -0
  53. package/wiki/index.md +176 -37
  54. package/wiki/references/base/application.md +27 -0
  55. package/wiki/references/base/bootstrapping.md +30 -26
  56. package/wiki/references/base/components.md +532 -31
  57. package/wiki/references/base/controllers.md +136 -38
  58. package/wiki/references/base/datasources.md +108 -5
  59. package/wiki/references/base/dependency-injection.md +39 -3
  60. package/wiki/references/base/filter-system/application-usage.md +224 -0
  61. package/wiki/references/base/filter-system/array-operators.md +132 -0
  62. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  63. package/wiki/references/base/filter-system/default-filter.md +428 -0
  64. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  65. package/wiki/references/base/filter-system/index.md +127 -0
  66. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  67. package/wiki/references/base/filter-system/list-operators.md +71 -0
  68. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  69. package/wiki/references/base/filter-system/null-operators.md +58 -0
  70. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  71. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  72. package/wiki/references/base/filter-system/range-operators.md +63 -0
  73. package/wiki/references/base/filter-system/tips.md +190 -0
  74. package/wiki/references/base/filter-system/use-cases.md +452 -0
  75. package/wiki/references/base/index.md +90 -0
  76. package/wiki/references/base/middlewares.md +602 -0
  77. package/wiki/references/base/models.md +215 -23
  78. package/wiki/references/base/providers.md +732 -0
  79. package/wiki/references/base/repositories/advanced.md +555 -0
  80. package/wiki/references/base/repositories/index.md +228 -0
  81. package/wiki/references/base/repositories/mixins.md +331 -0
  82. package/wiki/references/base/repositories/relations.md +486 -0
  83. package/wiki/references/base/repositories.md +40 -549
  84. package/wiki/references/base/services.md +28 -4
  85. package/wiki/references/components/authentication.md +22 -2
  86. package/wiki/references/components/health-check.md +12 -0
  87. package/wiki/references/components/index.md +23 -0
  88. package/wiki/references/components/mail.md +687 -0
  89. package/wiki/references/components/request-tracker.md +16 -0
  90. package/wiki/references/components/socket-io.md +18 -0
  91. package/wiki/references/components/static-asset.md +14 -26
  92. package/wiki/references/components/swagger.md +17 -0
  93. package/wiki/references/configuration/environment-variables.md +427 -0
  94. package/wiki/references/configuration/index.md +73 -0
  95. package/wiki/references/helpers/cron.md +14 -0
  96. package/wiki/references/helpers/crypto.md +15 -0
  97. package/wiki/references/helpers/env.md +16 -0
  98. package/wiki/references/helpers/error.md +17 -0
  99. package/wiki/references/helpers/index.md +15 -0
  100. package/wiki/references/helpers/inversion.md +24 -4
  101. package/wiki/references/helpers/logger.md +19 -0
  102. package/wiki/references/helpers/network.md +11 -0
  103. package/wiki/references/helpers/queue.md +19 -0
  104. package/wiki/references/helpers/redis.md +21 -0
  105. package/wiki/references/helpers/socket-io.md +24 -5
  106. package/wiki/references/helpers/storage.md +18 -10
  107. package/wiki/references/helpers/testing.md +18 -0
  108. package/wiki/references/helpers/types.md +167 -0
  109. package/wiki/references/helpers/uid.md +167 -0
  110. package/wiki/references/helpers/worker-thread.md +16 -0
  111. package/wiki/references/index.md +177 -0
  112. package/wiki/references/quick-reference.md +634 -0
  113. package/wiki/references/src-details/boot.md +3 -3
  114. package/wiki/references/src-details/dev-configs.md +0 -4
  115. package/wiki/references/src-details/docs.md +2 -2
  116. package/wiki/references/src-details/index.md +86 -0
  117. package/wiki/references/src-details/inversion.md +1 -6
  118. package/wiki/references/src-details/mcp-server.md +3 -15
  119. package/wiki/references/utilities/index.md +86 -10
  120. package/wiki/references/utilities/jsx.md +577 -0
  121. package/wiki/references/utilities/request.md +0 -2
  122. package/wiki/references/utilities/statuses.md +740 -0
  123. package/wiki/changelogs/planned-transaction-support.md +0 -216
  124. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  125. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  126. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  127. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  128. package/wiki/get-started/best-practices/performance-optimization.md +0 -88
  129. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  130. package/wiki/get-started/core-concepts/components.md +0 -98
  131. package/wiki/get-started/core-concepts/persistent.md +0 -543
  132. package/wiki/get-started/index.md +0 -65
  133. package/wiki/get-started/philosophy.md +0 -296
  134. package/wiki/get-started/prerequisites.md +0 -113
@@ -1,216 +0,0 @@
1
- ---
2
- title: Planned - Transaction Support
3
- description: Implementation plan for Loopback 4-style explicit transaction objects
4
- ---
5
-
6
- # Planned: Transaction Support
7
-
8
- **Status:** Planned (Not Yet Implemented)
9
- **Priority:** Future Enhancement
10
-
11
- ## Goal
12
-
13
- Implement Loopback 4-style explicit transaction objects, allowing transactions to be passed through multiple services/repositories instead of using Drizzle's callback-based approach.
14
-
15
- ## Target API
16
-
17
- ```typescript
18
- // Default isolation level (READ COMMITTED)
19
- const tx = await userRepo.beginTransaction();
20
-
21
- // Or with specific isolation level
22
- const tx = await userRepo.beginTransaction({
23
- isolationLevel: 'SERIALIZABLE'
24
- });
25
-
26
- try {
27
- await userRepo.create({ data, options: { transaction: tx } });
28
- await profileRepo.create({ data, options: { transaction: tx } });
29
- await tx.commit();
30
- } catch (err) {
31
- await tx.rollback();
32
- throw err;
33
- }
34
- ```
35
-
36
- ### Isolation Levels
37
-
38
- | Level | Description | Use Case |
39
- |-------|-------------|----------|
40
- | `READ COMMITTED` | Default. Sees only committed data at query start | General use, most common |
41
- | `REPEATABLE READ` | Sees snapshot from transaction start | Reports, consistent reads |
42
- | `SERIALIZABLE` | Strictest. Full isolation, may throw serialization errors | Financial transactions, critical data |
43
-
44
- ---
45
-
46
- ## Implementation Steps
47
-
48
- ### Step 1: Define Transaction Types
49
-
50
- **File:** `packages/core/src/base/datasources/types.ts`
51
-
52
- ```typescript
53
- /** PostgreSQL transaction isolation levels */
54
- export type TIsolationLevel = 'READ COMMITTED' | 'REPEATABLE READ' | 'SERIALIZABLE';
55
-
56
- /** Options for starting a transaction */
57
- export interface ITransactionOptions {
58
- isolationLevel?: TIsolationLevel;
59
- }
60
-
61
- /** Transaction object returned by beginTransaction() */
62
- export interface ITransaction<Connector = TNodePostgresConnector> {
63
- /** Isolated Drizzle instance bound to this transaction */
64
- connector: Connector;
65
-
66
- /** Commit the transaction */
67
- commit(): Promise<void>;
68
-
69
- /** Rollback the transaction */
70
- rollback(): Promise<void>;
71
-
72
- /** Check if transaction is still active */
73
- isActive: boolean;
74
-
75
- /** The isolation level used for this transaction */
76
- isolationLevel: TIsolationLevel;
77
- }
78
- ```
79
-
80
- ### Step 2: Add `beginTransaction()` to DataSource
81
-
82
- **File:** `packages/core/src/base/datasources/base.ts`
83
-
84
- ```typescript
85
- async beginTransaction(
86
- opts?: ITransactionOptions
87
- ): Promise<ITransaction<Connector>> {
88
- // 1. Get raw client from pool
89
- const pool = this.connector.client as Pool;
90
- const client = await pool.connect();
91
-
92
- // 2. Determine isolation level (default: READ COMMITTED)
93
- const isolationLevel: TIsolationLevel = opts?.isolationLevel ?? 'READ COMMITTED';
94
-
95
- // 3. Execute BEGIN with isolation level
96
- await client.query(`BEGIN TRANSACTION ISOLATION LEVEL ${isolationLevel}`);
97
-
98
- // 4. Create isolated Drizzle instance with this client
99
- const txConnector = drizzle({ client, schema: this.schema });
100
-
101
- // 5. Return transaction object
102
- let isActive = true;
103
-
104
- return {
105
- connector: txConnector as Connector,
106
- isActive,
107
- isolationLevel,
108
-
109
- async commit() {
110
- if (!isActive) throw new Error('Transaction already ended');
111
- try {
112
- await client.query('COMMIT');
113
- } finally {
114
- isActive = false;
115
- client.release();
116
- }
117
- },
118
-
119
- async rollback() {
120
- if (!isActive) throw new Error('Transaction already ended');
121
- try {
122
- await client.query('ROLLBACK');
123
- } finally {
124
- isActive = false;
125
- client.release();
126
- }
127
- },
128
- };
129
- }
130
- ```
131
-
132
- ### Step 3: Update Repository Base
133
-
134
- **File:** `packages/core/src/base/repositories/core/base.ts`
135
-
136
- ```typescript
137
- // Add method to start transaction (delegates to DataSource)
138
- async beginTransaction(opts?: ITransactionOptions): Promise<ITransaction> {
139
- return this.dataSource.beginTransaction(opts);
140
- }
141
-
142
- // Replace this.connector with getConnector(opts)
143
- protected getConnector(opts?: { transaction?: ITransaction }) {
144
- if (opts?.transaction) {
145
- if (!opts.transaction.isActive) {
146
- throw getError({ message: 'Transaction is no longer active' });
147
- }
148
- return opts.transaction.connector;
149
- }
150
- return this.dataSource.connector;
151
- }
152
- ```
153
-
154
- ### Step 4: Update CRUD Options Types
155
-
156
- **File:** `packages/core/src/base/repositories/common/types.ts`
157
-
158
- ```typescript
159
- export type TTransactionOption = {
160
- transaction?: ITransaction;
161
- };
162
-
163
- // Add to existing option types
164
- export type TCreateOptions = TTransactionOption & {
165
- shouldReturn?: boolean;
166
- log?: TRepositoryLogOptions;
167
- };
168
- ```
169
-
170
- ### Step 5: Update CRUD Methods
171
-
172
- **Files:** `readable.ts`, `persistable.ts`
173
-
174
- Change all methods from:
175
- ```typescript
176
- this.connector.insert(...)
177
- ```
178
-
179
- To:
180
- ```typescript
181
- this.getConnector(opts.options).insert(...)
182
- ```
183
-
184
- ---
185
-
186
- ## Files to Modify
187
-
188
- | File | Changes |
189
- |------|---------|
190
- | `packages/core/src/base/datasources/types.ts` | Add `TIsolationLevel`, `ITransactionOptions`, `ITransaction` |
191
- | `packages/core/src/base/datasources/base.ts` | Add `beginTransaction(opts?)` method |
192
- | `packages/core/src/base/repositories/common/types.ts` | Add `TTransactionOption` |
193
- | `packages/core/src/base/repositories/core/base.ts` | Add `beginTransaction(opts?)`, `getConnector(opts)` |
194
- | `packages/core/src/base/repositories/core/readable.ts` | Use `getConnector(opts)` in all methods |
195
- | `packages/core/src/base/repositories/core/persistable.ts` | Use `getConnector(opts)` in all methods |
196
-
197
- ---
198
-
199
- ## Breaking Changes
200
-
201
- 1. **`this.connector`** → `this.getConnector(opts)`
202
- - Backward compatible when called without args
203
-
204
- 2. **Options parameter** - Now includes optional `transaction` field
205
- - Non-breaking: transaction is optional
206
-
207
- ---
208
-
209
- ## Benefits
210
-
211
- | Aspect | Current (Drizzle Callback) | After (Pass-through) |
212
- |--------|---------------------------|----------------------|
213
- | Service composition | Hard - all in one callback | Easy - pass tx anywhere |
214
- | Separation of concerns | Services must know each other | Services stay independent |
215
- | Testing | Complex mocking | Easy to mock tx object |
216
- | Code organization | Nested callbacks | Flat, sequential flow |
@@ -1,266 +0,0 @@
1
- # API Usage Examples
2
-
3
- Practical examples for defining endpoints and working with data in Ignis applications.
4
-
5
- ## Routing Patterns
6
-
7
- ### Decorator-Based Routing (Recommended)
8
-
9
- Use `@get`, `@post` decorators with `as const` route configs for full type safety:
10
-
11
- **`src/controllers/test/definitions.ts`**
12
- ```typescript
13
- import { z } from '@hono/zod-openapi';
14
- import { Authentication, HTTP, jsonContent, jsonResponse } from '@venizia/ignis';
15
-
16
- // Define route configs as const for type inference
17
- export const ROUTE_CONFIGS = {
18
- // ... (other routes)
19
- ['/4']: {
20
- method: HTTP.Methods.GET,
21
- path: '/4',
22
- responses: jsonResponse({
23
- description: 'Test decorator GET endpoint',
24
- schema: z.object({ message: z.string(), method: z.string() }),
25
- }),
26
- },
27
- ['/5']: {
28
- method: HTTP.Methods.POST,
29
- path: '/5',
30
- authStrategies: [Authentication.STRATEGY_JWT], // Secure this endpoint
31
- request: {
32
- body: jsonContent({
33
- description: 'Request body for POST',
34
- schema: z.object({ name: z.string(), age: z.number().int().positive() }),
35
- }),
36
- },
37
- responses: jsonResponse({
38
- description: 'Test decorator POST endpoint',
39
- schema: z.object({ id: z.string(), name: z.string(), age: z.number() }),
40
- }),
41
- },
42
- } as const;
43
- ```
44
-
45
- Then, use the decorators in your controller class. The `TRouteContext` type provides a fully typed context, including request parameters, body, and response types.
46
-
47
- **`src/controllers/test/controller.ts`**
48
- ```typescript
49
- import {
50
- BaseController,
51
- controller,
52
- get,
53
- post,
54
- TRouteContext,
55
- HTTP,
56
- } from '@venizia/ignis';
57
- import { ROUTE_CONFIGS } from './definitions';
58
-
59
- @controller({ path: '/test' })
60
- export class TestController extends BaseController {
61
- // ...
62
-
63
- @get({ configs: ROUTE_CONFIGS['/4'] })
64
- getWithDecorator(context: TRouteContext<(typeof ROUTE_CONFIGS)['/4']>) {
65
- // context is fully typed!
66
- return context.json({ message: 'Hello from decorator', method: 'GET' }, HTTP.ResultCodes.RS_2.Ok);
67
- }
68
-
69
- @post({ configs: ROUTE_CONFIGS['/5'] })
70
- createWithDecorator(context: TRouteContext<(typeof ROUTE_CONFIGS)['/5']>) {
71
- // context.req.valid('json') is automatically typed as { name: string, age: number }
72
- const body = context.req.valid('json');
73
-
74
- // The response is validated against the schema
75
- return context.json(
76
- {
77
- id: crypto.randomUUID(),
78
- name: body.name,
79
- age: body.age,
80
- },
81
- HTTP.ResultCodes.RS_2.Ok,
82
- );
83
- }
84
- }
85
- ```
86
-
87
- ### Example 2: Manual Route Definition in `binding()`
88
-
89
- You can also define routes manually within the controller's `binding()` method using `defineRoute` or `bindRoute`. This is useful for more complex scenarios or for developers who prefer a non-decorator syntax.
90
-
91
- **`src/controllers/test/controller.ts`**
92
- ```typescript
93
- import { BaseController, controller, HTTP, ValueOrPromise } from '@venizia/ignis';
94
- import { ROUTE_CONFIGS } from './definitions';
95
-
96
- @controller({ path: '/test' })
97
- export class TestController extends BaseController {
98
- // ...
99
- override binding(): ValueOrPromise<void> {
100
- // Using 'defineRoute'
101
- this.defineRoute({
102
- configs: ROUTE_CONFIGS['/1'],
103
- handler: context => {
104
- return context.json({ message: 'Hello' }, HTTP.ResultCodes.RS_2.Ok);
105
- },
106
- });
107
-
108
- // Using 'bindRoute' for a fluent API
109
- this.bindRoute({
110
- configs: ROUTE_CONFIGS['/3'],
111
- }).to({
112
- handler: context => {
113
- return context.json({ message: 'Hello 3' }, HTTP.ResultCodes.RS_2.Ok);
114
- },
115
- });
116
- }
117
- // ...
118
- }
119
- ```
120
-
121
- ### Example 3: Auto-Generated CRUD Controller
122
-
123
- For standard database entities, you can use `ControllerFactory.defineCrudController` to instantly generate a controller with a full set of CRUD endpoints.
124
-
125
- **`src/controllers/configuration.controller.ts`**
126
- ```typescript
127
- import { Configuration } from '@/models';
128
- import { ConfigurationRepository } from '@/repositories';
129
- import {
130
- BindingKeys,
131
- BindingNamespaces,
132
- controller,
133
- ControllerFactory,
134
- inject,
135
- } from '@venizia/ignis';
136
-
137
- const BASE_PATH = '/configurations';
138
-
139
- // 1. The factory generates a controller class with all CRUD routes
140
- const _Controller = ControllerFactory.defineCrudController({
141
- repository: { name: ConfigurationRepository.name },
142
- controller: {
143
- name: 'ConfigurationController',
144
- basePath: BASE_PATH,
145
- },
146
- entity: () => Configuration, // The entity is used to generate OpenAPI schemas
147
- });
148
-
149
- // 2. Extend the generated controller to inject the repository
150
- @controller({ path: BASE_PATH })
151
- export class ConfigurationController extends _Controller {
152
- constructor(
153
- @inject({
154
- key: BindingKeys.build({
155
- namespace: BindingNamespaces.REPOSITORY,
156
- key: ConfigurationRepository.name,
157
- }),
158
- })
159
- repository: ConfigurationRepository,
160
- ) {
161
- super(repository);
162
- }
163
- }
164
- ```
165
- This automatically creates endpoints like `GET /configurations`, `POST /configurations`, `GET /configurations/:id`, etc.
166
-
167
- ## Repository (Data Access) Usage
168
-
169
- Repositories are used to interact with your database. The `DefaultCRUDRepository` provides a rich set of methods for data manipulation. Here are examples from the `postConfigure` method in `src/application.ts`, which demonstrates how to use an injected repository.
170
-
171
- ```typescript
172
- // In src/application.ts
173
-
174
- // Get the repository instance from the DI container
175
- const configurationRepository = this.get<ConfigurationRepository>({
176
- key: BindingKeys.build({
177
- namespace: BindingNamespaces.REPOSITORY,
178
- key: ConfigurationRepository.name,
179
- }),
180
- });
181
-
182
- // --- Find One Record ---
183
- const record = await configurationRepository.findOne({
184
- filter: { where: { code: 'CODE_1' } },
185
- });
186
-
187
- // --- Find Multiple Records with Relations ---
188
- const records = await configurationRepository.find({
189
- filter: {
190
- where: { code: 'CODE_2' },
191
- fields: { id: true, code: true, createdBy: true },
192
- limit: 100,
193
- include: [{ relation: 'creator' }], // Eager load the 'creator' relation
194
- },
195
- });
196
-
197
- // --- Create a Single Record ---
198
- const newRecord = await configurationRepository.create({
199
- data: {
200
- code: 'NEW_CODE',
201
- group: 'SYSTEM',
202
- dataType: 'TEXT',
203
- tValue: 'some value',
204
- },
205
- });
206
-
207
- // --- Create Multiple Records ---
208
- const newRecords = await configurationRepository.createAll({
209
- data: [
210
- { code: 'CODE_A', group: 'SYSTEM' },
211
- { code: 'CODE_B', group: 'SYSTEM' },
212
- ],
213
- });
214
-
215
- // --- Update a Record by ID ---
216
- const updated = await configurationRepository.updateById({
217
- id: 'some-uuid',
218
- data: { tValue: 'new value' },
219
- });
220
-
221
- // --- Delete a Record by ID ---
222
- const deleted = await configurationRepository.deleteById({
223
- id: newRecord.data!.id,
224
- options: { shouldReturn: true }, // Option to return the deleted record
225
- });
226
-
227
- ## Server-Side Rendering (JSX)
228
-
229
- Ignis supports server-side rendering using Hono's JSX middleware. This is useful for returning HTML content, such as landing pages or simple admin views.
230
-
231
- **Usage:**
232
-
233
- Use `defineJSXRoute` in your controller and `htmlResponse` for documentation.
234
-
235
- ```typescript
236
- import { BaseController, controller, htmlResponse } from '@venizia/ignis';
237
-
238
- @controller({ path: '/pages' })
239
- export class PageController extends BaseController {
240
-
241
- override binding(): void {
242
- this.defineJSXRoute({
243
- configs: {
244
- method: 'get',
245
- path: '/welcome',
246
- description: 'Welcome Page',
247
- responses: htmlResponse({ description: 'HTML Welcome Page' }),
248
- },
249
- handler: (c) => {
250
- const title = 'Welcome to Ignis';
251
-
252
- // Return JSX directly
253
- return c.html(
254
- <html>
255
- <head><title>{title}</title></head>
256
- <body>
257
- <h1>{title}</h1>
258
- <p>Server-side rendered content.</p>
259
- </body>
260
- </html>
261
- );
262
- },
263
- });
264
- }
265
- }
266
- ```
@@ -1,170 +0,0 @@
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
- ```