#Ranvier 설계 원칙
버전: 0.33.0 최종 업데이트: 2026-03-15 적용 대상: ranvier-core, ranvier-runtime, ranvier-http 카테고리: Architecture
#소개
이 문서는 Ranvier의 설계를 형성한 아키텍처 결정을 기록합니다. PHILOSOPHY.md가 Ranvier가 왜 존재하고 어떤 원칙이 사용을 안내하는지 설명한다면, 이 문서는 특정 기술적 선택을 어떻게 결정했는지 설명합니다.
각 결정은 맥락, 대안, 결과를 포함한 Architecture Decision Record (ADR) 형식으로 기록됩니다.
#목차
| ID | 제목 | 상태 | 날짜 |
|---|---|---|---|
| DP-1 | 패러다임 테스트: 크레이트 통합 | 승인됨 | 2025-10 |
| DP-2 | Tower 분리: Hyper 1.0 네이티브 | 승인됨 | 2025-09 |
| DP-3 | Opinionated Core: 비협상 패러다임 | 승인됨 | 2025-08 |
#DP-1: Paradigm Test: Crate Consolidation
상태: 승인됨 날짜: 2025-10 버전: v0.21.0
#맥락
Ranvier v0.20에는 23개의 크레이트가 있었으며, 각각 에코시스템 라이브러리의 얇은 추상화를 나타냈습니다:
ranvier-graphql(async-graphql 래핑)ranvier-grpc(tonic 래핑)ranvier-db(sqlx 래핑)ranvier-redis,ranvier-session,ranvier-cluster,ranvier-job등- 4개의 미들웨어 크레이트:
ranvier-auth,ranvier-guard,ranvier-observe,ranvier-multitenancy
문제점:
- 유지보수 부담: 23개의 크레이트를 버전 관리, 배포, 문서화, 유지보수해야 함
- 정체성 희석: 사용자가 어떤 크레이트가 "핵심 Ranvier"이고 어떤 것이 편의 래퍼인지 혼란스러워 함
- 거짓 추상화: 대부분의 래퍼 크레이트가 1-2개 파일의 얇은 래퍼로, 실질적인 가치를 제공하지 못함
- 에코시스템 중복: sqlx/tonic/redis가 이미 잘 제공하는 것을 재구현하고 있었음
질문: 어떤 크레이트가 Ranvier 패러다임에 필수적(Transition/Outcome/Bus/Schematic)이고, 어떤 것이 단순한 얇은 래퍼인가?
#결정
패러다임 테스트를 적용했습니다:
"이 크레이트가 존재하기 위해 근본적으로 Transition, Outcome, Bus, 또는 Schematic이 필요한가?"
테스트 결과:
✅
ranvier-core— Transition/Outcome/Bus/Schematic 정의 → 유지✅
ranvier-runtime— Axon (Transition 파이프라인) 실행 → 유지✅
ranvier-http— Transition을 위한 Ingress/Egress 경계 → 유지✅
ranvier-macros—#[transition]매크로 → 유지✅
ranvier-std— 표준 Transition (Guard 노드) → 유지✅
ranvier-inspector,ranvier-audit,ranvier-compliance,ranvier-openapi— 패러다임을 사용하는 확장 → 유지✅
kit/(facade 크레이트ranvier) — 편의 재내보내기 → 유지❌
ranvier-graphql— async-graphql을 재내보내기만 함 → 삭제❌
ranvier-grpc— tonic을 재내보내기만 함 → 삭제❌
ranvier-db,ranvier-redis,ranvier-session,ranvier-cluster,ranvier-job,ranvier-status→ 삭제❌ 4개의 미들웨어 크레이트 (auth/guard/observe/multitenancy) → core/std로 통합
결과: 23개 크레이트 → 10개 크레이트
#결과
긍정적:
- ✅ 명확성: 사용자가 핵심 Ranvier(10개 크레이트)와 에코시스템 도구를 즉시 구분 가능
- ✅ 유지보수: 배포, 버전 관리, 문서화할 크레이트가 13개 감소
- ✅ 정체성: Ranvier는 이제 "모든 것의 래퍼"가 아닌 명확한 "패러다임"
- ✅ 에코시스템 수용: 사용자가 sqlx, tonic, redis를 직접 사용 — 중간에 Ranvier 레이어 없음
- ✅ 유연성: 강제된 추상화 없음 — DB/캐시/큐 라이브러리를 자유롭게 선택
부정적:
- ⚠️ 마이그레이션 부담: v0.20 사용자는 import를 업데이트해야 함 (단, 명확한 마이그레이션 가이드 제공)
- ⚠️ 인식된 기능 손실: 일부 사용자가 기능을 "제거"했다고 생각할 수 있음 (실제로는 얇은 래퍼만 제거)
완화 조치:
- 마이그레이션 지침이 포함된 종합 CHANGELOG
- Transition에서 직접 sqlx/tonic/redis 사용을 보여주는 예제
- "Flexible Edges" 원칙을 설명하는 PHILOSOPHY.md
#검토된 대안
대안 A: 23개 크레이트 모두 유지
- ❌ 거부: 유지보수 부담이 너무 높고, 정체성 불명확
대안 B: 래퍼를 별도 조직으로 이동 (ranvier-contrib)
- ⚠️ 검토: 여전히 유지보수 필요, 사용자가 공식 지원을 기대할 수 있음
- ❌ 거부: 사용자를 에코시스템 직접 사용으로 안내하는 것이 나음
대안 C: 기존 크레이트를 폐기하되 계속 배포
- ⚠️ 검토: 마이그레이션이 더 쉬움
- ❌ 거부: 신규 사용자를 혼란스럽게 하고, 지속적 지원을 암시함
#참고 자료
- 통합 PR: ranvier#210
- 마이그레이션 가이드:
CHANGELOG.mdv0.21.0 섹션 - 예제 업데이트:
examples/db-sqlx-demo,examples/graphql-async-graphql-demo,examples/grpc-tonic-demo
#DP-2: Tower Separation: Hyper 1.0 Native
상태: 승인됨 날짜: 2025-09 버전: v0.21.0
#맥락
Ranvier v0.20은 HTTP 처리에 Tower (tower, tower-http)를 사용했습니다:
ranvier-http가 Tower의Service트레이트를 래핑- Tower의
Layer트레이트를 통한 미들웨어 합성 tower v0.4+tower-http v0.4의존성
문제점:
- Tower는 미들웨어이지 Ranvier의 패러다임이 아님: Tower의
Service<Request>트레이트는 Ranvier의Transition트레이트와 근본적으로 다릅니다. 둘을 혼합하면 사용자가 혼란스러워합니다. - 숨겨진 복잡성: Tower의
Layer순서와Service::poll_ready시맨틱은 복잡합니다. Ranvier의 가치 제안은 명시적 실행이지만, Tower는 제어 흐름을 숨깁니다. - Hyper 1.0 호환성: Hyper 1.0이
tower::Service지원을 제거 — Tower는 이제 선택 사항이지 필수가 아닙니다. - 의존성 비대화: 기본 HTTP에 Tower를 끌어오는 것은 Hyper 1.0이 네이티브 async fn 지원을 제공할 때 과도합니다.
질문: Ranvier가 Tower를 계속 필수로 할 것인가, 아니면 Hyper 1.0을 네이티브로 수용할 것인가?
#결정
Tower 의존성 제거. Hyper 1.0 직접 사용.
구현:
ranvier-http가 이제hyper::service::service_fn을 직접 사용Ingress트레이트가 HTTP 요청을 Axon 입력으로 매핑Egress트레이트가 Axon 출력을 HTTP 응답으로 매핑tower::Service구현 없음 — Ranvier는 Tower 미들웨어가 아님
사용자 마이그레이션 경로:
// v0.20 (Tower-based)
let app = ServiceBuilder::new()
.layer(CorsLayer::permissive())
.service(ranvier_handler);
// v0.21+ (Hyper native, optional Tower)
// Option A: Pure Ranvier
let app = Ranvier::http()
.route("/", axon)
.run(resources).await?;
// Option B: Hybrid (user wraps Ranvier with Tower)
let ranvier_service = /* Ranvier handler wrapped as Tower Service */;
let app = ServiceBuilder::new()
.layer(CorsLayer::permissive())
.service(ranvier_service);핵심 통찰: Tower는 이제 선택적 통합이지 필수 의존성이 아닙니다. Tower를 원하는 사용자는 Ranvier 핸들러를 래핑할 수 있지만, Ranvier 자체는 Tower에 의존하지 않습니다.
#결과
긍정적:
- ✅ 더 단순한 멘탈 모델: Ranvier는 Service 기반이 아닌 Transition 기반
- ✅ 가벼운 의존성: 사용자가 선택하지 않는 한 tower/tower-http 불필요
- ✅ Hyper 1.0 네이티브: Hyper의 async fn 개선 사항 활용
- ✅ 명확한 경계: HTTP는 Ingress/Egress에 있으며 핵심 패러다임이 아님
- ✅ 유연한 통합: 사용자가 필요 시 Tower/Axum/actix로 Ranvier를 래핑 가능
부정적:
- ⚠️ 호환성 깨짐: v0.20 사용자는 코드를 업데이트해야 함 (단, 마이그레이션은 간단)
- ⚠️ Tower 에코시스템 손실:
tower-http레이어를 직접 사용 불가 (단, 경계에서 통합 가능)
마이그레이션 지원:
- Tower 통합을 보여주는 예제 (
examples/auth-tower-integration) - 에코시스템 통합을 설명하는 PHILOSOPHY.md 섹션 3.1
- 비교 가이드 (
docs/guides/auth-comparison.md)
#검토된 대안
대안 A: Tower를 핵심 의존성으로 유지
- ❌ 거부: "Opinionated Core, Flexible Edges"에 모순 — Tower는 엣지 관심사
- ❌ 거부: 미들웨어가 필요하지 않은 사용자도 Tower를 학습해야 함
대안 B: Tower를 선택적 기능으로 만들기
- ⚠️ 검토:
ranvier-http/tower기능 - ❌ 거부: 여전히 Tower가 "공인된" 통합임을 암시, 실제로는 어떤 프레임워크든 작동해야 함
대안 C: Tower 및 비-Tower API 모두 제공
- ❌ 거부: 유지보수가 두 배, "올바른 방법"에 대해 사용자를 혼란스럽게 함
#참고 자료
- Tower 제거 PR: ranvier#210
- Hyper 1.0 마이그레이션: ranvier#195
- 통합 예제:
examples/auth-tower-integration,examples/http-hyper-native - 철학: PHILOSOPHY.md 섹션 3
#DP-3: Opinionated Core: Non-Negotiable Paradigm
상태: 승인됨 날짜: 2025-08 버전: v0.18.0+
#맥락
초기 Ranvier 버전(v0.1-v0.17)은 다양한 수준의 유연성을 실험했습니다:
- 선택적
#[transition]매크로 (사용자가Transition트레이트를 구현하거나 자유 함수를 사용할 수 있음) - 선택적 Bus (일부 파이프라인은 Bus를 사용하고, 다른 일부는 함수 인자를 사용)
- 선택적 Schematic 내보내기 (사용자가 JSON 생성을 선택 해제할 수 있음)
문제점:
- 일관성 없는 코드베이스: 일부 프로젝트는 Transition을 사용하고, 일부는 자유 함수를 사용 → 파편화된 에코시스템
- 도구 지원 불가: VSCode 확장이 Schematic의 존재를 가정할 수 없음 → Circuit 뷰 작동 불가
- 정체성 상실: "Ranvier가 Axum/Actix와 다른 점은?" → 불명확한 답변
- 문서화 부담: 모든 가이드가 "X 또는 Y 또는 Z를 할 수 있습니다"를 설명해야 함
질문: Ranvier가 유연해야(여러 패턴 지원) 하는가, 아니면 고집스러워야(하나의 패러다임 강제) 하는가?
#결정
Ranvier의 핵심 패러다임(Transition/Outcome/Bus/Schematic)은 비협상 사항입니다.
강제 사항:
- Transition 필수: 모든 비즈니스 로직은
#[transition]매크로 또는Transition트레이트를 사용해야 함 - Outcome 필수: Transition은
Result<T, E>가 아닌Outcome<T, E>를 반환해야 함 - Bus 필수: 모든 Axon 실행에 Bus가 있음 (비어 있더라도)
- Schematic 생성: 모든 Axon이 Schematic을 생성 (JSON 내보내기는 선택적이지만 구조는 존재)
왜 고집스러운가:
- 정체성: Ranvier = "Schematic 우선, 시각화 가능한 프레임워크" (고유한 가치 제안)
- 학습 곡선: 하나의 공인된 경로 → 더 빠른 온보딩 (PHILOSOPHY.md 섹션 2.2 참조)
- 일관성: 모든 Ranvier 코드베이스가 유사하게 보임 → 더 쉬운 코드 리뷰, 온보딩
- 도구 지원: VSCode 확장이 Schematic의 존재를 가정할 수 있음 → 안정적인 Circuit 뷰
유연하게 유지되는 부분:
- HTTP 서버 선택 (Hyper, actix, Axum) — Ingress/Egress
- 데이터베이스 선택 (sqlx, diesel, sea-orm) — Transition으로 래핑
- 비동기 런타임 (tokio, async-std) — Ranvier는 런타임에 무관
- 배포 (Docker, K8s, Lambda) — Ranvier는 단순한 Rust 코드
전체 "Opinionated Core, Flexible Edges" 설명은 PHILOSOPHY.md를 참조하세요.
#결과
긍정적:
- ✅ 명확한 정체성: Ranvier는 "Transition/Schematic 프레임워크" ("47번째 Rust 웹 프레임워크"가 아님)
- ✅ 일관된 에코시스템: 모든 예제, 튜토리얼, 프로젝트가 동일한 패턴을 따름
- ✅ 안정적인 도구: VSCode 확장, CLI 도구, 웹 대시보드 모두 Schematic의 존재를 가정
- ✅ 빠른 학습: 신규 사용자가 여러 경쟁 패턴이 아닌 하나의 경로를 따름
- ✅ 더 나은 디버깅: Schematic 시각화가 100% 작동 ("선택적 기능"이 아님)
부정적:
- ⚠️ 덜 유연함:
Result또는 자유 함수를 원하는 사용자는 Transition으로 래핑해야 함 - ⚠️ 높은 진입 장벽: 사소한 앱에는 "Transition을 배우는" 것보다 "Axum을 사용"하는 것이 더 단순
- ⚠️ Actix/Axum에서의 마이그레이션: Transition 패러다임을 채택해야 하며, 드롭인 대체가 아님
완화 조치:
- 하이브리드 접근: 사용자가 기존 앱에 Ranvier를 삽입 가능 (PHILOSOPHY.md 섹션 6.2 경로 3 참조)
- 명확한 문서: PHILOSOPHY.md 의사결정 트리가 "언제 Ranvier를 사용할지" 안내
- 예제: "순수 Ranvier"와 "하이브리드" 패턴 모두 표시
#검토된 대안
대안 A: 모든 것을 선택적으로 (최대 유연성)
- ❌ 거부: 정체성 상실, 도구 작동 불가, 코드베이스 불일관
대안 B: Transition과 자유 함수 모두 지원
- ⚠️ 검토: 마이그레이션이 더 쉬움
- ❌ 거부: 에코시스템 분열, 도구가 구조를 가정할 수 없음
대안 C: Opinionated core + 탈출구
- ⚠️ 검토: 예를 들어 비-Transition 코드용
Axon::raw_fn() - ❌ 거부: 모든 탈출구는 일관성의 구멍
대안 D (현재 접근 방식): Opinionated core + flexible edges
- ✅ 승인: 코어는 엄격 (Transition 필수), 엣지는 유연 (모든 HTTP/DB 라이브러리)
#참고 자료
- 패러다임 강제:
ranvier-corev0.18.0+ - 철학 문서: PHILOSOPHY.md
- 의사결정 프레임워크: PHILOSOPHY.md 섹션 5
- 예제: 모든 61개 예제가 Transition 패턴을 사용
#이 문서에 기여하기
새로운 설계 결정을 추가할 때:
- ADR 형식 사용: 맥락, 결정, 결과, 대안
- 목차 추가: 상단의 표 업데이트
- 코드에서 참조: 특정 DP-X에 링크하는 rustdoc 주석 추가
- 상호 참조: 관련된 곳에서 PHILOSOPHY.md에 링크 (PHILOSOPHY = 왜, DP = 어떻게)
템플릿:
## DP-X: [Title]
**Status:** Proposed | Accepted | Deprecated
**Date:** YYYY-MM
**Version:** vX.Y.Z
### Context
[Why did this decision come up? What problem were we solving?]
### Decision
[What did we decide? Be specific.]
### Consequences
**Positive**: [Benefits]
**Negative**: [Costs/trade-offs]
### Alternatives Considered
[What other options did we evaluate? Why rejected?]
### References
[Links to PRs, issues, discussions, code]#관련 문서
- PHILOSOPHY.md — 설계 철학 (Ranvier가 왜 존재하는지, 언제 사용하는지)
- README.md — 프로젝트 개요 및 빠른 시작
- CHANGELOG.md — 버전 이력 및 마이그레이션 가이드
- examples/README.md — 66개 참조 구현
이 문서는 Ranvier v0.33.0의 일부입니다. 최종 업데이트: 2026-03-15.