agentic-team-templates 0.13.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +6 -1
  2. package/package.json +1 -1
  3. package/src/index.js +91 -13
  4. package/src/index.test.js +95 -1
  5. package/templates/cpp-expert/.cursorrules/concurrency.md +211 -0
  6. package/templates/cpp-expert/.cursorrules/error-handling.md +170 -0
  7. package/templates/cpp-expert/.cursorrules/memory-and-ownership.md +220 -0
  8. package/templates/cpp-expert/.cursorrules/modern-cpp.md +211 -0
  9. package/templates/cpp-expert/.cursorrules/overview.md +87 -0
  10. package/templates/cpp-expert/.cursorrules/performance.md +223 -0
  11. package/templates/cpp-expert/.cursorrules/testing.md +230 -0
  12. package/templates/cpp-expert/.cursorrules/tooling.md +312 -0
  13. package/templates/cpp-expert/CLAUDE.md +242 -0
  14. package/templates/csharp-expert/.cursorrules/aspnet-core.md +311 -0
  15. package/templates/csharp-expert/.cursorrules/async-patterns.md +206 -0
  16. package/templates/csharp-expert/.cursorrules/dependency-injection.md +206 -0
  17. package/templates/csharp-expert/.cursorrules/error-handling.md +235 -0
  18. package/templates/csharp-expert/.cursorrules/language-features.md +204 -0
  19. package/templates/csharp-expert/.cursorrules/overview.md +92 -0
  20. package/templates/csharp-expert/.cursorrules/performance.md +251 -0
  21. package/templates/csharp-expert/.cursorrules/testing.md +282 -0
  22. package/templates/csharp-expert/.cursorrules/tooling.md +254 -0
  23. package/templates/csharp-expert/CLAUDE.md +360 -0
  24. package/templates/java-expert/.cursorrules/concurrency.md +209 -0
  25. package/templates/java-expert/.cursorrules/error-handling.md +205 -0
  26. package/templates/java-expert/.cursorrules/modern-java.md +216 -0
  27. package/templates/java-expert/.cursorrules/overview.md +81 -0
  28. package/templates/java-expert/.cursorrules/performance.md +239 -0
  29. package/templates/java-expert/.cursorrules/persistence.md +262 -0
  30. package/templates/java-expert/.cursorrules/spring-boot.md +262 -0
  31. package/templates/java-expert/.cursorrules/testing.md +272 -0
  32. package/templates/java-expert/.cursorrules/tooling.md +301 -0
  33. package/templates/java-expert/CLAUDE.md +325 -0
  34. package/templates/javascript-expert/.cursorrules/overview.md +5 -3
  35. package/templates/javascript-expert/.cursorrules/typescript-deep-dive.md +348 -0
  36. package/templates/javascript-expert/CLAUDE.md +34 -3
  37. package/templates/kotlin-expert/.cursorrules/coroutines.md +237 -0
  38. package/templates/kotlin-expert/.cursorrules/error-handling.md +149 -0
  39. package/templates/kotlin-expert/.cursorrules/frameworks.md +227 -0
  40. package/templates/kotlin-expert/.cursorrules/language-features.md +231 -0
  41. package/templates/kotlin-expert/.cursorrules/overview.md +77 -0
  42. package/templates/kotlin-expert/.cursorrules/performance.md +185 -0
  43. package/templates/kotlin-expert/.cursorrules/testing.md +213 -0
  44. package/templates/kotlin-expert/.cursorrules/tooling.md +258 -0
  45. package/templates/kotlin-expert/CLAUDE.md +276 -0
  46. package/templates/swift-expert/.cursorrules/concurrency.md +230 -0
  47. package/templates/swift-expert/.cursorrules/error-handling.md +213 -0
  48. package/templates/swift-expert/.cursorrules/language-features.md +246 -0
  49. package/templates/swift-expert/.cursorrules/overview.md +88 -0
  50. package/templates/swift-expert/.cursorrules/performance.md +260 -0
  51. package/templates/swift-expert/.cursorrules/swiftui.md +260 -0
  52. package/templates/swift-expert/.cursorrules/testing.md +286 -0
  53. package/templates/swift-expert/.cursorrules/tooling.md +285 -0
  54. package/templates/swift-expert/CLAUDE.md +275 -0
@@ -0,0 +1,81 @@
1
+ # Java Expert Overview
2
+
3
+ Principal-level Java engineering. Deep JVM knowledge, modern language features, and production-grade patterns.
4
+
5
+ ## Scope
6
+
7
+ This guide applies to:
8
+ - Web services and APIs (Spring Boot, Quarkus, Micronaut)
9
+ - Microservices and distributed systems
10
+ - Event-driven architectures (Kafka, RabbitMQ)
11
+ - Batch processing and data pipelines
12
+ - Libraries and Maven/Gradle artifacts
13
+ - Cloud-native applications (Kubernetes, GraalVM native images)
14
+
15
+ ## Core Philosophy
16
+
17
+ Java's strength is its ecosystem maturity and runtime reliability. The best Java code is clear, testable, and boring.
18
+
19
+ - **Readability over cleverness.** A junior engineer should understand your code without a tutorial.
20
+ - **Immutability by default.** Records, `final` fields, unmodifiable collections. Mutation is the exception.
21
+ - **Composition over inheritance.** Interfaces, delegation, and dependency injection — not deep class hierarchies.
22
+ - **The JVM is your ally.** Understand garbage collection, JIT compilation, and memory model — don't fight them.
23
+ - **Fail fast, fail loud.** Validate at boundaries, throw meaningful exceptions, never swallow errors.
24
+ - **If you don't know, say so.** Admitting uncertainty is professional. Guessing at JVM behavior you haven't verified is not.
25
+
26
+ ## Key Principles
27
+
28
+ 1. **Modern Java Is Required** — Java 21+ features: records, sealed classes, pattern matching, virtual threads
29
+ 2. **Null Is a Bug** — Use `Optional` for return types, `@Nullable`/`@NonNull` annotations, and validation at boundaries
30
+ 3. **Dependency Injection Is the Architecture** — Constructor injection, interface segregation, Spring's application context
31
+ 4. **Tests Are Documentation** — Descriptive names, Arrange-Act-Assert, behavior over implementation
32
+ 5. **Observability Is Not Optional** — Structured logging, metrics, distributed tracing from day one
33
+
34
+ ## Project Structure
35
+
36
+ ```
37
+ project/
38
+ ├── src/main/java/com/example/myapp/
39
+ │ ├── Application.java # Entry point
40
+ │ ├── config/ # Configuration classes
41
+ │ ├── domain/ # Core domain (no framework deps)
42
+ │ │ ├── model/ # Entities, value objects, records
43
+ │ │ ├── service/ # Domain services
44
+ │ │ └── event/ # Domain events
45
+ │ ├── application/ # Use cases, orchestration
46
+ │ │ ├── command/ # Write operations
47
+ │ │ ├── query/ # Read operations
48
+ │ │ └── port/ # Interfaces (driven/driving)
49
+ │ ├── infrastructure/ # External concerns
50
+ │ │ ├── persistence/ # JPA repositories, entity mappings
51
+ │ │ ├── messaging/ # Kafka/RabbitMQ producers/consumers
52
+ │ │ └── client/ # HTTP clients, external APIs
53
+ │ └── api/ # REST controllers, DTOs
54
+ │ ├── controller/
55
+ │ ├── dto/
56
+ │ └── exception/ # Exception handlers
57
+ ├── src/main/resources/
58
+ │ ├── application.yml
59
+ │ └── db/migration/ # Flyway/Liquibase migrations
60
+ ├── src/test/java/com/example/myapp/
61
+ │ ├── unit/
62
+ │ ├── integration/
63
+ │ └── architecture/
64
+ ├── pom.xml or build.gradle.kts
65
+ └── Dockerfile
66
+ ```
67
+
68
+ ## Definition of Done
69
+
70
+ A Java feature is complete when:
71
+
72
+ - [ ] Code compiles with zero warnings (`-Xlint:all -Werror`)
73
+ - [ ] All tests pass (`mvn verify` or `gradle check`)
74
+ - [ ] No SpotBugs/ErrorProne findings
75
+ - [ ] No SonarQube code smells or security hotspots
76
+ - [ ] Null safety enforced (no raw `null` returns from public APIs)
77
+ - [ ] Javadoc on all public classes and methods
78
+ - [ ] Error paths are tested
79
+ - [ ] Thread safety verified for shared mutable state
80
+ - [ ] No `TODO` without an associated issue
81
+ - [ ] Code reviewed and approved
@@ -0,0 +1,239 @@
1
+ # Java Performance
2
+
3
+ The JVM is a world-class runtime. Understand it, don't fight it. Measure first.
4
+
5
+ ## Profile Before Optimizing
6
+
7
+ ```bash
8
+ # JFR (Java Flight Recorder) — production-safe profiling
9
+ java -XX:StartFlightRecording=filename=recording.jfr,duration=60s -jar app.jar
10
+
11
+ # Async-profiler — low-overhead sampling
12
+ ./asprof -d 30 -f profile.html <pid>
13
+
14
+ # JVM flags for diagnostics
15
+ -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xlog:gc*:file=gc.log
16
+ ```
17
+
18
+ ### Diagnostic Tools
19
+
20
+ | Tool | Purpose |
21
+ |------|---------|
22
+ | JFR + JMC | Production profiling (CPU, memory, I/O, locks) |
23
+ | async-profiler | Low-overhead CPU/allocation profiling |
24
+ | JMH | Micro-benchmarks with statistical rigor |
25
+ | jcmd | Runtime diagnostics (heap dump, thread dump) |
26
+ | VisualVM | Real-time monitoring |
27
+
28
+ ## JMH Benchmarks
29
+
30
+ ```java
31
+ @BenchmarkMode(Mode.AverageTime)
32
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
33
+ @State(Scope.Benchmark)
34
+ @Warmup(iterations = 5, time = 1)
35
+ @Measurement(iterations = 5, time = 1)
36
+ public class StringConcatBenchmark {
37
+
38
+ private List<String> items;
39
+
40
+ @Setup
41
+ public void setup() {
42
+ items = IntStream.range(0, 1000)
43
+ .mapToObj(Integer::toString)
44
+ .toList();
45
+ }
46
+
47
+ @Benchmark
48
+ public String concatenation() {
49
+ var result = "";
50
+ for (var item : items) result += item;
51
+ return result;
52
+ }
53
+
54
+ @Benchmark
55
+ public String stringBuilder() {
56
+ var sb = new StringBuilder();
57
+ for (var item : items) sb.append(item);
58
+ return sb.toString();
59
+ }
60
+
61
+ @Benchmark
62
+ public String stringJoin() {
63
+ return String.join("", items);
64
+ }
65
+ }
66
+ ```
67
+
68
+ ## GC Tuning
69
+
70
+ ```bash
71
+ # G1GC (default, good for most workloads)
72
+ -XX:+UseG1GC
73
+ -XX:MaxGCPauseMillis=200
74
+
75
+ # ZGC (ultra-low latency, Java 21+ production-ready)
76
+ -XX:+UseZGC
77
+ -XX:+ZGenerational # Generational ZGC (Java 21+)
78
+
79
+ # Heap sizing
80
+ -Xms2g -Xmx2g # Fixed heap size (no resize pauses)
81
+ -XX:+AlwaysPreTouch # Touch memory pages at startup
82
+ ```
83
+
84
+ ### GC Rules
85
+
86
+ - Start with G1GC defaults — they're good
87
+ - Use ZGC if you need < 1ms pause times
88
+ - Set `-Xms` = `-Xmx` for predictable behavior
89
+ - Monitor with JFR, not gut feeling
90
+
91
+ ## Memory Patterns
92
+
93
+ ### Avoid Unnecessary Allocations
94
+
95
+ ```java
96
+ // Bad: autoboxing in hot loops
97
+ long sum = 0;
98
+ for (Integer value : integerList) {
99
+ sum += value; // Unboxing on every iteration
100
+ }
101
+
102
+ // Good: use primitive streams
103
+ long sum = integerList.stream().mapToLong(Integer::longValue).sum();
104
+
105
+ // Bad: intermediate collections
106
+ var result = users.stream()
107
+ .map(User::name)
108
+ .collect(Collectors.toList()) // Intermediate list
109
+ .stream()
110
+ .filter(n -> n.startsWith("A"))
111
+ .toList();
112
+
113
+ // Good: single pipeline
114
+ var result = users.stream()
115
+ .map(User::name)
116
+ .filter(n -> n.startsWith("A"))
117
+ .toList();
118
+ ```
119
+
120
+ ### String Optimization
121
+
122
+ ```java
123
+ // String.intern() for repeated strings (use carefully — fills PermGen/Metaspace)
124
+ // Better: use enum or constants for known string sets
125
+
126
+ // StringBuilder for complex concatenation
127
+ var sb = new StringBuilder(256); // Pre-size if length is known
128
+ for (var item : items) {
129
+ sb.append(item.name()).append(": ").append(item.value()).append('\n');
130
+ }
131
+ return sb.toString();
132
+ ```
133
+
134
+ ### Collection Sizing
135
+
136
+ ```java
137
+ // Pre-size collections when capacity is known
138
+ var map = new HashMap<String, User>(expectedSize * 4 / 3 + 1); // Account for load factor
139
+ var list = new ArrayList<User>(expectedSize);
140
+
141
+ // Use specialized collections
142
+ EnumMap<Status, List<Order>> byStatus = new EnumMap<>(Status.class);
143
+ EnumSet<Permission> permissions = EnumSet.of(READ, WRITE);
144
+ ```
145
+
146
+ ## Connection Pooling (HikariCP)
147
+
148
+ ```yaml
149
+ spring:
150
+ datasource:
151
+ hikari:
152
+ maximum-pool-size: 10 # Default is fine for most apps
153
+ minimum-idle: 5
154
+ idle-timeout: 300000 # 5 minutes
155
+ connection-timeout: 30000 # 30 seconds
156
+ max-lifetime: 1800000 # 30 minutes
157
+ leak-detection-threshold: 60000 # 1 minute — detect leaked connections
158
+ ```
159
+
160
+ ### Pool Sizing Rule
161
+
162
+ Formula: `connections = (core_count * 2) + effective_spindle_count`
163
+ For SSDs: `connections ≈ core_count * 2`
164
+
165
+ Most apps need 10-20 connections. More is rarely better.
166
+
167
+ ## HTTP Client Performance
168
+
169
+ ```java
170
+ // Shared HttpClient instance with connection pooling
171
+ private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
172
+ .connectTimeout(Duration.ofSeconds(5))
173
+ .executor(Executors.newVirtualThreadPerTaskExecutor())
174
+ .build();
175
+
176
+ // Or with Spring: RestClient / WebClient (reuse instances)
177
+ @Bean
178
+ public RestClient restClient(RestClient.Builder builder) {
179
+ return builder
180
+ .baseUrl("https://api.example.com")
181
+ .requestFactory(new JdkClientHttpRequestFactory(HTTP_CLIENT))
182
+ .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
183
+ .build();
184
+ }
185
+ ```
186
+
187
+ ## Caching
188
+
189
+ ```java
190
+ // Spring Cache with Caffeine (in-process)
191
+ @Configuration
192
+ @EnableCaching
193
+ public class CacheConfig {
194
+ @Bean
195
+ public CacheManager cacheManager() {
196
+ var caffeine = Caffeine.newBuilder()
197
+ .maximumSize(10_000)
198
+ .expireAfterWrite(Duration.ofMinutes(10))
199
+ .recordStats();
200
+
201
+ var manager = new CaffeineCacheManager();
202
+ manager.setCaffeine(caffeine);
203
+ return manager;
204
+ }
205
+ }
206
+
207
+ @Cacheable(value = "users", key = "#id")
208
+ public Optional<User> findById(UUID id) {
209
+ return userRepository.findById(id);
210
+ }
211
+
212
+ @CacheEvict(value = "users", key = "#user.id")
213
+ public void update(User user) {
214
+ userRepository.save(user);
215
+ }
216
+ ```
217
+
218
+ ## Anti-Patterns
219
+
220
+ ```java
221
+ // Never: premature optimization without profiling
222
+ // "I think this is slow" — prove it with JMH or JFR
223
+
224
+ // Never: creating threads manually
225
+ new Thread(() -> process(item)).start(); // No lifecycle management, no error handling
226
+ // Use ExecutorService or virtual threads
227
+
228
+ // Never: synchronizing on string literals
229
+ synchronized ("lock") { } // String interning means unexpected sharing
230
+ // Use: private final Object lock = new Object();
231
+
232
+ // Never: reflection in hot paths
233
+ method.invoke(target, args); // Orders of magnitude slower than direct calls
234
+ // Use interfaces and polymorphism
235
+
236
+ // Never: unbounded caches
237
+ private final Map<String, Object> cache = new HashMap<>(); // Grows forever → OOM
238
+ // Use Caffeine with eviction policies
239
+ ```
@@ -0,0 +1,262 @@
1
+ # Java Persistence
2
+
3
+ JPA/Hibernate done right, JDBC when you need control, and database patterns that scale.
4
+
5
+ ## JPA Entity Design
6
+
7
+ ```java
8
+ @Entity
9
+ @Table(name = "orders")
10
+ public class Order {
11
+
12
+ @Id
13
+ @GeneratedValue(strategy = GenerationType.UUID)
14
+ private UUID id;
15
+
16
+ @Column(nullable = false)
17
+ private String customerId;
18
+
19
+ @Enumerated(EnumType.STRING)
20
+ @Column(nullable = false)
21
+ private OrderStatus status;
22
+
23
+ @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
24
+ private List<OrderItem> items = new ArrayList<>();
25
+
26
+ @Version
27
+ private Long version; // Optimistic locking
28
+
29
+ @CreationTimestamp
30
+ private Instant createdAt;
31
+
32
+ @UpdateTimestamp
33
+ private Instant updatedAt;
34
+
35
+ // Protected no-arg constructor for JPA
36
+ protected Order() {}
37
+
38
+ // Factory method with validation
39
+ public static Order create(String customerId, List<OrderItem> items) {
40
+ Objects.requireNonNull(customerId, "customerId is required");
41
+ if (items.isEmpty()) throw new IllegalArgumentException("Order must have items");
42
+
43
+ var order = new Order();
44
+ order.customerId = customerId;
45
+ order.status = OrderStatus.PENDING;
46
+ items.forEach(order::addItem);
47
+ return order;
48
+ }
49
+
50
+ public void addItem(OrderItem item) {
51
+ items.add(item);
52
+ item.setOrder(this);
53
+ }
54
+
55
+ // Getters only — no public setters for domain-relevant fields
56
+ public UUID getId() { return id; }
57
+ public OrderStatus getStatus() { return status; }
58
+ public List<OrderItem> getItems() { return Collections.unmodifiableList(items); }
59
+ }
60
+ ```
61
+
62
+ ## Repository Pattern
63
+
64
+ ```java
65
+ // Spring Data JPA — let the framework generate implementations
66
+ public interface OrderRepository extends JpaRepository<Order, UUID> {
67
+
68
+ // Derived query
69
+ List<Order> findByCustomerIdAndStatus(String customerId, OrderStatus status);
70
+
71
+ // JPQL for complex queries
72
+ @Query("""
73
+ SELECT o FROM Order o
74
+ JOIN FETCH o.items
75
+ WHERE o.customerId = :customerId
76
+ ORDER BY o.createdAt DESC
77
+ """)
78
+ List<Order> findWithItemsByCustomerId(@Param("customerId") String customerId);
79
+
80
+ // Native query when JPQL isn't enough
81
+ @Query(value = """
82
+ SELECT o.* FROM orders o
83
+ WHERE o.created_at > :since
84
+ AND o.status = 'PENDING'
85
+ FOR UPDATE SKIP LOCKED
86
+ LIMIT :limit
87
+ """, nativeQuery = true)
88
+ List<Order> findPendingForProcessing(
89
+ @Param("since") Instant since,
90
+ @Param("limit") int limit);
91
+
92
+ // Projections for read-only queries
93
+ @Query("""
94
+ SELECT new com.example.dto.OrderSummary(o.id, o.status, o.createdAt, SIZE(o.items))
95
+ FROM Order o
96
+ WHERE o.customerId = :customerId
97
+ """)
98
+ Page<OrderSummary> findSummariesByCustomerId(
99
+ @Param("customerId") String customerId, Pageable pageable);
100
+ }
101
+ ```
102
+
103
+ ## Transaction Management
104
+
105
+ ```java
106
+ @Service
107
+ @RequiredArgsConstructor
108
+ public class OrderService {
109
+
110
+ private final OrderRepository orderRepository;
111
+ private final PaymentService paymentService;
112
+ private final TransactionTemplate txTemplate;
113
+
114
+ // Declarative — for simple cases
115
+ @Transactional
116
+ public Order create(CreateOrderRequest request) {
117
+ var order = Order.create(request.customerId(), request.items());
118
+ return orderRepository.save(order);
119
+ }
120
+
121
+ // Read-only transactions — enables optimizations
122
+ @Transactional(readOnly = true)
123
+ public Optional<Order> findById(UUID id) {
124
+ return orderRepository.findById(id);
125
+ }
126
+
127
+ // Programmatic — for fine-grained control
128
+ public OrderResult processOrder(UUID orderId) {
129
+ // Step 1: Update order status (transactional)
130
+ var order = txTemplate.execute(status -> {
131
+ var o = orderRepository.findById(orderId)
132
+ .orElseThrow(() -> new NotFoundException("Order: " + orderId));
133
+ o.markProcessing();
134
+ return orderRepository.save(o);
135
+ });
136
+
137
+ // Step 2: Call external payment (non-transactional)
138
+ var paymentResult = paymentService.charge(order);
139
+
140
+ // Step 3: Update with result (new transaction)
141
+ return txTemplate.execute(status -> {
142
+ if (paymentResult.isSuccess()) {
143
+ order.markPaid(paymentResult.transactionId());
144
+ } else {
145
+ order.markFailed(paymentResult.errorMessage());
146
+ }
147
+ orderRepository.save(order);
148
+ return OrderResult.from(order);
149
+ });
150
+ }
151
+ }
152
+ ```
153
+
154
+ ## N+1 Query Prevention
155
+
156
+ ```java
157
+ // Bad: lazy loading in a loop
158
+ var orders = orderRepository.findAll(); // 1 query
159
+ for (var order : orders) {
160
+ order.getItems().size(); // N queries!
161
+ }
162
+
163
+ // Good: JOIN FETCH
164
+ @Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.status = :status")
165
+ List<Order> findWithItemsByStatus(@Param("status") OrderStatus status);
166
+
167
+ // Good: @EntityGraph
168
+ @EntityGraph(attributePaths = {"items", "customer"})
169
+ List<Order> findByStatus(OrderStatus status);
170
+
171
+ // Good: DTO projection (best performance)
172
+ @Query("""
173
+ SELECT new com.example.dto.OrderSummary(o.id, o.status, SIZE(o.items))
174
+ FROM Order o WHERE o.status = :status
175
+ """)
176
+ List<OrderSummary> findSummariesByStatus(@Param("status") OrderStatus status);
177
+ ```
178
+
179
+ ## Database Migrations (Flyway)
180
+
181
+ ```sql
182
+ -- V1__create_orders_table.sql
183
+ CREATE TABLE orders (
184
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
185
+ customer_id VARCHAR(255) NOT NULL,
186
+ status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
187
+ version BIGINT NOT NULL DEFAULT 0,
188
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
189
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
190
+ );
191
+
192
+ CREATE INDEX idx_orders_customer_id ON orders(customer_id);
193
+ CREATE INDEX idx_orders_status ON orders(status);
194
+
195
+ -- V2__add_order_items_table.sql
196
+ CREATE TABLE order_items (
197
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
198
+ order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
199
+ sku VARCHAR(100) NOT NULL,
200
+ quantity INT NOT NULL CHECK (quantity > 0),
201
+ price NUMERIC(10, 2) NOT NULL CHECK (price >= 0)
202
+ );
203
+
204
+ CREATE INDEX idx_order_items_order_id ON order_items(order_id);
205
+ ```
206
+
207
+ ## JDBC Template (When JPA Is Overkill)
208
+
209
+ ```java
210
+ // For bulk operations, complex queries, or maximum performance
211
+ @Repository
212
+ @RequiredArgsConstructor
213
+ public class OrderReportRepository {
214
+
215
+ private final JdbcTemplate jdbc;
216
+
217
+ public List<DailyRevenue> getDailyRevenue(LocalDate from, LocalDate to) {
218
+ return jdbc.query("""
219
+ SELECT DATE(created_at) as day, SUM(total) as revenue, COUNT(*) as order_count
220
+ FROM orders
221
+ WHERE created_at BETWEEN ? AND ?
222
+ AND status = 'COMPLETED'
223
+ GROUP BY DATE(created_at)
224
+ ORDER BY day
225
+ """,
226
+ (rs, rowNum) -> new DailyRevenue(
227
+ rs.getDate("day").toLocalDate(),
228
+ rs.getBigDecimal("revenue"),
229
+ rs.getInt("order_count")),
230
+ from, to);
231
+ }
232
+ }
233
+ ```
234
+
235
+ ## Anti-Patterns
236
+
237
+ ```java
238
+ // Never: open-in-view (lazy loading in presentation layer)
239
+ spring.jpa.open-in-view=true // Masks N+1 problems, unclear data boundaries
240
+
241
+ // Never: entities as API responses
242
+ @GetMapping("/{id}")
243
+ public Order getById(@PathVariable UUID id) {
244
+ return orderRepository.findById(id).orElseThrow();
245
+ // Exposes internal structure, lazy loading exceptions, circular references
246
+ }
247
+ // Map to DTOs/records
248
+
249
+ // Never: manual ID generation with UUID.randomUUID() in application code
250
+ // Let the database or JPA strategy handle it
251
+
252
+ // Never: ignoring @Version for concurrent writes
253
+ // Optimistic locking prevents lost updates — use it
254
+
255
+ // Never: long-running transactions
256
+ @Transactional
257
+ public void processAllOrders() {
258
+ var orders = orderRepository.findAll(); // Locks for entire method
259
+ orders.forEach(this::processExpensiveOperation);
260
+ }
261
+ // Process in batches with separate transactions
262
+ ```