[BE-03] 대기질 도메인 계약 — CAI 등급 VO + Gateway·Cache 인터페이스 + DomainService

작업 내용 (설계 의도)

변경 사항

신규 airquality 바운디드 컨텍스트의 순수 도메인 계약을 확립한다. 대기질 후행 티켓(Gateway 구현·UseCase/Controller)이 import하는 병목이라 wave 1에 배치한다. facility·region과 파일·패키지가 완전히 독립이라 BE-01과 병렬. 근거 TDD: ../TDD.md FR-11 등급 기준·“인터페이스 시그니처”.

  • domain/airquality/vo/AirQualityGrade.kt — CAI 4단계 enum(GOOD/MODERATE/BAD/VERY_BAD/UNKNOWN). ofPm10(0-30/31-80/81-150/151+), ofPm25(0-15/16-35/36-75/76+), worseOf(a,b), isBadOrWorse(). 경계값·null 처리 캡슐화(매직넘버는 상수).
  • domain/airquality/vo/AirQualityMeasurement.kt — 원시 측정값(pm10/pm25/stationName/measuredAt nullable) + empty().
  • domain/airquality/vo/AirQuality.kt — 등급 포함 값 + of(measurement)(등급 조립·대표 등급) + empty()(UNKNOWN).
  • domain/airquality/gateway/AirQualityGateway.ktcurrent(lat,lng): AirQualityMeasurement(실패 시 empty 계약 주석).
  • domain/airquality/repository/AirQualityMeasurementCache.ktfindBy(gridKey), save(gridKey, measurement).
  • domain/airquality/service/AirQualityDomainService.ktcurrent(lat,lng): AirQuality(gateway 위임 + AirQuality.of). 시간 타입은 ZonedDateTime.

의존

  • 없음

다이어그램

클래스 의존

flowchart LR
    Grade[AirQualityGrade]
    AQ[AirQuality VO]
    M[AirQualityMeasurement VO]
    Gw[AirQualityGateway iface]
    Cache[AirQualityMeasurementCache iface]
    Svc[AirQualityDomainService]
    AQ --> Grade
    AQ --> M
    Svc --> Gw
    Svc --> AQ

테스트 케이스

  • PM10 80은 MODERATE, 81은 BAD로 등급화된다(경계값)
  • PM2.5 76은 VERY_BAD, PM10 값이 null이면 UNKNOWN으로 등급화된다
  • pm10 BAD·pm25 MODERATE이면 대표 등급은 더 나쁜 BAD가 된다
  • pm10만 존재하고 pm25가 null이면 대표 등급은 pm10 등급을 따른다(UNKNOWN 제외)
  • AirQualityDomainService는 gateway가 empty를 반환하면 representativeGrade UNKNOWN인 AirQuality를 반환한다