Palpitero FC is an app designed for Brazilians who love football and enjoy predicting match results without the risks associated with betting. The app offers a fun and competitive experience, where users can score points based on the accuracy of their predictions and compete in global rankings or personalized groups with friends.
Palpitero is a full-stack, multi-platform sports prediction application with a layered architecture spanning mobile (React Native/Expo), web (Vue.js), and server-side (.NET) components. The system follows a Clean Architecture / Layered Architecture pattern with clear separation of concerns across domain logic, application services, and infrastructure.
Core Stack:
- Backend API: ASP.NET Core 8 (C#) with PostgreSQL (relational) + MongoDB (NoSQL)
- Mobile Client: React Native (Expo) with TypeScript
- Web Client: Vue.js 3 with JavaScript
- Background Jobs: Node.js with TypeScript (Scheduled via cron)
- Containerization: Docker Compose (dev, jobs, infrastructure, production configurations)
- External Integrations: API-Football (match data), Twilio (SMS), AWS S3 (storage), Expo (push notifications)
| Component | Tech | Responsibility | Evidence |
|---|---|---|---|
| API Server | ASP.NET Core 8, PostgreSQL, MongoDB | Core business logic, predictions, rankings, user management | server/api/src/API/ |
| Mobile App | React Native (Expo 53), TypeScript | User predictions, live match feed, group management, rankings | client/app/ |
| Web Dashboard | Vue.js 3, Axios | Administrative dashboard, user analytics (planned) | client/web/ |
| Background Jobs | Node.js, TypeScript, node-cron | Match synchronization, prediction scoring, prize calculation | server/bot/ |
| Data Layer | PostgreSQL + MongoDB | Persistent storage (transactions) + NoSQL documents | docker-compose.dev.yml, server/api/src/Infra/Data/ |
| Authentication | JWT Bearer Tokens | User identity and authorization across all clients | server/api/src/API/Configurations/ |
| Notifications | Expo Push Notifications, Twilio | Real-time updates for predictions, match status, prizes | server/api/src/Infra/Services/NotificationService.cs |
server/api/src/
├── API/ # Web API entry point
│ ├── Controllers/ # REST endpoints (Predictions, Matches, Users, etc.)
│ ├── Configurations/ # DI, Authentication, CORS, Localization setup
│ ├── Middlewares/ # Request/response interceptors
│ ├── Localization/ # Multi-language JSON files (pt-BR, en, es, fr, de, it)
│ ├── Attributes/ # Custom validation attributes
│ └── Program.cs # ASP.NET Core configuration entry point
│
├── Application/ # Application services & use-case orchestration
│ ├── Services/ # Business logic (PredictionService, UserService, etc.)
│ ├── Interfaces/ # Service contracts
│ ├── ViewModels/ # DTO contracts for requests/responses
│ └── Mapping/ # AutoMapper profiles (Domain ↔ ViewModel)
│
├── Domain/ # Domain entities & business rules (per aggregate)
│ ├── Predictions/ # Prediction entity, repository, validations
│ ├── Matches/ # Match entity, repository, business logic
│ ├── Users/ # User entity, repository
│ ├── Leagues/ # League configuration & filtering
│ ├── Groups/ # Group creation & membership
│ ├── Rankings/ # Custom ranking definitions
│ ├── Prizes/ # Prize management & eligibility
│ ├── Minigames/ # Quiz & logo minigames
│ └── [...] # Other aggregates
│
├── Core.Domain/ # Cross-cutting domain concerns
│ ├── Entities/ # Base entity classes
│ ├── Interfaces/ # IMediatorHandler, IUser, ITranslator
│ ├── Notifications/ # Domain notification pattern (error collection)
│ ├── Events/ # Domain events (if any)
│ ├── Auth/ # JWT, authentication contracts
│ └── Utils/ # PasswordEncryption, DateTimeUtils
│
└── Infra/ # Infrastructure & external services
├── Data/ # PostgreSQL DbContext, repositories, EF migrations
│ ├── DataContext.cs # Entity Framework DbSet configuration
│ ├── *Repository.cs # SQL-based repositories for each aggregate
│ └── Mappings/ # EF entity mappings (fluent configuration)
├── Services/ # External service implementations
│ ├── TwilioService # SMS integration
│ ├── StorageService # AWS S3 file upload
│ ├── MailService # Email (SMTP)
│ ├── NotificationService # Expo push notifications
│ └── Translator # Localization provider
└── CrossCutting/ # Password encryption, logging
See: Palpitero.sln for project references.
client/app/
├── app/ # Expo Router file-based routing
│ ├── (auth)/ # Auth routes (login, signup, verify email)
│ ├── (tabs)/ # Tabbed home layout (feed, groups, predictions, profile)
│ ├── groups/ # Group management screens
│ ├── minigames/ # Quiz and logo prediction minigames
│ ├── profiles/ # User profile & statistics
│ ├── _layout.tsx # Root layout, auth initialization, token/user loading
│ └── [...routes] # Feature screens
│
├── components/ # Reusable UI components (50+ components)
│ ├── MatchCard.tsx # Individual match prediction card
│ ├── PredictionCard.tsx # User prediction display
│ ├── RankingCard.tsx # User ranking in groups/leagues
│ ├── LeaguesPicker.tsx # Multi-select league filter
│ ├── CustomButton.tsx # Themed button wrapper
│ ├── CountdownTimer.tsx # Match countdown display
│ └── [...] # Other UI components
│
├── repository/ # HTTP data access layer (Repository pattern)
│ ├── HttpRepository.ts # Base class with axios instance & auth headers
│ ├── user.repository.ts # User sign-in/up, profile endpoints
│ ├── prediction.repository.ts # Prediction CRUD operations
│ ├── match.repository.ts # Match feed retrieval
│ ├── group.repository.ts # Group management
│ └── [...] # Other repositories
│
├── services/ # Business logic & utilities
│ ├── auth.service.ts # JWT token storage, user state singleton
│ ├── storage.service.ts # AsyncStorage wrapper for local state
│ └── [...] # Other services
│
├── models/ # TypeScript interfaces for API responses
│ ├── predictions/ # Prediction, SetScore DTOs
│ ├── matches/ # Match, MatchCard models
│ ├── users/ # User, SignIn, SignUp models
│ ├── pagination.model.ts # Pagination wrapper
│ └── [...] # Other domain models
│
├── utils/ # Helper functions (date, localization, formatting)
├── types/ # Global TypeScript types
├── constants/ # App-wide constants, context
├── assets/ # Icons, images, locale JSON files (i18n)
├── hooks/ # Custom React hooks (push notifications)
├── globalstyles.ts # Theming, color palette
├── app.json # Expo app configuration
├── eas.json # Expo Application Services (iOS/Android builds)
├── package.json # Dependencies (Expo, React Native, i18n, etc.)
└── tsconfig.json # TypeScript configuration
See: client/app/app.json, client/app/package.json
client/web/
├── src/
│ ├── components/ # Reusable Vue components
│ ├── views/ # Page-level components
│ ├── router/ # Vue Router configuration
│ ├── services/ # API/business logic
│ └── App.vue # Root component
├── public/ # Static assets
├── package.json # Vue 3, Axios, Router
└── vue.config.js # Webpack configuration
server/bot/
├── src/
│ ├── jobs/ # Scheduled job implementations
│ │ ├── future-matches.job.ts # Fetch next 24h matches → sync DB
│ │ ├── past-matches.job.ts # Verify match results, update predictions
│ │ ├── upcoming-matches.job.ts # Notify users of upcoming matches
│ │ └── prizes.job.ts # Calculate daily/monthly prizes
│ ├── services/ # External API clients
│ │ ├── api-football.service.ts # API-Football SDK integration
│ │ └── notification.service.ts # Expo & Twilio notifications
│ ├── repository/ # Direct DB access (pg client)
│ │ └── *Repository.ts # PostgreSQL queries via raw SQL/Dapper
│ ├── entities/ # TypeScript DTOs for API responses
│ ├── models/ # Data transformation models
│ ├── mapper/ # Entity ↔ Model mappings
│ ├── utils/ # Timestamp, logging utilities
│ ├── logger.ts # Colored console output
│ └── main.ts # Cron scheduler entry point
├── package.json # Dependencies (amqplib, pg, node-cron, etc.)
└── tsconfig.json # TypeScript configuration
| Pattern | Where Used | Evidence |
|---|---|---|
| Clean Architecture | API (Domain → Application → Infra → API layers) | Palpitero.sln project structure |
| Repository Pattern | API data access + Mobile HTTP | server/api/src/Infra/Data/, client/app/repository/ |
| Dependency Injection | API (Autofac via .NET DI) | server/api/src/API/Configurations/DependencyInjectionConfiguration.cs |
| MediatR / Domain Events | API notifications & error handling | Core.Domain/Notifications/, DomainNotificationHandler |
| AutoMapper | API ViewModel mapping | server/api/src/Application/Mapping/ |
| Entity Framework | API database abstraction | server/api/src/Infra/Data/DataContext.cs |
| Fluent Validation | API input validation | Core.Domain/Validations/, Domain/*/Validations/ |
| Service Layer | API business orchestration | server/api/src/Application/Services/ |
| Factory Pattern | Domain entity creation | server/api/src/Domain/Predictions/Prediction.cs (likely Prediction.Factory.Create()) |
| Singleton Pattern | Mobile client auth state | client/app/services/auth.service.ts AuthService.getInstance() |
| Observer / MediatR Pattern | API domain notifications | server/api/src/Core.Domain/Notifications/DomainNotificationHandler.cs |
| Template Method | Base repository & controller | server/api/src/Infra/Data/SqlRepository.cs, server/api/src/API/Controllers/BaseController.cs |
| Strategy Pattern | Authentication (JWT, Google, Apple) | client/app/repository/user.repository.ts sign-in methods |
| Adapter Pattern | External service wrappers | server/api/src/Infra/Services/TwilioService.cs, server/api/src/Infra/Services/StorageService.cs |
-
PostgreSQL (primary relational store):
- Entity Framework Core 8 with Npgsql provider
- Entities mapped via fluent configuration in server/api/src/Infra/Mappings/
- Repositories implement query logic in server/api/src/Infra/Data/
- See: DataContext.cs - all DbSet definitions
-
MongoDB (document storage):
- Minigame scores and quiz answers (NoSQL flexibility)
- Configured in DependencyInjectionConfiguration.cs line ~40
- Repository: MongoRepository.cs
-
JWT Bearer Tokens:
- Issued on sign-in/sign-up
- Mobile client stores token in AsyncStorage (persistent)
- All API requests include
Authorization: Bearer <token>header - Server validates via JWT middleware configured in Program.cs
-
Social Sign-In:
- Google OAuth 2.0 (Google Sign-In SDK)
- Apple Sign-In (native integration)
- Repositories: client/app/repository/user.repository.ts methods
signInWithGoogle(),signInWithApple()
-
API-Football (match data):
- Fetches live/upcoming matches by date
- Used by background jobs: future-matches.job.ts, past-matches.job.ts
- Service: server/bot/src/services/api-football.service.ts
-
Twilio (SMS):
- SMS verification codes during sign-up
- Service: server/api/src/Infra/Services/TwilioService.cs
-
AWS S3 (file storage):
- Profile picture uploads, team logos
- Service: server/api/src/Infra/Services/StorageService.cs
-
Expo Push Notifications:
- Real-time alerts (upcoming matches, predictions closed)
- Mobile: client/app/hooks/usePushNotifications.ts
- Server: server/api/src/Infra/Services/NotificationService.cs
- Multi-language support (pt-BR, en, es, fr, de, it):
- API localization files: server/api/src/API/Localization/
- Mobile i18n: client/app/assets/locale/, client/app/app/i18n.ts
- Requests include
X-Languageheader for server-side translation - Service: server/api/src/Infra/Services/Translator.cs
┌─────────────────────────────────────────────────────────┐
│ 1. MATCH SYNC (Background Job - every 24h) │
├─────────────────────────────────────────────────────────┤
│ server/bot/jobs/future-matches.job.ts │
│ ↓ │
│ Fetch from API-Football (configured leagues) │
│ ↓ │
│ Insert/Update in PostgreSQL (Matches table) │
│ ↓ │
│ Notify via Expo (new matches available) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 2. USER PREDICTION (Mobile App - real-time) │
├─────────────────────────────────────────────────────────┤
│ client/app/screens/PredictionScreen │
│ ↓ │
│ POST /predictions/set-score (API endpoint) │
│ ↓ │
│ API/Controllers/PredictionsController.SetScore() │
│ ↓ │
│ Application/Services/PredictionService.SetScore() │
│ ├─ Validate via FluentValidation │
│ ├─ Check match status (not closed) │
│ ├─ Create/Update Prediction entity │
│ └─ Persist to PostgreSQL │
│ (Infra/Data/PredictionRepository) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 3. SCORING & RESULTS (Background Job - after matches) │
├─────────────────────────────────────────────────────────┤
│ server/bot/jobs/past-matches.job.ts (every min) │
│ ↓ │
│ Check for finished matches from API-Football │
│ ↓ │
│ Calculate points (3 for exact, 1 for result) │
│ ↓ │
│ Update Prediction scores in PostgreSQL │
│ ↓ │
│ Notify users via Expo (prediction scored) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 4. RANKING UPDATES (Automatic via Entity Framework) │
├─────────────────────────────────────────────────────────┤
│ RecordRepository aggregates scores │
│ ↓ │
│ Global rankings (all leagues) │
│ League-specific rankings │
│ Custom group rankings │
│ ↓ │
│ GET /records (ranking endpoints) return aggregated │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ USER CREATES GROUP │
├─────────────────────────────────────────────────────────┤
│ POST /groups (API) │
│ → GroupService.CreateGroup() │
│ → GroupRepository.InsertAsync() │
│ → Insert Group + Member records (PostgreSQL) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ USER CREATES CUSTOM RANKING (within group) │
├─────────────────────────────────────────────────────────┤
│ POST /custom-rankings (API) │
│ → CustomRankingService.CreateCustomRanking() │
│ → Define league filters + scoring rules │
│ → CustomRankingRepository.InsertAsync() │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ GET GROUP RANKING │
├─────────────────────────────────────────────────────────┤
│ GET /records/group/{groupId} (API) │
│ → RecordService.GetGroupRanking() │
│ → Join Predictions + Matches + CustomRankings │
│ → Calculate points per league filter │
│ → Return ranked members │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ CLIENT APPLICATIONS │
├──────────────────────────┬─────────────────────────────┬────────────────────┤
│ React Native (Expo) │ Vue.js Web │ Node.js Jobs │
│ - User predictions │ - Dashboard (planned) │ - Cron scheduler │
│ - Feeds & rankings │ - Analytics │ - Match sync │
│ - Group management │ │ - Score scoring │
│ - Minigames │ │ - Prize calc │
└──────────────────────────┴─────────────────────────────┴────────────────────┘
↓ HTTP/REST ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│ ASP.NET CORE 8 API │
├─────────────────────────────────────────────────────────────────────────────┤
│ Controllers (PredictionsController, MatchesController, etc.) │
│ ↓ │
│ Application Layer (Services: PredictionService, RankingService, etc.) │
│ ↓ │
│ Domain Layer (Entities, Validations, Business Rules) │
│ ↓ │
│ Infrastructure Layer (Repositories, EF Context, External Services) │
│ ↓ SQL ↓ NoSQL ↓ │
└─────────────────────────────────────────────────────────────────────────────┘
↓ ↓ ↓
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────┐
│ PostgreSQL │ │ MongoDB │ │ External APIs │ │ AWS S3 │
│ - Users │ │ - Minigame │ │ - API-Football │ │ - Assets │
│ - Predictions │ │ scores │ │ - Twilio │ │ - Photos │
│ - Matches │ │ - Quiz answers │ │ - Expo Notif. │ │ │
│ - Rankings │ │ │ │ │ │ │
│ - Groups │ │ │ │ │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────┘
- Docker & Docker Compose (recommended for full stack)
- .NET 8 SDK (for API development)
- Node.js 18+ (for mobile/web/jobs development)
- PostgreSQL client (psql, optional)
- Expo CLI (for mobile:
npm install -g expo-cli)
1. Start the full dev stack (API + PostgreSQL + MongoDB + MailHog):
docker compose --env-file ./environments/dev.env -f docker-compose.dev.yml up --build -dThis starts:
- API on
http://localhost:8080 - PostgreSQL on
localhost:5432(user: postgres, password: palpitero) - pgAdmin on
http://localhost:8081(optional DB management) - MongoDB on
localhost:27017 - MailHog (mock SMTP) on
http://localhost:8025
Healthcheck: curl http://localhost:8080/healthcheck
2. Configure environment variables:
# Copy and edit dev environment
cp environments/local.env.example environments/dev.env
# Update: DATABASE_CONNECTION_STRING, MONGO_HOST, API_KEYS, etc.3. Run API migrations (first time):
cd server/api
dotnet ef migrations add Initial --project src/Infra --startup-project src/API
dotnet ef database update --startup-project src/APIOr use the init script if provided:
./scripts/init-db.sh # Linux/Mac
.\scripts\init-db.ps1 # Windows4. Start mobile app (Expo):
cd client/app
npm install
npm startThen press:
wfor Expo webafor Android emulatorifor iOS simulator
5. Start web client:
cd client/web
npm install
npm run serveOpen http://localhost:8080 in your browser.
6. Run background jobs (optional, for testing):
cd server/bot
npm install
npm run future-matches # Fetch matches
npm run past-matches # Score predictions
npm run upcoming-matches # Upcoming notifications
npm run prizes # Calculate prizesdocker compose --env-file ./environments/prod.env -f docker-compose.prod.yml up -dEnvironment variables should include:
ASPNETCORE_ENVIRONMENT=ProductionConnectionStrings:DefaultConnection=<prod-postgres-uri>mongoConnection:server=<prod-mongo-uri>- API keys (API-Football, Twilio, AWS S3, Expo)
- SSL/TLS certificates (if reverse proxy)
CI/CD via GitHub Actions:
- See: .github/workflows/cd-main.yml (badge in header)
- Deploys API and job containers on main branch push
API unit tests (if present):
cd server/api/src
dotnet testMobile integration tests:
cd client/app
npm run test- API: Console logs + optional Serilog (see Program.cs)
- Mobile: Console logs + Expo error reporting
- Jobs: Custom logger with color output - see server/bot/src/logger.ts
- API healthcheck endpoint:
GET http://localhost:8080/healthcheck - PostgreSQL: Via pgAdmin UI or
psqlclient - MongoDB:
mongo --host localhost --eval "db.adminCommand('ping')"
API:
cd server/api
dotnet watch run # Hot reload in developmentMobile:
# React Native debugger
npm start --dev
# Then open Expo dev menu (shake device or Ctrl+M)Jobs:
cd server/bot
NODE_OPTIONS=--inspect npm run future-matches
# Debug in Chrome DevTools: chrome://inspect- PostgreSQL: pgAdmin at
http://localhost:8081orpsqlCLI - MongoDB: MongoDB Compass (GUI tool) or mongo shell
| Package | Version | Purpose |
|---|---|---|
| ASP.NET Core | 8.0.8 | REST API framework |
| Entity Framework Core | 8.0.8 | ORM for PostgreSQL |
| MediatR | 12.4.0 | Domain events & mediator pattern |
| AutoMapper | 13.0.1 | DTO mapping |
| FluentValidation | 11.9.2 | Input validation |
| JWT Bearer | 8.0.8 | JWT token validation |
| MongoDB.Driver | 3.3.0 | NoSQL database |
| Twilio | 7.8.0 | SMS integration |
| AWS SDK S3 | 3.7.402.12 | File storage |
| Package | Version | Purpose |
|---|---|---|
| expo | 53.0.10 | React Native framework |
| expo-router | 5.0.7 | File-based routing |
| react-native | (via Expo) | Native UI |
| axios | 1.7.7 | HTTP client |
| i18next | 25.2.1 | Localization |
| expo-notifications | 0.31.3 | Push notifications |
| react-native-elements | 3.4.3 | UI component library |
| Package | Version | Purpose |
|---|---|---|
| node-cron | 3.0.3 | Job scheduling |
| axios | 1.7.7 | HTTP client (API-Football) |
| pg | 8.12.0 | PostgreSQL connection |
| amqplib | 0.10.4 | Message queue (if used) |
| whatsapp-web.js | 1.26.0 | WhatsApp integration (optional) |
Key configuration files:
- config.json - Supported leagues & competition metadata
- environments/dev.env - Development secrets & URLs
- environments/prod.env - Production secrets (not in git)
- docker-compose.dev.yml - Dev infrastructure
- docker-compose.prod.yml - Prod infrastructure
- docker-compose.jobs.yml - Job containers
Required environment variables:
# Database
POSTGRES_USER=postgres
POSTGRES_PASSWORD=<secure-password>
POSTGRES_DB=palpitero
POSTGRES_HOST=postgres
# MongoDB
MONGO_HOST_USERNAME=<username>
MONGO_HOST_PASSWORD=<secure-password>
# API Keys
API_FOOTBALL_KEY=<api-football.com-key>
# External Services
TWILIO_ACCOUNT_SID=<twilio-sid>
TWILIO_AUTH_TOKEN=<twilio-token>
AWS_ACCESS_KEY_ID=<aws-key>
AWS_SECRET_ACCESS_KEY=<aws-secret>
AWS_S3_BUCKET=<bucket-name>
# Expo
EXPO_PROJECT_ID=<expo-project-id>
EXPO_ACCESS_TOKEN=<expo-token>
# JWT
JWT_SECRET=<long-secure-secret>
JWT_EXPIRATION_HOURS=24
# Localization
DEFAULT_TIMEZONE=America/Sao_Paulo
# API
ASPNETCORE_ENVIRONMENT=Development
ASPNETCORE_EXPORT_PORT=8080
# Frontend
API_ENDPOINT=http://localhost:8080/ # or production URLThis software was developed by Alexandre Beato and is licensed by Koppler. Unauthorized use is illegal. For more information, check out the LICENSE file.
