算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
spring-boot-architecture【设计与创意】Use this skill when the user says 'Spring Boot structure', 'Spring architecture', 'Spring Boot clean arch', 'Spring layered', 'Spring hexagonal', 'Spring WebFlux', 'Spring Data', 'Spring Boot folder', or when building a Spring Boot application. This skill enforces: package-by-feature (not by layer), hexagonal architecture with ports and adapters, Spr…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Structure Spring Boot applications with package-by-feature and hexagonal architecture. Domain has zero Spring imports. Annotations only in infrastructure and presentation.
Exact user phrases: "Spring Boot structure", "Spring architecture", "Spring Boot clean arch", "Spring layered", "Spring hexagonal", "Spring WebFlux", "Spring Data", "Spring Boot folder", "Spring project layout".
Before activating, verify:
No file output. Produces folder structure and code examples as text.
Folder structure:
src/main/java/com/project/{feature}/
domain/
application/
infrastructure/
presentation/
Code: Java class-level only. Package declaration omitted.
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.
Folder structure: unlimited. Code: 20 lines per example.
com.project/
Application.java -- @SpringBootApplication
config/
SecurityConfig.java
ObjectMapperConfig.java
OpenApiConfig.java
shared/
BaseEntity.java
DomainEvent.java
PagedResult.java
{feature}/
domain/
Order.java -- Domain entity (POJO)
OrderStatus.java -- Value object / enum
OrderRepository.java -- Port (interface)
OrderService.java -- Domain service
application/
in/
PlaceOrderUseCase.java -- Inbound port interface
CancelOrderUseCase.java
out/
OrderEventPublisher.java -- Outbound port interface
PaymentService.java
dto/
PlaceOrderCommand.java
OrderResponse.java
PagedOrdersQuery.java
infrastructure/
persistence/
OrderJpaEntity.java -- @Entity (JPA concern)
OrderJpaRepository.java -- Spring Data interface
OrderJpaRepositoryImpl.java -- Implements OrderRepository
messaging/
OrderKafkaPublisher.java
adapters/
PaymentGatewayAdapter.java
presentation/
OrderController.java -- @RestController
OrderControllerAdvice.java -- @ExceptionHandler
// PORT (domain layer -- no Spring annotation)
public interface OrderRepository {
Optional<Order> findById(OrderId id);
Order save(Order order);
Page<Order> findAll(Pageable pageable);
void delete(OrderId id);
}
// ADAPTER (infrastructure layer -- Spring-managed)
@Component
public class OrderJpaRepositoryImpl implements OrderRepository {
private final SpringDataOrderRepository springRepo;
private final OrderJpaMapper mapper;
public OrderJpaRepositoryImpl(SpringDataOrderRepository springRepo, OrderJpaMapper mapper) {
this.springRepo = springRepo;
this.mapper = mapper;
}
@Override
public Order save(Order order) {
OrderJpaEntity entity = mapper.toJpaEntity(order);
OrderJpaEntity saved = springRepo.save(entity);
return mapper.toDomain(saved);
}
@Override
public Optional<Order> findById(OrderId id) {
return springRepo.findById(id.getValue())
.map(mapper::toDomain);
}
@Override
public Page<Order> findAll(Pageable pageable) {
return springRepo.findAll(pageable)
.map(mapper::toDomain);
}
@Override
public void delete(OrderId id) {
springRepo.deleteById(id.getValue());
}
}
// USE CASE (application layer)
@Service
public class PlaceOrderService implements PlaceOrderUseCase {
private final OrderRepository orderRepo;
private final OrderEventPublisher eventPublisher;
public PlaceOrderService(OrderRepository orderRepo, OrderEventPublisher eventPublisher) {
this.orderRepo = orderRepo;
this.eventPublisher = eventPublisher;
}
@Transactional
public OrderId execute(PlaceOrderCommand command) {
Order order = Order.create(command.customerId(), command.items());
order = orderRepo.save(order);
eventPublisher.publish(new OrderPlacedEvent(order.getId()));
return order.getId();
}
}
// CONTROLLER (presentation layer)
@RestController
@RequestMapping("/v1/orders")
public class OrderController {
private final PlaceOrderUseCase placeOrder;
private final CancelOrderUseCase cancelOrder;
public OrderController(PlaceOrderUseCase placeOrder, CancelOrderUseCase cancelOrder) {
this.placeOrder = placeOrder;
this.cancelOrder = cancelOrder;
}
@PostMapping
public ResponseEntity<OrderResponse> create(@RequestBody @Valid PlaceOrderCommand command) {
OrderId id = placeOrder.execute(command);
return ResponseEntity.status(HttpStatus.CREATED)
.body(new OrderResponse(id.getValue()));
}
@PostMapping("/{id}/cancel")
public ResponseEntity<Void> cancel(@PathVariable UUID id) {
cancelOrder.execute(new OrderId(id));
return ResponseEntity.noContent().build();
}
}
| Use Spring MVC when | Use WebFlux when |
|---|---|
| JDBC/JPA/ORM-based persistence | High concurrency (>10K req/s) |
| Standard REST API | Streaming / Server-Sent Events |
| Team is more familiar with blocking | Long-lived connections |
| Existing MVC infrastructure | WebSocket-heavy application |
| Transactional database operations | Reactive database (R2DBC, MongoDB) |
| Mature library support needed | Backpressure-aware processing |
@Configuration
@ConfigurationProperties(prefix = "feature.order")
public class OrderConfig {
private int maxItemsPerOrder = 50;
private Duration paymentTimeout = Duration.ofSeconds(300);
private List<String> supportedCurrencies = List.of("USD", "EUR");
private boolean autoConfirmEnabled = true;
public int getMaxItemsPerOrder() { return maxItemsPerOrder; }
public void setMaxItemsPerOrder(int maxItemsPerOrder) { this.maxItemsPerOrder = maxItemsPerOrder; }
public Duration getPaymentTimeout() { return paymentTimeout; }
public void setPaymentTimeout(Duration paymentTimeout) { this.paymentTimeout = paymentTimeout; }
public List<String> getSupportedCurrencies() { return supportedCurrencies; }
public void setSupportedCurrencies(List<String> supportedCurrencies) { this.supportedCurrencies = supportedCurrencies; }
public boolean isAutoConfirmEnabled() { return autoConfirmEnabled; }
public void setAutoConfirmEnabled(boolean autoConfirmEnabled) { this.autoConfirmEnabled = autoConfirmEnabled; }
}
// application.yml
feature:
order:
max-items-per-order: 50
payment-timeout-seconds: 300
supported-currencies: USD, EUR
auto-confirm-enabled: true
spring:
datasource:
url: ${DB_URL}
username: ${DB_USER}
password: ${DB_PASS}
jpa:
hibernate:
ddl-auto: validate
show-sql: false
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(OrderNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(OrderNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("NOT_FOUND", ex.getMessage()));
}
@ExceptionHandler(OrderValidationException.class)
public ResponseEntity<ErrorResponse> handleValidation(OrderValidationException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("VALIDATION", ex.getMessage(), ex.getErrors()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleMethodValidation(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.toList();
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("VALIDATION", "Input validation failed", errors));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
log.error("Unhandled exception", ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred"));
}
}
public record ErrorResponse(String code, String message, Object details) {
public ErrorResponse(String code, String message) {
this(code, message, null);
}
}
@SpringBootTest
@AutoConfigureMockMvc
class OrderControllerTest {
@Autowired private MockMvc mockMvc;
@MockitoBean private PlaceOrderUseCase placeOrderUseCase;
@Test
void shouldCreateOrder() throws Exception {
OrderId orderId = new OrderId(UUID.randomUUID());
when(placeOrderUseCase.execute(any(PlaceOrderCommand.class)))
.thenReturn(orderId);
mockMvc.perform(post("/v1/orders")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"customerId":"cust-1","items":[{"productId":"prod-1","quantity":2,"unitPrice":19.99}]}
"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(orderId.getValue().toString()));
}
@Test
void shouldReturn400ForInvalidInput() throws Exception {
mockMvc.perform(post("/v1/orders")
.contentType(MediaType.APPLICATION_JSON)
.content("""{"customerId":""}"""))
.andExpect(status().isBadRequest());
}
}
Feature count > 20?
+-- Yes -> Can domain boundaries change?
| +-- Yes -> Package by feature (recommended)
| +-- No -> Package by layer (only for tiny projects)
+-- No -> Package by feature (always default)
Need to swap infrastructure (DB, messaging) independently?
+-- Yes -> Hexagonal (ports/adapters). Domain has zero framework deps.
+-- No -> Layered is simpler. Accept package coupling.
Primary DB is relational (JPA/JDBC)?
+-- Yes -> Use MVC. JPA is blocking, doesn't benefit from reactive.
+-- No -> Need >10K concurrent connections?
+-- Yes -> WebFlux with R2DBC or MongoDB
+-- No -> MVC is simpler and better-supported
@Autowired field injection: Makes testing impossible without Spring context. Always use constructor injection.
@Transactional at controller level: Too coarse. One controller method may call multiple services. Keep @Transactional at application service.
Domain entities with JPA annotations: Pure domain objects should be POJOs. JPA entities are infrastructure concerns mapped separately.
Circular dependencies between services: Usually signals missing domain concept. Extract shared logic into a third service.
Service/Repository interfaces in the wrong layer: Domain defines the interface (port), infrastructure implements it (adapter).
Using @Entity for domain entities: Creates tight coupling to JPA. Map JPA entities to domain objects via a mapper.
LazyInitializationException: Accessing lazy-loaded relations outside a transaction. Use JOIN FETCH or entity graphs.
Giant application service classes: More than 10 methods per service suggests missing domain subdivisions.
@MockBean overuse in tests: Prefer @MockitoBean (Spring Boot 3.4+) or test slices for focused tests.
Missing @Validated on @ConfigurationProperties classes: Properties binding silently fails without proper validation.
Domain entities validated in constructors. Use factory methods like Order.create() that enforce invariants.
@ExceptionHandler in @ControllerAdvice maps domain exceptions to HTTP. Controllers never have try-catch for domain logic.
@Valid on controller request bodies for input validation. Domain entities are validated in domain constructors.
@Profile for environment-specific beans. Dev, test, staging, production configurations are explicit.
Spring Data JPA specifications for dynamic queries. Avoid creating repository methods for every query combination.
Audit fields (createdAt, updatedAt) in base entity. Use @EnableJpaAuditing and @CreatedDate/@LastModifiedDate.
Fluent builder pattern for complex domain objects. Avoid telescoping constructors.
Record types for DTOs. Java 16+ records reduce boilerplate.
| Feature | Spring Boot MVC | Spring Boot WebFlux | Quarkus |
|---|---|---|---|
| Startup time | 2-4s | 2-4s | <0.3s |
| Memory footprint | ~150MB | ~150MB | ~50MB |
| GraalVM support | Limited | Limited | First-class |
| Reactive support | No | Yes | Yes (via RESTEasy Reactive) |
| JPA support | Full | Via Hibernate Reactive | Via Panache |
| Testing | @SpringBootTest | @WebFluxTest | @QuarkusTest |
| OpenAPI | springdoc-openapi | springdoc-openapi | SmallRye OpenAPI |
server.compression.enabled=true for text payloads.spring.jpa.properties.hibernate.jdbc.batch_size=20 for bulk operations.@Transactional(readOnly=true) for DB optimizations.| Tool | Purpose |
|---|---|
| Spring Initializr | Project scaffolding |
| springdoc-openapi | OpenAPI 3 documentation generation |
| MapStruct | Entity-DTO mapping code generation |
| Lombok | Boilerplate reduction |
| Testcontainers | Integration testing with real databases |
| ArchUnit | Architecture rule enforcement |
| Checkstyle / PMD | Code quality |
| JaCoCo | Code coverage |
| Gradle Enterprise / Maven | Build tooling |
| Spring Boot Actuator | Health checks, metrics |
No artifact produced. Next skill: backend-testing — test Spring Boot. Carry forward: package structure, DI configuration, persistence strategy (JPA/R2DBC).
function createModule<T>(config: ModuleConfig): T { const dependencies = initializeDependencies(config); const module = new Module(dependencies); module.hooks.onInit(); return module as T; }
class ConfigBuilder { private config: AppConfig = new AppConfig(); withDatabase(url: string): ConfigBuilder { ... } withCache(ttl: number): ConfigBuilder { ... } withLogging(level: string): ConfigBuilder { ... } build(): AppConfig { return this.config; } }
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% | Critical | Rollback or fix |
| p95 latency | > 500ms | Warning | Profile and optimize |
| Uptime | < 99.9% | Critical | Investigate infrastructure |
| Memory usage | > 80% | Warning | Check for leaks |
| CPU usage | > 80% | Warning | Scale up or optimize |