Sport Tech Club - UML Class Diagram
Comprehensive Domain Model: Complete representation of all entities, relationships, and business logic in the Sport Tech Club system.
Table of Contents
- Complete System Class Diagram
- Core Domain Models
- Supporting Domain Models
- Generic Domain Models
- Enumerations
- Relationships Summary
Complete System Class Diagram
Core Domain Models
Identity & Access Context
The Identity & Access context manages authentication, authorization, and user lifecycle.
Key Classes:
- User: Aggregate root representing a system user with authentication credentials and profile
- Session: Manages JWT-based authentication sessions with expiry and refresh capabilities
- Role: Enum defining role-based access control (RBAC) levels
- TenantMembership: Value object linking users to arena tenants with permissions
Business Rules:
- Email must be unique across the system
- Password requires minimum 8 characters with complexity rules
- MFA is mandatory for ARENA_OWNER and administrative roles
- Sessions expire after 24 hours, refresh tokens after 7 days
- Users can be members of multiple arenas (multi-tenancy)
Arena Management Context
Manages the physical infrastructure and operational configuration of arenas.
Key Classes:
- Arena: Aggregate root representing a sports facility with courts, areas, and operational rules
- Court: Entity representing a playable space with sport type, surface, and availability status
- CourtProfile: Value object defining recommended usage patterns and player requirements
- OperatingCalendar: Defines regular hours and special dates (holidays, events)
- ArenaSettings: Centralized configuration for booking policies, pricing, and queue rules
Business Rules:
- CNPJ must be valid and unique per arena
- Operating hours must not overlap with special dates
- Court capacity must be enforced based on sport type
- Priority scoring determines court allocation preferences
- Maintenance blocks override all other reservations
Courts & Allocation Context (CORE)
⭐ Core Domain - Intelligent court allocation and queue management system.
Key Classes:
- AllocationEngine: Service that orchestrates the complex allocation logic using multiple rules
- PlayerQueue: Entity managing FIFO queue with priority adjustments
- QueueEntry: Value object representing a player's position with metadata
- AllocationRule: Strategy pattern for pluggable allocation algorithms
- WinnerStaysRule: Implements "winner stays, loser rotates" gameplay mode
Business Rules:
- Allocation considers: skill level, wait time, membership tier, court preferences
- Winner can stay maximum 3 consecutive victories
- Teams are formed automatically based on skill rating (ELO-based)
- Priority formula:
priority = (membershipTier * 10) + (waitMinutes * 0.5) - (todayGames * 2) - Queue entries timeout after 2 minutes when called
- Court profile matching ensures compatible skill levels play together
Allocation Algorithm:
// Pseudocode for allocation decision
function allocateNext(courtId: UUID): AllocationResult {
const court = getCourt(courtId)
const queue = getQueue(courtId)
const requiredPlayers = court.capacity
// Get top N players from queue
const candidates = queue.getNext(requiredPlayers)
// Validate compatibility
if (!areCompatible(candidates, court.profile)) {
return AllocationResult.NoMatch
}
// Balance teams if needed
const teams = balanceTeams(candidates)
// Execute allocation
court.allocate(teams)
queue.remove(candidates)
return AllocationResult.Success(teams)
}Scheduling Context (CORE)
⭐ Core Domain - Unified multi-purpose booking system.
Key Classes:
- Booking: Aggregate root for all types of reservations (court rental, lessons, events)
- TimeRange: Value object with validation and overlap detection
- BookingPolicy: Encapsulates cancellation rules, advance notice requirements
- Participant: Represents additional people involved in a booking
Business Rules:
- Minimum advance booking: 2 hours (configurable per arena)
- Maximum advance booking: 30 days
- Cancellation >24h: 100% refund
- Cancellation 2-24h: 50% refund
- Cancellation <2h: no refund
- No-show policy: 3 strikes = 7-day suspension
- Check-in required within 15 minutes of start time
- Bookings cannot overlap on the same court
State Machine:
PENDING → CONFIRMED → IN_PROGRESS → COMPLETED
↓ ↓
CANCELLED NO_SHOWWallet & Value Context (CORE)
⭐ Core Domain - Internal economy with tokens, transactions, and stakes.
Key Classes:
- Wallet: Aggregate root with event sourcing for full audit trail
- Balance: Multi-currency support (BRL, USD, ARENA_TOKEN) with locked/available amounts
- Transaction: Immutable ledger entry with idempotency key
- MatchStakes: Stakes placed on matches with automatic distribution
Business Rules:
- All transactions are idempotent (duplicate protection via idempotencyKey)
- Wallet uses event sourcing: state reconstructed from transaction history
- Token exchange rate defined per arena (e.g., 1 token = R$ 0.10)
- Locked balance cannot be withdrawn (used for pending stakes)
- Transaction history is immutable and auditable
- Arena takes 10% commission on stake winnings
Event Sourcing Events:
WalletCreatedFundsCreditedFundsDebitedFundsTransferredFundsLockedFundsUnlockedTransactionCompletedTransactionFailed
Supporting Domain Models
Lessons & Coaching Context
Key Classes:
- Lesson: Aggregate root for structured teaching sessions
- Instructor: Certified professional with specialties and availability
- Student: Enrolled participant with progress tracking
- Attendance: Records presence with status (present, absent, late, excused)
Business Rules:
- Instructors can only teach sports they're certified in
- Group lessons limited by court capacity
- Attendance tracked automatically via check-in
- Students can evaluate instructors (1-5 stars)
- Payment split: 70% instructor, 30% arena
Play & Match Context
Key Classes:
- Match: Aggregate root with event sourcing for real-time scoring
- Team: 1-2 players competing as a unit
- Player: Domain representation with profile, stats, and skill rating
- Score: Tracks sets, games, points with sport-specific rules
- MatchStakes: Optional stakes (tokens or ranking points)
Business Rules:
- Skill rating uses ELO algorithm
- Stats updated automatically after match completion
- Winner stays rule applies in queue mode
- Match stakes distributed automatically to winners
- Points awarded: +25 XP for win, +10 XP for participation
ELO Rating Formula:
New Rating = Old Rating + K × (Actual - Expected)
Where:
K = 32 (rating volatility factor)
Expected = 1 / (1 + 10^((OpponentRating - YourRating) / 400))
Actual = 1 (win), 0.5 (draw), 0 (loss)Access & Presence Context
Key Classes:
- PresenceSession: Tracks user physical presence with check-in/out
- PresenceEvidence: Multi-source validation (GPS, RFID, facial recognition)
- PresenceAudit: Service for compliance and usage verification
Business Rules:
- Check-in requires being within 50m of arena (GPS)
- Alternative check-in methods: QR code, RFID bracelet, facial recognition
- Auto check-out after 2 hours of inactivity
- Presence evidence captured every 5 minutes
- Audit trail for billing disputes
Events Context
Key Classes:
- Event: Aggregate root for tournaments, clinics, festivals
- Tournament: Structured competition with brackets and rounds
- EventZone: Defines access-controlled areas during events
- EventParticipant: Registered attendee with entry fee payment
Business Rules:
- Arena enters "Event Mode" with special access rules
- Participants receive RFID pass for automated access
- Tournament brackets generated automatically based on seeds
- Entry fees collected upfront
- Refund policy: 100% until 7 days before, 50% until 3 days, 0% after
Engagement & Gamification Context
Key Classes:
- PlayerRanking: Aggregate root tracking player positions
- Achievement: Unlockable goals with badge rewards
- Challenge: Time-limited objectives (daily, weekly, seasonal)
- XPSystem: Service calculating experience points for activities
Business Rules:
- XP awarded for: check-ins (+10), matches (+50), wins (+25), streaks (+10/day)
- Level up at XP thresholds: [100, 300, 600, 1000, 1500, ...]
- Rankings reset monthly for fresh competition
- Achievements give XP bonuses and unlock perks
- Top 10 players each month receive prizes
Achievement Examples:
- "First Blood": Play first match (+50 XP)
- "Centurion": Win 100 matches (+500 XP, VIP badge)
- "Iron Streak": Play 7 days consecutively (+200 XP)
- "Master of Court 1": Win 50 times on same court (+300 XP)
Generic Domain Models
Commerce & Billing Context
Key Classes:
- Invoice: Aggregate root for billing with line items
- Payment: Handles transaction processing with external gateways
- Subscription: Recurring membership plans
- LineItem: Individual charge on an invoice
Business Rules:
- Payment methods: PIX (instant), Credit Card (T+1), Boleto (T+2)
- Invoice due date: 5 days after generation
- Subscription auto-renews unless cancelled
- Prorated credits for plan changes
- Invoice must balance:
total = subtotal + taxes
Media & IoT Context
Key Classes:
- MediaAsset: Recorded videos/photos from matches
- Highlight: Short clips (5-30s) of key moments
- IoTDevice: Tablets, cameras, sensors, RFID readers
Business Rules:
- Videos auto-recorded for all matches
- Highlights triggered by: button press, AI detection, post-match tagging
- Retention: 7 days for full videos, 90 days for highlights
- Users can download their match videos
- Cameras track ball movement using computer vision
Sponsorship & Ads Context
Key Classes:
- Sponsor: Paying advertiser with contracts
- Advertisement: Ad creative with targeting rules
- AdInventory: Available ad slots by location/type
Business Rules:
- Ad types: Court banners, app banners, video pre-rolls, push notifications
- Pricing: CPM (cost per 1000 impressions) or flat rate
- Max 1 ad per 5 minutes of video
- Sponsors receive monthly reports with impressions/clicks/ROI
Enumerations
User & Access Enums
enum Role {
SUPER_ADMIN // Full system access
ARENA_OWNER // Arena ownership, billing
ARENA_MANAGER // Operational management
RECEPTIONIST // Check-in, manual bookings
INSTRUCTOR // Teach lessons, manage students
PLAYER // Default user role
}
enum UserStatus {
PENDING // Email verification pending
ACTIVE // Normal active user
SUSPENDED // Temporarily blocked
INACTIVE // Voluntarily deactivated
DELETED // LGPD deletion in progress
}
enum MembershipStatus {
ACTIVE
SUSPENDED
CANCELLED
EXPIRED
}Arena & Court Enums
enum SportType {
BEACH_TENNIS
BEACH_VOLLEYBALL
FOOTVOLLEY
TENNIS
PADEL
}
enum CourtStatus {
AVAILABLE // Ready for use
OCCUPIED // Currently in use
RESERVED // Future booking exists
MAINTENANCE // Undergoing repairs
BLOCKED // Admin blocked
}
enum SurfaceType {
SAND
CLAY
GRASS
SYNTHETIC
HARD_COURT
}
enum AreaType {
COURT
LOCKER_ROOM
BAR
PARKING
LOUNGE
}Booking & Queue Enums
enum BookingType {
COURT_RENTAL // Standard hourly rental
LESSON // Instruction session
DAY_USE // Pay-per-entry access
EVENT // Special event booking
TOURNAMENT // Competition
}
enum BookingStatus {
PENDING // Awaiting payment/approval
CONFIRMED // Paid and scheduled
IN_PROGRESS // Currently happening
COMPLETED // Finished successfully
CANCELLED // User cancelled
NO_SHOW // Didn't show up
}
enum QueueStatus {
WAITING // In queue
CALLED // Notified, awaiting response
ACCEPTED // Confirmed ready to play
DECLINED // Refused the call
TIMEOUT // Didn't respond in time
}
enum Priority {
VIP // Top priority (VIP members)
HIGH // Monthly members, long wait
NORMAL // Standard users
LOW // Recent players, penalties
}Match & Gamification Enums
enum MatchType {
CASUAL // Friendly game
COMPETITIVE // Ranked match
TOURNAMENT // Part of competition
TRAINING // Practice session
}
enum MatchStatus {
SCHEDULED
IN_PROGRESS
PAUSED
FINISHED
CANCELLED
}
enum SkillLevel {
BEGINNER // 0-6 months experience
INTERMEDIATE // 6-24 months
ADVANCED // 2-5 years
PROFESSIONAL // 5+ years, competitive
}
enum RankingScope {
GLOBAL // Across all arenas
ARENA // Single arena
WEEKLY // Reset every 7 days
MONTHLY // Reset monthly
SEASONAL // 3-month seasons
}
enum Rarity {
COMMON // Easy to unlock
RARE // Requires effort
EPIC // Major achievement
LEGENDARY // Very rare milestone
}Commerce Enums
enum PaymentMethod {
PIX // Brazilian instant payment
CREDIT_CARD // Card payment
DEBIT_CARD // Debit card
BOLETO // Brazilian bank slip
WALLET // Internal wallet balance
CASH // On-site cash
}
enum PaymentStatus {
PENDING // Awaiting processing
PROCESSING // Being processed
APPROVED // Successfully paid
DECLINED // Payment failed
REFUNDED // Money returned
CANCELLED // Cancelled before processing
}
enum InvoiceStatus {
DRAFT // Being created
PENDING // Awaiting payment
PAID // Fully paid
OVERDUE // Past due date
CANCELLED // Cancelled before payment
}Transaction Enums
enum TransactionType {
CREDIT // Money added to wallet
DEBIT // Money removed from wallet
TRANSFER // Between wallets
PURCHASE // Payment for service
REFUND // Money returned
REWARD // Bonus/prize received
STAKE // Bet placed on match
}
enum TransactionStatus {
PENDING // Waiting to process
PROCESSING // Being processed
COMPLETED // Successfully finished
FAILED // Processing failed
CANCELLED // User cancelled
REFUNDED // Reversed transaction
}
enum Currency {
BRL // Brazilian Real
USD // US Dollar
ARENA_TOKEN // Internal virtual currency
}Relationships Summary
One-to-One Relationships
User 1:1 UserProfile
User 1:1 Wallet
Arena 1:1 OperatingCalendar
Arena 1:1 ArenaSettings
Court 1:1 CourtProfile
Booking 1:1 TimeRange
Booking 1:1 BookingPolicy
Match 1:1 Score
Player 1:1 PlayerProfile
Player 1:1 PlayerStats
Subscription 1:1 SubscriptionPlanOne-to-Many Relationships
Arena 1:N Court
Arena 1:N PhysicalArea
Arena 1:N Event
User 1:N Session
User 1:N Booking
User 1:N TenantMembership
Court 1:N Booking
Court 1:N Match
AllocationEngine 1:N PlayerQueue
PlayerQueue 1:N QueueEntry
Lesson 1:N Student
Lesson 1:N Attendance
Match 1:N Team
Team 1:N Player
Wallet 1:N Balance
Wallet 1:N Transaction
Invoice 1:N LineItem
Invoice 1:N Payment
Tournament 1:N Match
Event 1:N EventZone
PlayerRanking 1:N RankingEntry
Sponsor 1:N AdvertisementMany-to-Many Relationships
Player N:M Achievement (earned achievements)
Player N:M Challenge (participated challenges)
User N:M Role (user can have multiple roles)
User N:M Arena (via TenantMembership)
Match N:M Player (via Team)
Event N:M User (via EventParticipant)Architectural Notes
Aggregate Roots
The following classes are Aggregate Roots (entry points for modifications):
- Core Domain:
User,Arena,Booking,Wallet,Match,AllocationEngine - Supporting:
Lesson,Event,PresenceSession,PlayerRanking,Achievement - Generic:
Invoice,Subscription,Sponsor,MediaAsset
Event Sourcing
The following aggregates use Event Sourcing:
- Wallet: All state reconstructed from transaction events
- Match: Score changes tracked as events for replay/audit
CQRS
Applied in:
- Wallet: Write (transactions) vs Read (balance queries)
- Match: Write (score updates) vs Read (leaderboards, stats)
- Booking: Write (reservations) vs Read (availability calendar)
Multi-Tenancy
All domain entities include:
tenantId(Arena identifier) for row-level isolation- Queries always filter by
tenantId - PostgreSQL Row Level Security (RLS) enforces access
Value Objects (Immutable)
TimeRange,Score,Balance,CourtProfilePlayerProfile,PlayerStats,Address,ContactBookingPolicy,CancellationPolicy,PricingConfig
Usage Examples
Creating a Booking
// User creates a booking
const booking = Booking.create({
userId: "user-123",
courtId: "court-1",
timeRange: TimeRange.create("2025-01-10T18:00", "2025-01-10T19:00"),
type: BookingType.COURT_RENTAL,
policy: arena.settings.bookingRules
})
// Validate availability
if (!court.isAvailableAt(booking.timeRange)) {
throw new ConflictError("Court not available")
}
// Process payment
const payment = Payment.create({
invoiceId: invoice.id,
amount: Money.fromCents(5000), // R$ 50.00
method: PaymentMethod.PIX
})
await payment.process()
// Confirm booking
booking.confirm()
// Publish event
eventBus.publish(new BookingConfirmedEvent(booking))Allocating Players from Queue
// Court becomes available
court.release()
// Allocation engine finds next players
const allocationResult = allocationEngine.allocateNext(court.id)
if (allocationResult.success) {
const players = allocationResult.players
// Balance teams using ELO rating
const teams = balanceTeams(players)
// Create match
const match = Match.create({
courtId: court.id,
teams: teams,
type: MatchType.CASUAL,
sport: court.sportType
})
// Notify players
players.forEach(player => {
notificationService.send(player.userId, {
title: "Your turn!",
body: `Court ${court.name} is ready`,
data: { matchId: match.id }
})
})
// Start match
match.start()
}Recording Match Result & Distributing Stakes
// Match finishes
match.recordSet(team1.id, 6, 4)
match.recordSet(team1.id, 7, 5)
match.finish()
// Update player ratings
const winner = match.getWinner()
players.forEach(player => {
const newRating = calculateEloRating(
player.rating,
match.getOpponentRating(player),
match.getResult(player)
)
player.updateRating(newRating)
player.updateStats(match)
})
// Distribute stakes if match had bets
if (match.stakes) {
const transactions = match.stakes.distribute(winner)
transactions.forEach(txn => txn.execute())
}
// Award XP
xpSystem.awardXP(winner.playerId, 25) // Win bonus
players.forEach(player => {
xpSystem.awardXP(player.playerId, 10) // Participation
})
// Update rankings
playerRanking.updateScore(winner.playerId, 100)Validation Rules Summary
Booking Validation
class BookingValidator {
validate(booking: Booking): ValidationResult {
// Time range validation
if (!booking.timeRange.isValid()) {
return ValidationResult.error("Invalid time range")
}
// Advance notice
const hoursAhead = booking.timeRange.startTime.diffInHours(now())
if (hoursAhead < booking.policy.minAdvance.hours) {
return ValidationResult.error("Minimum 2 hours advance required")
}
// Court availability
if (!court.isAvailableAt(booking.timeRange)) {
return ValidationResult.error("Court not available")
}
// User eligibility
if (user.hasOutstandingNoShows()) {
return ValidationResult.error("No-show suspension active")
}
return ValidationResult.success()
}
}Wallet Transaction Validation
class TransactionValidator {
validate(transaction: Transaction): ValidationResult {
// Idempotency check
if (this.isDuplicate(transaction.idempotencyKey)) {
return ValidationResult.duplicate(this.findByKey(transaction.idempotencyKey))
}
// Sufficient balance
if (transaction.type === TransactionType.DEBIT) {
const wallet = getWallet(transaction.fromWalletId)
if (!wallet.hasAvailableBalance(transaction.amount)) {
return ValidationResult.error("Insufficient balance")
}
}
// Wallet not frozen
if (wallet.status === WalletStatus.FROZEN) {
return ValidationResult.error("Wallet is frozen")
}
return ValidationResult.success()
}
}Next Steps
This class diagram serves as the single source of truth for the domain model. Use it to:
- Generate Database Schema: Map entities to tables, value objects to JSON columns
- Implement Domain Layer: Create TypeScript classes following this structure
- Define API Contracts: OpenAPI specs mirror these entities
- Write Tests: Test cases validate business rules defined here
- Team Alignment: Ensure developers, PO, and stakeholders share same vocabulary
Version: 1.0.0
Last Updated: 2025-01-09
Author: Atlas (Claude Opus 4.5)
Status: Approved
"The class diagram is not just documentation—it's the architectural DNA of the system."
— Vaughn Vernon, Implementing Domain-Driven Design