說明

Service classes must depend on interfaces (abstractions) for external dependencies, not on concrete implementations. This applies to:

  • Repository interfaces (not JPA implementations)
  • External API clients (not HTTP client implementations)
  • Message producers/consumers (not queue client implementations)

為什麼重要

  • Testability — Mocks/stubs can be injected in unit tests
  • Flexibility — Implementation can be swapped without changing Service code
  • Compile-time safety — Interface changes surface at compile time, not runtime

Enforcement

@ArchTest
static final ArchRule dependencyInversionRule = classes()
    .that().resideInAPackage("..service..")
    .and().areNotInterfaces()
    .should().onlyDependOnClassesThat()
    .resideInAnyPackage(
        "..service..",       // own package
        "..model..",         // domain types
        "java..",            // JDK
        "javax..",
        "org.springframework..",
        "org.springframework.web.."
    );

正例

@Service
public class OrderService {
    private final OrderRepository orderRepository; // ✅ interface

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
}

反例

@Service
public class OrderService {
    private final JpaOrderRepository jpaOrderRepository; // ❌ concrete implementation

    public OrderService(JpaOrderRepository jpaOrderRepository) {
        this.jpaOrderRepository = jpaOrderRepository;
    }
}

相關規則