아키텍처
워크스페이스 구성과 각 구성 요소 간의 통신 방식.
AxumKit은 두 개의 바이너리와 공유 계약(contract) 크레이트들로 이루어진 Cargo 워크스페이스입니다.
두 개의 바이너리
server— API 서버입니다. routes → services → repositories → entities로 계층화되어 있으며, 권한, 미들웨어, extractor, 아웃바운드 클라이언트 등 횡단 관심사 모듈을 포함합니다.worker— 백그라운드 잡입니다. NATS JetStream 컨슈머와 크론 스케줄러로 구성됩니다.
두 바이너리는 서로를 절대 링크하지 않습니다. 경계를 넘나드는 모든 것은 전용 계약 크레이트에 위치하므로, 컴파일러가 불일치(drift)를 잡아냅니다:
| 크레이트 | 계약 |
|---|---|
job_queue | 잡 페이로드, 스트림/서브젝트/컨슈머 이름, 멱등한 스트림 생성. 두 바이너리 모두 시작 시 initialize_all_streams를 호출하므로, 부팅 순서와 무관하게 새 NATS에서도 동작합니다. |
notification_repository | 알림 이벤트와 수신자별 전달(delivery)을 기록하는 방식, 그리고 수신 설정 기반 필터링. |
search_index | Meilisearch 인덱스 uid와 문서 스키마. 워커가 SearchUser를 직렬화해 넣고, 서버가 동일한 구조체로 역직렬화해 꺼냅니다. |
entity / migration | SeaORM 엔티티와 스키마 자체. |
dto | 요청/응답 타입과 검증기. |
errors | Errors enum, 도메인별 핸들러 체인, protocol.rs의 와이어 코드. |
config | ServerConfig / WorkerConfig (환경 변수 기반이며, 누락된 변수를 한 번에 모두 보고합니다). |
constants | 캐시 키와 코드네임 enum (Permission, NotificationAction, ModerationAction). |
auth-core | 프로젝트에 종속되지 않는 암호화 프리미티브 (AEAD, 상수 시간 비교, keyed hash, 토큰). |
storage | 연산별 타임아웃이 설정된 R2/S3 클라이언트. |
서버 내부 구조
코드를 작성하기 전에 알아둘 만한 컨벤션:
- 핸들러는 데이터베이스를 직접 다루지 않습니다. 서비스가 트랜잭션을 소유하고 리포지토리를 호출합니다.
- 만료 시각이 있는 행(역할, 밴, 그룹 멤버십)은 읽는 시점에 필터링되며, 만료된 행은 이후 크론 잡이 정리합니다.
- 실패해도 되는(best-effort) 부수 효과(인덱싱, 알림, 캐시 무효화)는 커밋 이후
tokio::spawn으로 큐에 넣습니다 — 큐 장애가 사용자 요청을 실패시켜서는 안 됩니다. - 코드네임 스타일 값(
board:pin_post,user_mentioned)은constants에 정의된 Rust enum이며 Postgres에는 TEXT로 저장됩니다. 배리언트를 추가해도 마이그레이션이 필요 없고, 더 이상 파싱되지 않는 저장 문자열은 안전하게 거부(fail closed)됩니다.
에러 처리
서비스와 핸들러는 Errors를 반환하며, IntoResponse 구현이 각 배리언트를
도메인별 핸들러 체인을 거쳐 (status, "domain:code", details)로 매핑합니다.
클라이언트 에러(4xx)는 항상 details를 포함하고, 서버 에러(5xx)는 개발 환경 밖에서는
이를 숨깁니다. errors/src/protocol.rs의 문자열 코드는 프런트엔드를 위한 안정적인
와이어 계약입니다.