스프링 내부 이벤트의 리스너 패키지 위치
ApplicationEventProducer로 스프링 내부 이벤트를 발행하고,
이 이벤트를 수신해서 트랜잭션이 커밋되고 나서
카프카 이벤트를 발행하는 로직이 있었다.
그때, 이 리스너를 어디에 둘까에 대한 고민이 있었는데
원래는 응용 계층에 뒀었다.
하지만 클린아키텍처 측면에서 보면 이 또한 원칙상 인프라에 두는 게 맞는데
이는 내부 로직을 처리하는 리스너라서 응용계층에 두어도 상관은 없었다.
하지만 헥사고날로 전환한다면 그때는 응용계층에 두지 못한다고 한다.
도메인에서 검증하는 종류
사실 null체크는 응용계층에서 해야하고, 도메인에서는 좀 더 비즈니스 로직에 대한 검증이 필요하다.
public class Competition {
private Competition(
UUID competitionId,
String name,
CompetitionType type,
CompetitionStatus status,
String description,
int firstSeed,
boolean isReadable,
RegisterPeriod registerPeriod,
CompetitionPeriod competitionPeriod,
ParticipantCount participantCount
) {
this.competitionId = competitionId;
this.name = name;
this.type = type;
this.status = status;
this.description = description;
this.firstSeed = firstSeed;
this.isReadable = isReadable;
this.registerPeriod = registerPeriod;
this.competitionPeriod = competitionPeriod;
this.participantCount = participantCount;
validate();
}
private void validate() {
if (name == null || name.isBlank()) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
if (type == null) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
if (description == null || description.isBlank()) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
if (firstSeed <= 0) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
if (registerPeriod == null) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
if (competitionPeriod == null) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
if (registerPeriod.getEndAt().isAfter(competitionPeriod.getStartAt())) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
if (participantCount == null) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
}
}
지금은 도메인에서 null체크를 하고 있지만, 도메인에서 처리해야 하는 기능이 많아지면
응용으로 옮기고 도메인에선 다른 것에 집중할 수 있도록 리펙토링 할 것 같다.
RESTful API 엔드포인트 네이밍
보통 status를 변경하는 로직은 PATCH로 처리하는데, 상태도 변경하고 이벤트도 발행하는 등 여러 로직을 처리해야 한다면
POST로 처리하고 앤드포인트로 /starts , /cancellations 등 명사의 복수형으로 끝내줘서
~~ 이벤트를 생성한다.라고 할 수 있다.
본래 post는 생성/등록이라는 의미를 가지기 때문이다.
@PostMapping("/{id}/starts")
public FindCompetitionResponse startCompetition(@PathVariable UUID id) {
Competition competition = competitionService.startCompetition(id);
return FindCompetitionResponse.from(competition);
}
@PostMapping("/{id}/finishes")
public FindCompetitionResponse finishCompetition(@PathVariable UUID id) {
Competition competition = competitionService.finishCompetition(id);
return FindCompetitionResponse.from(competition);
}
@PostMapping("/{id}/cancellations")
public FindCompetitionResponse cancelCompetition(@PathVariable UUID id) {
Competition competition = competitionService.cancelCompetition(id);
return FindCompetitionResponse.from(competition);
}
기존의 앤드포인트는 PATCH /{id} 였지만 -> POST /{id}/starts로 변경했다.
'내배캠' 카테고리의 다른 글
| TIL - 작업하던 브랜치에서 바로 git pull origin dev를 하면? (0) | 2026.05.08 |
|---|---|
| TIL - 프로젝트에 같은 모듈을 중복으로 import 하면? (0) | 2026.05.08 |
| TIL - 대회/랭킹 도메인 흐름 정리 (0) | 2026.05.06 |
| TIL - Redis 및 Kafka 사용 시 DB와의 분산트랜잭션 문제 해결하기 (0) | 2026.05.02 |
| 전자기기로 이해하는 헥사고날 아키텍처 (1) | 2026.04.30 |