LockKey ArchUnit 규칙
LockKey VO 아키텍처 규칙 및 ArchUnit 테스트 가이드
1. 개요
1.1 위치
domain/src/test/java/
└── com/ryuqq/domain/architecture/vo/
└── LockKeyArchTest.java # LockKey VO 아키텍처 검증
1.2 테스트 태그
@Tag("architecture")
@Tag("domain")
@Tag("vo")
@Tag("lock")
1.3 실행 방법
# LockKey ArchUnit 테스트만 실행
./gradlew :domain:test --tests "*LockKeyArchTest" -x jacocoTestCoverageVerification
# 전체 VO ArchUnit 테스트 실행
./gradlew :domain:test --tests "*VOArchTest" --tests "*LockKeyArchTest"
# 태그 기반 실행
./gradlew :domain:test -PincludeTags=lock
2. ArchUnit 규칙 (10개)
2.1 필수 구조 규칙 (3개)
| 규칙 |
설명 |
검증 내용 |
| 규칙 1 |
LockKey 인터페이스 구현 |
implements LockKey 필수 |
| 규칙 2 |
Record 타입 필수 |
public record 사용 |
| 규칙 3 |
value() 메서드 필수 |
String value() 반환 |
2.2 네이밍 및 패키지 규칙 (2개)
| 규칙 |
설명 |
검증 내용 |
| 규칙 4 |
*LockKey 네이밍 |
OrderLockKey 형식 |
| 규칙 5 |
vo 패키지 위치 |
domain.[bc].vo 패키지 |
2.3 금지 규칙 (4개)
| 규칙 |
설명 |
검증 내용 |
| 규칙 6 |
Lombok 금지 |
@Data, @Value, @Builder 등 금지 |
| 규칙 7 |
JPA 금지 |
@Entity, @Embeddable 등 금지 |
| 규칙 8 |
Spring 어노테이션 금지 |
@Component, @Service 등 금지 |
| 규칙 9 |
Spring 의존성 금지 |
org.springframework.. 패키지 의존 금지 |
2.4 레이어 의존성 규칙 (1개)
| 규칙 |
설명 |
검증 내용 |
| 규칙 10 |
외부 레이어 의존 금지 |
application, adapter 패키지 의존 금지 |
3. DomainEvent와의 비교
LockKey는 DomainEvent와 동일한 상속 패턴을 따릅니다:
| 항목 |
DomainEvent |
LockKey |
| 기반 인터페이스 |
interface DomainEvent |
interface LockKey |
| 위치 |
domain/common/event/ |
domain/common/vo/ |
| 구현체 위치 |
domain/{bc}/event/ |
domain/{bc}/vo/ |
| 구현체 예시 |
OrderPlacedEvent |
OrderLockKey |
| 필수 메서드 |
occurredAt() |
value() |
| 타입 |
Record |
Record |
4. 올바른 구현 예시
4.1 기본 LockKey
// domain/order/vo/OrderLockKey.java
public record OrderLockKey(Long orderId) implements LockKey {
private static final String PREFIX = "lock:order:";
public OrderLockKey {
if (orderId == null || orderId <= 0) {
throw new IllegalArgumentException("orderId must be positive");
}
}
@Override
public String value() {
return PREFIX + orderId;
}
}
4.2 복합 키 LockKey
// domain/stock/vo/StockWarehouseLockKey.java
public record StockWarehouseLockKey(
Long productId,
Long warehouseId
) implements LockKey {
public StockWarehouseLockKey {
if (productId == null || productId <= 0) {
throw new IllegalArgumentException("productId must be positive");
}
if (warehouseId == null || warehouseId <= 0) {
throw new IllegalArgumentException("warehouseId must be positive");
}
}
@Override
public String value() {
return "lock:stock:" + productId + ":warehouse:" + warehouseId;
}
}
5. 위반 사례
5.1 Record가 아닌 경우
// ❌ 잘못된 예: class 사용
public class OrderLockKey implements LockKey {
private final Long orderId;
// ...
}
// ✅ 올바른 예: record 사용
public record OrderLockKey(Long orderId) implements LockKey {
// ...
}
5.2 LockKey 인터페이스 미구현
// ❌ 잘못된 예: LockKey 인터페이스 없음
public record OrderLockKey(Long orderId) {
public String value() { return "lock:order:" + orderId; }
}
// ✅ 올바른 예: LockKey 구현
public record OrderLockKey(Long orderId) implements LockKey {
@Override
public String value() { return "lock:order:" + orderId; }
}
5.3 잘못된 패키지 위치
// ❌ 잘못된 예: aggregate 패키지
package com.ryuqq.domain.order.aggregate;
// ✅ 올바른 예: vo 패키지
package com.ryuqq.domain.order.vo;
5.4 Lombok 사용
// ❌ 잘못된 예: Lombok 사용
@Value
public class OrderLockKey implements LockKey {
Long orderId;
}
// ✅ 올바른 예: Pure Java Record
public record OrderLockKey(Long orderId) implements LockKey {
// ...
}
6. 키 형식 규칙
6.1 형식
lock:{domain}:{id}
lock:{domain}:{entity}:{id}
lock:{domain}:{entity}:{id}:{sub-entity}:{sub-id}
6.2 예시
| 도메인 |
키 형식 |
예시 |
| 주문 |
lock:order:{orderId} |
lock:order:123 |
| 재고 |
lock:stock:item:{productId} |
lock:stock:item:456 |
| 재고+창고 |
lock:stock:{productId}:warehouse:{warehouseId} |
lock:stock:456:warehouse:1 |
7. 체크리스트
LockKey 구현 시
ArchUnit 테스트 실행 시