아키텍처¶
App Backend는 REST 컨트롤러, command/query 서비스, Spring Data JPA 저장소를 분리한 DDD·CQRS-lite 구조입니다. 요청 인증 뒤 워크스페이스 컨텍스트를 해석하고, 서비스 계층의 트랜잭션 안에서 PostgreSQL을 읽거나 씁니다.
flowchart LR
WEB["웹·운영 클라이언트"]
APP["App Backend<br/>REST :8080"]
AUTH["Spring Security<br/>JWT · API key · tenant"]
SVC["Command / Query<br/>도메인 서비스"]
DB[("공유 PostgreSQL<br/>스키마 정본: sohri-db")]
GB["Graph Builder<br/>POST /v1/session/validate"]
SOCKET["Socket Backend<br/>WebSocket·실시간 파이프라인"]
GCS[("GCS 음성 아카이브")]
OBS["Actuator :9090<br/>Prometheus · Trace"]
WEB -->|"HTTPS REST"| APP
APP --> AUTH --> SVC
SVC <--> DB
SVC -->|"템플릿 등록 전 검증만"| GB
SOCKET -->|"대화·메시지·분석 결과 쓰기"| DB
APP -->|"음성 프록시 읽기"| GCS
APP --> OBS
BOUNDARY["App ↔ Socket 직접 HTTP 없음<br/>공유 DB·계약으로만 결합"]
서비스 경계¶
App Backend가 소유하는 경로
- 인증과 회원 셀프서비스
- 워크스페이스, API key, client-secret 발급
- 시나리오·시나리오 이력과 전역 템플릿 카탈로그
- 대화·분석·통계의 REST 읽기 모델
- GCS 보관 음성의 인증 프록시
다른 서비스가 소유하는 경로
- Socket Backend: WebSocket, EPD/STT, DM 턴 처리, TTS, 분석과 DB 기록
- Graph Builder: 시나리오 그래프 런타임 유효성 판정
sohri-db: 테이블·인덱스·제약·파티션의 물리 정본
App Backend는 Socket Backend의 HTTP API를 호출하지 않습니다. 두 서비스의 통합 지점은
공유 PostgreSQL과 client-secret·scenario·voicePreset·error 계약입니다.
요청 처리 계층¶
JwtAuthenticationFilter가Authorization: Bearer ...를 HMAC JWT 또는 워크스페이스 API key로 분류해 인증합니다.TenantResolutionFilter가 API key의 귀속 워크스페이스 또는 JWT 요청의X-Workspace-Id를TenantContext에 설정합니다.endpoint/*Api가 요청 형식과 메서드별 권한을 적용합니다.- command/query 서비스가 트랜잭션과 도메인 규칙을 담당합니다.
- JPA 저장소가 PostgreSQL을 접근하고 전역 예외 처리기가 응답 계약으로 변환합니다.
테넌트 컨텍스트가 필요한 서비스는 TenantContext.require()로 헤더 누락을 400으로
거절합니다. 단, 모든 API가 같은 방식을 쓰는 것은 아니므로 자세한 적용 범위는
워크스페이스와 테넌시를 확인하세요.
데이터 소유권¶
spring.jpa.hibernate.ddl-auto=none과 spring.sql.init.mode=never가 운영 스키마 생성을
막습니다. 저장소의 src/main/resources/schema.sql은 PostgreSQL 통합 테스트용 커밋 사본이고,
운영 변경은 반드시 sohri-db에서 시작해야 합니다. App Backend와 Socket Backend의 엔티티가
공유 계약과 어긋나지 않도록 정본 변경 뒤 양쪽 매핑을 함께 검증해야 합니다.
대화 데이터의 읽기/쓰기 분리는 대화·분석 조회, 인증 경계는 로그인과 토큰 인증을 참고하세요.