Last Updated: 2025-11-14
Status: Production Ready
Version: 1.0
Gaji (κ°μ§, Korean for "branch") is a "What If?" storytelling platform where users explore alternative timelines in classic literature through AI-powered conversations with characters adapted to hypothetical scenarios.
Core Innovation: Git-style forking applied to book discussions
- Scenario Forking: Unlimited depth meta-scenarios
- Conversation Forking: ROOT-only (depth 1) with 6-message context
- AI Adaptation: Characters know complete story for consistent responses
Frontend (Vue.js :443)
β HTTPS /api/*
Spring Boot :8080 (API Gateway + Business Logic)
β Internal WebClient
FastAPI :8000 (AI Service - Internal Only)
β
VectorDB + Gemini API
Why Pattern B?
| Factor | Weight | Score | Key Benefit |
|---|---|---|---|
| Security | 30% | 10/10 | FastAPI not exposed, API keys protected |
| Simplicity | 25% | 10/10 | 1 API client, centralized auth/CORS |
| Performance | 20% | 8/10 | +50ms overhead (1% on 5s AI tasks) |
| Cost | 15% | 9/10 | -$700/year (SSL/domains) |
| Operations | 10% | 9/10 | Centralized logging |
| Total | 100% | 9.25/10 | Winner |
Decision: Spring Boot (PostgreSQL) + FastAPI (VectorDB)
- Spring Boot: User management, CRUD operations, business logic
- FastAPI: AI/ML, RAG, VectorDB, Gemini integration
- Rationale: Python dominates AI ecosystem, Java excels at enterprise logic
Decision: PostgreSQL (metadata) + VectorDB (content/embeddings)
Data Distribution:
- PostgreSQL: 13 tables (users, novels, scenarios, conversations, messages)
- VectorDB: 5 collections (passages, characters, locations, events, themes)
Performance: 10x faster semantic search vs pgvector on 768-dim embeddings
Decision: Frontend β Spring Boot Only β FastAPI (Internal)
Implementation:
// Spring Boot: AIProxyController
@PostMapping("/api/ai/search/passages")
public Mono<ResponseEntity<PassageSearchResponse>> searchPassages(
@RequestBody PassageSearchRequest request
) {
return fastApiClient.post()
.uri("/api/ai/search/passages")
.bodyValue(request)
.retrieve()
.toEntity(PassageSearchResponse.class);
}Impact:
- π Security: -50% attack surface
- π° Cost: -$700/year
- π― Simplicity: 2 API clients β 1
- β‘ Performance: +50ms (+1% on AI tasks)
Decision: Copy min(6, total) messages on fork
Rationale:
- Gemini 2.5 Flash: ~2000 token context recommended
- 6 messages β 600 tokens
- Users remember 2-3 recent turns
Storage: Reuse messages via conversation_message_links join table
Decision: Separate repositories for each service (Multirepo)
Structure:
gaji-core-backend/ # Repository 1: Spring Boot
βββ src/main/java/
βββ src/main/resources/
βββ build.gradle
βββ Dockerfile
gaji-ai-backend/ # Repository 2: FastAPI
βββ app/
βββ requirements.txt
βββ Dockerfile
gaji-frontend/ # Repository 3: Vue.js (Current: gajiFE)
βββ src/
βββ package.json
βββ docs/ # Project documentation
β βββ epics/ # Epic-level documentation
β βββ stories/ # Story-level implementation details
β βββ PRD.md # Product Requirements Document
β βββ ARCHITECTURE.md # This file
β βββ ...
βββ Dockerfile
gaji-api-contracts/ # Repository 4: OpenAPI specs (shared)
βββ openapi.yaml
Benefits:
- Independent deployment cycles
- Clear ownership boundaries
- Easier CI/CD pipelines per service
- Better suited for team growth (3+ developers)
- Documentation co-located with frontend code for easier access
Trade-offs:
- Type sharing via npm/Maven packages from api-contracts repo
- Cross-service changes require multiple PRs
- No monorepo build caching
Documentation Strategy:
- Epic files (
docs/epics/) provide high-level feature descriptions and business value - Story files (
docs/stories/) contain detailed acceptance criteria and implementation guides - See
docs/EPIC_STORY_ALIGNMENT_SUMMARY.mdfor cross-reference mapping
Decision: Server-Sent Events for AI message streaming
Performance:
- Before: 15 polls/conversation (450 requests)
- After: 1 SSE connection
- Improvement: 93% fewer requests
Implementation:
// Frontend
const eventSource = new EventSource(`/api/ai/stream/${id}`);
eventSource.onmessage = (event) => appendToken(event.data);// Spring Boot Proxy
@GetMapping(value = "/ai/stream/{id}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamMessage(@PathVariable UUID id) {
return fastApiClient.get()
.uri("/api/ai/stream/" + id)
.retrieve()
.bodyToFlux(String.class)
.map(token -> ServerSentEvent.<String>builder().data(token).build());
}| Component | Technology | Port | Purpose |
|---|---|---|---|
| API Gateway | Spring Boot 3.x | 8080 | Single entry point |
| AI Service | FastAPI 0.110+ | 8000 | RAG, VectorDB, Gemini |
| Task Queue | Celery + Redis | 6379 | Async AI operations |
| Component | Technology | Access |
|---|---|---|
| Metadata DB | PostgreSQL 15.x | Spring Boot only |
| Content DB | ChromaDB/Pinecone | FastAPI only |
- Framework: Vue 3 + TypeScript
- UI Library: PrimeVue
- Styling: Panda CSS
- State: Pinia
- Router: Vue Router
- LLM: Gemini 2.5 Flash
- Embeddings: Gemini Embedding API (768-dim)
- RAG: Custom FastAPI service
Gutenberg File β FastAPI Parse β Chunk Text
β Gemini Embeddings β VectorDB Storage
β Gemini LLM Analysis (characters/locations/events)
β Spring Boot Metadata Update
User Request β Spring Boot
β FastAPI VectorDB Search (similar passages)
β Spring Boot Save (PostgreSQL with passage_ids)
Frontend β Spring Boot
β FastAPI Async (Celery)
β VectorDB Query (character + passages)
β Gemini 2.5 Flash
β Spring Boot Save Messages
β SSE Stream to Frontend
Impact: 40% response time reduction (520ms β 310ms)
@CircuitBreaker(name = "fastapi", fallbackMethod = "fallbackResponse")
public Mono<Response> callFastAPI() { ... }Impact: 99.9% availability
@Cacheable(value = "passages", key = "#novelId + ':' + #query")
public List<Passage> searchPassages(UUID novelId, String query) { ... }Impact: 60% DB load reduction, 70% faster repeated queries
Impact: 5x concurrency (200 β 1000 users)
| Item | Cost |
|---|---|
| SSL + Domain (1 domain) | $215 |
| Load Balancer (1 instance) | $120 |
| Total | $335 |
| Savings vs Pattern A | -$335 |
| Operation | Cost |
|---|---|
| Gemini 2.5 Flash Text | $15 |
| Gemini Embedding | $5 |
| VectorDB (ChromaDB self-hosted) | $0 |
| VectorDB (Pinecone cloud) | $70/month |
-
API Gateway Protection
- FastAPI port 8000 internal only
- Gemini API keys in Spring Boot only
- Single CORS origin
-
Authentication
- JWT tokens (Spring Security)
- Role-based access control
- Redis session management
-
Rate Limiting
- 10 requests/minute/user (Resilience4j)
-
Input Validation
@Validannotations (Spring)- Pydantic models (FastAPI)
| Phase | Epic | Hours | Focus |
|---|---|---|---|
| 1 | Epic 0 | 54h | Infrastructure, Novel Ingestion, LLM Setup |
| 2 | Epic 1-2 | 80h | Scenarios, AI Adaptation |
| 3 | Epic 3-4 | 72h | Discovery, Conversation System |
| 4 | Epic 5 | 24h | Tree Visualization |
| 5 | Epic 6 | 60h | Auth, Social Features |
| Total | 0-6 | 290h | ~12 weeks |
| Metric | Target |
|---|---|
| API Response Time (P95) | < 500ms |
| AI First Token | < 1000ms |
| Error Rate | < 0.1% |
| Test Coverage | > 80% |
| Metric | MVP | Beta |
|---|---|---|
| Daily Active Users | 10 | 100 |
| Scenarios Created | 50 | 500 |
| Conversations | 100 | 1000 |
- README.md - Project overview
- CLAUDE.md - AI development guide
- architecture.md - Detailed architecture
- DEVELOPMENT_SETUP.md - Local setup
- MSA_BACKEND_OPTIMIZATION.md - Optimization strategies
- DATABASE_STRATEGY_COMPARISON.md - DB design
- PRD.md - Product requirements
- ERD.md - Database schema
- API_DOCUMENTATION.md - API reference
- TESTING_STRATEGY.md - Testing guidelines
- UI_UX_SPECIFICATIONS.md - Design specs
- Implement AIProxyController (16h)
- Update Frontend API client (8h)
- Infrastructure updates (4h)
- Testing (12h)
- Spring Boot + FastAPI setup
- PostgreSQL + VectorDB setup
- Docker configuration
- Novel ingestion pipeline
- LLM character extraction
- Epic 1: Scenario Foundation
- Epic 2: AI Character Adaptation
Status: Ready for Implementation π
Next Review: After Pattern B migration