guardian-framework 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.
- package/LICENSE +21 -0
- package/README.md +463 -0
- package/dist/cli.js +12958 -0
- package/dist/exports.js +12765 -0
- package/package.json +61 -0
- package/templates/languages/go-patterns.md +411 -0
- package/templates/languages/java-patterns.md +545 -0
- package/templates/languages/python-patterns.md +312 -0
- package/templates/languages/rust-patterns.md +232 -0
- package/templates/languages/typescript-patterns.md +313 -0
- package/templates/pi/INDEX.md +437 -0
- package/templates/pi/agent/AGENTS.md +166 -0
- package/templates/pi/agents/architecture-coordinator.md +40 -0
- package/templates/pi/agents/architecture-validator.md +36 -0
- package/templates/pi/agents/bootstrap-implementer.md +37 -0
- package/templates/pi/agents/issue-factory.md +31 -0
- package/templates/pi/agents/operations-validator.md +32 -0
- package/templates/pi/agents/security-validator.md +33 -0
- package/templates/pi/architecture/CHANGELOG.md +78 -0
- package/templates/pi/architecture/decisions/ADR-template.md +76 -0
- package/templates/pi/architecture/diagrams/system-overview.md +159 -0
- package/templates/pi/architecture/modules/module-template.md +152 -0
- package/templates/pi/context/checklists.md +91 -0
- package/templates/pi/context/domain-workflow.md +180 -0
- package/templates/pi/context/output-formats.md +135 -0
- package/templates/pi/context/patterns-base.md +47 -0
- package/templates/pi/context/patterns.md +38 -0
- package/templates/pi/context/project.md +88 -0
- package/templates/pi/domain/exploration.md +93 -0
- package/templates/pi/domain/ubiquitous-language.md +24 -0
- package/templates/pi/extensions/architect-lib/generators.ts +528 -0
- package/templates/pi/extensions/architect-lib/helpers.ts +344 -0
- package/templates/pi/extensions/architect-lib/types.ts +117 -0
- package/templates/pi/extensions/architect.ts +1425 -0
- package/templates/pi/extensions/ask-user-question.ts +579 -0
- package/templates/pi/extensions/bash-guard.ts +556 -0
- package/templates/pi/extensions/config-reload.ts +115 -0
- package/templates/pi/extensions/coordinator.ts +304 -0
- package/templates/pi/extensions/curator.ts +543 -0
- package/templates/pi/extensions/domain-explorer.ts +990 -0
- package/templates/pi/extensions/filechanges.ts +656 -0
- package/templates/pi/extensions/goal-loop.ts +717 -0
- package/templates/pi/extensions/hooks.ts +452 -0
- package/templates/pi/extensions/kanban.ts +547 -0
- package/templates/pi/extensions/pipeline.ts +1246 -0
- package/templates/pi/extensions/plan-mode.ts +209 -0
- package/templates/pi/extensions/project-scaffolder.ts +284 -0
- package/templates/pi/extensions/read-only-mode.ts +192 -0
- package/templates/pi/extensions/redaction.ts +128 -0
- package/templates/pi/extensions/session-persistence.ts +174 -0
- package/templates/pi/extensions/slash-commands.ts +229 -0
- package/templates/pi/extensions/snippets.ts +201 -0
- package/templates/pi/extensions/validation-runner.ts +100 -0
- package/templates/pi/github/agents/architecture-coordinator.agent.md +27 -0
- package/templates/pi/github/agents/architecture-validator.agent.md +20 -0
- package/templates/pi/github/agents/bootstrap-implementer.agent.md +20 -0
- package/templates/pi/github/agents/epic-planner.agent.md +70 -0
- package/templates/pi/github/agents/issue-factory.agent.md +20 -0
- package/templates/pi/github/agents/operations-validator.agent.md +20 -0
- package/templates/pi/github/agents/security-validator.agent.md +20 -0
- package/templates/pi/github/copilot/settings.json +16 -0
- package/templates/pi/github/copilot-instructions.md +155 -0
- package/templates/pi/github/instructions/architecture.instructions.md +71 -0
- package/templates/pi/github/instructions/validation.instructions.md +69 -0
- package/templates/pi/github/workflows/01-planning-workflow.md +75 -0
- package/templates/pi/github/workflows/02-issue-generation-workflow.md +69 -0
- package/templates/pi/github/workflows/03-implementation-workflow.md +80 -0
- package/templates/pi/github/workflows/04-validation-workflow.md +68 -0
- package/templates/pi/preflight_report.json +99 -0
- package/templates/pi/prompts/blueprint-update.md +262 -0
- package/templates/pi/prompts/blueprint-validate.md +218 -0
- package/templates/pi/prompts/bug-fix.md +50 -0
- package/templates/pi/prompts/ci-blueprint.md +131 -0
- package/templates/pi/prompts/context-refresh.md +258 -0
- package/templates/pi/prompts/epic-plan.md +356 -0
- package/templates/pi/prompts/epic-template.md +186 -0
- package/templates/pi/prompts/feature-development.md +131 -0
- package/templates/pi/prompts/git-issues.md +243 -0
- package/templates/pi/prompts/hotfix.md +71 -0
- package/templates/pi/prompts/issue-closeout.md +342 -0
- package/templates/pi/prompts/issue-draft.md +218 -0
- package/templates/pi/prompts/issue-implementation-series.md +173 -0
- package/templates/pi/prompts/issue-merge.md +372 -0
- package/templates/pi/prompts/issue-template-set.md +392 -0
- package/templates/pi/prompts/issue-template.md +207 -0
- package/templates/pi/prompts/pattern-extract.md +269 -0
- package/templates/pi/prompts/plan-to-issues.md +430 -0
- package/templates/pi/prompts/refactoring.md +82 -0
- package/templates/pi/prompts/scope-analyzer.md +254 -0
- package/templates/pi/prompts/sync-check.md +230 -0
- package/templates/pi/scripts/categorize-issues.sh +171 -0
- package/templates/pi/scripts/ci/check_architecture_conformance.sh +748 -0
- package/templates/pi/scripts/ci/check_planning_packet.py +228 -0
- package/templates/pi/scripts/ci/run_hardening_stages.sh +264 -0
- package/templates/pi/scripts/ci/run_preflight.sh +649 -0
- package/templates/pi/scripts/ci/run_stage.sh +193 -0
- package/templates/pi/scripts/ci/stage_docs_policy.sh +82 -0
- package/templates/pi/scripts/ci/stage_integration.sh +3 -0
- package/templates/pi/scripts/ci/stage_lint.sh +130 -0
- package/templates/pi/scripts/ci/stage_migration_verify.sh +3 -0
- package/templates/pi/scripts/ci/stage_package_build.sh +3 -0
- package/templates/pi/scripts/ci/stage_release_readiness.sh +3 -0
- package/templates/pi/scripts/ci/stage_remaining.sh +242 -0
- package/templates/pi/scripts/ci/stage_security.sh +3 -0
- package/templates/pi/scripts/ci/stage_static_analysis.sh +114 -0
- package/templates/pi/scripts/ci/stage_unit.sh +119 -0
- package/templates/pi/scripts/ci/validate_agent_output.py +181 -0
- package/templates/pi/scripts/ci/validate_agent_output.sh +243 -0
- package/templates/pi/scripts/create-feature-branch.sh +136 -0
- package/templates/pi/scripts/create-mr.sh +117 -0
- package/templates/pi/scripts/fetch-issues.sh +131 -0
- package/templates/pi/scripts/generate-architecture.sh +161 -0
- package/templates/pi/scripts/git/close-epic.sh +69 -0
- package/templates/pi/scripts/git/close-issue.sh +35 -0
- package/templates/pi/scripts/git/create-tracking-issue.sh +174 -0
- package/templates/pi/scripts/git/link-issue-to-epic.sh +51 -0
- package/templates/pi/scripts/git/update-tracking-issue.sh +66 -0
- package/templates/pi/scripts/languages/go/validate-architecture.sh +139 -0
- package/templates/pi/scripts/languages/go/validate-canonical.sh +129 -0
- package/templates/pi/scripts/languages/go/validate-ci.sh +132 -0
- package/templates/pi/scripts/languages/go/validate-integration.sh +156 -0
- package/templates/pi/scripts/languages/go/validate-operations.sh +146 -0
- package/templates/pi/scripts/languages/go/validate-security.sh +135 -0
- package/templates/pi/scripts/languages/go/validate-tests.sh +125 -0
- package/templates/pi/scripts/languages/java/validate-annotations.sh +185 -0
- package/templates/pi/scripts/languages/java/validate-architecture.sh +159 -0
- package/templates/pi/scripts/languages/java/validate-canonical.sh +114 -0
- package/templates/pi/scripts/languages/java/validate-ci.sh +269 -0
- package/templates/pi/scripts/languages/java/validate-integration.sh +149 -0
- package/templates/pi/scripts/languages/java/validate-operations.sh +142 -0
- package/templates/pi/scripts/languages/java/validate-security.sh +174 -0
- package/templates/pi/scripts/languages/java/validate-spring-architecture.sh +227 -0
- package/templates/pi/scripts/languages/java/validate-tests.sh +159 -0
- package/templates/pi/scripts/languages/python/validate-architecture.sh +99 -0
- package/templates/pi/scripts/languages/python/validate-canonical.sh +124 -0
- package/templates/pi/scripts/languages/python/validate-ci.sh +130 -0
- package/templates/pi/scripts/languages/python/validate-integration.sh +119 -0
- package/templates/pi/scripts/languages/python/validate-operations.sh +107 -0
- package/templates/pi/scripts/languages/python/validate-security.sh +68 -0
- package/templates/pi/scripts/languages/python/validate-tests.sh +102 -0
- package/templates/pi/scripts/languages/rust/validate-architecture.sh +188 -0
- package/templates/pi/scripts/languages/rust/validate-canonical.sh +146 -0
- package/templates/pi/scripts/languages/rust/validate-ci.sh +147 -0
- package/templates/pi/scripts/languages/rust/validate-integration.sh +149 -0
- package/templates/pi/scripts/languages/rust/validate-operations.sh +154 -0
- package/templates/pi/scripts/languages/rust/validate-security.sh +153 -0
- package/templates/pi/scripts/languages/rust/validate-tests.sh +150 -0
- package/templates/pi/scripts/languages/typescript/validate-architecture.sh +157 -0
- package/templates/pi/scripts/languages/typescript/validate-canonical.sh +135 -0
- package/templates/pi/scripts/languages/typescript/validate-ci.sh +244 -0
- package/templates/pi/scripts/languages/typescript/validate-integration.sh +166 -0
- package/templates/pi/scripts/languages/typescript/validate-operations.sh +162 -0
- package/templates/pi/scripts/languages/typescript/validate-security.sh +169 -0
- package/templates/pi/scripts/languages/typescript/validate-tests.sh +158 -0
- package/templates/pi/scripts/merge-mr.sh +95 -0
- package/templates/pi/scripts/mr-validation.sh +228 -0
- package/templates/pi/scripts/validate-architecture-readiness.sh +153 -0
- package/templates/pi/scripts/validate-architecture.sh +22 -0
- package/templates/pi/scripts/validate-canonical.sh +22 -0
- package/templates/pi/scripts/validate-ci.sh +37 -0
- package/templates/pi/scripts/validate-integration.sh +22 -0
- package/templates/pi/scripts/validate-operations.sh +22 -0
- package/templates/pi/scripts/validate-security.sh +22 -0
- package/templates/pi/scripts/validate-tests.sh +22 -0
- package/templates/pi/scripts/validate-ubiquitous-language.sh +291 -0
- package/templates/pi/scripts/validation-cache.sh +139 -0
- package/templates/pi/skills/agents/architecture-coordinator.md +178 -0
- package/templates/pi/skills/agents/architecture-generator.md +79 -0
- package/templates/pi/skills/agents/architecture-validator.md +74 -0
- package/templates/pi/skills/agents/ci-mr-validator.md +46 -0
- package/templates/pi/skills/agents/code-developer.md +76 -0
- package/templates/pi/skills/agents/commit.md +46 -0
- package/templates/pi/skills/agents/curator.md +71 -0
- package/templates/pi/skills/agents/debug.md +59 -0
- package/templates/pi/skills/agents/documentation-maintainer.md +36 -0
- package/templates/pi/skills/agents/goal-loop.md +125 -0
- package/templates/pi/skills/agents/hooks.md +122 -0
- package/templates/pi/skills/agents/integration-validator.md +35 -0
- package/templates/pi/skills/agents/issue-creator.md +75 -0
- package/templates/pi/skills/agents/issue-factory.md +143 -0
- package/templates/pi/skills/agents/kanban.md +85 -0
- package/templates/pi/skills/agents/land.md +59 -0
- package/templates/pi/skills/agents/operations-validator.md +43 -0
- package/templates/pi/skills/agents/pipeline.md +113 -0
- package/templates/pi/skills/agents/plan-mode.md +77 -0
- package/templates/pi/skills/agents/pull.md +40 -0
- package/templates/pi/skills/agents/push.md +41 -0
- package/templates/pi/skills/agents/security-validator.md +85 -0
- package/templates/pi/skills/agents/session-persistence.md +119 -0
- package/templates/pi/skills/agents/slash-commands.md +117 -0
- package/templates/pi/skills/agents/snippets.md +99 -0
- package/templates/pi/skills/agents/subagent-registry.md +78 -0
- package/templates/pi/skills/agents/test-validator.md +42 -0
- package/templates/pi/skills/validators/architecture-validator.md +43 -0
- package/templates/pi/skills/validators/ci-validator.md +27 -0
- package/templates/pi/skills/validators/context-compaction.md +73 -0
- package/templates/pi/skills/validators/integration-validator.md +38 -0
- package/templates/pi/skills/validators/model-registry.md +96 -0
- package/templates/pi/skills/validators/operations-validator.md +34 -0
- package/templates/pi/skills/validators/security-guards.md +72 -0
- package/templates/pi/skills/validators/security-validator.md +41 -0
- package/templates/pi/skills/validators/system-prompt-tiers.md +69 -0
- package/templates/pi/skills/validators/test-validator.md +33 -0
- package/templates/pi/types.ts +62 -0
- package/templates/pi/validators/README.md +93 -0
- package/templates/pi/validators/default.toml +209 -0
- package/templates/pi/validators/spring.toml +142 -0
- package/templates/pi/workpad.md +53 -0
- package/templates/project/java/Dockerfile +14 -0
- package/templates/project/java/README.md +40 -0
- package/templates/project/java/pom.xml +132 -0
- package/templates/project/typescript/Dockerfile +15 -0
- package/templates/project/typescript/README.md +29 -0
- package/templates/project/typescript/package.json +23 -0
- package/templates/project/typescript/tsconfig.json +21 -0
- package/templates/workflow.md +123 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# Java & Spring Boot Code Patterns
|
|
2
|
+
|
|
3
|
+
> **Purpose:** Reusable Java and Spring Boot patterns for Guardian projects.
|
|
4
|
+
> **Source:** Guardian Framework — Java/Spring Support Module
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Clean Architecture Layering
|
|
9
|
+
|
|
10
|
+
```java
|
|
11
|
+
// Package structure with dependency direction:
|
|
12
|
+
//
|
|
13
|
+
// interfaces/ (controllers, DTOs, web config)
|
|
14
|
+
// ↓
|
|
15
|
+
// application/ (use cases, ports, DTOs)
|
|
16
|
+
// ↓
|
|
17
|
+
// domain/ (entities, value objects, domain services)
|
|
18
|
+
// ↑
|
|
19
|
+
// infrastructure/ (repositories, JPA, external APIs, messaging)
|
|
20
|
+
//
|
|
21
|
+
// Rules:
|
|
22
|
+
// domain/ → imports NOTHING from outside domain
|
|
23
|
+
// application/ → imports ONLY domain/
|
|
24
|
+
// infrastructure/ → imports domain/ + application/
|
|
25
|
+
// interfaces/ → imports application/ only
|
|
26
|
+
|
|
27
|
+
// Example package structure
|
|
28
|
+
// com.myproject.domain.model/
|
|
29
|
+
// com.myproject.domain.service/
|
|
30
|
+
// com.myproject.application.port/
|
|
31
|
+
// com.myproject.application.usecase/
|
|
32
|
+
// com.myproject.infrastructure.persistence/
|
|
33
|
+
// com.myproject.infrastructure.messaging/
|
|
34
|
+
// com.myproject.interfaces.web/
|
|
35
|
+
// com.myproject.interfaces.config/
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Spring Annotations Reference
|
|
41
|
+
|
|
42
|
+
```java
|
|
43
|
+
// === STEREOTYPES ===
|
|
44
|
+
@Component // Generic component (auto-detected via scanning)
|
|
45
|
+
@Service // Business logic in application/domain layer
|
|
46
|
+
@Repository // Data access (Spring adds PersistenceExceptionTranslation)
|
|
47
|
+
@Controller // MVC controller (returns view name)
|
|
48
|
+
@RestController // REST controller (returns response body)
|
|
49
|
+
|
|
50
|
+
// === DEPENDENCY INJECTION ===
|
|
51
|
+
@Autowired // Injects bean (use on constructors, not fields)
|
|
52
|
+
@Qualifier("name") // Disambiguate when multiple beans of same type
|
|
53
|
+
@Primary // Prefer this bean when multiple candidates
|
|
54
|
+
@Scope("prototype") // Non-singleton scope (default is singleton)
|
|
55
|
+
|
|
56
|
+
// === WEB LAYER ===
|
|
57
|
+
@RequestMapping("/api/users") // Class-level mapping
|
|
58
|
+
@GetMapping("/{id}") // GET
|
|
59
|
+
@PostMapping // POST
|
|
60
|
+
@PutMapping("/{id}") // PUT
|
|
61
|
+
@DeleteMapping("/{id}") // DELETE
|
|
62
|
+
@PatchMapping("/{id}") // PATCH
|
|
63
|
+
@PathVariable("id") // Path variable
|
|
64
|
+
@RequestParam("page") // Query parameter
|
|
65
|
+
@RequestBody // Request body deserialization
|
|
66
|
+
@ResponseStatus(HttpStatus.CREATED) // Response status override
|
|
67
|
+
|
|
68
|
+
// === DATA ===
|
|
69
|
+
@Transactional // Transaction boundary (service layer)
|
|
70
|
+
@Transactional(readOnly = true) // Read-only optimization
|
|
71
|
+
@Query("SELECT u FROM User u WHERE u.email = :email")
|
|
72
|
+
@Modifying // Indicates an UPDATE/DELETE @Query
|
|
73
|
+
@Entity // JPA entity
|
|
74
|
+
@Table(name = "users") // Table mapping
|
|
75
|
+
@Column(name = "email", nullable = false, unique = true)
|
|
76
|
+
@Id // Primary key
|
|
77
|
+
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
78
|
+
@ManyToOne(fetch = FetchType.LAZY)
|
|
79
|
+
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
|
|
80
|
+
@Enumerated(EnumType.STRING)
|
|
81
|
+
|
|
82
|
+
// === SECURITY ===
|
|
83
|
+
@PreAuthorize("hasRole('ADMIN')")
|
|
84
|
+
@PostAuthorize("returnObject.owner == authentication.name")
|
|
85
|
+
@Secured("ROLE_ADMIN")
|
|
86
|
+
@RolesAllowed("ADMIN")
|
|
87
|
+
|
|
88
|
+
// === OBSERVABILITY ===
|
|
89
|
+
@Cacheable("users")
|
|
90
|
+
@CacheEvict(value = "users", allEntries = true)
|
|
91
|
+
@Scheduled(cron = "0 0 * * * *")
|
|
92
|
+
@Async
|
|
93
|
+
@EventListener
|
|
94
|
+
@EventListener(condition = "#event.success")
|
|
95
|
+
|
|
96
|
+
// === LIFECYCLE ===
|
|
97
|
+
@PostConstruct // Init callback (ONLY in service/config/component packages)
|
|
98
|
+
@PreDestroy // Cleanup callback
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Dependency Injection Patterns
|
|
104
|
+
|
|
105
|
+
```java
|
|
106
|
+
// ✅ PREFERRED: Constructor injection (immutable, testable, required deps)
|
|
107
|
+
@Service
|
|
108
|
+
public class UserService {
|
|
109
|
+
private final UserRepository userRepository;
|
|
110
|
+
private final NotificationService notificationService;
|
|
111
|
+
|
|
112
|
+
public UserService(UserRepository userRepository,
|
|
113
|
+
NotificationService notificationService) {
|
|
114
|
+
this.userRepository = userRepository;
|
|
115
|
+
this.notificationService = notificationService;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ✅ ACCEPTABLE: Setter injection (optional dependencies)
|
|
120
|
+
@Service
|
|
121
|
+
public class AuditService {
|
|
122
|
+
private MetricsCollector metricsCollector;
|
|
123
|
+
|
|
124
|
+
@Autowired(required = false)
|
|
125
|
+
public void setMetricsCollector(MetricsCollector metricsCollector) {
|
|
126
|
+
this.metricsCollector = metricsCollector;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ❌ NEVER: Field injection (hard to test, breaks immutability, hidden deps)
|
|
131
|
+
@Service
|
|
132
|
+
public class BadService {
|
|
133
|
+
@Autowired // BAD
|
|
134
|
+
private UserRepository userRepository;
|
|
135
|
+
|
|
136
|
+
@Autowired // BAD
|
|
137
|
+
private NotificationService notificationService;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## JPA Patterns
|
|
144
|
+
|
|
145
|
+
```java
|
|
146
|
+
// === ENTITY DESIGN ===
|
|
147
|
+
@Entity
|
|
148
|
+
@Table(name = "orders")
|
|
149
|
+
public class Order {
|
|
150
|
+
@Id
|
|
151
|
+
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
152
|
+
private Long id;
|
|
153
|
+
|
|
154
|
+
@Column(nullable = false)
|
|
155
|
+
private String orderNumber;
|
|
156
|
+
|
|
157
|
+
@Enumerated(EnumType.STRING)
|
|
158
|
+
private OrderStatus status;
|
|
159
|
+
|
|
160
|
+
@ManyToOne(fetch = FetchType.LAZY)
|
|
161
|
+
@JoinColumn(name = "customer_id")
|
|
162
|
+
private Customer customer;
|
|
163
|
+
|
|
164
|
+
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
|
|
165
|
+
private List<OrderItem> items = new ArrayList<>();
|
|
166
|
+
|
|
167
|
+
@Version
|
|
168
|
+
private Long version; // Optimistic locking
|
|
169
|
+
|
|
170
|
+
@CreatedDate
|
|
171
|
+
private LocalDateTime createdAt;
|
|
172
|
+
|
|
173
|
+
@LastModifiedDate
|
|
174
|
+
private LocalDateTime updatedAt;
|
|
175
|
+
|
|
176
|
+
// Protected default constructor for JPA
|
|
177
|
+
protected Order() {}
|
|
178
|
+
|
|
179
|
+
// Business-meaningful constructor
|
|
180
|
+
public Order(Customer customer, List<OrderItem> items) {
|
|
181
|
+
this.customer = customer;
|
|
182
|
+
this.items = new ArrayList<>(items);
|
|
183
|
+
this.status = OrderStatus.PENDING;
|
|
184
|
+
this.orderNumber = generateOrderNumber();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// === REPOSITORY PATTERNS ===
|
|
189
|
+
public interface OrderRepository extends JpaRepository<Order, Long> {
|
|
190
|
+
// Derived query
|
|
191
|
+
List<Order> findByCustomerIdAndStatus(Long customerId, OrderStatus status);
|
|
192
|
+
|
|
193
|
+
// JPQL query
|
|
194
|
+
@Query("SELECT o FROM Order o WHERE o.orderNumber = :orderNumber")
|
|
195
|
+
Optional<Order> findByOrderNumber(@Param("orderNumber") String orderNumber);
|
|
196
|
+
|
|
197
|
+
// Native query
|
|
198
|
+
@Query(value = "SELECT * FROM orders WHERE total > :amount", nativeQuery = true)
|
|
199
|
+
List<Order> findOrdersAboveAmount(@Param("amount") BigDecimal amount);
|
|
200
|
+
|
|
201
|
+
// Projection
|
|
202
|
+
@Query("SELECT new com.myproject.application.dto.OrderSummary(o.id, o.orderNumber, o.status) FROM Order o")
|
|
203
|
+
List<OrderSummary> findAllSummaries();
|
|
204
|
+
|
|
205
|
+
// Pagination
|
|
206
|
+
Page<Order> findByCustomerId(Long customerId, Pageable pageable);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// === AUDITING ===
|
|
210
|
+
@EntityListeners(AuditingEntityListener.class)
|
|
211
|
+
@Entity
|
|
212
|
+
@Table(name = "orders")
|
|
213
|
+
public class AuditableOrder {
|
|
214
|
+
@CreatedBy
|
|
215
|
+
private String createdBy;
|
|
216
|
+
|
|
217
|
+
@LastModifiedBy
|
|
218
|
+
private String lastModifiedBy;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Testing Patterns
|
|
225
|
+
|
|
226
|
+
```java
|
|
227
|
+
// === FULL CONTEXT TEST ===
|
|
228
|
+
@SpringBootTest
|
|
229
|
+
class UserServiceTest {
|
|
230
|
+
@Autowired
|
|
231
|
+
private UserService userService;
|
|
232
|
+
|
|
233
|
+
@Test
|
|
234
|
+
void shouldCreateUser() {
|
|
235
|
+
// Given
|
|
236
|
+
CreateUserCommand command = new CreateUserCommand("john@example.com", "John");
|
|
237
|
+
|
|
238
|
+
// When
|
|
239
|
+
User user = userService.createUser(command);
|
|
240
|
+
|
|
241
|
+
// Then
|
|
242
|
+
assertThat(user.getEmail()).isEqualTo("john@example.com");
|
|
243
|
+
assertThat(user.getName()).isEqualTo("John");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// === WEB LAYER TEST (sliced) ===
|
|
248
|
+
@WebMvcTest(UserController.class)
|
|
249
|
+
class UserControllerTest {
|
|
250
|
+
@Autowired
|
|
251
|
+
private MockMvc mockMvc;
|
|
252
|
+
|
|
253
|
+
@MockBean
|
|
254
|
+
private UserService userService;
|
|
255
|
+
|
|
256
|
+
@Test
|
|
257
|
+
void shouldReturnUser() throws Exception {
|
|
258
|
+
// Given
|
|
259
|
+
given(userService.findById(1L)).willReturn(new UserDTO(1L, "John"));
|
|
260
|
+
|
|
261
|
+
// When/Then
|
|
262
|
+
mockMvc.perform(get("/api/users/1"))
|
|
263
|
+
.andExpect(status().isOk())
|
|
264
|
+
.andExpect(jsonPath("$.name").value("John"));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// === DATA LAYER TEST (sliced) ===
|
|
269
|
+
@DataJpaTest
|
|
270
|
+
class OrderRepositoryTest {
|
|
271
|
+
@Autowired
|
|
272
|
+
private TestEntityManager entityManager;
|
|
273
|
+
|
|
274
|
+
@Autowired
|
|
275
|
+
private OrderRepository orderRepository;
|
|
276
|
+
|
|
277
|
+
@Test
|
|
278
|
+
void shouldFindByCustomerId() {
|
|
279
|
+
// Given
|
|
280
|
+
Customer customer = new Customer("John");
|
|
281
|
+
entityManager.persist(customer);
|
|
282
|
+
Order order = new Order(customer, List.of());
|
|
283
|
+
entityManager.persist(order);
|
|
284
|
+
entityManager.flush();
|
|
285
|
+
|
|
286
|
+
// When
|
|
287
|
+
List<Order> found = orderRepository.findByCustomerId(customer.getId());
|
|
288
|
+
|
|
289
|
+
// Then
|
|
290
|
+
assertThat(found).hasSize(1);
|
|
291
|
+
assertThat(found.get(0).getCustomer().getId()).isEqualTo(customer.getId());
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Error Handling
|
|
299
|
+
|
|
300
|
+
```java
|
|
301
|
+
// === GLOBAL EXCEPTION HANDLER ===
|
|
302
|
+
@RestControllerAdvice
|
|
303
|
+
public class GlobalExceptionHandler {
|
|
304
|
+
|
|
305
|
+
@ExceptionHandler(ResourceNotFoundException.class)
|
|
306
|
+
@ResponseStatus(HttpStatus.NOT_FOUND)
|
|
307
|
+
public ProblemDetail handleNotFound(ResourceNotFoundException ex) {
|
|
308
|
+
return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
@ExceptionHandler(ValidationException.class)
|
|
312
|
+
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
|
313
|
+
public ProblemDetail handleValidation(ValidationException ex) {
|
|
314
|
+
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
|
|
315
|
+
HttpStatus.BAD_REQUEST, ex.getMessage());
|
|
316
|
+
problem.setTitle("Validation Error");
|
|
317
|
+
problem.setProperty("errors", ex.getErrors());
|
|
318
|
+
return problem;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@ExceptionHandler(AccessDeniedException.class)
|
|
322
|
+
@ResponseStatus(HttpStatus.FORBIDDEN)
|
|
323
|
+
public ProblemDetail handleAccessDenied(AccessDeniedException ex) {
|
|
324
|
+
return ProblemDetail.forStatusAndDetail(
|
|
325
|
+
HttpStatus.FORBIDDEN, "Access denied");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@ExceptionHandler(Exception.class)
|
|
329
|
+
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
|
330
|
+
public ProblemDetail handleGeneric(Exception ex) {
|
|
331
|
+
// Log full exception, return sanitized response
|
|
332
|
+
log.error("Unexpected error", ex);
|
|
333
|
+
return ProblemDetail.forStatusAndDetail(
|
|
334
|
+
HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error");
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// === DOMAIN RESULTS ===
|
|
339
|
+
// For operations that can fail without exceptions
|
|
340
|
+
public sealed interface Result<T> {
|
|
341
|
+
record Success<T>(T value) implements Result<T> {}
|
|
342
|
+
record Failure<T>(String error, Map<String, Object> details) implements Result<T> {}
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Configuration
|
|
349
|
+
|
|
350
|
+
```java
|
|
351
|
+
// === TYPE-SAFE CONFIGURATION ===
|
|
352
|
+
@ConfigurationProperties(prefix = "app.notification")
|
|
353
|
+
public class NotificationProperties {
|
|
354
|
+
private String fromEmail;
|
|
355
|
+
private List<String> adminEmails = new ArrayList<>();
|
|
356
|
+
private Duration timeout = Duration.ofSeconds(30);
|
|
357
|
+
private boolean enabled = true;
|
|
358
|
+
|
|
359
|
+
// getters and setters
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// === PROFILE-SPECIFIC CONFIG ===
|
|
363
|
+
// application-dev.yml
|
|
364
|
+
// application-prod.yml
|
|
365
|
+
// application-test.yml
|
|
366
|
+
|
|
367
|
+
// Active profile
|
|
368
|
+
@Profile("dev")
|
|
369
|
+
@Component
|
|
370
|
+
public class DevDataInitializer implements CommandLineRunner { ... }
|
|
371
|
+
|
|
372
|
+
// === CONDITIONAL BEANS ===
|
|
373
|
+
@ConditionalOnProperty(name = "app.feature.notifications", havingValue = "true")
|
|
374
|
+
@ConditionalOnClass(name = "com.amazonaws.services.sqs.AmazonSQS")
|
|
375
|
+
@ConditionalOnMissingBean(NotificationService.class)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Transaction Management
|
|
381
|
+
|
|
382
|
+
```java
|
|
383
|
+
@Service
|
|
384
|
+
public class OrderService {
|
|
385
|
+
|
|
386
|
+
// Required by default — joins existing or creates new
|
|
387
|
+
@Transactional
|
|
388
|
+
public Order createOrder(CreateOrderCommand command) {
|
|
389
|
+
Order order = new Order(command.customer(), mapItems(command.items()));
|
|
390
|
+
return orderRepository.save(order);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Read-only optimization — hints JPA to skip dirty checking
|
|
394
|
+
@Transactional(readOnly = true)
|
|
395
|
+
public Optional<Order> findById(Long id) {
|
|
396
|
+
return orderRepository.findById(id);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Explicit rollback
|
|
400
|
+
@Transactional(rollbackFor = InsufficientBalanceException.class)
|
|
401
|
+
public void processPayment(Long orderId, BigDecimal amount) {
|
|
402
|
+
// throws InsufficientBalanceException → rollback
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Isolation level
|
|
406
|
+
@Transactional(isolation = Isolation.REPEATABLE_READ)
|
|
407
|
+
public BigDecimal calculateDailyRevenue() { ... }
|
|
408
|
+
|
|
409
|
+
// Propagation control
|
|
410
|
+
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
|
411
|
+
public void logAuditTrail(AuditEntry entry) {
|
|
412
|
+
// Always runs in a new transaction
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Anti-Patterns (NEVER DO)
|
|
420
|
+
|
|
421
|
+
```java
|
|
422
|
+
// ❌ Field injection
|
|
423
|
+
@Service
|
|
424
|
+
public class BadService {
|
|
425
|
+
@Autowired private UserRepository userRepository; // BAD
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ❌ @PostConstruct in controllers
|
|
429
|
+
@RestController
|
|
430
|
+
public class BadController {
|
|
431
|
+
@PostConstruct // BAD — lifecycle hooks don't belong in web layer
|
|
432
|
+
public void init() { ... }
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ❌ Domain layer importing infrastructure
|
|
436
|
+
// package com.myproject.domain.model;
|
|
437
|
+
// import com.myproject.infrastructure.persistence.JpaUtils; // BAD
|
|
438
|
+
|
|
439
|
+
// ❌ Direct repository access from web layer
|
|
440
|
+
@RestController
|
|
441
|
+
public class BadController {
|
|
442
|
+
@Autowired private UserRepository userRepository; // BAD — use service layer
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// ❌ Unbounded @RequestMapping
|
|
446
|
+
@RequestMapping // BAD — always specify path
|
|
447
|
+
public class VagueController { ... }
|
|
448
|
+
|
|
449
|
+
// ❌ Using RuntimeException directly
|
|
450
|
+
throw new RuntimeException("something failed"); // BAD — use specific exceptions
|
|
451
|
+
|
|
452
|
+
// ❌ Ignoring @Transactional on service methods
|
|
453
|
+
@Service
|
|
454
|
+
public class OrderService {
|
|
455
|
+
public void createOrder(...) { // BAD — no @Transactional
|
|
456
|
+
// each JPA operation runs in its own transaction
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// ❌ Eager fetching in entity relationships
|
|
461
|
+
@ManyToOne(fetch = FetchType.EAGER) // BAD — always use LAZY
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Build Commands
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
# Maven
|
|
470
|
+
mvn clean compile -q # Build
|
|
471
|
+
mvn test -q # Test
|
|
472
|
+
mvn checkstyle:check -q # Lint
|
|
473
|
+
mvn spotless:apply # Format
|
|
474
|
+
mvn spotless:check # Format check
|
|
475
|
+
mvn dependency-check:check # Security audit
|
|
476
|
+
mvn verify # Full verification
|
|
477
|
+
|
|
478
|
+
# Gradle
|
|
479
|
+
gradle build -q # Build
|
|
480
|
+
gradle test -q # Test
|
|
481
|
+
gradle checkstyleMain -q # Lint
|
|
482
|
+
gradle spotlessApply # Format
|
|
483
|
+
gradle spotlessCheck # Format check
|
|
484
|
+
gradle dependencyCheck # Security audit
|
|
485
|
+
gradle check # Full verification
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## Dependencies
|
|
491
|
+
|
|
492
|
+
```xml
|
|
493
|
+
<!-- Maven — spring-boot-starter-parent -->
|
|
494
|
+
<parent>
|
|
495
|
+
<groupId>org.springframework.boot</groupId>
|
|
496
|
+
<artifactId>spring-boot-starter-parent</artifactId>
|
|
497
|
+
<version>3.2.0</version>
|
|
498
|
+
</parent>
|
|
499
|
+
|
|
500
|
+
<dependencies>
|
|
501
|
+
<!-- Web -->
|
|
502
|
+
<dependency>
|
|
503
|
+
<groupId>org.springframework.boot</groupId>
|
|
504
|
+
<artifactId>spring-boot-starter-web</artifactId>
|
|
505
|
+
</dependency>
|
|
506
|
+
|
|
507
|
+
<!-- JPA -->
|
|
508
|
+
<dependency>
|
|
509
|
+
<groupId>org.springframework.boot</groupId>
|
|
510
|
+
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
|
511
|
+
</dependency>
|
|
512
|
+
|
|
513
|
+
<!-- Security -->
|
|
514
|
+
<dependency>
|
|
515
|
+
<groupId>org.springframework.boot</groupId>
|
|
516
|
+
<artifactId>spring-boot-starter-security</artifactId>
|
|
517
|
+
</dependency>
|
|
518
|
+
|
|
519
|
+
<!-- Validation -->
|
|
520
|
+
<dependency>
|
|
521
|
+
<groupId>org.springframework.boot</groupId>
|
|
522
|
+
<artifactId>spring-boot-starter-validation</artifactId>
|
|
523
|
+
</dependency>
|
|
524
|
+
|
|
525
|
+
<!-- Database -->
|
|
526
|
+
<dependency>
|
|
527
|
+
<groupId>org.postgresql</groupId>
|
|
528
|
+
<artifactId>postgresql</artifactId>
|
|
529
|
+
<scope>runtime</scope>
|
|
530
|
+
</dependency>
|
|
531
|
+
|
|
532
|
+
<!-- Test -->
|
|
533
|
+
<dependency>
|
|
534
|
+
<groupId>org.springframework.boot</groupId>
|
|
535
|
+
<artifactId>spring-boot-starter-test</artifactId>
|
|
536
|
+
<scope>test</scope>
|
|
537
|
+
</dependency>
|
|
538
|
+
|
|
539
|
+
<!-- Observability -->
|
|
540
|
+
<dependency>
|
|
541
|
+
<groupId>io.micrometer</groupId>
|
|
542
|
+
<artifactId>micrometer-tracing-bridge-brave</artifactId>
|
|
543
|
+
</dependency>
|
|
544
|
+
</dependencies>
|
|
545
|
+
```
|