// personal project

Practice English
with real AI
conversations

ASAP Poliglota is a mobile app that puts learners in real-world English scenarios — voice or text — and delivers AI-powered feedback after every session.

Spring AI Java 21 WebRTC React Native Firebase Crashlytics
Splash screen Home screen Scenario selection Level selection Text conversation AI Evaluation My Plan

// what it does

Built for real practice

Every feature is designed around one goal: getting the learner to speak and write more English, in realistic situations, with immediate feedback.

🎙️
Real-time Voice Practice

Direct WebRTC connection to OpenAI Realtime API. Sub-second latency, full-duplex audio. The backend never touches the audio stream — only issues ephemeral tokens.

💬
Text Conversation Mode

Practice at your own pace with an AI conversation partner. Choose a scenario, pick your level, and the AI adapts vocabulary and complexity accordingly.

🧠
AI Evaluation Engine

After each session, Spring AI sends the transcript to GPT-4o-mini. Returns a score, strengths, recurring issues, and one actionable improvement tip.

🌍
Contextual Scenarios

Airport, workplace, hospital, social settings. Each scenario has curated sub-situations. Users practice vocabulary that actually matters for their lives.

📊
Progress Tracking

Weekly practice frequency, score trends, best recent performance, and recurring weak points — all surfaced on a clean dashboard with no data noise.

🔐
Subscription & Access Control

Passwordless access through secure Firebase email links, plus plan-based limits enforced via Redis TTL buckets and Hotmart webhooks for the billing lifecycle.


System Architecture

Two domain microservices own customer and conversation data. Firebase provides identity, while a dedicated notification service handles SMTP delivery only.

📱 asap-mobile-app React Native 0.83 · TypeScript · react-native-webrtc Firebase Passwordless · ID Token Bearer · Crashlytics login link + API ID Token Bearer asap-customer-subscription-app :8083/api • Identity provisioning & onboarding • Login-link orchestration • Plans & subscriptions • Hotmart webhooks • Firebase Admin · FreeMarker · Feign PostgreSQL · Flyway Feign HTTP asap-language-conversation-app :8082/api • Conversations (text + voice) • Spring AI evaluation engine • Usage limits (Redis TTL) • OpenAI Realtime token proxy • Firebase ID Token validation PostgreSQL · Redis · Flyway 🔐 Firebase Authentication Email Link · Admin SDK · ID Token issuer Public JWK · issuer + audience validation ✉️ notifications-app Authenticated internal HTTP API Pre-rendered email delivery via SMTP only 🤖 OpenAI Realtime API · WebRTC direct gpt-4o-mini evaluation generate link rendered email validate Firebase JWK AI + token

Infrastructure: Docker · Nginx (reverse proxy / API gateway) · Jenkins CI/CD · VPS

Passwordless Authentication

01
Request access

The mobile app posts the normalized email to /subscriptions/auth/login-link. The subscription service checks eligibility and applies rate limiting without revealing whether the account exists.

02
Generate & deliver

Firebase Admin generates a temporary, one-time link. FreeMarker renders branded HTML and notifications-app delivers it through SMTP. The public API never returns the link.

03
Complete sign-in

An Android App Link opens the app, which calls signInWithEmailLink. The resulting Firebase ID Token is sent as Bearer, with one forced refresh and retry after a 401.

The mobile app never calls notifications-app directly, and the delivery service never receives Firebase Admin credentials or decides login eligibility. Initial provisioning also triggers one idempotent welcome email with a fresh first-access link; email failures never roll back customer provisioning.

Voice Pipeline

📱 Mobile conversation-app 🤖 OpenAI POST /conversation/voice/token Request ephemeral token Ephemeral token Return token to app WebRTC — full-duplex audio (direct) Audio never touches the backend POST /evaluate (transcript) Spring AI → gpt-4o-mini Score · Strengths · Issues · Tip 01 02 03

// tech stack

What powers it

Production-grade choices for each layer, with independent deployability as the guiding constraint.

asap-language-conversation-app
LanguageJava 21
FrameworkSpring Boot 3.5.8
AISpring AI 1.1.2 gpt-4o-mini
DatabasePostgreSQL Flyway
CacheRedis
MappingMapStruct 1.6.3
AuthFirebase ID Token OAuth2 RS JWK Validation
HTTP ClientOpenFeign
TestingJUnit 5 Testcontainers JaCoCo
asap-customer-subscription-app
LanguageJava 21
FrameworkSpring Boot 3.5.8
DatabasePostgreSQL Flyway Hibernate Types
AuthFirebase Auth Admin SDK ID Token
EmailFreeMarker notifications-app SMTP
BillingHotmart Webhooks
HTTP ClientOpenFeign
TestingJUnit 5 Testcontainers JaCoCo
Docsspringdoc-openapi
notifications-app
LanguageJava 21
FrameworkSpring Boot 3.4.3
EmailSpring Mail SMTP
APISpring Web Bean Validation
SecuritySpring Security Internal API
OperationsActuator Docker
ResponsibilityPre-rendered email delivery only
asap-mobile-app
FrameworkReact Native 0.83
LanguageTypeScript 5.8
AuthFirebase Auth Email Link Android App Links
API SecurityID Token Bearer 401 Refresh
MonitoringFirebase Crashlytics
Voicereact-native-webrtc
HTTPAxios 1.13
NavigationReact Navigation 7
UILottie lucide-react-native
TestingJest 29 @testing-library/rn
Infrastructure
ContainersDocker
GatewayNginx
CI/CDJenkins
DeployVPS
IdentityFirebase Authentication
Emailnotifications-app SMTP
BuildMaven

// engineering decisions

Why it's built this way

Seven decisions that shaped the architecture — each one made to solve a real constraint, not to add complexity.

01
WebRTC direct-to-OpenAI — audio never hits the backend

Rather than proxying audio through our servers, the app gets a short-lived token and opens a WebRTC session directly with OpenAI. This eliminates infrastructure audio latency, avoids storing sensitive audio data, and drastically reduces bandwidth costs. The backend's only role in a voice session is minting the token and evaluating the transcript afterward.

02
Separate databases per service — no shared schemas

Each microservice owns its own PostgreSQL database. The conversation service never reads the subscription DB directly — it calls the subscription service via Feign when it needs plan or level data. This enforces proper domain boundaries and makes each service independently deployable and testable.

03
Redis for usage enforcement — DB writes avoided on every request

Usage limits (voice minutes, text conversations, AI evaluations per plan) are tracked in Redis with TTL-based windows that reset on schedule. This avoids expensive DB writes on every user action and enables sub-millisecond limit checks at the API layer without adding a caching proxy.

04
Prompt versioning as repeatable SQL seeds

AI evaluation prompts are stored as Flyway repeatable migrations (R__prompts.sql). This allows prompt iteration — adjusting tone, evaluation criteria, output format — without code changes or redeployment. A DB migration is all that's needed, keeping prompt engineering fully decoupled from application releases.

05
Testcontainers for realistic integration tests

Both services spin up real PostgreSQL and Redis instances in CI via Testcontainers — no mocks, no in-memory substitutes. JaCoCo merges unit and integration coverage into a single report, so coverage numbers actually reflect what runs in production. The test suite runs with mvn verify, not just mvn test.

06
Backend-generated Firebase passwordless links

Firebase remains the identity provider, while customer-subscription owns email normalization, eligibility, anti-abuse controls, anti-enumeration responses, branding, and delivery orchestration. Every request creates a temporary, one-time link that is sent by notifications-app and never returned by the public endpoint.

07
Sanitized Crashlytics reporting

Crashlytics captures native and fatal JavaScript failures plus selected handled technical errors. Reports contain only controlled operation, reason, and optional HTTP status — never email, Firebase UID or ID Token, Authorization headers, magic links, oobCode, payloads, raw responses, SDP, ephemeral keys, audio, messages, or transcripts.


// app screens

The experience

From splash to AI feedback — every screen designed for clarity and focus on the learning task.

Available on Google Play — production Android release