본문으로 건너뛰기

Research 도메인 모델

개요

Research 도메인의 상세한 도메인 모델을 정의합니다. 본 문서는 requirements.mdevent-storming.md에서 합의된 용어와 규칙을 기준으로 합니다.

주의: 본 도메인 문서는 Position 기반 역할 분류를 사용하지 않습니다.
조회 스코프는 IAM 권한 평가 결과(그룹/권한) + siteId 컨텍스트로 결정됩니다.


1. 도메인 엔티티(Entity)

1.1 ResearchParticipant

연구 시트에 포함되어 연구 대상으로 관리되는 사용자입니다.

식별자:

  • participantId: ResearchParticipant 식별자(내부)
  • userId: 슬립큐 사용자(앱 사용자) 식별자(외부 컨텍스트 참조)
  • siteId: 병원(Site) 식별자(외부 컨텍스트 참조)

핵심 속성:

  • randomizationNumber: 무작위번호(병원 단위 유니크)
  • memo: 메모(단일 최신본)
  • enrolledAt: 연구 시트 포함 시각
  • removedAt: 연구 시트 제외 시각(선택)
  • nonCompliance: 불성실 상태(파생/상태)

행동(Behavior):

  • enrollFromClinical(): 진료용 환자에서 연구 참여자로 포함
  • createForResearch(): 임상 연구용 신규 등록으로 포함
  • updateRandomizationNumber(): 무작위번호 변경(검증/중복 포함)
  • updateMemo(): 메모 변경(최신본만)
  • removeFromResearchList(): 연구 리스트 제외

2. 값 객체(Value Object)

2.1 RandomizationNumber

무작위번호는 연구 참여자의 식별 코드입니다.

  • 유니크 스코프: siteId + randomizationNumber
  • 검증: 입력이 1자 이상인 경우를 최소 요건으로 하되, 연구 종류에 따라 포맷 규칙이 확장될 수 있음
  • UX: 클라이언트는 0.5초 디바운스 후 중복/유효성/가용성 메시지 표시(요구사항 참조)

2.2 Memo

연구 참여자의 특이사항 메모입니다.

  • 단일 최신본만 저장
  • 이력 제공하지 않음

3. 상태/열거형(Conceptual Enums)

3.1 ResearchScopeType

연구 데이터 조회 스코프의 유형입니다.

  • ALL_SITES: 모든 임상연구 참여 Site 조회 가능
  • SITE_SCOPED: 본인 소속 Site 범위만 조회 가능

스코프 결정은 IAM 권한 평가 결과(그룹/권한) + siteId 컨텍스트로 이루어집니다.


4. 도메인 서비스(Domain Service)

4.1 ResearchAccessService

책임:

  • clinicalResearch=true 여부 확인 (Site 컨텍스트 의존)
  • IAM 평가 결과 및 siteId 컨텍스트를 기반으로 스코프 결정

4.2 CardMetricsDerivationService

책임:

  • 연구 참여자 카드에 필요한 파생 지표 산출
  • 산출 규칙은 Research 도메인 요구사항을 따른다.

4.3 NonComplianceEvaluationService

책임:

  • 불성실 판정(수면기록률, 마지막 설문 등)을 평가하고 상태/사유를 결정

5. 읽기 모델(Read Model)

5.1 ResearchParticipantCardView

연구 홈 화면 카드 렌더링을 위한 조회 모델입니다.

구성 예시:

  • participant: 식별/표시 정보(이름, 생년월일, site, 담당의 등)
  • randomizationNumber, memo
  • metrics: 치료주차, 접속 횟수/42, 설문 n/4, 수면기록 A/B, 복용 A/B, 준수율, 주차 평균(SE/WASO/TST/SOL)
  • nonCompliance: 전체 불성실 여부 및 세부 사유(수면기록/설문)

6. 도메인 이벤트(개념 정의)

이벤트 이름/페이로드는 시스템 구현에 따라 달라질 수 있으며, 본 섹션은 도메인 수준의 개념 정의입니다.

type ISO8601 = string;

export interface ResearchParticipantImported {
type: 'research.participant.imported';
eventId: string;
timestamp: ISO8601;
payload: {
participantId: string;
userId: string;
siteId: string;
randomizationNumber: string;
enrolledAt: ISO8601;
};
}

export interface RandomizationNumberUpdated {
type: 'research.participant.randomization-number.updated';
eventId: string;
timestamp: ISO8601;
payload: {
participantId: string;
siteId: string;
randomizationNumber: string;
updatedAt: ISO8601;
};
}

export interface ParticipantMemoUpdated {
type: 'research.participant.memo.updated';
eventId: string;
timestamp: ISO8601;
payload: {
participantId: string;
memo: string;
updatedAt: ISO8601;
};
}

변경 이력

버전날짜작성자변경 내용
0.1.02025-12-29jeff@weltcorp.com최초 작성