nestjs-ddd-cli 2.2.1 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +247 -408
  2. package/ddd.schema.json +111 -0
  3. package/dist/commands/aggregate-validator.d.ts +9 -0
  4. package/dist/commands/aggregate-validator.js +953 -0
  5. package/dist/commands/aggregate-validator.js.map +1 -0
  6. package/dist/commands/ai-assist.d.ts +8 -0
  7. package/dist/commands/ai-assist.js +337 -0
  8. package/dist/commands/ai-assist.js.map +1 -0
  9. package/dist/commands/api-contracts.d.ts +9 -0
  10. package/dist/commands/api-contracts.js +1368 -0
  11. package/dist/commands/api-contracts.js.map +1 -0
  12. package/dist/commands/api-docs.d.ts +8 -0
  13. package/dist/commands/api-docs.js +408 -0
  14. package/dist/commands/api-docs.js.map +1 -0
  15. package/dist/commands/api-versioning.d.ts +11 -0
  16. package/dist/commands/api-versioning.js +643 -0
  17. package/dist/commands/api-versioning.js.map +1 -0
  18. package/dist/commands/audit-logging.d.ts +9 -0
  19. package/dist/commands/audit-logging.js +1129 -0
  20. package/dist/commands/audit-logging.js.map +1 -0
  21. package/dist/commands/batch-generate.d.ts +10 -0
  22. package/dist/commands/batch-generate.js +405 -0
  23. package/dist/commands/batch-generate.js.map +1 -0
  24. package/dist/commands/caching-strategies.d.ts +9 -0
  25. package/dist/commands/caching-strategies.js +874 -0
  26. package/dist/commands/caching-strategies.js.map +1 -0
  27. package/dist/commands/code-analyzer.d.ts +42 -0
  28. package/dist/commands/code-analyzer.js +474 -0
  29. package/dist/commands/code-analyzer.js.map +1 -0
  30. package/dist/commands/database-seeding.d.ts +6 -0
  31. package/dist/commands/database-seeding.js +621 -0
  32. package/dist/commands/database-seeding.js.map +1 -0
  33. package/dist/commands/db-optimization.d.ts +7 -0
  34. package/dist/commands/db-optimization.js +687 -0
  35. package/dist/commands/db-optimization.js.map +1 -0
  36. package/dist/commands/dependency-graph.d.ts +6 -0
  37. package/dist/commands/dependency-graph.js +329 -0
  38. package/dist/commands/dependency-graph.js.map +1 -0
  39. package/dist/commands/doctor-enhanced.d.ts +22 -0
  40. package/dist/commands/doctor-enhanced.js +543 -0
  41. package/dist/commands/doctor-enhanced.js.map +1 -0
  42. package/dist/commands/doctor.d.ts +4 -0
  43. package/dist/commands/doctor.js +151 -0
  44. package/dist/commands/doctor.js.map +1 -0
  45. package/dist/commands/env-manager.d.ts +6 -0
  46. package/dist/commands/env-manager.js +419 -0
  47. package/dist/commands/env-manager.js.map +1 -0
  48. package/dist/commands/event-sourcing-full.d.ts +10 -0
  49. package/dist/commands/event-sourcing-full.js +1107 -0
  50. package/dist/commands/event-sourcing-full.js.map +1 -0
  51. package/dist/commands/feature-flags.d.ts +9 -0
  52. package/dist/commands/feature-flags.js +824 -0
  53. package/dist/commands/feature-flags.js.map +1 -0
  54. package/dist/commands/filter-dsl.d.ts +10 -0
  55. package/dist/commands/filter-dsl.js +1407 -0
  56. package/dist/commands/filter-dsl.js.map +1 -0
  57. package/dist/commands/generate-all.js +485 -32
  58. package/dist/commands/generate-all.js.map +1 -1
  59. package/dist/commands/generate-deployment.d.ts +8 -0
  60. package/dist/commands/generate-deployment.js +746 -0
  61. package/dist/commands/generate-deployment.js.map +1 -0
  62. package/dist/commands/generate-domain-service.d.ts +14 -0
  63. package/dist/commands/generate-domain-service.js +796 -0
  64. package/dist/commands/generate-domain-service.js.map +1 -0
  65. package/dist/commands/generate-entity.js +82 -24
  66. package/dist/commands/generate-entity.js.map +1 -1
  67. package/dist/commands/generate-from-schema.d.ts +56 -0
  68. package/dist/commands/generate-from-schema.js +222 -0
  69. package/dist/commands/generate-from-schema.js.map +1 -0
  70. package/dist/commands/generate-orchestrator.d.ts +14 -0
  71. package/dist/commands/generate-orchestrator.js +887 -0
  72. package/dist/commands/generate-orchestrator.js.map +1 -0
  73. package/dist/commands/generate-repository.d.ts +14 -0
  74. package/dist/commands/generate-repository.js +1019 -0
  75. package/dist/commands/generate-repository.js.map +1 -0
  76. package/dist/commands/generate-shared.d.ts +4 -0
  77. package/dist/commands/generate-shared.js +388 -0
  78. package/dist/commands/generate-shared.js.map +1 -0
  79. package/dist/commands/generate-value-object.d.ts +32 -0
  80. package/dist/commands/generate-value-object.js +700 -0
  81. package/dist/commands/generate-value-object.js.map +1 -0
  82. package/dist/commands/graphql-subscriptions.d.ts +6 -0
  83. package/dist/commands/graphql-subscriptions.js +607 -0
  84. package/dist/commands/graphql-subscriptions.js.map +1 -0
  85. package/dist/commands/graphql-types.d.ts +5 -0
  86. package/dist/commands/graphql-types.js +423 -0
  87. package/dist/commands/graphql-types.js.map +1 -0
  88. package/dist/commands/health-probes-advanced.d.ts +6 -0
  89. package/dist/commands/health-probes-advanced.js +655 -0
  90. package/dist/commands/health-probes-advanced.js.map +1 -0
  91. package/dist/commands/i18n-setup.d.ts +10 -0
  92. package/dist/commands/i18n-setup.js +677 -0
  93. package/dist/commands/i18n-setup.js.map +1 -0
  94. package/dist/commands/init-config.d.ts +6 -0
  95. package/dist/commands/init-config.js +370 -0
  96. package/dist/commands/init-config.js.map +1 -0
  97. package/dist/commands/init-project.js +56 -6
  98. package/dist/commands/init-project.js.map +1 -1
  99. package/dist/commands/interactive-scaffold.d.ts +5 -0
  100. package/dist/commands/interactive-scaffold.js +271 -0
  101. package/dist/commands/interactive-scaffold.js.map +1 -0
  102. package/dist/commands/metrics-prometheus.d.ts +6 -0
  103. package/dist/commands/metrics-prometheus.js +681 -0
  104. package/dist/commands/metrics-prometheus.js.map +1 -0
  105. package/dist/commands/migration-engine.d.ts +6 -0
  106. package/dist/commands/migration-engine.js +446 -0
  107. package/dist/commands/migration-engine.js.map +1 -0
  108. package/dist/commands/migration.d.ts +12 -0
  109. package/dist/commands/migration.js +484 -0
  110. package/dist/commands/migration.js.map +1 -0
  111. package/dist/commands/monorepo.d.ts +8 -0
  112. package/dist/commands/monorepo.js +483 -0
  113. package/dist/commands/monorepo.js.map +1 -0
  114. package/dist/commands/multi-database.d.ts +5 -0
  115. package/dist/commands/multi-database.js +439 -0
  116. package/dist/commands/multi-database.js.map +1 -0
  117. package/dist/commands/observability-tracing.d.ts +10 -0
  118. package/dist/commands/observability-tracing.js +740 -0
  119. package/dist/commands/observability-tracing.js.map +1 -0
  120. package/dist/commands/openapi-export.d.ts +8 -0
  121. package/dist/commands/openapi-export.js +359 -0
  122. package/dist/commands/openapi-export.js.map +1 -0
  123. package/dist/commands/perf-analyzer.d.ts +8 -0
  124. package/dist/commands/perf-analyzer.js +423 -0
  125. package/dist/commands/perf-analyzer.js.map +1 -0
  126. package/dist/commands/rate-limiting.d.ts +10 -0
  127. package/dist/commands/rate-limiting.js +953 -0
  128. package/dist/commands/rate-limiting.js.map +1 -0
  129. package/dist/commands/recipe-plugin.d.ts +56 -0
  130. package/dist/commands/recipe-plugin.js +315 -0
  131. package/dist/commands/recipe-plugin.js.map +1 -0
  132. package/dist/commands/recipe.d.ts +6 -0
  133. package/dist/commands/recipe.js +3941 -0
  134. package/dist/commands/recipe.js.map +1 -0
  135. package/dist/commands/recipes/elasticsearch.recipe.d.ts +1 -0
  136. package/dist/commands/recipes/elasticsearch.recipe.js +761 -0
  137. package/dist/commands/recipes/elasticsearch.recipe.js.map +1 -0
  138. package/dist/commands/recipes/event-sourcing.recipe.d.ts +1 -0
  139. package/dist/commands/recipes/event-sourcing.recipe.js +889 -0
  140. package/dist/commands/recipes/event-sourcing.recipe.js.map +1 -0
  141. package/dist/commands/recipes/index.d.ts +7 -0
  142. package/dist/commands/recipes/index.js +24 -0
  143. package/dist/commands/recipes/index.js.map +1 -0
  144. package/dist/commands/recipes/message-queue.recipe.d.ts +1 -0
  145. package/dist/commands/recipes/message-queue.recipe.js +706 -0
  146. package/dist/commands/recipes/message-queue.recipe.js.map +1 -0
  147. package/dist/commands/recipes/middleware.recipe.d.ts +1 -0
  148. package/dist/commands/recipes/middleware.recipe.js +383 -0
  149. package/dist/commands/recipes/middleware.recipe.js.map +1 -0
  150. package/dist/commands/recipes/multi-tenancy.recipe.d.ts +1 -0
  151. package/dist/commands/recipes/multi-tenancy.recipe.js +520 -0
  152. package/dist/commands/recipes/multi-tenancy.recipe.js.map +1 -0
  153. package/dist/commands/recipes/oauth2.recipe.d.ts +1 -0
  154. package/dist/commands/recipes/oauth2.recipe.js +472 -0
  155. package/dist/commands/recipes/oauth2.recipe.js.map +1 -0
  156. package/dist/commands/recipes/websocket.recipe.d.ts +1 -0
  157. package/dist/commands/recipes/websocket.recipe.js +453 -0
  158. package/dist/commands/recipes/websocket.recipe.js.map +1 -0
  159. package/dist/commands/resilience-patterns.d.ts +13 -0
  160. package/dist/commands/resilience-patterns.js +1029 -0
  161. package/dist/commands/resilience-patterns.js.map +1 -0
  162. package/dist/commands/security-patterns.d.ts +11 -0
  163. package/dist/commands/security-patterns.js +2233 -0
  164. package/dist/commands/security-patterns.js.map +1 -0
  165. package/dist/commands/template-debug.d.ts +27 -0
  166. package/dist/commands/template-debug.js +388 -0
  167. package/dist/commands/template-debug.js.map +1 -0
  168. package/dist/commands/test-factory-full.d.ts +9 -0
  169. package/dist/commands/test-factory-full.js +1570 -0
  170. package/dist/commands/test-factory-full.js.map +1 -0
  171. package/dist/commands/test-scaffold.d.ts +7 -0
  172. package/dist/commands/test-scaffold.js +621 -0
  173. package/dist/commands/test-scaffold.js.map +1 -0
  174. package/dist/index.js +1088 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/templates/ai-context/CLAUDE.md.hbs +158 -0
  177. package/dist/templates/ai-context/conventions.md.hbs +154 -0
  178. package/dist/templates/command/create-command.hbs +6 -14
  179. package/dist/templates/command/delete-command.hbs +19 -0
  180. package/dist/templates/command/update-command.hbs +24 -0
  181. package/dist/templates/controller/controller.hbs +64 -17
  182. package/dist/templates/dto/create-dto.hbs +29 -5
  183. package/dist/templates/dto/filter-dto.hbs +52 -0
  184. package/dist/templates/dto/filter-query.dto.hbs +148 -0
  185. package/dist/templates/dto/paginated-response.dto.hbs +29 -0
  186. package/dist/templates/dto/pagination-query.dto.hbs +30 -0
  187. package/dist/templates/dto/response-dto.hbs +38 -0
  188. package/dist/templates/dto/update-dto.hbs +11 -0
  189. package/dist/templates/entity/entity.hbs +32 -1
  190. package/dist/templates/event/domain-event.hbs +33 -7
  191. package/dist/templates/event/event-handler.hbs +40 -0
  192. package/dist/templates/exception/base-exceptions.hbs +69 -0
  193. package/dist/templates/exception/entity-not-found.exception.hbs +7 -0
  194. package/dist/templates/mapper/mapper.hbs +49 -24
  195. package/dist/templates/module/module.hbs +34 -10
  196. package/dist/templates/orm-entity/orm-entity.hbs +63 -12
  197. package/dist/templates/prisma/prisma-mapper.hbs +71 -0
  198. package/dist/templates/prisma/prisma-repository.hbs +114 -0
  199. package/dist/templates/prisma/prisma-schema.hbs +20 -0
  200. package/dist/templates/prisma/prisma-service.hbs +51 -0
  201. package/dist/templates/query/get-all.query.hbs +50 -0
  202. package/dist/templates/query/get-by-id.query.hbs +31 -0
  203. package/dist/templates/repository/repository.hbs +55 -13
  204. package/dist/templates/resolver/graphql-input.hbs +54 -0
  205. package/dist/templates/resolver/graphql-type.hbs +58 -0
  206. package/dist/templates/resolver/pagination-args.hbs +33 -0
  207. package/dist/templates/resolver/resolver.hbs +62 -0
  208. package/dist/templates/shared/prisma-query-builder.util.hbs +189 -0
  209. package/dist/templates/shared/query-builder.util.hbs +218 -0
  210. package/dist/templates/test/controller.spec.hbs +124 -0
  211. package/dist/templates/test/repository.spec.hbs +158 -0
  212. package/dist/templates/test/usecase.spec.hbs +116 -0
  213. package/dist/templates/usecase/create-usecase.hbs +19 -7
  214. package/dist/templates/usecase/delete-usecase.hbs +17 -0
  215. package/dist/templates/usecase/update-usecase.hbs +31 -0
  216. package/dist/utils/config.utils.d.ts +45 -0
  217. package/dist/utils/config.utils.js +211 -0
  218. package/dist/utils/config.utils.js.map +1 -0
  219. package/dist/utils/error.utils.d.ts +145 -0
  220. package/dist/utils/error.utils.js +422 -0
  221. package/dist/utils/error.utils.js.map +1 -0
  222. package/dist/utils/field.utils.d.ts +54 -0
  223. package/dist/utils/field.utils.js +389 -0
  224. package/dist/utils/field.utils.js.map +1 -0
  225. package/dist/utils/file.utils.d.ts +19 -8
  226. package/dist/utils/file.utils.js +135 -4
  227. package/dist/utils/file.utils.js.map +1 -1
  228. package/dist/utils/idempotency.utils.d.ts +123 -0
  229. package/dist/utils/idempotency.utils.js +444 -0
  230. package/dist/utils/idempotency.utils.js.map +1 -0
  231. package/dist/utils/naming.utils.js +24 -5
  232. package/dist/utils/naming.utils.js.map +1 -1
  233. package/dist/utils/performance.utils.d.ts +37 -0
  234. package/dist/utils/performance.utils.js +158 -0
  235. package/dist/utils/performance.utils.js.map +1 -0
  236. package/dist/utils/relation.utils.d.ts +92 -0
  237. package/dist/utils/relation.utils.js +388 -0
  238. package/dist/utils/relation.utils.js.map +1 -0
  239. package/dist/utils/rollback.utils.d.ts +49 -0
  240. package/dist/utils/rollback.utils.js +306 -0
  241. package/dist/utils/rollback.utils.js.map +1 -0
  242. package/dist/utils/schema.utils.d.ts +123 -0
  243. package/dist/utils/schema.utils.js +419 -0
  244. package/dist/utils/schema.utils.js.map +1 -0
  245. package/dist/utils/security.utils.d.ts +57 -0
  246. package/dist/utils/security.utils.js +315 -0
  247. package/dist/utils/security.utils.js.map +1 -0
  248. package/dist/utils/template-engine.utils.d.ts +80 -0
  249. package/dist/utils/template-engine.utils.js +463 -0
  250. package/dist/utils/template-engine.utils.js.map +1 -0
  251. package/dist/utils/validation-registry.utils.d.ts +160 -0
  252. package/dist/utils/validation-registry.utils.js +526 -0
  253. package/dist/utils/validation-registry.utils.js.map +1 -0
  254. package/package.json +3 -1
@@ -0,0 +1,52 @@
1
+ import { ApiPropertyOptional } from "@nestjs/swagger";
2
+ import { IsOptional, IsString, IsBoolean, IsDate, IsUUID } from "class-validator";
3
+ import { Type } from "class-transformer";
4
+ import { PaginationQueryDto } from "./pagination.query.dto";
5
+
6
+ export class {{entityNamePascal}}FilterDto extends PaginationQueryDto {
7
+ @ApiPropertyOptional({ description: "Search by ID" })
8
+ @IsOptional()
9
+ @IsUUID()
10
+ id?: string;
11
+
12
+ @ApiPropertyOptional({ description: "Filter by active status" })
13
+ @IsOptional()
14
+ @IsBoolean()
15
+ @Type(() => Boolean)
16
+ isActive?: boolean;
17
+
18
+ @ApiPropertyOptional({ description: "Search term for text fields" })
19
+ @IsOptional()
20
+ @IsString()
21
+ search?: string;
22
+
23
+ @ApiPropertyOptional({ description: "Filter by creation date (from)" })
24
+ @IsOptional()
25
+ @Type(() => Date)
26
+ @IsDate()
27
+ createdFrom?: Date;
28
+
29
+ @ApiPropertyOptional({ description: "Filter by creation date (to)" })
30
+ @IsOptional()
31
+ @Type(() => Date)
32
+ @IsDate()
33
+ createdTo?: Date;
34
+
35
+ {{#if hasFields}}
36
+ {{#each fields}}
37
+ {{#unless this.isArray}}
38
+ @ApiPropertyOptional({ description: "Filter by {{this.camelCase}}" })
39
+ @IsOptional()
40
+ {{this.snakeCase}}?: {{this.tsType}};
41
+
42
+ {{/unless}}
43
+ {{/each}}
44
+ {{else}}
45
+ // Add your filter fields here
46
+ // Example:
47
+ // @ApiPropertyOptional({ description: "Filter by name" })
48
+ // @IsOptional()
49
+ // @IsString()
50
+ // name?: string;
51
+ {{/if}}
52
+ }
@@ -0,0 +1,148 @@
1
+ import { ApiPropertyOptional } from "@nestjs/swagger";
2
+ import { IsOptional, IsString, IsIn, IsArray, ValidateNested } from "class-validator";
3
+ import { Type, Transform } from "class-transformer";
4
+
5
+ /**
6
+ * Filter operators for query building
7
+ */
8
+ export type FilterOperator =
9
+ | "eq" // Equal
10
+ | "ne" // Not equal
11
+ | "gt" // Greater than
12
+ | "gte" // Greater than or equal
13
+ | "lt" // Less than
14
+ | "lte" // Less than or equal
15
+ | "in" // In array
16
+ | "nin" // Not in array
17
+ | "like" // Contains (case insensitive)
18
+ | "ilike" // Contains (case insensitive) - PostgreSQL
19
+ | "between" // Between two values
20
+ | "isNull" // Is null
21
+ | "isNotNull"; // Is not null
22
+
23
+ export class FilterCondition {
24
+ @ApiPropertyOptional({ description: "Field name to filter" })
25
+ @IsString()
26
+ field: string;
27
+
28
+ @ApiPropertyOptional({
29
+ description: "Filter operator",
30
+ enum: ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "like", "ilike", "between", "isNull", "isNotNull"]
31
+ })
32
+ @IsIn(["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "like", "ilike", "between", "isNull", "isNotNull"])
33
+ operator: FilterOperator;
34
+
35
+ @ApiPropertyOptional({ description: "Filter value" })
36
+ value?: any;
37
+ }
38
+
39
+ export class {{entityNamePascal}}FilterDto {
40
+ {{#if hasFields}}
41
+ {{#each fields}}
42
+ @ApiPropertyOptional({ description: "Filter by {{this.camelCase}}" })
43
+ @IsOptional()
44
+ {{this.camelCase}}?: {{this.tsType}};
45
+
46
+ @ApiPropertyOptional({ description: "Filter by {{this.camelCase}} with operator (e.g., {{this.camelCase}}__gte=10)" })
47
+ @IsOptional()
48
+ {{this.camelCase}}__eq?: {{this.tsType}};
49
+
50
+ @ApiPropertyOptional()
51
+ @IsOptional()
52
+ {{this.camelCase}}__ne?: {{this.tsType}};
53
+
54
+ {{#if (or (eq this.type 'number') (eq this.type 'date') (eq this.type 'datetime'))}}
55
+ @ApiPropertyOptional()
56
+ @IsOptional()
57
+ {{this.camelCase}}__gt?: {{this.tsType}};
58
+
59
+ @ApiPropertyOptional()
60
+ @IsOptional()
61
+ {{this.camelCase}}__gte?: {{this.tsType}};
62
+
63
+ @ApiPropertyOptional()
64
+ @IsOptional()
65
+ {{this.camelCase}}__lt?: {{this.tsType}};
66
+
67
+ @ApiPropertyOptional()
68
+ @IsOptional()
69
+ {{this.camelCase}}__lte?: {{this.tsType}};
70
+
71
+ {{/if}}
72
+ {{#if (eq this.type 'string')}}
73
+ @ApiPropertyOptional({ description: "Contains search for {{this.camelCase}}" })
74
+ @IsOptional()
75
+ {{this.camelCase}}__like?: string;
76
+
77
+ {{/if}}
78
+ {{/each}}
79
+ {{else}}
80
+ // Add filter fields based on your entity properties
81
+ // Example:
82
+ // @ApiPropertyOptional()
83
+ // @IsOptional()
84
+ // name?: string;
85
+ //
86
+ // @ApiPropertyOptional()
87
+ // @IsOptional()
88
+ // name__like?: string;
89
+ {{/if}}
90
+
91
+ @ApiPropertyOptional({ description: "Filter by active status" })
92
+ @IsOptional()
93
+ isActive?: boolean;
94
+
95
+ @ApiPropertyOptional({ description: "Search across multiple fields" })
96
+ @IsOptional()
97
+ @IsString()
98
+ search?: string;
99
+
100
+ @ApiPropertyOptional({ description: "Fields to search in (comma-separated)" })
101
+ @IsOptional()
102
+ @Transform(({ value }) => typeof value === "string" ? value.split(",") : value)
103
+ @IsArray()
104
+ searchFields?: string[];
105
+
106
+ @ApiPropertyOptional({ description: "Filter by date range - start" })
107
+ @IsOptional()
108
+ @Type(() => Date)
109
+ createdAtFrom?: Date;
110
+
111
+ @ApiPropertyOptional({ description: "Filter by date range - end" })
112
+ @IsOptional()
113
+ @Type(() => Date)
114
+ createdAtTo?: Date;
115
+ }
116
+
117
+ /**
118
+ * Combined filter and pagination DTO
119
+ */
120
+ export class {{entityNamePascal}}QueryDto extends {{entityNamePascal}}FilterDto {
121
+ @ApiPropertyOptional({ description: "Page number", default: 1, minimum: 1 })
122
+ @IsOptional()
123
+ @Type(() => Number)
124
+ page?: number = 1;
125
+
126
+ @ApiPropertyOptional({ description: "Items per page", default: 10, minimum: 1, maximum: 100 })
127
+ @IsOptional()
128
+ @Type(() => Number)
129
+ limit?: number = 10;
130
+
131
+ @ApiPropertyOptional({ description: "Field to sort by", default: "createdAt" })
132
+ @IsOptional()
133
+ @IsString()
134
+ sortBy?: string = "createdAt";
135
+
136
+ @ApiPropertyOptional({ description: "Sort order", enum: ["ASC", "DESC"], default: "DESC" })
137
+ @IsOptional()
138
+ @IsIn(["ASC", "DESC"])
139
+ sortOrder?: "ASC" | "DESC" = "DESC";
140
+
141
+ get skip(): number {
142
+ return ((this.page || 1) - 1) * (this.limit || 10);
143
+ }
144
+
145
+ get take(): number {
146
+ return this.limit || 10;
147
+ }
148
+ }
@@ -0,0 +1,29 @@
1
+ import { ApiProperty } from "@nestjs/swagger";
2
+
3
+ export class PaginationMeta {
4
+ @ApiProperty({ description: "Total number of items" })
5
+ total: number;
6
+
7
+ @ApiProperty({ description: "Current page number" })
8
+ page: number;
9
+
10
+ @ApiProperty({ description: "Number of items per page" })
11
+ limit: number;
12
+
13
+ @ApiProperty({ description: "Total number of pages" })
14
+ totalPages: number;
15
+
16
+ @ApiProperty({ description: "Whether there is a next page" })
17
+ hasNextPage: boolean;
18
+
19
+ @ApiProperty({ description: "Whether there is a previous page" })
20
+ hasPreviousPage: boolean;
21
+ }
22
+
23
+ export class PaginatedResponseDto<T> {
24
+ @ApiProperty({ description: "Array of items", isArray: true })
25
+ items: T[];
26
+
27
+ @ApiProperty({ description: "Pagination metadata", type: PaginationMeta })
28
+ meta: PaginationMeta;
29
+ }
@@ -0,0 +1,30 @@
1
+ import { ApiPropertyOptional } from "@nestjs/swagger";
2
+ import { IsOptional, IsInt, Min, Max, IsString, IsIn } from "class-validator";
3
+ import { Type } from "class-transformer";
4
+
5
+ export class PaginationQueryDto {
6
+ @ApiPropertyOptional({ description: "Page number", default: 1, minimum: 1 })
7
+ @IsOptional()
8
+ @Type(() => Number)
9
+ @IsInt()
10
+ @Min(1)
11
+ page?: number = 1;
12
+
13
+ @ApiPropertyOptional({ description: "Items per page", default: 10, minimum: 1, maximum: 100 })
14
+ @IsOptional()
15
+ @Type(() => Number)
16
+ @IsInt()
17
+ @Min(1)
18
+ @Max(100)
19
+ limit?: number = 10;
20
+
21
+ @ApiPropertyOptional({ description: "Field to sort by", default: "createdAt" })
22
+ @IsOptional()
23
+ @IsString()
24
+ sortBy?: string = "createdAt";
25
+
26
+ @ApiPropertyOptional({ description: "Sort order", enum: ["ASC", "DESC"], default: "DESC" })
27
+ @IsOptional()
28
+ @IsIn(["ASC", "DESC"])
29
+ sortOrder?: "ASC" | "DESC" = "DESC";
30
+ }
@@ -0,0 +1,38 @@
1
+ import { ApiProperty } from "@nestjs/swagger";
2
+ import { Expose } from "class-transformer";
3
+
4
+ export class {{entityNamePascal}}ResponseDto {
5
+ @ApiProperty({ description: "Unique identifier", format: "uuid" })
6
+ @Expose()
7
+ id: string;
8
+
9
+ {{#if hasFields}}
10
+ {{#each fields}}
11
+ @ApiProperty({ description: "{{this.description}}"{{#if this.example}}, example: "{{this.example}}"{{/if}} })
12
+ @Expose()
13
+ {{this.camelCase}}: {{this.tsType}};
14
+
15
+ {{/each}}
16
+ {{else}}
17
+ // Add your response properties here
18
+ // Example:
19
+ // @ApiProperty({ description: "User name" })
20
+ // @Expose()
21
+ // name: string;
22
+ {{/if}}
23
+ @ApiProperty({ description: "Whether the record is active" })
24
+ @Expose()
25
+ isActive: boolean;
26
+
27
+ @ApiProperty({ description: "Creation timestamp" })
28
+ @Expose()
29
+ createdAt: Date;
30
+
31
+ @ApiProperty({ description: "Last update timestamp" })
32
+ @Expose()
33
+ updatedAt: Date;
34
+
35
+ constructor(partial: Partial<{{entityNamePascal}}ResponseDto>) {
36
+ Object.assign(this, partial);
37
+ }
38
+ }
@@ -0,0 +1,11 @@
1
+ import { ApiPropertyOptional } from "@nestjs/swagger";
2
+ import { IsOptional, IsBoolean } from "class-validator";
3
+ import { PartialType } from "@nestjs/swagger";
4
+ import { Create{{entityNamePascal}}Dto } from "./create-{{entityNameKebab}}.dto";
5
+
6
+ export class Update{{entityNamePascal}}Dto extends PartialType(Create{{entityNamePascal}}Dto) {
7
+ @ApiPropertyOptional({ description: "Whether the record is active" })
8
+ @IsOptional()
9
+ @IsBoolean()
10
+ isActive?: boolean;
11
+ }
@@ -4,8 +4,14 @@ export interface {{entityNamePascal}}EntityProps {
4
4
  createdAt?: Date;
5
5
  updatedAt?: Date;
6
6
  deletedAt?: Date;
7
+ {{#if hasFields}}
8
+ {{#each fields}}
9
+ {{this.camelCase}}{{#if this.isOptional}}?{{/if}}: {{this.tsType}};
10
+ {{/each}}
11
+ {{else}}
7
12
  // Add your domain properties here
8
13
  // Example: name: string;
14
+ {{/if}}
9
15
  }
10
16
 
11
17
  export class {{entityNamePascal}}Entity {
@@ -14,10 +20,35 @@ export class {{entityNamePascal}}Entity {
14
20
  public readonly createdAt?: Date;
15
21
  public readonly updatedAt?: Date;
16
22
  public readonly deletedAt?: Date;
23
+ {{#if hasFields}}
24
+ {{#each fields}}
25
+ public readonly {{this.camelCase}}{{#if this.isOptional}}?{{/if}}: {{this.tsType}};
26
+ {{/each}}
27
+ {{else}}
17
28
  // Add your domain properties here
18
29
  // Example: public readonly name: string;
30
+ {{/if}}
19
31
 
20
32
  constructor(props: {{entityNamePascal}}EntityProps) {
21
33
  Object.assign(this, props);
22
34
  }
23
- }
35
+
36
+ /**
37
+ * Create a copy of the entity with updated properties
38
+ */
39
+ public update(props: Partial<{{entityNamePascal}}EntityProps>): {{entityNamePascal}}Entity {
40
+ return new {{entityNamePascal}}Entity({
41
+ ...this,
42
+ ...props,
43
+ updatedAt: new Date(),
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Check if the entity is valid for persistence
49
+ */
50
+ public isValid(): boolean {
51
+ // Add your validation logic here
52
+ return true;
53
+ }
54
+ }
@@ -1,9 +1,35 @@
1
- import { IEvent } from '@nestjs/cqrs';
1
+ import { IEvent } from "@nestjs/cqrs";
2
+
3
+ export interface {{entityNamePascal}}EventData {
4
+ // Define your event payload here
5
+ // Example:
6
+ // name: string;
7
+ // email: string;
8
+ {{#each fields}}
9
+ {{this.camelCase}}{{#if this.isOptional}}?{{/if}}: {{this.tsType}};
10
+ {{/each}}
11
+ }
12
+
13
+ export class {{entityNamePascal}}{{eventName}}Event implements IEvent {
14
+ public readonly occurredAt: Date;
15
+ public readonly eventType = "{{entityNamePascal}}{{eventName}}";
2
16
 
3
- export class {{entityName}}Event implements IEvent {
4
17
  constructor(
5
- public readonly id: string,
6
- public readonly data: any,
7
- public readonly occurredAt: Date = new Date(),
8
- ) {}
9
- }
18
+ public readonly {{entityNameCamel}}Id: string,
19
+ public readonly data: {{entityNamePascal}}EventData,
20
+ public readonly metadata?: {
21
+ correlationId?: string;
22
+ causationId?: string;
23
+ userId?: string;
24
+ }
25
+ ) {
26
+ this.occurredAt = new Date();
27
+ }
28
+
29
+ /**
30
+ * Get a string representation of the event for logging
31
+ */
32
+ toString(): string {
33
+ return `${this.eventType}(${this.{{entityNameCamel}}Id}) at ${this.occurredAt.toISOString()}`;
34
+ }
35
+ }
@@ -0,0 +1,40 @@
1
+ import { EventsHandler, IEventHandler } from "@nestjs/cqrs";
2
+ import { Logger } from "@nestjs/common";
3
+ import { {{entityNamePascal}}{{eventName}}Event } from "./{{entityNameKebab}}-{{eventNameKebab}}.event";
4
+
5
+ @EventsHandler({{entityNamePascal}}{{eventName}}Event)
6
+ export class {{entityNamePascal}}{{eventName}}Handler
7
+ implements IEventHandler<{{entityNamePascal}}{{eventName}}Event>
8
+ {
9
+ private readonly logger = new Logger({{entityNamePascal}}{{eventName}}Handler.name);
10
+
11
+ async handle(event: {{entityNamePascal}}{{eventName}}Event): Promise<void> {
12
+ this.logger.log(
13
+ `Handling {{entityNamePascal}}{{eventName}}Event for {{entityNameCamel}} ${event.{{entityNameCamel}}Id}`
14
+ );
15
+
16
+ // Implement your event handling logic here
17
+ // Examples:
18
+ // - Send notification emails
19
+ // - Update related aggregates
20
+ // - Publish to external systems
21
+ // - Update read models
22
+ // - Trigger workflows
23
+
24
+ try {
25
+ // Your async operations here
26
+ await this.processEvent(event);
27
+ } catch (error) {
28
+ this.logger.error(
29
+ `Failed to handle {{entityNamePascal}}{{eventName}}Event: ${error.message}`,
30
+ error.stack
31
+ );
32
+ // Consider: retry logic, dead letter queue, or alerting
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ private async processEvent(event: {{entityNamePascal}}{{eventName}}Event): Promise<void> {
38
+ // Implement your specific event processing logic
39
+ }
40
+ }
@@ -0,0 +1,69 @@
1
+ import {
2
+ BadRequestException,
3
+ ConflictException,
4
+ ForbiddenException,
5
+ NotFoundException,
6
+ UnauthorizedException,
7
+ } from "@nestjs/common";
8
+
9
+ /**
10
+ * Base exception for domain validation errors
11
+ */
12
+ export class DomainValidationException extends BadRequestException {
13
+ constructor(message: string, public readonly errors?: Record<string, string[]>) {
14
+ super({
15
+ statusCode: 400,
16
+ message,
17
+ errors,
18
+ });
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Exception for when an entity already exists (duplicate)
24
+ */
25
+ export class EntityAlreadyExistsException extends ConflictException {
26
+ constructor(entityName: string, field: string, value: string) {
27
+ super(`${entityName} with ${field} '${value}' already exists`);
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Exception for when an entity is not found
33
+ */
34
+ export class EntityNotFoundException extends NotFoundException {
35
+ constructor(entityName: string, id: string) {
36
+ super(`${entityName} with id '${id}' was not found`);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Exception for when a business rule is violated
42
+ */
43
+ export class BusinessRuleViolationException extends BadRequestException {
44
+ constructor(message: string) {
45
+ super({
46
+ statusCode: 400,
47
+ message,
48
+ error: 'Business Rule Violation',
49
+ });
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Exception for unauthorized access
55
+ */
56
+ export class UnauthorizedAccessException extends UnauthorizedException {
57
+ constructor(message = 'You are not authorized to perform this action') {
58
+ super(message);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Exception for forbidden operations
64
+ */
65
+ export class ForbiddenOperationException extends ForbiddenException {
66
+ constructor(message = 'You do not have permission to perform this operation') {
67
+ super(message);
68
+ }
69
+ }
@@ -0,0 +1,7 @@
1
+ import { NotFoundException } from "@nestjs/common";
2
+
3
+ export class {{entityNamePascal}}NotFoundException extends NotFoundException {
4
+ constructor(id: string) {
5
+ super(`{{entityNamePascal}} with id '${id}' was not found`);
6
+ }
7
+ }
@@ -1,37 +1,62 @@
1
- import { {{entityNamePascal}}OrmEntity } from "@modules/{{moduleNameKebab}}/infrastructure/orm-entities/{{entityNameKebab}}.orm-entity";
2
- import { {{entityNamePascal}}Entity } from "@modules/{{moduleNameKebab}}/domain/entities/{{entityNameKebab}}.entity";
3
1
  import { Injectable } from "@nestjs/common";
2
+ import { {{entityNamePascal}}Entity } from "@modules/{{moduleNameKebab}}/domain/entities/{{entityNameKebab}}.entity";
3
+ import { {{entityNamePascal}}OrmEntity } from "@modules/{{moduleNameKebab}}/infrastructure/orm-entities/{{entityNameKebab}}.orm-entity";
4
+ import { {{entityNamePascal}}ResponseDto } from "@modules/{{moduleNameKebab}}/application/dto/responses/{{entityNameKebab}}.response.dto";
4
5
 
5
6
  @Injectable()
6
7
  export class {{entityNamePascal}}Mapper {
7
- toOrmEntity(domainEntity: {{entityNamePascal}}Entity): {{entityNamePascal}}OrmEntity {
8
- if (!domainEntity) {
9
- throw new Error('Domain entity is required');
8
+ toDomainEntity(ormEntity: {{entityNamePascal}}OrmEntity): {{entityNamePascal}}Entity {
9
+ if (!ormEntity) {
10
+ throw new Error("ORM entity is required");
10
11
  }
11
12
 
12
- const ormEntity = new {{entityNamePascal}}OrmEntity();
13
- ormEntity.id = domainEntity.id;
14
- ormEntity.isActive = domainEntity.isActive;
15
- ormEntity.createdAt = domainEntity.createdAt;
16
- ormEntity.updatedAt = domainEntity.updatedAt;
17
- ormEntity.deletedAt = domainEntity.deletedAt;
18
- // Map your properties from camelCase to snake_case
19
- // Example: ormEntity.property_name = domainEntity.propertyName;
20
-
21
- return ormEntity;
22
- }
23
-
24
- toDomainEntity(ormEntity: {{entityNamePascal}}OrmEntity): {{entityNamePascal}}Entity {
25
- const entity = new {{entityNamePascal}}Entity({
13
+ return new {{entityNamePascal}}Entity({
26
14
  id: ormEntity.id,
27
15
  isActive: ormEntity.isActive,
28
16
  createdAt: ormEntity.createdAt,
29
17
  updatedAt: ormEntity.updatedAt,
30
18
  deletedAt: ormEntity.deletedAt,
31
- // Map your properties from snake_case to camelCase
32
- // Example: propertyName: ormEntity.property_name,
19
+ // Map additional fields from ORM entity (snake_case) to domain entity (camelCase)
20
+ {{#each fields}}
21
+ {{this.camelCase}}: ormEntity.{{this.snakeCase}},
22
+ {{/each}}
23
+ });
24
+ }
25
+
26
+ toOrmEntity(domainEntity: {{entityNamePascal}}Entity): Partial<{{entityNamePascal}}OrmEntity> {
27
+ if (!domainEntity) {
28
+ throw new Error("Domain entity is required");
29
+ }
30
+
31
+ const ormEntity: Partial<{{entityNamePascal}}OrmEntity> = {
32
+ id: domainEntity.id,
33
+ isActive: domainEntity.isActive,
34
+ createdAt: domainEntity.createdAt,
35
+ updatedAt: domainEntity.updatedAt,
36
+ deletedAt: domainEntity.deletedAt,
37
+ // Map additional fields from domain entity (camelCase) to ORM entity (snake_case)
38
+ {{#each fields}}
39
+ {{this.snakeCase}}: domainEntity.{{this.camelCase}},
40
+ {{/each}}
41
+ };
42
+
43
+ return ormEntity;
44
+ }
45
+
46
+ toResponseDto(entity: {{entityNamePascal}}Entity): {{entityNamePascal}}ResponseDto {
47
+ return new {{entityNamePascal}}ResponseDto({
48
+ id: entity.id,
49
+ isActive: entity.isActive,
50
+ createdAt: entity.createdAt,
51
+ updatedAt: entity.updatedAt,
52
+ // Map additional fields for response
53
+ {{#each fields}}
54
+ {{this.camelCase}}: entity.{{this.camelCase}},
55
+ {{/each}}
33
56
  });
34
-
35
- return entity;
36
57
  }
37
- }
58
+
59
+ toResponseDtoList(entities: {{entityNamePascal}}Entity[]): {{entityNamePascal}}ResponseDto[] {
60
+ return entities.map((entity) => this.toResponseDto(entity));
61
+ }
62
+ }
@@ -1,25 +1,49 @@
1
1
  import { Module } from "@nestjs/common";
2
2
  import { CqrsModule } from "@nestjs/cqrs";
3
+ import { TypeOrmModule } from "@nestjs/typeorm";
4
+
5
+ // Infrastructure
6
+ import { OrmEntities } from "@modules/{{moduleNameKebab}}/infrastructure/orm-entities";
3
7
  import { Repositories } from "@modules/{{moduleNameKebab}}/infrastructure/repositories";
4
- import { Queries } from "@modules/{{moduleNameKebab}}/application/queries";
5
8
  import { Mappers } from "@modules/{{moduleNameKebab}}/infrastructure/mappers";
6
- import { UseCases } from "@modules/{{moduleNameKebab}}/application/domain/usecases";
7
- import { Controllers } from "@modules/{{moduleNameKebab}}/application/controllers";
9
+
10
+ // Application - Commands
8
11
  import { CommandHandlers } from "@modules/{{moduleNameKebab}}/application/commands";
9
- import { OrmEntities } from "@modules/{{moduleNameKebab}}/infrastructure/orm-entities";
10
- import { TypeOrmModule } from "@nestjs/typeorm";
12
+
13
+ // Application - Queries
14
+ import { Queries } from "@modules/{{moduleNameKebab}}/application/queries";
15
+
16
+ // Application - Use Cases
17
+ import { UseCases } from "@modules/{{moduleNameKebab}}/application/domain/usecases";
18
+
19
+ // Application - Services
11
20
  import { Services } from "@modules/{{moduleNameKebab}}/application/domain/services";
12
21
 
22
+ // Application - Controllers
23
+ import { Controllers } from "@modules/{{moduleNameKebab}}/application/controllers";
24
+
13
25
  @Module({
14
- imports: [TypeOrmModule.forFeature([...OrmEntities]), CqrsModule],
26
+ imports: [
27
+ TypeOrmModule.forFeature([...OrmEntities]),
28
+ CqrsModule,
29
+ ],
30
+ controllers: [...Controllers],
15
31
  providers: [
32
+ // CQRS handlers
16
33
  ...CommandHandlers,
17
34
  ...Queries,
18
- ...Repositories,
19
- ...Mappers,
35
+ // Domain layer
20
36
  ...UseCases,
21
37
  ...Services,
38
+ // Infrastructure layer
39
+ ...Repositories,
40
+ ...Mappers,
41
+ ],
42
+ exports: [
43
+ // Export repositories for use in other modules
44
+ ...Repositories,
45
+ // Export mappers for use in other modules
46
+ ...Mappers,
22
47
  ],
23
- controllers: [...Controllers],
24
48
  })
25
- export class {{moduleName}}Module {}
49
+ export class {{moduleNamePascal}}Module {}