8.3 KiB
8.3 KiB
MVP Routing Implementation
Overview
Implement the Minimum Viable Product for the multimodal trip planning service, focusing on core routing functionality for single transport mode (trains) with maximum 1 transfer. This foundation will enable subsequent stages adding multimodality, deeper search, and station closure detection.
Problem it solves: Users can search for train routes between cities with up to 1 transfer, with basic GeoJSON map visualization and proper API caching to respect Yandex.Schedules API quota limits.
Key benefits:
- Core routing engine within API quota constraints
- Cache-aside pattern prevents API overuse
- GeoJSON output enables immediate map visualization
- TDD-guaranteed correctness for critical routing logic
Context (from discovery)
- Files/components involved:
internal/routing(graph, search algorithm, MCT rules),internal/yandex(API client with rate limiter, retries, circuit breaker),internal/cache(Redis cache-aside),cmd/api(HTTP handlers),cmd/cron(station status detection) - Related patterns: Lazy graph expansion with hub stations, BFS/Dijkstra with depth limiting (4-5 transfers max), Pareto-front ranking (time, transfers, cost), cache-aside with multi-layer TTL
- Dependencies: PostgreSQL for station/city directories, Redis for cache TTL, Yandex.Schedules API (
/search,/schedule,/nearest_stations,/stations_list)
Development Approach
- Testing approach: TDD (tests first) - user preference confirmed
- Complete each task fully before moving to the next
- Make small, focused changes with tests
- CRITICAL: every task MUST include new/updated tests for code changes in that task
- tests are not optional - they are a required part of the checklist
- write unit tests for new functions/methods
- write unit tests for modified functions/methods
- add new test cases for new code paths
- update existing test cases if behavior changes
- tests cover both success and error scenarios
- CRITICAL: all tests must pass before starting next task - no exceptions
- CRITICAL: update this plan file when scope changes during implementation
- Run tests after each change
- Maintain backward compatibility
Testing Strategy
- Unit tests: required for every task (see Development Approach above)
- E2E tests: project has UI-based e2e tests considerations:
- UI changes → add/update e2e tests in same task as UI code
- Backend changes supporting UI → add/update e2e tests in same task
- Treat e2e tests with same rigor as unit tests (must pass before next task)
- Store e2e tests alongside unit tests (or in designated e2e directory)
Progress Tracking
- Mark completed items with
[x]immediately when done - Add newly discovered tasks with ➕ prefix
- Document issues/blockers with ⚠️ prefix
- Update plan if implementation deviates from original scope
- Keep plan in sync with actual work done
What Goes Where
- Implementation Steps (
[ ]checkboxes): tasks achievable within this codebase - code changes, tests, documentation updates - Post-Completion (no checkboxes): items requiring external action - manual testing, changes in consuming projects, deployment configs, third-party verifications
- Checkbox placement: Checkboxes belong only in Task sections (
### Task N:or### Iteration N:). Do not put checkboxes in Success criteria, Overview, or Context — they cause extra loop iterations.
Implementation Steps
Task 1: Set up project structure and dependencies
- Initialize Go module (
go mod init trip-planner) if not already done - Add dependencies:
github.com/go-redis/redis/v8,github.com/jmoiron/sqlx(golang-jason/jason removed, using stdlib) - Configure Docker Compose for local development (API, Redis, PostgreSQL)
- Write basic Go project structure with go.mod, main.go, and internal packages
- Verify
go fmt ./...andgo vet ./...pass - Run initial tests - must pass
Task 2: Implement Yandex API client with rate limiter and circuit breaker
- Create
internal/yandex/client.gowith Yandex API wrapper - Implement token bucket rate limiter (configurable TPS limit)
- Implement circuit breaker pattern (states: closed, open, half-open)
- Add retry with exponential backoff for transient errors
- Write tests for rate limiter (token consumption, refill rate)
- Write tests for circuit breaker (state transitions, trip to open state)
- Write tests for retry (success after backoff, exhaustion)
- Run tests - must pass before task 3
Task 3: Implement cache-aside layer for reference data and search results
- Create
internal/cache/store.gowith Redis cache interface - Implement cache keys:
cities:{code},stations:{id},search:{from}:{to}:{date} - Implement cache-aside pattern: Redis → miss → Postgres/API → write-back to Redis
- Set TTL policies: cities/stations 30 days, search near-term 2-6 hours, search far-term 7 days
- Write tests for cache operations (get, set, invalidate, TTL expiry)
- Write tests for cache-aside pattern (cache hit, cache miss → API call → cache write)
- Run tests - must pass before task 4
Task 4: Implement routing graph and search algorithm (max 1 transfer)
- Create
internal/routing/graph.gowith Node and Edge types - Implement Node types: Station, City; Edge kinds: Flight (real), Synthetic
- Build graph from station directory (Postgres + Redis cache)
- Implement BFS/Dijkstra search with 1-transfer depth limit
- Apply MCT (Minimum Connection Time) rules from transfer_rules table
- Write tests for graph construction (node/edge creation, directory loading)
- Write tests for search algorithm (successful 1-transfer route, no-route case, 2-transfer rejected)
- Write tests for MCT rule application (different node types, city tiers, check-in types)
- Run tests - must pass before task 5
Task 5: Implement API handlers for MVP endpoints
- Create
cmd/api/handlers.gowith HTTP handlers - Implement
GET /v1/cities?query=- city autocomplete from cached directory - Implement
GET /v1/cities/{id}/stations- city stations including neighbors if main closed - Implement
POST /v1/routes/search- body: from_city_id, to_city_id, date; response: Pareto-optimal routes (time, transfers) - Implement
GET /v1/routes/{search_id}/{route_id}/geojson- geometry for map visualization - Implement
GET /v1/stations/{id}/status- current station status - Write handlers tests (success cases, error cases, input validation)
- Write integration tests (handler → cache → routing → API client flow)
- Run tests - must pass before task 6
Task 6: Implement cron job for station status detection
- Create
cmd/cron/station_status.godaily cron job - Query
/schedulefor each monitored station, count flights on upcoming dates - Implement closure detection: N consecutive days of zero trips (N=3 recommended) → status
closed - Implement reactivation: status
activewhen >0 trips appear - Write tests for cron logic (status transition, zero-flight detection, reactivation)
- Run tests - must pass before task 7
Task 7: End-to-end integration and full test suite
- Write integration tests connecting all components: API → cache → routing → Yandex client
- Write synthetic timetable fixtures for routing tests (no real API calls)
- Run full test suite:
go test ./... -cover - Verify coverage meets project standard (80%+)
- Fix any failing tests
- Run
go fmt ./...andgo vet ./...- all issues must be fixed - Final verification: manual API endpoint testing with curl or Postman (manual test - skipped, not automatable)
Post-Completion
Items requiring manual intervention or external systems - no checkboxes, informational only
Manual verification:
- Test API endpoints with sample requests
- Verify GeoJSON output format for map visualization
- Test cache hit/miss scenarios
- Test station status cron job behavior
External system updates:
- Docker Compose setup for local development (
docker-compose up -d) - Gitea Actions workflow
.gitea/workflows/deploy.ymlCI/CD pipeline - Docker image build and push configuration
- Watchtower configuration for auto-updates
Note: ralphex automatically moves completed plans to docs/plans/completed/