說明

The codebase follows a strict three-layer architecture:

  1. Controller — handles HTTP requests, delegates to Service
  2. Service — contains business logic, delegates to Repository
  3. Repository — handles data access

Dependencies must flow downward only. A Controller may never import a Repository class directly. A Service may never import a Controller class.

為什麼重要

  • Testability — Services can be tested without HTTP; Repositories can be tested without business logic
  • Replaceability — Swapping a REST Controller for a gRPC Controller only touches the Controller layer
  • Cognitive load — Developers know exactly where to put new code and where to look for existing logic

Enforcement

@ArchTest
static final ArchRule layeringRule = layered()
    .consideringAllDependencies()
    .that().resideInAPackage("..controller..")
    .should().onlyDependOnClassesThat()
    .resideInAnyPackage(
        "..service..",
        "..model..",
        "java..",
        "javax..",
        "org.springframework.."
    );

Rule: java:S2583 (Make "class diamond" visits only through "interface" types)

This rule catches direct Repository references from Controller classes.

正例

@RestController
public class OrderController {
    private final OrderService orderService; // ✅ depends on Service layer

    @GetMapping("/orders/{id}")
    public ResponseEntity<OrderDto> getOrder(@PathVariable Long id) {
        return ResponseEntity.ok(orderService.getOrder(id));
    }
}

反例

@RestController
public class OrderController {
    private final OrderRepository orderRepo; // ❌ bypasses Service layer

    @GetMapping("/orders/{id}")
    public ResponseEntity<OrderDto> getOrder(@PathVariable Long id) {
        return ResponseEntity.ok(orderRepo.findById(id));
    }
}

相關規則