guardian-framework 0.1.11 → 0.1.13
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/dist/cli.js +1 -1
- package/dist/exports.js +1 -1
- package/package.json +1 -1
- package/templates/pi/extensions/architect-lib/generators.ts +2 -0
- package/templates/pi/extensions/architect-lib/helpers.ts +21 -0
- package/templates/pi/extensions/architect.ts +28 -1
- package/templates/pi/preflight_report.json +2 -2
- package/templates/pi/scripts/ci/check_architecture_conformance.sh +34 -5
- package/templates/pi/scripts/ci/check_ddd_structure.sh +290 -0
- package/templates/pi/skills/agents/go-codegen.md +42 -0
- package/templates/pi/skills/agents/java-codegen.md +42 -0
- package/templates/pi/skills/agents/python-codegen.md +41 -0
- package/templates/pi/skills/agents/typescript-codegen.md +40 -0
- package/templates/pi/skills/go-enterprise-codegen.md +450 -0
- package/templates/pi/skills/java-spring-enterprise-codegen.md +498 -0
- package/templates/pi/skills/python-enterprise-codegen.md +381 -0
- package/templates/pi/skills/typescript-enterprise-codegen.md +411 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
# Java/Spring Boot Enterprise Code Generation — DDD + Clean Architecture
|
|
2
|
+
|
|
3
|
+
> Canonical skill for generating production-grade Java code with Spring Boot following Clean Architecture with DDD.
|
|
4
|
+
> All code MUST follow these patterns. Validators enforce compliance.
|
|
5
|
+
>
|
|
6
|
+
> Source: Clean Architecture principles + Spring Boot best practices + DDD patterns from context7 (/jkazama/ddd-java, /ardalis/cleanarchitecture, /websites/spring_io_spring-boot).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Project Structure — Clean Architecture with DDD
|
|
11
|
+
|
|
12
|
+
Every bounded context follows the same 4-layer structure:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
module/
|
|
16
|
+
├── domain/ # Pure domain entities, value objects, events
|
|
17
|
+
│ ├── model/ # Aggregate roots and entities
|
|
18
|
+
│ │ ├── Entity.java
|
|
19
|
+
│ │ └── Status.java
|
|
20
|
+
│ ├── vo/ # Value objects
|
|
21
|
+
│ │ └── Money.java
|
|
22
|
+
│ ├── event/ # Domain event payloads
|
|
23
|
+
│ │ └── EntityEvent.java
|
|
24
|
+
│ ├── repository/ # Repository interfaces
|
|
25
|
+
│ │ └── EntityRepository.java
|
|
26
|
+
│ └── error/ # Typed exception classes
|
|
27
|
+
│ └── DomainError.java
|
|
28
|
+
├── application/ # Service interfaces and DTOs
|
|
29
|
+
│ ├── service/ # Service interfaces
|
|
30
|
+
│ │ └── EntityService.java
|
|
31
|
+
│ ├── dto/ # Input/Output DTOs
|
|
32
|
+
│ │ ├── Command.java
|
|
33
|
+
│ │ └── EntityResponse.java
|
|
34
|
+
│ └── impl/ # Service implementations
|
|
35
|
+
│ └── EntityServiceImpl.java
|
|
36
|
+
├── infrastructure/ # Repository implementations, external adapters
|
|
37
|
+
│ ├── repository/ # JPA repository implementations
|
|
38
|
+
│ │ └── JpaEntityRepository.java
|
|
39
|
+
│ └── persistence/ # JPA entities, migrations
|
|
40
|
+
└── interfaces/ # API contracts (HTTP)
|
|
41
|
+
├── rest/ # REST controllers
|
|
42
|
+
│ └── EntityController.java
|
|
43
|
+
├── dto/ # Request/Response DTOs
|
|
44
|
+
└── advice/ # Exception handlers
|
|
45
|
+
└── GlobalExceptionHandler.java
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Dependency Direction Rule
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
domain → application → infrastructure → interfaces
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- **domain/** — pure Java, no Spring annotations
|
|
55
|
+
- **application/** — depends on domain, uses Spring `@Service`
|
|
56
|
+
- **infrastructure/** — depends on domain + application, uses Spring Data JPA
|
|
57
|
+
- **interfaces/** — depends on application, uses Spring `@RestController`
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. Domain Layer
|
|
62
|
+
|
|
63
|
+
### Entity
|
|
64
|
+
|
|
65
|
+
```java
|
|
66
|
+
// domain/model/Entity.java
|
|
67
|
+
|
|
68
|
+
package com.project.module.domain.model;
|
|
69
|
+
|
|
70
|
+
import com.project.module.domain.vo.Money;
|
|
71
|
+
import com.project.module.domain.error.DomainError;
|
|
72
|
+
import java.time.Instant;
|
|
73
|
+
import java.util.UUID;
|
|
74
|
+
|
|
75
|
+
public class Entity {
|
|
76
|
+
private final UUID id;
|
|
77
|
+
private Status status;
|
|
78
|
+
private final Instant createdAt;
|
|
79
|
+
private Instant updatedAt;
|
|
80
|
+
|
|
81
|
+
public Entity() {
|
|
82
|
+
this.id = UUID.randomUUID();
|
|
83
|
+
this.status = Status.PENDING;
|
|
84
|
+
this.createdAt = Instant.now();
|
|
85
|
+
this.updatedAt = Instant.now();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public UUID getId() { return id; }
|
|
89
|
+
public Status getStatus() { return status; }
|
|
90
|
+
public Instant getCreatedAt() { return createdAt; }
|
|
91
|
+
|
|
92
|
+
public void transition(Status newStatus) {
|
|
93
|
+
if (!status.canTransitionTo(newStatus)) {
|
|
94
|
+
throw DomainError.invalidTransition(status, newStatus);
|
|
95
|
+
}
|
|
96
|
+
this.status = newStatus;
|
|
97
|
+
this.updatedAt = Instant.now();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Value Object
|
|
103
|
+
|
|
104
|
+
```java
|
|
105
|
+
// domain/vo/Money.java
|
|
106
|
+
|
|
107
|
+
package com.project.module.domain.vo;
|
|
108
|
+
|
|
109
|
+
import com.project.module.domain.error.DomainError;
|
|
110
|
+
|
|
111
|
+
public record Money(long amount, String currency) {
|
|
112
|
+
|
|
113
|
+
public Money {
|
|
114
|
+
if (amount < 0) throw new DomainError("Amount must be non-negative");
|
|
115
|
+
if (currency == null || currency.length() != 3)
|
|
116
|
+
throw new DomainError("Currency must be ISO 4217");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public Money add(Money other) {
|
|
120
|
+
if (!this.currency.equals(other.currency)) {
|
|
121
|
+
throw new DomainError("Currency mismatch");
|
|
122
|
+
}
|
|
123
|
+
return new Money(this.amount + other.amount, this.currency);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Repository Interface
|
|
129
|
+
|
|
130
|
+
```java
|
|
131
|
+
// domain/repository/EntityRepository.java
|
|
132
|
+
|
|
133
|
+
package com.project.module.domain.repository;
|
|
134
|
+
|
|
135
|
+
import com.project.module.domain.model.Entity;
|
|
136
|
+
import com.project.module.domain.model.Status;
|
|
137
|
+
import java.util.List;
|
|
138
|
+
import java.util.Optional;
|
|
139
|
+
import java.util.UUID;
|
|
140
|
+
|
|
141
|
+
public interface EntityRepository {
|
|
142
|
+
Optional<Entity> findById(UUID id);
|
|
143
|
+
void save(Entity entity);
|
|
144
|
+
void delete(UUID id);
|
|
145
|
+
List<Entity> findByStatus(Status status);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Status Enum with Transitions
|
|
150
|
+
|
|
151
|
+
```java
|
|
152
|
+
// domain/model/Status.java
|
|
153
|
+
|
|
154
|
+
public enum Status {
|
|
155
|
+
PENDING,
|
|
156
|
+
ACTIVE,
|
|
157
|
+
COMPLETED,
|
|
158
|
+
FAILED;
|
|
159
|
+
|
|
160
|
+
public boolean canTransitionTo(Status target) {
|
|
161
|
+
return switch (this) {
|
|
162
|
+
case PENDING -> target == ACTIVE;
|
|
163
|
+
case ACTIVE -> target == COMPLETED || target == FAILED;
|
|
164
|
+
case COMPLETED, FAILED -> false;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 3. Domain Error Handling
|
|
173
|
+
|
|
174
|
+
```java
|
|
175
|
+
// domain/error/DomainError.java
|
|
176
|
+
|
|
177
|
+
package com.project.module.domain.error;
|
|
178
|
+
|
|
179
|
+
public class DomainError extends RuntimeException {
|
|
180
|
+
|
|
181
|
+
private final ErrorCode code;
|
|
182
|
+
|
|
183
|
+
public DomainError(String message) {
|
|
184
|
+
super(message);
|
|
185
|
+
this.code = ErrorCode.VALIDATION;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public DomainError(ErrorCode code, String message) {
|
|
189
|
+
super(message);
|
|
190
|
+
this.code = code;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public ErrorCode getCode() { return code; }
|
|
194
|
+
|
|
195
|
+
public static DomainError notFound(String id) {
|
|
196
|
+
return new DomainError(ErrorCode.NOT_FOUND, "Resource " + id + " not found");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public static DomainError invalidTransition(Status current, Status target) {
|
|
200
|
+
return new DomainError(
|
|
201
|
+
ErrorCode.INVALID_TRANSITION,
|
|
202
|
+
"Cannot transition from " + current + " to " + target
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// domain/error/ErrorCode.java
|
|
208
|
+
|
|
209
|
+
public enum ErrorCode {
|
|
210
|
+
NOT_FOUND,
|
|
211
|
+
INVALID_STATE,
|
|
212
|
+
VALIDATION,
|
|
213
|
+
DUPLICATE,
|
|
214
|
+
INVALID_TRANSITION
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Rules
|
|
219
|
+
- ✅ Use `RuntimeException` subclasses for domain errors
|
|
220
|
+
- ✅ Use `ErrorCode` enum for machine-readable error types
|
|
221
|
+
- ✅ Use `record` for value objects (Java 16+)
|
|
222
|
+
- ❌ Don't use Spring annotations in domain layer
|
|
223
|
+
- ❌ Don't use checked exceptions for domain errors
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 4. Application Layer
|
|
228
|
+
|
|
229
|
+
```java
|
|
230
|
+
// application/service/EntityService.java
|
|
231
|
+
|
|
232
|
+
package com.project.module.application.service;
|
|
233
|
+
|
|
234
|
+
import com.project.module.domain.model.Entity;
|
|
235
|
+
import com.project.module.application.dto.Command;
|
|
236
|
+
import java.util.UUID;
|
|
237
|
+
|
|
238
|
+
public interface EntityService {
|
|
239
|
+
Entity execute(Command cmd);
|
|
240
|
+
Entity getById(UUID id);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```java
|
|
245
|
+
// application/impl/EntityServiceImpl.java
|
|
246
|
+
|
|
247
|
+
package com.project.module.application.impl;
|
|
248
|
+
|
|
249
|
+
import com.project.module.domain.model.Entity;
|
|
250
|
+
import com.project.module.domain.repository.EntityRepository;
|
|
251
|
+
import com.project.module.domain.error.DomainError;
|
|
252
|
+
import com.project.module.application.service.EntityService;
|
|
253
|
+
import com.project.module.application.dto.Command;
|
|
254
|
+
import lombok.RequiredArgsConstructor;
|
|
255
|
+
import lombok.extern.slf4j.Slf4j;
|
|
256
|
+
import org.springframework.stereotype.Service;
|
|
257
|
+
import org.springframework.transaction.annotation.Transactional;
|
|
258
|
+
|
|
259
|
+
@Slf4j
|
|
260
|
+
@Service
|
|
261
|
+
@RequiredArgsConstructor
|
|
262
|
+
public class EntityServiceImpl implements EntityService {
|
|
263
|
+
|
|
264
|
+
private final EntityRepository repository;
|
|
265
|
+
|
|
266
|
+
@Override
|
|
267
|
+
@Transactional
|
|
268
|
+
public Entity execute(Command cmd) {
|
|
269
|
+
var entity = new Entity();
|
|
270
|
+
entity.transition(cmd.desiredStatus());
|
|
271
|
+
repository.save(entity);
|
|
272
|
+
log.info("Entity executed: {}", entity.getId());
|
|
273
|
+
return entity;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@Override
|
|
277
|
+
@Transactional(readOnly = true)
|
|
278
|
+
public Entity getById(UUID id) {
|
|
279
|
+
return repository.findById(id)
|
|
280
|
+
.orElseThrow(() -> DomainError.notFound(id.toString()));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## 5. Infrastructure Layer — JPA Repository
|
|
288
|
+
|
|
289
|
+
```java
|
|
290
|
+
// infrastructure/repository/JpaEntityRepository.java
|
|
291
|
+
|
|
292
|
+
package com.project.module.infrastructure.repository;
|
|
293
|
+
|
|
294
|
+
import com.project.module.domain.model.Entity;
|
|
295
|
+
import com.project.module.domain.model.Status;
|
|
296
|
+
import com.project.module.domain.repository.EntityRepository;
|
|
297
|
+
import jakarta.persistence.EntityManager;
|
|
298
|
+
import lombok.RequiredArgsConstructor;
|
|
299
|
+
import org.springframework.stereotype.Repository;
|
|
300
|
+
import java.util.*;
|
|
301
|
+
|
|
302
|
+
@Repository
|
|
303
|
+
@RequiredArgsConstructor
|
|
304
|
+
public class JpaEntityRepository implements EntityRepository {
|
|
305
|
+
|
|
306
|
+
private final EntityManager em;
|
|
307
|
+
|
|
308
|
+
@Override
|
|
309
|
+
public Optional<Entity> findById(UUID id) {
|
|
310
|
+
return Optional.ofNullable(em.find(Entity.class, id));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@Override
|
|
314
|
+
public void save(Entity entity) {
|
|
315
|
+
em.persist(entity);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@Override
|
|
319
|
+
public void delete(UUID id) {
|
|
320
|
+
findById(id).ifPresent(em::remove);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
@Override
|
|
324
|
+
public List<Entity> findByStatus(Status status) {
|
|
325
|
+
return em.createQuery("SELECT e FROM Entity e WHERE e.status = :status", Entity.class)
|
|
326
|
+
.setParameter("status", status)
|
|
327
|
+
.getResultList();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 6. Interfaces Layer — REST Controllers
|
|
335
|
+
|
|
336
|
+
```java
|
|
337
|
+
// interfaces/rest/EntityController.java
|
|
338
|
+
|
|
339
|
+
package com.project.module.interfaces.rest;
|
|
340
|
+
|
|
341
|
+
import com.project.module.application.service.EntityService;
|
|
342
|
+
import com.project.module.application.dto.Command;
|
|
343
|
+
import com.project.module.interfaces.dto.CreateEntityRequest;
|
|
344
|
+
import com.project.module.interfaces.dto.EntityResponse;
|
|
345
|
+
import jakarta.validation.Valid;
|
|
346
|
+
import lombok.RequiredArgsConstructor;
|
|
347
|
+
import org.springframework.http.HttpStatus;
|
|
348
|
+
import org.springframework.web.bind.annotation.*;
|
|
349
|
+
|
|
350
|
+
@RestController
|
|
351
|
+
@RequestMapping("/api/v1/entities")
|
|
352
|
+
@RequiredArgsConstructor
|
|
353
|
+
public class EntityController {
|
|
354
|
+
|
|
355
|
+
private final EntityService service;
|
|
356
|
+
|
|
357
|
+
@PostMapping
|
|
358
|
+
@ResponseStatus(HttpStatus.CREATED)
|
|
359
|
+
public EntityResponse execute(@Valid @RequestBody CreateEntityRequest request) {
|
|
360
|
+
var entity = service.execute(request.toCommand());
|
|
361
|
+
return EntityResponse.from(entity);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
@GetMapping("/{id}")
|
|
365
|
+
public EntityResponse getById(@PathVariable UUID id) {
|
|
366
|
+
var entity = service.getById(id);
|
|
367
|
+
return EntityResponse.from(entity);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Global Exception Handler
|
|
373
|
+
|
|
374
|
+
```java
|
|
375
|
+
// interfaces/advice/GlobalExceptionHandler.java
|
|
376
|
+
|
|
377
|
+
@RestControllerAdvice
|
|
378
|
+
public class GlobalExceptionHandler {
|
|
379
|
+
|
|
380
|
+
@ExceptionHandler(DomainError.class)
|
|
381
|
+
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
|
382
|
+
public ErrorResponse handleDomainError(DomainError e) {
|
|
383
|
+
return new ErrorResponse(e.getCode().name(), e.getMessage());
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
@ExceptionHandler(NotFoundException.class)
|
|
387
|
+
@ResponseStatus(HttpStatus.NOT_FOUND)
|
|
388
|
+
public ErrorResponse handleNotFound(NotFoundException e) {
|
|
389
|
+
return new ErrorResponse("NOT_FOUND", e.getMessage());
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
@ExceptionHandler(MethodArgumentNotValidException.class)
|
|
393
|
+
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
|
394
|
+
public ErrorResponse handleValidation(MethodArgumentNotValidException e) {
|
|
395
|
+
return new ErrorResponse("VALIDATION", "Invalid request body");
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## 7. Testing Patterns
|
|
403
|
+
|
|
404
|
+
```java
|
|
405
|
+
@SpringBootTest
|
|
406
|
+
@AutoConfigureMockMvc
|
|
407
|
+
class EntityControllerTest {
|
|
408
|
+
|
|
409
|
+
@Autowired
|
|
410
|
+
private MockMvc mockMvc;
|
|
411
|
+
|
|
412
|
+
@MockBean
|
|
413
|
+
private EntityService service;
|
|
414
|
+
|
|
415
|
+
@Test
|
|
416
|
+
void shouldCreateEntity() throws Exception {
|
|
417
|
+
var entity = new Entity();
|
|
418
|
+
when(service.execute(any())).thenReturn(entity);
|
|
419
|
+
|
|
420
|
+
mockMvc.perform(post("/api/v1/entities")
|
|
421
|
+
.contentType(MediaType.APPLICATION_JSON)
|
|
422
|
+
.content("""
|
|
423
|
+
{"desiredStatus": "ACTIVE"}
|
|
424
|
+
"""))
|
|
425
|
+
.andExpect(status().isCreated())
|
|
426
|
+
.andExpect(jsonPath("$.id").value(entity.getId().toString()));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## 8. Anti-Patterns — NEVER DO
|
|
434
|
+
|
|
435
|
+
```java
|
|
436
|
+
// ❌ Domain entities with Spring annotations
|
|
437
|
+
@Entity
|
|
438
|
+
@Table(name = "entities")
|
|
439
|
+
public class Entity { ... } // BAD — domain is JPA-free
|
|
440
|
+
|
|
441
|
+
// ❌ Business logic in controllers
|
|
442
|
+
@PostMapping
|
|
443
|
+
public void execute(@RequestBody Request req) {
|
|
444
|
+
// business logic here // BAD — use service layer
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ❌ Field injection
|
|
448
|
+
@Autowired
|
|
449
|
+
private EntityService service; // BAD — use constructor injection
|
|
450
|
+
|
|
451
|
+
// ❌ Mutable value objects without defensive copies
|
|
452
|
+
public class Money {
|
|
453
|
+
private int amount; // BAD — should be final
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ❌ Leaking JPA entities to interfaces
|
|
457
|
+
@Entity
|
|
458
|
+
public class User {
|
|
459
|
+
// used directly in REST responses // BAD — use DTOs
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## 9. Project-Level Structure
|
|
466
|
+
|
|
467
|
+
```
|
|
468
|
+
src/main/java/com/company/project/
|
|
469
|
+
module/
|
|
470
|
+
domain/
|
|
471
|
+
model/
|
|
472
|
+
vo/
|
|
473
|
+
event/
|
|
474
|
+
repository/
|
|
475
|
+
error/
|
|
476
|
+
application/
|
|
477
|
+
service/
|
|
478
|
+
dto/
|
|
479
|
+
impl/
|
|
480
|
+
infrastructure/
|
|
481
|
+
repository/
|
|
482
|
+
persistence/
|
|
483
|
+
interfaces/
|
|
484
|
+
rest/
|
|
485
|
+
dto/
|
|
486
|
+
advice/
|
|
487
|
+
shared/
|
|
488
|
+
config/
|
|
489
|
+
logging/
|
|
490
|
+
src/test/java/com/company/project/
|
|
491
|
+
pom.xml (or build.gradle)
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
*Version: 1.0.0*
|
|
497
|
+
*Last updated: 2026-07-03*
|
|
498
|
+
*Source: Guardian DDD patterns + Spring Boot best practices + context7 (/jkazama/ddd-java, /websites/spring_io_spring-boot)*
|