evnict-kit 0.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 (138) hide show
  1. package/README.md +19 -0
  2. package/bin/cli.js +38 -0
  3. package/package.json +48 -0
  4. package/src/commands/add.js +129 -0
  5. package/src/commands/init-check.js +19 -0
  6. package/src/commands/init-context.js +37 -0
  7. package/src/commands/init-rules.js +42 -0
  8. package/src/commands/init-workflow.js +36 -0
  9. package/src/commands/init.js +722 -0
  10. package/src/utils/config.js +167 -0
  11. package/src/utils/file.js +53 -0
  12. package/templates/GETTING-STARTED.md +196 -0
  13. package/templates/content/context/AGENTS.md.template +462 -0
  14. package/templates/content/rules/01-evnict-kit-general-rules.md +303 -0
  15. package/templates/content/rules/02-evnict-kit-security-rules.md +423 -0
  16. package/templates/content/rules/03-evnict-kit-backend-conventions.md +383 -0
  17. package/templates/content/rules/04-evnict-kit-frontend-conventions.md +402 -0
  18. package/templates/content/rules/05-evnict-kit-project-conventions.md +228 -0
  19. package/templates/content/skills/evnict-kit-brainstorm/SKILL.md +140 -0
  20. package/templates/content/skills/evnict-kit-bug-fix/SKILL.md +108 -0
  21. package/templates/content/skills/evnict-kit-checkpoint/SKILL.md +156 -0
  22. package/templates/content/skills/evnict-kit-code-review/SKILL.md +158 -0
  23. package/templates/content/skills/evnict-kit-coordinate/SKILL.md +274 -0
  24. package/templates/content/skills/evnict-kit-create-api/SKILL.md +281 -0
  25. package/templates/content/skills/evnict-kit-create-component/SKILL.md +263 -0
  26. package/templates/content/skills/evnict-kit-create-page/SKILL.md +247 -0
  27. package/templates/content/skills/evnict-kit-database-migration/SKILL.md +164 -0
  28. package/templates/content/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
  29. package/templates/content/skills/evnict-kit-finish-branch/SKILL.md +87 -0
  30. package/templates/content/skills/evnict-kit-fix-attt/SKILL.md +129 -0
  31. package/templates/content/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
  32. package/templates/content/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
  33. package/templates/content/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
  34. package/templates/content/skills/evnict-kit-onboard/SKILL.md +143 -0
  35. package/templates/content/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
  36. package/templates/content/skills/evnict-kit-receiving-review/SKILL.md +89 -0
  37. package/templates/content/skills/evnict-kit-security-audit/SKILL.md +190 -0
  38. package/templates/content/skills/evnict-kit-spec/SKILL.md +237 -0
  39. package/templates/content/skills/evnict-kit-tdd/SKILL.md +413 -0
  40. package/templates/content/skills/evnict-kit-wiki/SKILL.md +412 -0
  41. package/templates/content/workflows/evnict-kit-archive-wiki.md +100 -0
  42. package/templates/content/workflows/evnict-kit-attt.md +100 -0
  43. package/templates/content/workflows/evnict-kit-bug-fix.md +107 -0
  44. package/templates/content/workflows/evnict-kit-feature-large.md +393 -0
  45. package/templates/content/workflows/evnict-kit-feature-small.md +86 -0
  46. package/templates/content/workflows/evnict-kit-handoff.md +243 -0
  47. package/templates/content/workflows/evnict-kit-implement.md +247 -0
  48. package/templates/content/workflows/evnict-kit-init-check.md +76 -0
  49. package/templates/content/workflows/evnict-kit-init-context.md +58 -0
  50. package/templates/content/workflows/evnict-kit-init-rules.md +114 -0
  51. package/templates/content/workflows/evnict-kit-init-wiki.md +80 -0
  52. package/templates/content/workflows/evnict-kit-plan.md +308 -0
  53. package/templates/content/workflows/evnict-kit-review.md +53 -0
  54. package/templates/content/workflows/evnict-kit-spec-archive.md +53 -0
  55. package/templates/content/workflows/evnict-kit-wiki-archive-feature.md +164 -0
  56. package/templates/content/workflows/evnict-kit-wiki-query.md +91 -0
  57. package/templates/content/workflows/evnict-kit-wiki-scan-project.md +272 -0
  58. package/templates/context/AGENT.md.template +9 -0
  59. package/templates/context/AGENTS.md.template +462 -0
  60. package/templates/context/CLAUDE.md.template +301 -0
  61. package/templates/context/copilot-instructions.md.template +60 -0
  62. package/templates/context/cursorrules.template +114 -0
  63. package/templates/instruct/Instruct-Agent-AI.be.md +96 -0
  64. package/templates/instruct/Instruct-Agent-AI.fe.md +79 -0
  65. package/templates/rules/antigravity/01-evnict-kit-general-rules.md +303 -0
  66. package/templates/rules/antigravity/02-evnict-kit-security-rules.md +423 -0
  67. package/templates/rules/antigravity/03-evnict-kit-backend-conventions.md +383 -0
  68. package/templates/rules/antigravity/04-evnict-kit-frontend-conventions.md +402 -0
  69. package/templates/rules/antigravity/05-evnict-kit-project-conventions.md +228 -0
  70. package/templates/rules/claude/README.md +8 -0
  71. package/templates/rules/cursor/01-evnict-kit-general-rules.mdc +46 -0
  72. package/templates/rules/cursor/02-evnict-kit-security-rules.mdc +46 -0
  73. package/templates/rules/cursor/03-evnict-kit-backend-conventions.mdc +50 -0
  74. package/templates/rules/cursor/04-evnict-kit-frontend-conventions.mdc +43 -0
  75. package/templates/rules/cursor/05-evnict-kit-project-conventions.mdc +63 -0
  76. package/templates/rules/cursor/README.md +7 -0
  77. package/templates/skills/evnict-kit-brainstorm/SKILL.md +140 -0
  78. package/templates/skills/evnict-kit-bug-fix/SKILL.md +108 -0
  79. package/templates/skills/evnict-kit-checkpoint/SKILL.md +156 -0
  80. package/templates/skills/evnict-kit-code-review/SKILL.md +158 -0
  81. package/templates/skills/evnict-kit-coordinate/SKILL.md +274 -0
  82. package/templates/skills/evnict-kit-create-api/SKILL.md +281 -0
  83. package/templates/skills/evnict-kit-create-component/SKILL.md +263 -0
  84. package/templates/skills/evnict-kit-create-page/SKILL.md +247 -0
  85. package/templates/skills/evnict-kit-database-migration/SKILL.md +164 -0
  86. package/templates/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
  87. package/templates/skills/evnict-kit-finish-branch/SKILL.md +87 -0
  88. package/templates/skills/evnict-kit-fix-attt/SKILL.md +129 -0
  89. package/templates/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
  90. package/templates/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
  91. package/templates/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
  92. package/templates/skills/evnict-kit-onboard/SKILL.md +143 -0
  93. package/templates/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
  94. package/templates/skills/evnict-kit-receiving-review/SKILL.md +89 -0
  95. package/templates/skills/evnict-kit-security-audit/SKILL.md +190 -0
  96. package/templates/skills/evnict-kit-spec/SKILL.md +237 -0
  97. package/templates/skills/evnict-kit-tdd/SKILL.md +413 -0
  98. package/templates/skills/evnict-kit-wiki/SKILL.md +412 -0
  99. package/templates/wiki/README.md +35 -0
  100. package/templates/wiki/config.example.yaml +17 -0
  101. package/templates/wiki/package.json +17 -0
  102. package/templates/wiki/raw/notes/.gitkeep +1 -0
  103. package/templates/wiki/scripts/ingest.js +66 -0
  104. package/templates/workflows/antigravity/evnict-kit-archive-wiki.md +100 -0
  105. package/templates/workflows/antigravity/evnict-kit-attt.md +100 -0
  106. package/templates/workflows/antigravity/evnict-kit-bug-fix.md +107 -0
  107. package/templates/workflows/antigravity/evnict-kit-feature-large.md +393 -0
  108. package/templates/workflows/antigravity/evnict-kit-feature-small.md +86 -0
  109. package/templates/workflows/antigravity/evnict-kit-handoff.md +243 -0
  110. package/templates/workflows/antigravity/evnict-kit-implement.md +247 -0
  111. package/templates/workflows/antigravity/evnict-kit-init-check.md +76 -0
  112. package/templates/workflows/antigravity/evnict-kit-init-context.md +58 -0
  113. package/templates/workflows/antigravity/evnict-kit-init-rules.md +114 -0
  114. package/templates/workflows/antigravity/evnict-kit-init-wiki.md +80 -0
  115. package/templates/workflows/antigravity/evnict-kit-plan.md +308 -0
  116. package/templates/workflows/antigravity/evnict-kit-review.md +53 -0
  117. package/templates/workflows/antigravity/evnict-kit-spec-archive.md +53 -0
  118. package/templates/workflows/antigravity/evnict-kit-wiki-archive-feature.md +164 -0
  119. package/templates/workflows/antigravity/evnict-kit-wiki-query.md +91 -0
  120. package/templates/workflows/antigravity/evnict-kit-wiki-scan-project.md +272 -0
  121. package/templates/workflows/claude/README.md +6 -0
  122. package/templates/workflows/claude/evnict-kit-archive-wiki.md +98 -0
  123. package/templates/workflows/claude/evnict-kit-attt.md +98 -0
  124. package/templates/workflows/claude/evnict-kit-bug-fix.md +105 -0
  125. package/templates/workflows/claude/evnict-kit-feature-large.md +391 -0
  126. package/templates/workflows/claude/evnict-kit-feature-small.md +84 -0
  127. package/templates/workflows/claude/evnict-kit-handoff.md +240 -0
  128. package/templates/workflows/claude/evnict-kit-implement.md +245 -0
  129. package/templates/workflows/claude/evnict-kit-init-check.md +74 -0
  130. package/templates/workflows/claude/evnict-kit-init-context.md +56 -0
  131. package/templates/workflows/claude/evnict-kit-init-rules.md +112 -0
  132. package/templates/workflows/claude/evnict-kit-init-wiki.md +78 -0
  133. package/templates/workflows/claude/evnict-kit-plan.md +305 -0
  134. package/templates/workflows/claude/evnict-kit-review.md +51 -0
  135. package/templates/workflows/claude/evnict-kit-spec-archive.md +51 -0
  136. package/templates/workflows/claude/evnict-kit-wiki-archive-feature.md +162 -0
  137. package/templates/workflows/claude/evnict-kit-wiki-query.md +89 -0
  138. package/templates/workflows/claude/evnict-kit-wiki-scan-project.md +270 -0
@@ -0,0 +1,383 @@
1
+ ---
2
+ trigger: always_on
3
+ ---
4
+ # Backend Conventions — EVNICT Standard
5
+ **Activation Mode: Always On**
6
+ **Source: QĐ-TTPM Điều 8, Mục 8.6 — Ràng buộc kỹ thuật**
7
+ **Tech: Java Spring Boot + JOOQ + Oracle**
8
+
9
+ > Áp dụng cho TẤT CẢ code backend. Mọi code mới hoặc sửa đổi PHẢI tuân thủ.
10
+
11
+ ---
12
+
13
+ ## 1. INPUT VALIDATION (RB01)
14
+
15
+ ### Quy tắc
16
+ - MỌI input từ client PHẢI được validate ở Controller layer
17
+ - Dùng Bean Validation annotations (@NotNull, @NotBlank, @Size, @Pattern)
18
+ - LUÔN dùng @Valid hoặc @Validated trên request body
19
+ - Custom validator cho business rules phức tạp
20
+
21
+ ### Code examples
22
+ ```java
23
+ // ❌ SAI — Không validate input
24
+ @PostMapping("/customers")
25
+ public ResponseEntity<?> create(@RequestBody CustomerDTO dto) {
26
+ return ResponseEntity.ok(service.create(dto));
27
+ }
28
+
29
+ // ✅ ĐÚNG — Validation annotations + @Valid
30
+ public class CustomerDTO {
31
+ @NotBlank(message = "Tên không được trống")
32
+ @Size(max = 200, message = "Tên không quá 200 ký tự")
33
+ private String name;
34
+
35
+ @Pattern(regexp = "^0[0-9]{9}$", message = "Số điện thoại không hợp lệ")
36
+ private String phone;
37
+
38
+ @NotNull(message = "Mã đơn vị bắt buộc")
39
+ private Long donViId;
40
+ }
41
+
42
+ @PostMapping("/customers")
43
+ public ResponseEntity<ResponseData> create(@Valid @RequestBody CustomerDTO dto) {
44
+ return ResponseEntity.ok(ResponseData.ok(service.create(dto)));
45
+ }
46
+ ```
47
+
48
+ ### Validation cho search/list parameters
49
+ ```java
50
+ // ✅ ĐÚNG — Validate sort column bằng whitelist
51
+ private static final Set<String> ALLOWED_SORT = Set.of("name", "createdDate", "status");
52
+
53
+ @GetMapping("/customers")
54
+ public ResponseEntity<ResponseData> search(
55
+ @RequestParam(defaultValue = "") String keyword,
56
+ @RequestParam(defaultValue = "0") @Min(0) int page,
57
+ @RequestParam(defaultValue = "20") @Min(1) @Max(100) int size,
58
+ @RequestParam(defaultValue = "name") String sortBy) {
59
+ if (!ALLOWED_SORT.contains(sortBy)) sortBy = "name";
60
+ return ResponseEntity.ok(ResponseData.ok(service.search(keyword, page, size, sortBy)));
61
+ }
62
+ ```
63
+
64
+ ---
65
+
66
+ ## 2. SERVICE LAYER PATTERN (RB02)
67
+
68
+ ### Cấu trúc bắt buộc
69
+ ```
70
+ Controller → Service → Repository → Database
71
+ ↓ ↓ ↓
72
+ Validate Business Data Access
73
+ + Auth Logic (JOOQ/JPA)
74
+ ```
75
+
76
+ ### Quy tắc
77
+ - Controller: validate input, call service, return ResponseData
78
+ - Service: business logic, transaction management, orchestration
79
+ - Repository: data access ONLY — KHÔNG chứa business logic
80
+ - DTO: data transfer giữa layers — KHÔNG dùng Entity trực tiếp
81
+
82
+ ### Code examples
83
+ ```java
84
+ // ❌ SAI — Business logic trong Controller
85
+ @PostMapping("/orders")
86
+ public ResponseEntity<?> createOrder(@RequestBody OrderDTO dto) {
87
+ // Business logic KHÔNG nên ở đây
88
+ if (dto.getTotal() > 1000000) {
89
+ dto.setNeedApproval(true);
90
+ }
91
+ Order order = orderRepository.save(mapToEntity(dto));
92
+ return ResponseEntity.ok(order);
93
+ }
94
+
95
+ // ✅ ĐÚNG — Controller gọi Service
96
+ @PostMapping("/orders")
97
+ public ResponseEntity<ResponseData> createOrder(@Valid @RequestBody OrderDTO dto) {
98
+ OrderDTO result = orderService.create(dto);
99
+ return ResponseEntity.ok(ResponseData.ok(result));
100
+ }
101
+
102
+ // ✅ Service xử lý business logic
103
+ @Service
104
+ @Transactional
105
+ public class OrderService {
106
+ public OrderDTO create(OrderDTO dto) {
107
+ // Business rule: đơn hàng > 1tr cần duyệt
108
+ if (dto.getTotal() > 1000000) {
109
+ dto.setNeedApproval(true);
110
+ }
111
+ // Repository chỉ lưu data
112
+ return orderRepository.create(dto);
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### ResponseData pattern (EVNICT standard)
118
+ ```java
119
+ // ✅ Mọi API response PHẢI dùng ResponseData wrapper
120
+ public class ResponseData {
121
+ private int status; // 0 = success, 1 = error
122
+ private String message;
123
+ private Object data;
124
+
125
+ public static ResponseData ok(Object data) {
126
+ return new ResponseData(0, "Thành công", data);
127
+ }
128
+
129
+ public static ResponseData error(String message) {
130
+ return new ResponseData(1, message, null);
131
+ }
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## 3. ERROR HANDLING (RB03)
138
+
139
+ ### Cấu trúc GlobalExceptionHandler
140
+ ```java
141
+ @RestControllerAdvice
142
+ public class GlobalExceptionHandler {
143
+
144
+ // Business exceptions — return message cho user
145
+ @ExceptionHandler(BusinessException.class)
146
+ public ResponseEntity<ResponseData> handleBusiness(BusinessException ex) {
147
+ log.warn("Business error: {}", ex.getMessage());
148
+ return ResponseEntity.ok(ResponseData.error(ex.getMessage()));
149
+ }
150
+
151
+ // Validation errors — return danh sách lỗi
152
+ @ExceptionHandler(MethodArgumentNotValidException.class)
153
+ public ResponseEntity<ResponseData> handleValidation(MethodArgumentNotValidException ex) {
154
+ List<String> errors = ex.getBindingResult().getFieldErrors().stream()
155
+ .map(e -> e.getField() + ": " + e.getDefaultMessage())
156
+ .toList();
157
+ log.warn("Validation errors: {}", errors);
158
+ return ResponseEntity.badRequest()
159
+ .body(ResponseData.error(String.join(", ", errors)));
160
+ }
161
+
162
+ // Unexpected errors — log full, return generic message
163
+ @ExceptionHandler(Exception.class)
164
+ public ResponseEntity<ResponseData> handleGeneral(Exception ex) {
165
+ log.error("Unexpected error", ex);
166
+ return ResponseEntity.status(500)
167
+ .body(ResponseData.error("Có lỗi xảy ra trong hệ thống"));
168
+ }
169
+ }
170
+ ```
171
+
172
+ ### ZERO TOLERANCE — KHÔNG expose stack trace
173
+ ```java
174
+ // ❌ SAI
175
+ return ResponseEntity.status(500).body(ex.toString());
176
+ return ResponseEntity.status(500).body(ex.getStackTrace());
177
+ return ResponseEntity.status(500).body(ex.getMessage()); // Risky cho unexpected
178
+ ```
179
+
180
+ ---
181
+
182
+ ## 4. DATABASE MIGRATION (RB04)
183
+
184
+ ### Quy tắc
185
+ - Schema changes PHẢI qua migration scripts — KHÔNG sửa DB trực tiếp
186
+ - Naming: `V{YYYYMMDD}_{seq}__{description}.sql`
187
+ - Mỗi migration PHẢI có rollback script
188
+ - Test cả UP và DOWN trước khi commit
189
+
190
+ ### Oracle-specific patterns
191
+ ```sql
192
+ -- UP: V20260401_001__add_customer_status.sql
193
+ ALTER TABLE CUSTOMER ADD (STATUS VARCHAR2(20) DEFAULT 'ACTIVE' NOT NULL);
194
+ CREATE INDEX IX_CUSTOMER_STATUS ON CUSTOMER(STATUS);
195
+ COMMENT ON COLUMN CUSTOMER.STATUS IS 'Trạng thái: ACTIVE, INACTIVE, SUSPENDED';
196
+
197
+ -- DOWN: V20260401_001__add_customer_status_ROLLBACK.sql
198
+ DROP INDEX IX_CUSTOMER_STATUS;
199
+ ALTER TABLE CUSTOMER DROP COLUMN STATUS;
200
+ ```
201
+
202
+ ### SQL Server patterns
203
+ ```sql
204
+ -- UP
205
+ ALTER TABLE Customer ADD Status NVARCHAR(20) NOT NULL DEFAULT 'ACTIVE';
206
+ CREATE INDEX IX_Customer_Status ON Customer(Status);
207
+
208
+ -- DOWN
209
+ DROP INDEX IX_Customer_Status ON Customer;
210
+ ALTER TABLE Customer DROP COLUMN Status;
211
+ ```
212
+
213
+ ### Scan commands
214
+ ```bash
215
+ # Tìm raw DDL trong Java code (KHÔNG nên có)
216
+ grep -rn "CREATE TABLE\|ALTER TABLE\|DROP TABLE" --include="*.java" src/
217
+ ```
218
+
219
+ ---
220
+
221
+ ## 5. AUTHENTICATION ON EVERY ROUTE (RB05)
222
+
223
+ ### Quy tắc
224
+ - MỌI endpoint mới PHẢI được bảo vệ (default deny)
225
+ - Public endpoints PHẢI được whitelist rõ ràng
226
+ - Data-level authorization khi cần (chỉ xem data đơn vị mình)
227
+
228
+ ### Spring Security configuration
229
+ ```java
230
+ @Configuration
231
+ public class SecurityConfig {
232
+ @Bean
233
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
234
+ http.authorizeHttpRequests(auth -> auth
235
+ // Public endpoints — whitelist RÕ RÀNG
236
+ .requestMatchers("/api/auth/login", "/api/auth/refresh").permitAll()
237
+ .requestMatchers("/api/public/**").permitAll()
238
+ .requestMatchers("/actuator/health").permitAll()
239
+ // Mọi route khác — PHẢI authenticated
240
+ .anyRequest().authenticated()
241
+ );
242
+ return http.build();
243
+ }
244
+ }
245
+ ```
246
+
247
+ ### Method-level security
248
+ ```java
249
+ // ✅ ĐÚNG — Role-based access
250
+ @PreAuthorize("hasRole('ADMIN')")
251
+ @DeleteMapping("/users/{id}")
252
+ public ResponseEntity<ResponseData> deleteUser(@PathVariable Long id) { ... }
253
+
254
+ // ✅ ĐÚNG — Data-level authorization
255
+ @GetMapping("/don-vi/{id}/customers")
256
+ public ResponseEntity<ResponseData> getCustomers(@PathVariable Long donViId) {
257
+ // Kiểm tra user có quyền xem data đơn vị này
258
+ Long userDonViId = SecurityUtils.getCurrentUserDonViId();
259
+ if (!donViId.equals(userDonViId) && !SecurityUtils.isAdmin()) {
260
+ throw new BusinessException("Không có quyền truy cập dữ liệu đơn vị này");
261
+ }
262
+ return ResponseEntity.ok(ResponseData.ok(service.getByDonVi(donViId)));
263
+ }
264
+ ```
265
+
266
+ ---
267
+
268
+ ## 6. RATE LIMITING (RB06)
269
+
270
+ ### Quy tắc
271
+ - API public (login, register) → rate limit BẮT BUỘC
272
+ - API nội bộ → rate limit khuyến nghị
273
+ - File upload → rate limit + size limit
274
+
275
+ ### Spring Boot implementation
276
+ ```java
277
+ // ✅ Sử dụng Bucket4j hoặc Resilience4j
278
+ @RateLimiter(name = "loginApi", fallbackMethod = "rateLimitFallback")
279
+ @PostMapping("/api/auth/login")
280
+ public ResponseEntity<ResponseData> login(@Valid @RequestBody LoginDTO dto) {
281
+ return ResponseEntity.ok(authService.login(dto));
282
+ }
283
+
284
+ public ResponseEntity<ResponseData> rateLimitFallback(LoginDTO dto, Exception ex) {
285
+ return ResponseEntity.status(429)
286
+ .body(ResponseData.error("Quá nhiều request, vui lòng thử lại sau"));
287
+ }
288
+ ```
289
+
290
+ ---
291
+
292
+ ## 7. JOOQ CONVENTIONS (EVNICT-specific)
293
+
294
+ ### Record mapping
295
+ ```java
296
+ // ❌ SAI — Manual mapping tất cả fields
297
+ CustomerDTO dto = new CustomerDTO();
298
+ dto.setId(record.get(CUSTOMER.ID));
299
+ dto.setName(record.get(CUSTOMER.NAME));
300
+ // ... 20 dòng mapping
301
+
302
+ // ✅ ĐÚNG — Dùng into() hoặc fetchInto()
303
+ List<CustomerDTO> results = dsl.selectFrom(CUSTOMER)
304
+ .where(CUSTOMER.STATUS.eq("ACTIVE"))
305
+ .fetchInto(CustomerDTO.class);
306
+
307
+ // ✅ ĐÚNG — Custom mapping khi cần join
308
+ List<CustomerDetailDTO> results = dsl
309
+ .select(CUSTOMER.fields())
310
+ .select(DON_VI.TEN_DON_VI)
311
+ .from(CUSTOMER)
312
+ .join(DON_VI).on(CUSTOMER.DON_VI_ID.eq(DON_VI.ID))
313
+ .fetchInto(CustomerDetailDTO.class);
314
+ ```
315
+
316
+ ### Pagination pattern
317
+ ```java
318
+ // ✅ Oracle + JOOQ pagination
319
+ public Page<CustomerDTO> search(String keyword, int page, int size) {
320
+ Condition condition = DSL.trueCondition();
321
+ if (StringUtils.isNotBlank(keyword)) {
322
+ condition = condition.and(CUSTOMER.NAME.containsIgnoreCase(keyword));
323
+ }
324
+
325
+ int total = dsl.fetchCount(CUSTOMER, condition);
326
+ List<CustomerDTO> data = dsl.selectFrom(CUSTOMER)
327
+ .where(condition)
328
+ .orderBy(CUSTOMER.CREATED_DATE.desc())
329
+ .offset(page * size)
330
+ .limit(size)
331
+ .fetchInto(CustomerDTO.class);
332
+
333
+ return new Page<>(data, total, page, size);
334
+ }
335
+ ```
336
+
337
+ ### Transaction management
338
+ ```java
339
+ // ✅ ĐÚNG — @Transactional ở Service layer
340
+ @Service
341
+ public class OrderService {
342
+ @Transactional // Tại method level
343
+ public OrderDTO create(OrderDTO dto) {
344
+ // Nhiều repository calls trong 1 transaction
345
+ Order order = orderRepo.create(dto);
346
+ orderDetailRepo.createBatch(dto.getDetails(), order.getId());
347
+ inventoryRepo.deduct(dto.getDetails());
348
+ return mapToDTO(order);
349
+ }
350
+ }
351
+ ```
352
+
353
+ ---
354
+
355
+ ## 8. NAMING CONVENTIONS
356
+
357
+ ### Java
358
+ | Loại | Convention | Ví dụ |
359
+ |------|-----------|-------|
360
+ | Class | PascalCase | `CustomerService`, `OrderDTO` |
361
+ | Method | camelCase | `findByStatus()`, `createOrder()` |
362
+ | Variable | camelCase | `customerId`, `orderTotal` |
363
+ | Constant | UPPER_SNAKE | `MAX_FILE_SIZE`, `DEFAULT_PAGE_SIZE` |
364
+ | Package | lowercase | `com.evn.cmis.customer` |
365
+ | Table | UPPER_SNAKE | `CUSTOMER`, `DON_VI` |
366
+ | Column | UPPER_SNAKE | `CREATED_DATE`, `DON_VI_ID` |
367
+
368
+ ### File naming
369
+ | Loại | Pattern | Ví dụ |
370
+ |------|---------|-------|
371
+ | Controller | `{Module}Controller.java` | `CustomerController.java` |
372
+ | Service | `{Module}Service.java` | `CustomerService.java` |
373
+ | Repository | `{Module}Repository.java` | `CustomerRepository.java` |
374
+ | DTO | `{Module}DTO.java` | `CustomerDTO.java` |
375
+ | Migration | `V{date}_{seq}__{desc}.sql` | `V20260401_001__add_status.sql` |
376
+
377
+ ### Scan commands
378
+ ```bash
379
+ # Tìm class names không đúng convention
380
+ grep -rn "class [a-z]" --include="*.java" src/
381
+ # Tìm constants không UPPER_SNAKE
382
+ grep -rn "static final .* [a-z].*=" --include="*.java" src/
383
+ ```