說明
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;
}
}
相關規則
- ARCH-001: Layering — the layering rule this builds upon