A modern web-based vocabulary learning tool inspired by Anki, built with Go and PostgreSQL.
Frontend repo »
Issues
·
Pull Requests
Table of Contents
Repeatro is a modern, open-source vocabulary learning app inspired by Anki. It leverages spaced repetition (SM2 algorithm) to help users efficiently retain vocabulary. Organize your words into decks, track your progress, and enjoy a simple, effective learning experience.
- Spaced repetition for efficient vocabulary retention (SM2 algorithm)
- JWT-based user authentication
- Decks to organize vocabulary by topic or language
- RESTful API with Swaggo auto-generated Swagger docs
- Go
- PostgreSQL
- Swaggo (API docs)
- lingua-go (language detection)
- JWT (authentication)
Repeatro follows a microservices architecture pattern, designed for scalability, maintainability, and clear separation of concerns. The system is composed of 5 main services that communicate via gRPC for internal operations and expose a unified REST API through an API gateway.
┌─────────────────┐ HTTP/REST ┌─────────────────┐
│ Frontend │ ◄────────────── │ Repeatro │
│ (Flutter) │ │ (API Gateway) │
└─────────────────┘ └─────────────────┘
│
gRPC │
┌────────────────────────┼────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ SSO │ │ Card │ │ Deck │
│ Service │ │ Service │ │ Service │
│(Auth/Users) │ │(Vocabulary) │ │(Collections)│
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ │ │
└────────────────────────┼────────────────────────┘
│
▼
┌─────────────┐
│ Stats │
│ Service │
│ (Analytics) │
└─────────────┘
- Repeatro (API Gateway): HTTP REST endpoints, request routing, response aggregation, Swagger documentation
- SSO Service: User authentication, JWT token management, authorization, admin role management
- Card Service: Individual vocabulary card CRUD, SM2 spaced repetition algorithm, card expiration logic
- Deck Service: Card collections management, deck organization, card-to-deck relationships
- Stats Service: Learning analytics, progress tracking, performance metrics, review history
- External Communication: REST API with JSON payloads, CORS-enabled for web clients
- Internal Communication: gRPC with Protocol Buffers, type-safe service contracts
- Authentication: JWT tokens passed through gRPC metadata for service-to-service auth
- Service Discovery: Direct addressing with configurable endpoints (Consul integration planned)
Each service follows Clean Architecture principles with consistent layering:
- Presentation Layer: gRPC controllers, HTTP handlers
- Business Layer: Domain services, business logic
- Data Layer: GORM repositories, PostgreSQL integration
- Cross-cutting: Logging (slog), security, configuration management
Repeatro implements a database-per-service pattern where each microservice owns its data domain while maintaining logical relationships through application-level coordination.
- Service Autonomy: Each service manages its own PostgreSQL schema
- Eventual Consistency: Cross-service data consistency through event-driven updates
- ACID Compliance: Local transactions within service boundaries
- UUID Primary Keys: Distributed system-friendly identifiers
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ SSO Service │ │ Card Service │ │ Deck Service │
│ │ │ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ User │ │ │ │ Card │ │ │ │ Deck │ │
│ │ + ID (PK) │ │ │ │ + CardId │ │ │ │ + DeckId │ │
│ │ + Email │ │ │ │ + CreatedBy │ │ │ │ + CreatedBy │ │
│ │ + PassHash │ │ │ │ + Word │ │ │ │ + Name │ │
│ │ + IsAdmin │ │ │ │ + Translation│ │ │ │ + Cards[] │ │
│ └─────────────┘ │ │ │ + Easiness │ │ │ └─────────────┘ │
│ │ │ │ + Interval │ │ │ │
│ ┌─────────────┐ │ │ │ + DeckID │ │ └─────────────────┘
│ │ App │ │ │ │ + ExpiresAt │ │ │
│ │ + ID (PK) │ │ │ └─────────────┘ │ │
│ │ + Name │ │ │ │ │
│ │ + Secret │ │ └─────────────────┘ │
│ └─────────────┘ │ │ │
└─────────────────┘ │ │
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Stats Service │ │ Cross-Service │
│ │ │ Relationships │
│ ┌─────────────┐ │ │ │
│ │ Review │ │ │ User 1:N Card │
│ │ + ResultId │ │ │ User 1:N Deck │
│ │ + UserID │ │ │ Deck 1:N Card │
│ │ + CardID │ │ │ Card 1:N Review │
│ │ + DeckId │ │ │ │
│ │ + Grade │ │ └─────────────────┘
│ │ + CreatedAt │ │
│ └─────────────┘ │
└─────────────────┘
Spaced Repetition (SM2 Algorithm):
Easiness: Difficulty factor (default 2.5)Interval: Days until next reviewRepetitionNumber: Count of successful reviewsExpiresAt: Automatic scheduling timestamp
Multi-tenancy & Security:
- UUID-based user identification across services
- Cascade deletion for data cleanup
- Row-level security through user ownership
Performance Optimizations:
- Indexed foreign keys (
DeckID,CreatedBy) - Connection pooling (5-20 connections per service)
- PostgreSQL array support for card tags
- Time-based queries for expired cards
Cross-Service Data Flow:
- User Operations: SSO → Card/Deck (user validation via gRPC)
- Learning Flow: Card → Stats (review recording)
- Deck Management: Deck ↔ Card (bidirectional updates)
Consistency Mechanisms:
- Synchronous: Real-time user validation, immediate feedback
- Asynchronous: Statistics aggregation, background processing
- Compensating Actions: Rollback strategies for cross-service failures
Each service uses GORM's AutoMigrate for schema management, with a planned migration to Goose for production-grade database versioning.
This section guides you through setting up Repeatro using Docker for quick deployment and testing.
Make sure you have the following installed on your system:
- Docker 27.3+ - Download & Install Docker
- Docker Compose - Usually included with Docker Desktop
- Git - Download & Install Git
Note: Go and PostgreSQL are not required for Docker setup as they run inside containers.
git clone https://github.com/GOeda-Co/backend.git
cd backendCreate environment files for all services:
# Copy the example environment file
cp .env.example .envEdit the .env file with your preferred settings:
# Database Configuration
DB_HOST=postgres
DB_PORT=5432
DB_USER=postgres
DB_PASS=postgres
DB_NAME=repeatro
# JWT Secret (generate a secure random string for production)
SECRET=your-super-secret-jwt-key-change-this-in-production
# Service Configuration
CARD_HOST_PORT=50051
CARD_CONTAINER_PORT=50051
DECK_HOST_PORT=50054
DECK_CONTAINER_PORT=50054
REPEATRO_HOST_PORT=8080
REPEATRO_CONTAINER_PORT=8080
SSO_HOST_PORT=44044
SSO_CONTAINER_PORT=44044
STAT_HOST_PORT=50055
STAT_CONTAINER_PORT=50055Security Note: Change the
SECRETvalue to a strong, unique string for production deployments.
Build and start all microservices with Docker Compose:
# Build and start all services in detached mode
docker-compose up --build -d
# View logs (optional)
docker-compose logs -fThis command will:
- Build Docker images for all microservices
- Start PostgreSQL database
- Launch all services (SSO, Card, Deck, Stats, Repeatro Gateway)
- Set up networking between containers
Add the application entry to the database (temporary setup step):
# Connect to the PostgreSQL container
docker exec -it postgres psql -U postgres -d repeatro
# Add the application record
INSERT INTO apps (id, name, secret) VALUES (1, 'repeatro', 'your-super-secret-jwt-key-change-this-in-production');
# Exit PostgreSQL
\qImportant: Replace
your-super-secret-jwt-key-change-this-in-productionwith the sameSECRETvalue from your.envfile.
Check that all services are running:
# View running containers
docker-compose ps
# Check service health
curl http://localhost:8080/swagger/index.htmlYou should see:
- All services in "Up" status
- Swagger documentation accessible at
http://localhost:8080/swagger/index.html
Once running, the following endpoints will be available:
- Repeatro Gateway (REST API):
http://localhost:8080 - Swagger Documentation:
http://localhost:8080/swagger/index.html - SSO Service (gRPC):
localhost:44044 - Card Service (gRPC):
localhost:50051 - Deck Service (gRPC):
localhost:50054 - Stats Service (gRPC):
localhost:50055 - PostgreSQL Database:
localhost:5432
# Stop all services
docker-compose down
# Stop and remove volumes (clears database)
docker-compose down -v
# View logs for specific service
docker-compose logs -f repeatro
# Rebuild specific service
docker-compose up --build repeatro
# Access database directly
docker exec -it postgres psql -U postgres -d repeatroThis section describes how to set up and run the Repeatro project locally for development without Docker.
- Go 1.24+ - Download & Install Go
- PostgreSQL 15+ - Download & Install PostgreSQL
- Git - Download & Install Git
git clone https://github.com/GOeda-Co/backend.git
cd backendCreate a PostgreSQL database for the project:
-- Connect to PostgreSQL as superuser
psql -U postgres
-- Create database
CREATE DATABASE repeatro;
-- Create user (optional, or use existing postgres user)
CREATE USER tomatocoder WITH PASSWORD 'postgres';
GRANT ALL PRIVILEGES ON DATABASE repeatro TO tomatocoder;Create a .env file in the root directory:
# Copy example environment file
cp .env.example .envEdit the .env file with your local settings:
# Database Configuration
DB_HOST=localhost
DB_PORT=5432
DB_USER=tomatocoder
DB_PASS=postgres
DB_NAME=repeatro
# JWT Secret (generate a secure random string)
SECRET=your-super-secret-jwt-key-here
# Config Path - use local.yaml for development
CONFIG_PATH=./config/local.yaml
# Service Ports for Local Development
CARD_HOST_PORT=50051
CARD_CONTAINER_PORT=50051
DECK_HOST_PORT=50054
DECK_CONTAINER_PORT=50054
REPEATRO_HOST_PORT=8080
REPEATRO_CONTAINER_PORT=8080
SSO_HOST_PORT=44044
SSO_CONTAINER_PORT=44044
STAT_HOST_PORT=50055
STAT_CONTAINER_PORT=50055Each service needs a local.yaml configuration file. Create them in each service's config directory:
SSO Service (sso/config/local.yaml):
env: local
connection_string: "host=localhost port=5432 user=tomatocoder password=postgres dbname=repeatro sslmode=disable"
grpc:
port: 44044
address: ":44044"
timeout: 10s
token_ttl: 15m
secret: ${SECRET}Card Service (card/config/local.yaml):
env: local
connection_string: "host=localhost port=5432 user=tomatocoder password=postgres dbname=repeatro sslmode=disable"
grpc:
port: 50051
address: ":50051"
timeout: 10s
clients:
sso:
address: ":44044"
timeout: 5s
retries_count: 3
stat:
address: ":50055"
timeout: 5s
retries_count: 3
secret: ${SECRET}Deck Service (deck/config/local.yaml):
env: local
connection_string: "host=localhost port=5432 user=tomatocoder password=postgres dbname=repeatro sslmode=disable"
grpc:
port: 50054
address: ":50054"
timeout: 10s
clients:
sso:
address: ":44044"
timeout: 5s
retries_count: 3
secret: ${SECRET}Stats Service (stats/config/local.yaml):
env: local
connection_string: "host=localhost port=5432 user=tomatocoder password=postgres dbname=repeatro sslmode=disable"
grpc:
port: 50055
address: ":50055"
timeout: 10s
clients:
sso:
address: ":44044"
timeout: 5s
retries_count: 3
secret: ${SECRET}Repeatro Gateway (repeatro/config/local.yaml):
env: local
connection_string: "host=localhost port=5432 user=tomatocoder password=postgres dbname=repeatro sslmode=disable"
clients:
card:
address: ":50051"
timeout: 5s
retries_count: 3
deck:
address: ":50054"
timeout: 5s
retries_count: 3
sso:
address: ":44044"
timeout: 5s
retries_count: 3
stat:
address: ":50055"
timeout: 5s
retries_count: 3
grpc:
port: 50054
address: ":50054"
timeout: 10s
http_server:
address: "0.0.0.0:8080"
port: 8080
timeout: 4s
idle_timeout: 30s
secret: ${SECRET}Install Go dependencies for each service:
# SSO Service
cd sso && go mod tidy && cd ..
# Card Service
cd card && go mod tidy && cd ..
# Deck Service
cd deck && go mod tidy && cd ..
# Stats Service
cd stats && go mod tidy && cd ..
# Repeatro Gateway
cd repeatro && go mod tidy && cd ..Start each service in separate terminal windows/tabs in the following order:
Terminal 1 - SSO Service:
cd sso
CONFIG_PATH=./config/local.yaml go run cmd/sso/main.goTerminal 2 - Stats Service:
cd stats
CONFIG_PATH=./config/local.yaml go run cmd/stats/main.goTerminal 3 - Card Service:
cd card
CONFIG_PATH=./config/local.yaml go run cmd/card/main.goTerminal 4 - Deck Service:
cd deck
CONFIG_PATH=./config/local.yaml go run cmd/deck/main.goTerminal 5 - Repeatro Gateway:
cd repeatro
CONFIG_PATH=./config/local.yaml go run cmd/repeatro/main.goOnce all services are running, you can:
- Check service health by accessing individual gRPC ports
- Access Swagger documentation at
http://localhost:8080/swagger/index.html - Test API endpoints using the Swagger UI or curl commands
- Hot Reload: Use tools like air for automatic reloading during development
- Database Migrations: GORM auto-migration is enabled, so tables will be created automatically
- Logging: Set
env: localin config files for detailed debug logging - Service Discovery: In local development, services communicate via
localhost:port - Config Changes: Restart services after modifying
local.yamlfiles
Port Already in Use:
# Find process using port
lsof -i :8080
# Kill process
kill -9 <PID>Database Connection Issues:
- Verify PostgreSQL is running:
brew services start postgresql(macOS) orsudo systemctl start postgresql(Linux) - Check connection string in
local.yamlfiles - Ensure database
repeatroexists
JWT Signature Issues:
- Ensure all services use the same
SECRETvalue in their config files
Once running, you can interact with the API (REST/gRPC) for deck and card management, user authentication, and spaced repetition review. See the Swagger docs for API details.
- Goose migrations
- Import/export via CSV, JSON
- Enhance current stats
- Language detection
- Simple frontend
See the open issues for a full list of proposed features (and known issues).
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Distributed under the MIT License. See LICENSE for more information.
Maintainers:
@tomatoCoderq
@constable
@Kaghorz
Project Link: https://github.com/GOeda-Co/backend
- Anki – inspiration
- Goose
- Swaggo
- lingua-go
- Img Shields
- Best-README-Template
Project contributors: