@venizia/ignis-docs 0.0.8-3 → 0.1.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 (213) hide show
  1. package/README.md +7 -7
  2. package/{wiki → content}/best-practices/api-usage-examples.md +15 -12
  3. package/{wiki → content}/best-practices/architectural-patterns.md +70 -78
  4. package/{wiki → content}/best-practices/architecture-decisions.md +91 -60
  5. package/{wiki → content}/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/{wiki → content}/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/{wiki → content}/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/{wiki → content}/best-practices/code-style-standards/documentation.md +13 -13
  9. package/{wiki → content}/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/{wiki → content}/best-practices/code-style-standards/index.md +1 -1
  11. package/{wiki → content}/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/{wiki → content}/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/{wiki → content}/best-practices/code-style-standards/tooling.md +8 -5
  14. package/{wiki → content}/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/{wiki → content}/best-practices/common-pitfalls.md +56 -37
  16. package/{wiki → content}/best-practices/contribution-workflow.md +13 -14
  17. package/{wiki → content}/best-practices/data-modeling.md +44 -20
  18. package/{wiki → content}/best-practices/deployment-strategies.md +28 -27
  19. package/{wiki → content}/best-practices/error-handling.md +48 -24
  20. package/{wiki → content}/best-practices/index.md +5 -5
  21. package/{wiki → content}/best-practices/performance-optimization.md +36 -28
  22. package/{wiki → content}/best-practices/security-guidelines.md +52 -23
  23. package/{wiki → content}/best-practices/testing-strategies.md +65 -51
  24. package/{wiki → content}/best-practices/troubleshooting-tips.md +24 -24
  25. package/{wiki/extensions/components/swagger.md → content/extensions/components/api-reference.md} +40 -31
  26. package/{wiki → content}/extensions/components/authentication/api.md +19 -19
  27. package/{wiki → content}/extensions/components/authentication/errors.md +7 -7
  28. package/{wiki → content}/extensions/components/authentication/index.md +10 -8
  29. package/{wiki → content}/extensions/components/authentication/usage.md +101 -6
  30. package/{wiki → content}/extensions/components/authorization/api.md +45 -25
  31. package/{wiki → content}/extensions/components/authorization/errors.md +6 -6
  32. package/{wiki → content}/extensions/components/authorization/index.md +11 -10
  33. package/{wiki → content}/extensions/components/authorization/usage.md +21 -21
  34. package/{wiki → content}/extensions/components/health-check.md +1 -1
  35. package/{wiki → content}/extensions/components/index.md +5 -5
  36. package/{wiki → content}/extensions/components/mail/errors.md +15 -15
  37. package/{wiki → content}/extensions/components/mail/index.md +1 -2
  38. package/{wiki → content}/extensions/components/mail/usage.md +1 -1
  39. package/{wiki → content}/extensions/components/request-tracker.md +1 -1
  40. package/{wiki → content}/extensions/components/socket-io/api.md +9 -9
  41. package/{wiki → content}/extensions/components/socket-io/errors.md +5 -5
  42. package/{wiki → content}/extensions/components/socket-io/index.md +8 -8
  43. package/{wiki → content}/extensions/components/socket-io/usage.md +1 -1
  44. package/{wiki → content}/extensions/components/static-asset/api.md +17 -4
  45. package/{wiki → content}/extensions/components/static-asset/errors.md +4 -4
  46. package/{wiki → content}/extensions/components/static-asset/index.md +26 -28
  47. package/{wiki → content}/extensions/components/static-asset/usage.md +13 -12
  48. package/{wiki → content}/extensions/components/template/index.md +2 -2
  49. package/{wiki → content}/extensions/components/template/setup-page.md +1 -1
  50. package/{wiki → content}/extensions/components/websocket/api.md +3 -3
  51. package/{wiki → content}/extensions/components/websocket/errors.md +5 -5
  52. package/{wiki → content}/extensions/components/websocket/index.md +5 -5
  53. package/{wiki → content}/extensions/components/websocket/usage.md +3 -3
  54. package/{wiki → content}/extensions/helpers/cron/index.md +2 -2
  55. package/{wiki → content}/extensions/helpers/crypto/index.md +1 -1
  56. package/{wiki → content}/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +283 -0
  58. package/{wiki → content}/extensions/helpers/index.md +2 -3
  59. package/{wiki → content}/extensions/helpers/inversion/index.md +15 -7
  60. package/{wiki → content}/extensions/helpers/kafka/examples.md +1 -1
  61. package/{wiki → content}/extensions/helpers/logger/index.md +32 -2
  62. package/{wiki → content}/extensions/helpers/network/index.md +6 -0
  63. package/{wiki → content}/extensions/helpers/queue/index.md +14 -17
  64. package/content/extensions/helpers/redis/index.md +713 -0
  65. package/{wiki → content}/extensions/helpers/socket-io/index.md +14 -10
  66. package/{wiki → content}/extensions/helpers/storage/api.md +44 -8
  67. package/{wiki → content}/extensions/helpers/storage/index.md +43 -7
  68. package/{wiki → content}/extensions/helpers/template/index.md +6 -3
  69. package/{wiki → content}/extensions/helpers/types/index.md +11 -8
  70. package/{wiki → content}/extensions/helpers/websocket/api.md +9 -9
  71. package/{wiki → content}/extensions/helpers/websocket/index.md +7 -7
  72. package/{wiki → content}/extensions/helpers/worker-thread/index.md +2 -2
  73. package/{wiki → content}/extensions/index.md +3 -4
  74. package/{wiki → content}/extensions/src-details/mcp-server.md +18 -24
  75. package/{wiki → content}/guides/core-concepts/application/bootstrapping.md +11 -14
  76. package/{wiki → content}/guides/core-concepts/application/index.md +3 -3
  77. package/{wiki → content}/guides/core-concepts/components.md +19 -10
  78. package/{wiki → content}/guides/core-concepts/dependency-injection.md +6 -3
  79. package/{wiki → content}/guides/core-concepts/grpc-controllers.md +6 -5
  80. package/{wiki → content}/guides/core-concepts/persistent/datasources.md +33 -27
  81. package/{wiki → content}/guides/core-concepts/persistent/index.md +16 -5
  82. package/{wiki → content}/guides/core-concepts/persistent/models.md +24 -20
  83. package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
  84. package/{wiki → content}/guides/core-concepts/persistent/repositories.md +40 -23
  85. package/content/guides/core-concepts/persistent/search-meilisearch.md +183 -0
  86. package/content/guides/core-concepts/persistent/search-typesense.md +429 -0
  87. package/{wiki → content}/guides/core-concepts/persistent/transactions.md +61 -25
  88. package/{wiki → content}/guides/core-concepts/rest-controllers.md +12 -9
  89. package/content/guides/core-concepts/services.md +389 -0
  90. package/{wiki → content}/guides/get-started/5-minute-quickstart.md +19 -19
  91. package/{wiki → content}/guides/get-started/philosophy.md +36 -36
  92. package/{wiki → content}/guides/get-started/setup.md +3 -3
  93. package/{wiki → content}/guides/index.md +3 -3
  94. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  95. package/{wiki → content}/guides/migrations/scoped-rbac-migration.md +17 -17
  96. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  97. package/{wiki → content}/guides/reference/glossary.md +19 -12
  98. package/{wiki → content}/guides/reference/mcp-docs-server.md +22 -18
  99. package/{wiki → content}/guides/tutorials/building-a-crud-api.md +30 -33
  100. package/{wiki → content}/guides/tutorials/complete-installation.md +17 -17
  101. package/{wiki → content}/guides/tutorials/ecommerce-api.md +158 -119
  102. package/{wiki → content}/guides/tutorials/realtime-chat.md +176 -130
  103. package/content/guides/tutorials/testing.md +264 -0
  104. package/content/index.md +5 -0
  105. package/content/public/apple-touch-icon.png +0 -0
  106. package/content/public/og-image.png +0 -0
  107. package/content/public/site.webmanifest +11 -0
  108. package/{wiki → content}/references/base/application.md +4 -5
  109. package/{wiki → content}/references/base/bootstrapping.md +18 -5
  110. package/{wiki → content}/references/base/components.md +149 -120
  111. package/content/references/base/connectors.md +178 -0
  112. package/{wiki → content}/references/base/controllers.md +41 -30
  113. package/content/references/base/datasources.md +527 -0
  114. package/{wiki → content}/references/base/dependency-injection.md +34 -22
  115. package/{wiki → content}/references/base/filter-system/application-usage.md +17 -14
  116. package/{wiki → content}/references/base/filter-system/array-operators.md +7 -2
  117. package/{wiki → content}/references/base/filter-system/comparison-operators.md +3 -0
  118. package/{wiki → content}/references/base/filter-system/default-filter.md +89 -71
  119. package/{wiki → content}/references/base/filter-system/fields-order-pagination.md +22 -22
  120. package/{wiki → content}/references/base/filter-system/index.md +6 -3
  121. package/{wiki → content}/references/base/filter-system/json-filtering.md +20 -1
  122. package/{wiki → content}/references/base/filter-system/list-operators.md +1 -1
  123. package/{wiki → content}/references/base/filter-system/logical-operators.md +33 -1
  124. package/{wiki → content}/references/base/filter-system/null-operators.md +30 -1
  125. package/{wiki → content}/references/base/filter-system/quick-reference.md +23 -4
  126. package/{wiki → content}/references/base/filter-system/tips.md +5 -5
  127. package/{wiki → content}/references/base/filter-system/use-cases.md +12 -12
  128. package/{wiki → content}/references/base/grpc-controllers.md +13 -13
  129. package/{wiki → content}/references/base/index.md +24 -12
  130. package/{wiki/references/base/middleware.md → content/references/base/middlewares.md} +205 -24
  131. package/{wiki → content}/references/base/models.md +63 -49
  132. package/{wiki → content}/references/base/providers.md +136 -130
  133. package/{wiki → content}/references/base/repositories/advanced.md +59 -58
  134. package/{wiki → content}/references/base/repositories/index.md +115 -91
  135. package/content/references/base/repositories/mixins.md +99 -0
  136. package/{wiki → content}/references/base/repositories/relations.md +54 -64
  137. package/{wiki → content}/references/base/repositories/soft-deletable.md +31 -30
  138. package/content/references/base/services.md +404 -0
  139. package/{wiki → content}/references/configuration/environment-variables.md +46 -30
  140. package/{wiki → content}/references/configuration/index.md +6 -6
  141. package/{wiki → content}/references/index.md +17 -12
  142. package/{wiki → content}/references/quick-reference.md +65 -106
  143. package/content/references/utilities/crypto.md +98 -0
  144. package/{wiki → content}/references/utilities/index.md +3 -3
  145. package/{wiki → content}/references/utilities/jsx.md +6 -4
  146. package/content/references/utilities/module.md +90 -0
  147. package/{wiki → content}/references/utilities/parse.md +4 -14
  148. package/{wiki → content}/references/utilities/promise.md +9 -7
  149. package/{wiki → content}/references/utilities/schema.md +5 -3
  150. package/dist/mcp-server/common/guards.d.ts +8 -0
  151. package/dist/mcp-server/common/guards.d.ts.map +1 -0
  152. package/dist/mcp-server/common/guards.js +14 -0
  153. package/dist/mcp-server/common/guards.js.map +1 -0
  154. package/dist/mcp-server/common/index.d.ts +1 -0
  155. package/dist/mcp-server/common/index.d.ts.map +1 -1
  156. package/dist/mcp-server/common/index.js +1 -0
  157. package/dist/mcp-server/common/index.js.map +1 -1
  158. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  159. package/dist/mcp-server/common/paths.js +2 -2
  160. package/dist/mcp-server/common/paths.js.map +1 -1
  161. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  162. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  163. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  164. package/dist/mcp-server/helpers/github.helper.js +1 -1
  165. package/dist/mcp-server/index.js +7 -2
  166. package/dist/mcp-server/index.js.map +1 -1
  167. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  168. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  169. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  170. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  171. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  173. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  174. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  175. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  176. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  177. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  178. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  179. package/package.json +12 -12
  180. package/wiki/extensions/helpers/error/index.md +0 -227
  181. package/wiki/extensions/helpers/redis/index.md +0 -488
  182. package/wiki/extensions/helpers/testing/index.md +0 -510
  183. package/wiki/guides/core-concepts/services.md +0 -119
  184. package/wiki/guides/tutorials/testing.md +0 -722
  185. package/wiki/index.md +0 -183
  186. package/wiki/references/base/datasources.md +0 -454
  187. package/wiki/references/base/middlewares.md +0 -590
  188. package/wiki/references/base/repositories/mixins.md +0 -335
  189. package/wiki/references/base/services.md +0 -201
  190. package/wiki/references/utilities/crypto.md +0 -56
  191. package/wiki/references/utilities/module.md +0 -42
  192. /package/{wiki → content}/extensions/components/mail/api.md +0 -0
  193. /package/{wiki → content}/extensions/components/template/api-page.md +0 -0
  194. /package/{wiki → content}/extensions/components/template/errors-page.md +0 -0
  195. /package/{wiki → content}/extensions/components/template/single-page.md +0 -0
  196. /package/{wiki → content}/extensions/components/template/usage-page.md +0 -0
  197. /package/{wiki → content}/extensions/helpers/kafka/admin.md +0 -0
  198. /package/{wiki → content}/extensions/helpers/kafka/consumer.md +0 -0
  199. /package/{wiki → content}/extensions/helpers/kafka/index.md +0 -0
  200. /package/{wiki → content}/extensions/helpers/kafka/producer.md +0 -0
  201. /package/{wiki → content}/extensions/helpers/kafka/schema-registry.md +0 -0
  202. /package/{wiki → content}/extensions/helpers/network/api.md +0 -0
  203. /package/{wiki → content}/extensions/helpers/socket-io/api.md +0 -0
  204. /package/{wiki → content}/extensions/helpers/template/single-page.md +0 -0
  205. /package/{wiki → content}/extensions/helpers/uid/index.md +0 -0
  206. /package/{wiki → content}/guides/core-concepts/components-guide.md +0 -0
  207. /package/{wiki → content}/public/logo.svg +0 -0
  208. /package/{wiki → content}/references/base/filter-system/pattern-matching.md +0 -0
  209. /package/{wiki → content}/references/base/filter-system/range-operators.md +0 -0
  210. /package/{wiki → content}/references/utilities/date.md +0 -0
  211. /package/{wiki → content}/references/utilities/performance.md +0 -0
  212. /package/{wiki → content}/references/utilities/request.md +0 -0
  213. /package/{wiki → content}/references/utilities/statuses.md +0 -0
@@ -0,0 +1,404 @@
1
+ ---
2
+ title: Services Reference
3
+ description: Technical reference for BaseService and the business logic layer in IGNIS
4
+ difficulty: beginner
5
+ ---
6
+
7
+ # Deep Dive: Services
8
+
9
+ Technical reference for `BaseService` - the foundation for the business logic layer in IGNIS.
10
+
11
+ **File:** `packages/core/src/base/services/base.ts`
12
+
13
+ ## Quick Reference
14
+
15
+ | Feature | Detail |
16
+ |---------|--------|
17
+ | **Import** | `import { BaseService, inject } from '@venizia/ignis'` |
18
+ | **Extends** | `BaseHelper` from `@venizia/ignis-helpers` |
19
+ | **Logging** | `this.logger` (scoped to constructor `scope`) |
20
+ | **Registration** | `this.service(MyService)` in application lifecycle |
21
+ | **Binding key** | `services.{ClassName}` (e.g., `services.AuthenticationService`) |
22
+ | **DI decorator** | `@injectable` is NOT used on services - registration is imperative only |
23
+ | **CRUD service** | Removed - use `DefaultCRUDRepository` for data access |
24
+
25
+ ---
26
+
27
+ ## `BaseService` Class
28
+
29
+ Abstract class that all application services must extend.
30
+
31
+ ```typescript
32
+ // packages/core/src/base/services/base.ts
33
+ import { BaseHelper } from '@venizia/ignis-helpers';
34
+ import { IService } from './types';
35
+
36
+ export abstract class BaseService extends BaseHelper implements IService {
37
+ constructor(opts: { scope: string }) {
38
+ super({ scope: opts.scope });
39
+ }
40
+ }
41
+ ```
42
+
43
+ `BaseHelper` wires a scoped logger at `this.logger`. Pass `scope: ClassName.name` so log lines are tagged with the service name.
44
+
45
+ ### `IService` Interface
46
+
47
+ Marker interface with no required methods - it exists purely for type-level contracts:
48
+
49
+ ```typescript
50
+ export interface IService {}
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Registering a Service
56
+
57
+ Services are registered imperatively in an application lifecycle method. There is no `@injectable` or `@service` decorator used on service classes in application code - `this.service()` handles everything.
58
+
59
+ ```typescript
60
+ // In your Application class (e.g., in preConfigure())
61
+ this.service(AuthenticationService); // binds as 'services.AuthenticationService'
62
+ this.service(GreeterService); // binds as 'services.GreeterService'
63
+ ```
64
+
65
+ `this.service(Ctor)` is implemented directly on `BaseApplication`:
66
+
67
+ ```typescript
68
+ // packages/core/src/base/applications/base.ts
69
+ service<Base extends IService, Args extends AnyObject = any>(
70
+ ctor: TClass<Base>,
71
+ opts?: TMixinOpts<Args>,
72
+ ): Binding<Base> {
73
+ return this.bind<Base>({
74
+ key: BindingKeys.build(
75
+ opts?.binding ?? {
76
+ namespace: BindingNamespaces.SERVICE, // 'services'
77
+ key: ctor.name, // class name
78
+ },
79
+ ),
80
+ }).toClass(ctor);
81
+ }
82
+ ```
83
+
84
+ The resulting binding key defaults to `services.{ClassName}` (overridable via `opts.binding`).
85
+
86
+ ### Lifecycle Placement
87
+
88
+ Register services before anything that depends on them. The correct hook is `preConfigure()`, or a private helper called from `preConfigure()`:
89
+
90
+ ```typescript
91
+ export class Application extends BaseApplication {
92
+ preConfigure(): void {
93
+ // DataSources and repositories that services depend on must come first
94
+ this.dataSource(PostgresDataSource);
95
+ this.repository(UserRepository);
96
+
97
+ // Then register services
98
+ this.service(AuthenticationService);
99
+ }
100
+ }
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Dependency Injection into Services
106
+
107
+ Inject repositories, other services, or datasources via `@inject` in the constructor. Two equivalent key forms are available:
108
+
109
+ ### String-literal keys
110
+
111
+ ```typescript
112
+ import { BaseService, inject } from '@venizia/ignis';
113
+ import { UserRepository } from '../repositories';
114
+ import { JWKSIssuerTokenService } from '@venizia/ignis';
115
+
116
+ export class AuthenticationService extends BaseService {
117
+ constructor(
118
+ @inject({ key: 'repositories.UserRepository' })
119
+ private userRepository: UserRepository,
120
+
121
+ @inject({ key: 'services.JWKSIssuerTokenService' })
122
+ private jwksTokenService: JWKSIssuerTokenService,
123
+ ) {
124
+ super({ scope: AuthenticationService.name });
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### Programmatic keys with `BindingKeys.build`
130
+
131
+ `BindingKeys.build({ namespace, key })` produces the same `namespace.key` string. Use this form when you want a compile-time reference to a class name rather than a plain string:
132
+
133
+ ```typescript
134
+ import { BaseService, BindingKeys, BindingNamespaces, inject } from '@venizia/ignis';
135
+ import { UserRepository } from '../repositories';
136
+
137
+ export class AuthenticationService extends BaseService {
138
+ constructor(
139
+ @inject({
140
+ key: BindingKeys.build({
141
+ namespace: BindingNamespaces.REPOSITORY, // 'repositories'
142
+ key: UserRepository.name, // 'UserRepository'
143
+ }),
144
+ })
145
+ private userRepository: UserRepository,
146
+ ) {
147
+ super({ scope: AuthenticationService.name });
148
+ }
149
+ }
150
+ ```
151
+
152
+ Both forms produce identical binding keys at runtime. The `BindingKeys.build` form avoids typo risk when the class name changes.
153
+
154
+ ### Available `BindingNamespaces`
155
+
156
+ | Constant | Value |
157
+ |----------|-------|
158
+ | `BindingNamespaces.SERVICE` | `'services'` |
159
+ | `BindingNamespaces.REPOSITORY` | `'repositories'` |
160
+ | `BindingNamespaces.DATASOURCE` | `'datasources'` |
161
+ | `BindingNamespaces.CONTROLLER` | `'controllers'` |
162
+ | `BindingNamespaces.COMPONENT` | `'components'` |
163
+ | `BindingNamespaces.PROVIDER` | `'providers'` |
164
+
165
+ ---
166
+
167
+ ## Logging
168
+
169
+ `BaseService` inherits `this.logger` from `BaseHelper`. Log with method scope for structured output:
170
+
171
+ ```typescript
172
+ export class AuthenticationService extends BaseService {
173
+ async signIn(opts: { identifier: string }): Promise<string> {
174
+ // Method-scoped log - produces tag "[signIn]" in log output
175
+ this.logger.for('signIn').info('SignIn called | identifier: %s', opts.identifier);
176
+
177
+ // ... business logic ...
178
+
179
+ this.logger.for('signIn').info('SignIn successful');
180
+ return token;
181
+ }
182
+ }
183
+ ```
184
+
185
+ Use `this.logger.for('methodName')` to scope log lines to the current method. This matches the project-wide convention seen in all examples.
186
+
187
+ ---
188
+
189
+ ## Service-to-Service Composition
190
+
191
+ Services can inject other services to compose business logic. Inject them the same way as repositories, using the `services.*` namespace:
192
+
193
+ ```typescript
194
+ import { BaseService, BindingKeys, BindingNamespaces, inject } from '@venizia/ignis';
195
+
196
+ export class OrderService extends BaseService {
197
+ constructor(
198
+ @inject({
199
+ key: BindingKeys.build({
200
+ namespace: BindingNamespaces.REPOSITORY,
201
+ key: OrderRepository.name,
202
+ }),
203
+ })
204
+ private orderRepository: OrderRepository,
205
+
206
+ @inject({
207
+ key: BindingKeys.build({
208
+ namespace: BindingNamespaces.REPOSITORY,
209
+ key: InventoryRepository.name,
210
+ }),
211
+ })
212
+ private inventoryRepository: InventoryRepository,
213
+
214
+ @inject({
215
+ key: BindingKeys.build({
216
+ namespace: BindingNamespaces.SERVICE,
217
+ key: NotificationService.name,
218
+ }),
219
+ })
220
+ private notificationService: NotificationService,
221
+ ) {
222
+ super({ scope: OrderService.name });
223
+ }
224
+
225
+ async createOrder(opts: { userId: string; items: OrderItem[] }): Promise<Order> {
226
+ this.logger.for('createOrder').info('Creating order | userId: %s', opts.userId);
227
+
228
+ const { data: order } = await this.orderRepository.create({ data: opts });
229
+
230
+ // Compose with another service
231
+ await this.notificationService.sendOrderConfirmation({ orderId: order.id });
232
+
233
+ return order;
234
+ }
235
+ }
236
+ ```
237
+
238
+ Register both services in `preConfigure()`:
239
+
240
+ ```typescript
241
+ this.service(NotificationService);
242
+ this.service(OrderService);
243
+ ```
244
+
245
+ ### Abstract Base Services
246
+
247
+ For shared dependencies across multiple related services, define an abstract base. The container only ever `instantiate()`s the **concrete** class (`this.service(UserAuditTestService)` registers `UserAuditTestService`, never `BaseTestService`), and the hard DI rule - every constructor parameter of a container-instantiated class must carry `@inject` - applies to that concrete constructor. A `scope: string` computed from `ClassName.name` is not something the container can supply anyway, so it cannot sit as a bare constructor parameter next to an `@inject`-decorated one. The shared repository is injected as a **property** on the base instead, and the concrete subclass's constructor is left with zero parameters - nothing to decorate, nothing to violate:
248
+
249
+ ```typescript
250
+ // Shared repository access for a group of test services - property injection,
251
+ // so the concrete subclass's constructor stays free of undecorated parameters
252
+ export abstract class BaseTestService extends BaseService {
253
+ @inject({
254
+ key: BindingKeys.build({
255
+ namespace: BindingNamespaces.REPOSITORY,
256
+ key: UserRepository.name,
257
+ }),
258
+ })
259
+ protected userRepository!: UserRepository;
260
+
261
+ constructor(opts: { scope: string }) {
262
+ super(opts);
263
+ }
264
+
265
+ abstract run(): Promise<void>;
266
+ }
267
+
268
+ // Concrete subclass takes no constructor parameters - only container-instantiated
269
+ // classes are subject to the "every parameter decorated" rule, and an empty
270
+ // parameter list trivially satisfies it
271
+ export class UserAuditTestService extends BaseTestService {
272
+ constructor() {
273
+ super({ scope: UserAuditTestService.name });
274
+ }
275
+
276
+ async run(): Promise<void> {
277
+ this.logger.for('run').info('Running user audit tests');
278
+ // ...
279
+ }
280
+ }
281
+ ```
282
+
283
+ Register the concrete class - never the abstract base:
284
+
285
+ ```typescript
286
+ this.service(UserAuditTestService);
287
+ ```
288
+
289
+ > [!IMPORTANT]
290
+ > `BaseTestService`'s own constructor (`opts: { scope: string }`) is never processed by the container - `BaseTestService` is abstract and is never passed to `instantiate()`. Only the concrete class the container actually instantiates is subject to the "every parameter decorated" rule. See [Dependency Injection Reference](./dependency-injection.md#instantiation-algorithm-two-phase) for the full rule.
291
+
292
+ ---
293
+
294
+ ## Transaction Orchestration
295
+
296
+ Services are the correct place to manage transactions that span multiple repository calls. Begin a transaction on the DataSource and pass it through repository options:
297
+
298
+ ```typescript
299
+ import { BaseService, BindingKeys, BindingNamespaces, inject } from '@venizia/ignis';
300
+ import { PostgresDataSource } from '../datasources';
301
+
302
+ export class CheckoutService extends BaseService {
303
+ constructor(
304
+ @inject({ key: 'datasources.PostgresDataSource' })
305
+ private dataSource: PostgresDataSource,
306
+
307
+ @inject({ key: 'repositories.OrderRepository' })
308
+ private orderRepository: OrderRepository,
309
+
310
+ @inject({ key: 'repositories.InventoryRepository' })
311
+ private inventoryRepository: InventoryRepository,
312
+ ) {
313
+ super({ scope: CheckoutService.name });
314
+ }
315
+
316
+ async placeOrder(opts: { userId: string; items: OrderItem[] }): Promise<Order> {
317
+ const log = this.logger.for('placeOrder');
318
+ const transaction = await this.dataSource.beginTransaction();
319
+
320
+ try {
321
+ const { data: order } = await this.orderRepository.create({
322
+ data: { userId: opts.userId },
323
+ options: { transaction },
324
+ });
325
+
326
+ for (const item of opts.items) {
327
+ await this.inventoryRepository.updateById({
328
+ id: item.productId,
329
+ data: { stock: item.quantity },
330
+ options: { transaction },
331
+ });
332
+ }
333
+
334
+ await transaction.commit();
335
+ log.info('Order placed | orderId: %s', order.id);
336
+ return order;
337
+ } catch (error) {
338
+ await transaction.rollback();
339
+ log.error('Order failed, rolled back | error: %s', error);
340
+ throw error;
341
+ }
342
+ }
343
+ }
344
+ ```
345
+
346
+ Pass `{ transaction }` in the `options` field of any repository call. The repository API is identical with or without a transaction - only the underlying Drizzle connector switches.
347
+
348
+ ---
349
+
350
+ ## Resolving a Service Imperatively
351
+
352
+ When you need to pull a service out of the container at runtime (rather than through constructor injection), use `this.get<T>`:
353
+
354
+ ```typescript
355
+ // In Application.postConfigure() or a lifecycle hook
356
+ const testService = this.get<RowLockingTestService>({
357
+ key: BindingKeys.build({
358
+ namespace: BindingNamespaces.SERVICE,
359
+ key: RowLockingTestService.name,
360
+ }),
361
+ });
362
+
363
+ await testService.run();
364
+ ```
365
+
366
+ This is useful for post-start hooks, one-off tasks that run after the server starts, or when the service is not a constructor dependency.
367
+
368
+ ---
369
+
370
+ ## No Built-in CRUD Service
371
+
372
+ IGNIS intentionally does not provide a `BaseCrudService`. CRUD operations belong in the Repository layer (`DefaultCRUDRepository`). Services exist for business logic that cannot be expressed as pure data access: cross-cutting validation, multi-repository coordination, transaction management, and workflow orchestration.
373
+
374
+ ---
375
+
376
+ ## Provider vs Service
377
+
378
+ | Aspect | Service | Provider |
379
+ |--------|---------|----------|
380
+ | **Purpose** | Business logic and orchestration | Factory - produces values or instances |
381
+ | **Base class** | `BaseService` | `BaseProvider<T>` |
382
+ | **Key method** | Business methods | `value(container): T` |
383
+ | **Pattern** | Singleton in DI scope | Factory pattern |
384
+ | **Registration** | `this.service(Ctor)` | `this.bind(...).toProvider(Ctor)` |
385
+
386
+ See [Providers Reference](./providers.md) for the factory pattern details.
387
+
388
+ ---
389
+
390
+ ## See Also
391
+
392
+ - **Related References:**
393
+ - [Controllers](./controllers.md) - HTTP handlers that call services
394
+ - [Repositories](./repositories/) - Data access layer injected into services
395
+ - [Providers](./providers.md) - Factory pattern, compare with services
396
+ - [Dependency Injection](./dependency-injection.md) - Container and injection system
397
+
398
+ - **Guides:**
399
+ - [Building Services](/guides/core-concepts/services.md)
400
+ - [Dependency Injection Guide](/guides/core-concepts/dependency-injection.md)
401
+
402
+ - **Best Practices:**
403
+ - [Architectural Patterns](/best-practices/architectural-patterns)
404
+ - [Testing Guide](/guides/tutorials/testing)
@@ -1,14 +1,14 @@
1
1
  # Environment Variables Reference
2
2
 
3
- Complete reference of all environment variables used by Ignis framework.
3
+ Complete reference of all environment variables used by IGNIS framework.
4
4
 
5
5
 
6
6
  ## Overview
7
7
 
8
- Ignis uses the `APP_ENV_` prefix for all framework-specific environment variables. This prevents conflicts with system variables like `PATH`, `HOME`, etc.
8
+ IGNIS uses the `APP_ENV_` prefix for all framework-specific environment variables. This prevents conflicts with system variables like `PATH`, `HOME`, etc.
9
9
 
10
10
  ```bash
11
- # ✅ Ignis variables
11
+ # ✅ IGNIS variables
12
12
  APP_ENV_POSTGRES_HOST=localhost
13
13
 
14
14
  # ❌ Might conflict with system
@@ -96,9 +96,9 @@ APP_ENV_APPLICATION_ROLES=api,worker
96
96
  |----------|----------|---------|-------------|
97
97
  | `APP_ENV_SERVER_HOST` | No | `localhost` | Server bind address |
98
98
  | `APP_ENV_SERVER_PORT` | No | `3000` | Server port |
99
- | `APP_ENV_SERVER_BASE_PATH` | No | `/` | Base path for all routes |
100
- | `HOST` | No | - | Alternative to `APP_ENV_SERVER_HOST` |
101
- | `PORT` | No | - | Alternative to `APP_ENV_SERVER_PORT` |
99
+ | `APP_ENV_SERVER_BASE_PATH` | No | - | Base path convention (`EnvironmentKeys` constant; not read by the framework itself) |
100
+ | `HOST` | No | - | Alternative to `APP_ENV_SERVER_HOST` (takes precedence) |
101
+ | `PORT` | No | - | Alternative to `APP_ENV_SERVER_PORT` (takes precedence) |
102
102
 
103
103
  ### Example
104
104
 
@@ -116,11 +116,11 @@ APP_ENV_SERVER_BASE_PATH=/v1/api
116
116
 
117
117
  ### Priority Order
118
118
 
119
- The server configuration uses this priority:
120
- 1. Explicit config in `appConfigs`
121
- 2. `APP_ENV_SERVER_*` variables
122
- 3. `HOST`/`PORT` variables (for cloud platforms)
123
- 4. Default values
119
+ The server host/port resolution uses this priority (`packages/core/src/base/applications/abstract.ts`):
120
+ 1. Explicit config passed to the application constructor
121
+ 2. `HOST`/`PORT` variables (for cloud platforms)
122
+ 3. `APP_ENV_SERVER_HOST`/`APP_ENV_SERVER_PORT` variables
123
+ 4. Default values (`localhost`/`3000`)
124
124
 
125
125
 
126
126
  ## Database Variables (PostgreSQL)
@@ -179,13 +179,15 @@ export class PostgresDataSource extends BaseDataSource {
179
179
 
180
180
  | Variable | Required | Default | Description |
181
181
  |----------|----------|---------|-------------|
182
- | `APP_ENV_APPLICATION_SECRET` | **Yes** | - | Secret for encrypting JWT payload |
183
- | `APP_ENV_JWT_SECRET` | **Yes** | - | Secret for signing JWT tokens |
184
- | `APP_ENV_JWT_EXPIRES_IN` | No | `86400` | Token expiration in seconds (24h default) |
182
+ | `APP_ENV_APPLICATION_SECRET` | Yes* | - | Secret for encrypting JWT payload |
183
+ | `APP_ENV_JWT_SECRET` | Yes* | - | Secret for signing JWT tokens |
184
+ | `APP_ENV_JWT_EXPIRES_IN` | No | - | Token expiration in seconds (e.g., `86400` = 24h) |
185
+
186
+ *Required by convention when using the authentication component - see below.
185
187
 
186
188
  ### Security Requirements
187
189
 
188
- Both `APP_ENV_APPLICATION_SECRET` and `APP_ENV_JWT_SECRET` are **mandatory** when using the authentication component. The application will fail to start if these are missing or empty.
190
+ The `AuthenticateComponent` receives its secrets programmatically via the `jwtOptions` binding (`jwtSecret`, `getTokenExpiresFn`), not by reading these environment variables directly. These `EnvironmentKeys` constants are the conventional way for your application to supply those values. The component throws at startup if `jwtSecret` is missing or left at the placeholder value - so wiring it from an unset environment variable will fail the boot.
189
191
 
190
192
  ### Generate Strong Secrets
191
193
 
@@ -218,6 +220,12 @@ APP_ENV_JWT_EXPIRES_IN=86400
218
220
  | Variable | Required | Default | Description |
219
221
  |----------|----------|---------|-------------|
220
222
  | `APP_ENV_LOGGER_FOLDER_PATH` | No | `./` | Directory for log files |
223
+ | `APP_ENV_LOGGER_FORMAT` | No | `text` | Log output format |
224
+ | `APP_ENV_LOGGER_INSPECT_DEPTH` | No | `5` | How deep a `%s` argument is inspected. Node hard-codes depth `0` for `%s`, which prints `[Object]` one level in; IGNIS widens it. Non-negative integers only - an absent, invalid or negative value falls back to `5` |
225
+ | `APP_ENV_LOGGER_FILE_FREQUENCY` | No | `1h` | Log file rotation frequency |
226
+ | `APP_ENV_LOGGER_FILE_MAX_SIZE` | No | `100m` | Max size per log file |
227
+ | `APP_ENV_LOGGER_FILE_MAX_FILES` | No | `5d` | Log file retention |
228
+ | `APP_ENV_LOGGER_FILE_DATE_PATTERN` | No | `YYYYMMDD_HH` | Rotated file date pattern |
221
229
  | `APP_ENV_LOGGER_DGRAM_HOST` | No | - | UDP log transport host |
222
230
  | `APP_ENV_LOGGER_DGRAM_PORT` | No | - | UDP log transport port |
223
231
  | `APP_ENV_LOGGER_DGRAM_LABEL` | No | - | Label for UDP logs |
@@ -240,6 +248,9 @@ APP_ENV_LOGGER_DGRAM_LEVELS=error,warn,info
240
248
 
241
249
  ## Storage Variables (MinIO/S3)
242
250
 
251
+ > [!NOTE]
252
+ > These are application-level conventions (used by the `vert` reference application), not variables read by the framework. `MinioHelper` and the StaticAsset component receive their configuration programmatically - your application wires these values in.
253
+
243
254
  | Variable | Required | Default | Description |
244
255
  |----------|----------|---------|-------------|
245
256
  | `APP_ENV_MINIO_HOST` | Yes* | - | MinIO/S3 endpoint |
@@ -248,7 +259,7 @@ APP_ENV_LOGGER_DGRAM_LEVELS=error,warn,info
248
259
  | `APP_ENV_MINIO_SECRET_KEY` | Yes* | - | Secret key |
249
260
  | `APP_ENV_MINIO_USE_SSL` | No | `false` | Enable SSL |
250
261
 
251
- *Required when using StaticAsset component.
262
+ *Required (by application convention) when wiring MinIO-backed storage.
252
263
 
253
264
  ### Example
254
265
 
@@ -271,17 +282,20 @@ APP_ENV_MINIO_USE_SSL=true
271
282
 
272
283
  ## Mail Variables
273
284
 
285
+ > [!NOTE]
286
+ > These are application-level conventions, not variables read by the framework. The Mail component's transporter receives its SMTP/OAuth2 configuration programmatically - your application wires these values in.
287
+
274
288
  | Variable | Required | Default | Description |
275
289
  |----------|----------|---------|-------------|
276
- | `APP_ENV_MAIL_HOST` | Yes* | `smtp.gmail.com` | SMTP host |
277
- | `APP_ENV_MAIL_PORT` | No | `465` | SMTP port |
278
- | `APP_ENV_MAIL_SECURE` | No | `true` | Use TLS |
290
+ | `APP_ENV_MAIL_HOST` | Yes* | - | SMTP host (e.g., `smtp.gmail.com`) |
291
+ | `APP_ENV_MAIL_PORT` | No | - | SMTP port (e.g., `465`) |
292
+ | `APP_ENV_MAIL_SECURE` | No | - | Use TLS |
279
293
  | `APP_ENV_MAIL_USER` | Yes* | - | SMTP username/email |
280
294
  | `APP_ENV_MAIL_CLIENT_ID` | Yes* | - | OAuth2 client ID |
281
295
  | `APP_ENV_MAIL_CLIENT_SECRET` | Yes* | - | OAuth2 client secret |
282
296
  | `APP_ENV_MAIL_REFRESH_TOKEN` | Yes* | - | OAuth2 refresh token |
283
297
 
284
- *Required when using Mail component with OAuth2.
298
+ *Required (by application convention) when wiring the Mail component with OAuth2.
285
299
 
286
300
  ### Example (Gmail with OAuth2)
287
301
 
@@ -310,14 +324,16 @@ APP_ENV_MAIL_REFRESH_TOKEN=your-oauth2-refresh-token
310
324
  | Variable | Required | Default | Description |
311
325
  |----------|----------|---------|-------------|
312
326
  | `DEBUG` | No | - | Enable debug mode |
313
- | `NODE_ENV` | No | `development` | Environment mode |
327
+ | `NODE_ENV` | No | `development` | Environment mode. One of `local`, `debug`, `development`, `dev`, `sit`, `uat`, `alpha`, `beta`, `staging`, `production` |
314
328
  | `ALLOW_EMPTY_ENV_VALUE` | No | `false` | Allow empty env values |
315
329
 
330
+ The gate is fail-closed: an environment IGNIS does not recognise is treated as production, so error responses are sanitized. `local`, `debug`, `development`, `dev` and `sit` are the development environments - only they expose internal error detail. `alpha`, `beta`, `staging` and `production` stay sanitized.
331
+
316
332
  ### Example
317
333
 
318
334
  ```bash
319
- # Development
320
- NODE_ENV=development
335
+ # Development - `dev` is an alias of `development`, and gets the same error detail
336
+ NODE_ENV=dev
321
337
  DEBUG=true
322
338
 
323
339
  # Production
@@ -347,20 +363,20 @@ project/
347
363
 
348
364
  ## Custom Environment Prefix
349
365
 
350
- You can customize the prefix from `APP_ENV_` to something else:
366
+ You can customize the prefix from `APP_ENV` to something else via the `APPLICATION_ENV_PREFIX` variable. It is read once when `@venizia/ignis-helpers` loads, so set it in the shell (or before any framework import):
351
367
 
352
- ```typescript
353
- // Set custom prefix
354
- process.env.APPLICATION_ENV_PREFIX = 'MY_APP';
368
+ ```bash
369
+ # Set custom prefix
370
+ APPLICATION_ENV_PREFIX=MY_APP
355
371
 
356
- // Now use MY_APP_ prefix
357
- // MY_APP_POSTGRES_HOST=localhost
372
+ # Now use MY_APP_ prefix
373
+ MY_APP_POSTGRES_HOST=localhost
358
374
  ```
359
375
 
360
376
 
361
377
  ## Validation
362
378
 
363
- Ignis validates `APP_ENV_*` variables on startup. Missing required variables will cause the application to fail with a clear error message.
379
+ On startup, IGNIS iterates every `APP_ENV_*` (prefixed) variable that is set and throws if any has an empty value (`validateEnvs` in `packages/core/src/base/applications/abstract.ts`). It does not check for variables that are absent entirely - component-level validation (e.g., the authentication component's `jwtSecret` check) covers required values.
364
380
 
365
381
  ### Disable Validation
366
382
 
@@ -1,6 +1,6 @@
1
1
  # Configuration Reference
2
2
 
3
- Configuration options and environment variables for Ignis applications.
3
+ Configuration options and environment variables for IGNIS applications.
4
4
 
5
5
  ## Quick Reference
6
6
 
@@ -11,15 +11,15 @@ Configuration options and environment variables for Ignis applications.
11
11
  | Database | PostgreSQL connection | `APP_ENV_POSTGRES_HOST`, `APP_ENV_POSTGRES_DATABASE` |
12
12
  | Authentication | JWT tokens and secrets | `APP_ENV_JWT_SECRET`, `APP_ENV_APPLICATION_SECRET` |
13
13
  | Logging | Log file paths and transports | `APP_ENV_LOGGER_FOLDER_PATH` |
14
- | Storage | MinIO/S3 file storage | `APP_ENV_MINIO_HOST`, `APP_ENV_MINIO_ACCESS_KEY` |
15
- | Mail | SMTP email sending | `APP_ENV_MAIL_HOST`, `APP_ENV_MAIL_USER` |
14
+ | Storage | MinIO/S3 file storage (application-level convention) | `APP_ENV_MINIO_HOST`, `APP_ENV_MINIO_ACCESS_KEY` |
15
+ | Mail | SMTP email sending (application-level convention) | `APP_ENV_MAIL_HOST`, `APP_ENV_MAIL_USER` |
16
16
 
17
17
  ## Environment Variable Prefix
18
18
 
19
- Ignis uses the `APP_ENV_` prefix to avoid conflicts with system variables:
19
+ IGNIS uses the `APP_ENV_` prefix to avoid conflicts with system variables:
20
20
 
21
21
  ```bash
22
- # ✅ Ignis variables
22
+ # ✅ IGNIS variables
23
23
  APP_ENV_POSTGRES_HOST=localhost
24
24
 
25
25
  # ❌ Might conflict with system
@@ -69,7 +69,7 @@ project/
69
69
 
70
70
  ### 3. Validation on Startup
71
71
 
72
- Ignis validates required variables on startup. Missing values cause clear error messages.
72
+ IGNIS validates every set `APP_ENV_*` variable on startup and fails with a clear error if any is empty (bypass with `ALLOW_EMPTY_ENV_VALUE=true`). Absent variables are not flagged - components validate their own required inputs.
73
73
 
74
74
  > **Related:** [Environment Variables Reference](./environment-variables.md) | [DataSources Guide](../../guides/core-concepts/persistent/datasources)
75
75
 
@@ -1,6 +1,6 @@
1
1
  # API Reference
2
2
 
3
- Complete reference documentation for the Ignis framework. Find detailed API docs, type definitions, and usage examples for every class, component, and utility in the framework.
3
+ Complete reference documentation for the IGNIS framework. Find detailed API docs, type definitions, and usage examples for every class, component, and utility in the framework.
4
4
 
5
5
  <div class="guide-cards">
6
6
 
@@ -63,7 +63,7 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
63
63
  <span class="stage-num">3</span>
64
64
  <h4>Adding Features</h4>
65
65
  </div>
66
- <p><a href="/ignis/extensions/components/authentication/">Auth</a> → <a href="/ignis/extensions/components/socket-io/">Real-time</a> → <a href="/ignis/extensions/components/mail/">Email</a> → <a href="/ignis/extensions/components/swagger">API Docs</a></p>
66
+ <p><a href="/ignis/extensions/components/authentication/">Auth</a> → <a href="/ignis/extensions/components/socket-io/">Real-time</a> → <a href="/ignis/extensions/components/mail/">Email</a> → <a href="/ignis/extensions/components/api-reference">API Docs</a></p>
67
67
  <span class="stage-desc">Pre-built components for common features</span>
68
68
  </div>
69
69
 
@@ -99,7 +99,7 @@ Complete reference documentation for the Ignis framework. Find detailed API docs
99
99
  <span class="stage-num">7</span>
100
100
  <h4>Testing & Quality</h4>
101
101
  </div>
102
- <p><a href="/ignis/extensions/helpers/testing/">Unit Testing</a> → <a href="./base/repositories/advanced">Mocking & Stubs</a> → <a href="./quick-reference">Best Practices</a></p>
102
+ <p><a href="./base/repositories/advanced">Mocking & Stubs</a> → <a href="./quick-reference">Best Practices</a></p>
103
103
  <span class="stage-desc">Testing strategies, quality assurance, and code review</span>
104
104
  </div>
105
105
 
@@ -123,18 +123,23 @@ class UserController extends BaseRestController {
123
123
 
124
124
  **Query with Repository:**
125
125
  ```typescript
126
- const users = await userRepo.find({
127
- where: { isActive: true },
128
- orderBy: { createdAt: 'desc' },
129
- limit: 10,
126
+ const users = await userRepository.find({
127
+ filter: {
128
+ where: { isActive: true },
129
+ order: ['createdAt DESC'],
130
+ limit: 10,
131
+ },
130
132
  });
131
133
  ```
132
134
 
133
135
  **Schedule a Job:**
134
136
  ```typescript
135
- CronHelper.schedule('0 * * * *', async () => {
136
- await cleanupExpiredSessions();
137
- });
137
+ CronHelper.newInstance({
138
+ cronTime: '0 * * * *',
139
+ onTick: async () => {
140
+ await cleanupExpiredSessions();
141
+ },
142
+ }).start();
138
143
  ```
139
144
 
140
145
  ## Common Imports
@@ -156,7 +161,7 @@ import {
156
161
  // Helpers
157
162
  import {
158
163
  LoggerFactory,
159
- RedisHelper,
164
+ RedisSingleHelper,
160
165
  QueueHelper,
161
166
  } from '@venizia/ignis-helpers';
162
167
 
@@ -166,7 +171,7 @@ import { Container } from '@venizia/ignis-inversion';
166
171
 
167
172
  ## See Also
168
173
 
169
- - [Getting Started](/guides/) - New to Ignis? Start here
174
+ - [Getting Started](/guides/) - New to IGNIS? Start here
170
175
  - [Core Concepts](/guides/core-concepts/application/) - Deep dive into architecture
171
176
  - [Best Practices](/best-practices/) - Production patterns
172
177
  - [Changelogs](/changelogs/) - Version history