Skip to content

UML Sequence Diagrams - Sport Tech Club

Version: 1.0.0
Date: 2026-01-09
Status: Comprehensive Documentation


Table of Contents

  1. Booking Creation Flow
  2. Queue Management Flow (Ganha-Fica)
  3. Check-in Process
  4. Match Registration & Gamification
  5. Event Mode Activation
  6. Payment Processing

1. Booking Creation Flow

Overview

User searches for court availability, selects time slot, adds participants, processes payment (PIX/Card), receives confirmation and notification.

Actors

  • User: Player making the booking
  • Frontend: Vue.js application
  • API Gateway: Entry point with auth
  • Scheduling Service: Core domain for bookings
  • Courts Service: Court availability management
  • Payment Gateway: External payment processor (Stripe/Mercado Pago)
  • Wallet Service: Internal wallet system
  • Notification Service: Push/Email/SMS notifications
  • Database: PostgreSQL with RLS

Key Decision Points

  1. Availability Check: Real-time validation prevents double-booking
  2. Pessimistic Locking: SELECT FOR UPDATE prevents race conditions
  3. Payment Methods: Multiple options with graceful fallback
  4. Webhook Reliability: Idempotency keys prevent duplicate processing
  5. Transaction Safety: ACID transactions ensure consistency

Error Handling

Error ScenarioHTTP StatusUser MessageSystem Action
Slot taken (race)409 Conflict"Slot just taken, try another"Rollback transaction
Payment failed402 Payment Required"Payment declined. Try another card?"Keep booking PENDING 15min
Insufficient wallet400 Bad Request"Insufficient credits. Top up?"Suggest top-up flow
Service timeout504 Gateway Timeout"Taking longer than usual..."Retry with exponential backoff

2. Queue Management Flow (Ganha-Fica)

Overview

Players enter a dynamic queue, receive position assignments, get ETA calculations, are called when court is free, accept/decline, play match, register score. Winner stays, loser goes to end of queue.

Actors

  • Player: User in queue or playing
  • Frontend: Real-time Vue.js app
  • Queue Service: Core allocation engine
  • Match Service: Match management
  • Courts Service: Court state management
  • Notification Service: Real-time notifications via WebSocket
  • Gamification Service: XP and ranking updates

Queue Allocation Algorithm

typescript
class AllocationEngine {
  async allocateNextMatch(courtId: CourtId): Promise<AllocationResult> {
    const players = await this.getNextPlayers(4);
    
    // Score match quality
    const matchQuality = this.calculateMatchQuality(players);
    
    if (matchQuality.score < 0.7) {
      // Wait for better match unless timeout
      if (players[0].waitTime > 30 * 60) {
        return this.allocate(players); // Force allocation
      }
      return AllocationResult.waitForBetterMatch();
    }
    
    // Form balanced teams
    const teams = this.balanceTeams(players);
    
    return AllocationResult.success(teams);
  }
  
  private calculateMatchQuality(players: Player[]): MatchQuality {
    const skillBalance = this.checkSkillBalance(players);
    const genderBalance = this.checkGenderBalance(players);
    const preferenceMatch = this.checkPreferences(players);
    
    return {
      score: (skillBalance * 0.5) + (genderBalance * 0.3) + (preferenceMatch * 0.2),
      factors: { skillBalance, genderBalance, preferenceMatch }
    };
  }
}

Real-Time Updates

All queue state changes are broadcast via WebSocket:

typescript
// Client-side subscription
socket.on('queue:position_updated', (data) => {
  updateQueuePosition(data.position, data.eta);
});

socket.on('queue:match_ready', (data) => {
  showMatchAcceptanceModal(data);
  startCountdown(60); // 60s to accept
});

socket.on('match:score_updated', (data) => {
  updateLiveScore(data.score);
});

3. Check-in Process

Overview

User arrives at arena, validates location (GPS/QR), system verifies booking, grants physical access, starts session, tracks game time, and handles check-out.

Actors

  • User: Player with booking
  • Mobile App: Vue.js PWA
  • Access Control: Ziggy integration
  • Presence Service: Check-in management
  • IoT Devices: Tablets, beacons, QR codes
  • Booking Service: Reservation validation

Access Control Integration (Ziggy)

typescript
class ZiggyAccessControlAdapter {
  async grantAccess(userId: UserId, zoneId: ZoneId, validUntil: Date): Promise<void> {
    const credential = await this.generateTemporaryCredential(userId);
    
    await this.ziggyApi.post('/access/grant', {
      credential_id: credential.id,
      zone_id: zoneId,
      valid_from: new Date(),
      valid_until: validUntil,
      access_level: 'TEMPORARY'
    });
    
    await this.ziggyApi.post('/devices/unlock', {
      zone_id: zoneId,
      duration_seconds: 5
    });
  }
  
  async revokeAccess(credentialId: string): Promise<void> {
    await this.ziggyApi.delete(`/access/${credentialId}`);
  }
}

Presence Session State Machine

typescript
enum PresenceStatus {
  PENDING = 'PENDING',           // Booking exists, not checked in
  CHECKED_IN = 'CHECKED_IN',     // User checked in, session active
  IN_PROGRESS = 'IN_PROGRESS',   // Game started
  COMPLETED = 'COMPLETED',       // Normal end
  NO_SHOW = 'NO_SHOW',           // Didn't check in
  TIMEOUT = 'TIMEOUT',           // Auto-ended
  CANCELLED = 'CANCELLED'        // Manually cancelled
}

class PresenceSession {
  canCheckIn(): boolean {
    const now = new Date();
    const windowStart = subMinutes(this.booking.startTime, 15);
    const windowEnd = addMinutes(this.booking.startTime, 5);
    
    return now >= windowStart && now <= windowEnd;
  }
  
  calculateGameTime(): number {
    const actualStart = this.checkInAt || this.booking.startTime;
    const scheduledEnd = this.booking.endTime;
    const lateness = differenceInMinutes(actualStart, this.booking.startTime);
    
    return Math.max(0, differenceInMinutes(scheduledEnd, actualStart));
  }
}

4. Match Registration & Gamification

Overview

Match ends, players submit scores, system resolves conflicts, awards XP, checks for level-ups, unlocks achievements, and updates rankings.

Actors

  • Players: Match participants
  • Match Service: Score and result management
  • Gamification Service: XP, levels, achievements
  • Ranking Service: Leaderboard updates
  • Wallet Service: Token rewards
  • Analytics Service: Stats tracking

XP Calculation Engine

typescript
class XPCalculator {
  calculate(match: Match, player: Player): XPAward {
    const baseXP = 25; // Match completion
    
    let bonusXP = 0;
    const multipliers: number[] = [];
    
    // Victory bonus
    if (match.winner === player.team) {
      bonusXP += 50;
    } else {
      bonusXP += 25; // Participation
    }
    
    // First game of day
    if (this.isFirstGameToday(player)) {
      multipliers.push(1.5); // +50%
    }
    
    // Streak bonus
    if (player.streakDays >= 7) {
      multipliers.push(1.25); // +25%
    }
    
    // Rank difference
    const opponentRankDiff = this.calculateRankDifference(match, player);
    if (opponentRankDiff > 100) {
      multipliers.push(1.2); // Upset bonus
    }
    
    // Special achievements
    if (match.mvp === player.id) {
      bonusXP += 100;
    }
    
    // Apply multipliers
    let totalXP = baseXP + bonusXP;
    multipliers.forEach(m => totalXP *= m);
    
    return {
      total: Math.floor(totalXP),
      breakdown: {
        base: baseXP,
        bonus: bonusXP,
        multipliers
      }
    };
  }
}

Achievement System

typescript
const ACHIEVEMENTS = {
  FIRST_MATCH: {
    id: 'first_match',
    name: 'First Steps',
    criteria: (player) => player.matchesPlayed >= 1,
    rarity: 'COMMON',
    reward: { tokens: 50, xp: 25 }
  },
  
  WIN_STREAK_5: {
    id: 'win_streak_5',
    name: 'On Fire!',
    criteria: (player) => player.consecutiveWins >= 5,
    rarity: 'RARE',
    reward: { tokens: 150, xp: 100 }
  },
  
  WEEKLY_WARRIOR: {
    id: 'weekly_warrior',
    name: 'Weekly Warrior',
    criteria: (player) => player.matchesThisWeek >= 10,
    rarity: 'EPIC',
    reward: { tokens: 500, xp: 250 }
  },
  
  PERFECT_MONTH: {
    id: 'perfect_month',
    name: 'Perfect Attendance',
    criteria: (player) => player.streakDays >= 30,
    rarity: 'LEGENDARY',
    reward: { tokens: 2000, xp: 1000 }
  }
};

5. Event Mode Activation

Overview

Arena manager creates special event, reserves courts, activates event mode with custom rules, participants check in, consumption is tracked separately, and event concludes.

Actors

  • Manager: Arena operator
  • Admin Console: Management interface
  • Event Service: Event lifecycle management
  • Courts Service: Court reservation
  • Access Control: Special event access
  • Participants: Event players
  • Commerce Service: Separate billing

Event Modes

typescript
enum EventMode {
  TOURNAMENT = 'TOURNAMENT',     // Bracketed competition
  CHAMPIONSHIP = 'CHAMPIONSHIP', // League format
  FESTIVAL = 'FESTIVAL',         // Social event
  CLINIC = 'CLINIC',             // Training/workshop
  PRIVATE = 'PRIVATE'            // Private rental
}

enum EventStatus {
  DRAFT = 'DRAFT',               // Being created
  PUBLISHED = 'PUBLISHED',       // Open for registration
  REGISTRATION_CLOSED = 'REGISTRATION_CLOSED',
  ACTIVE = 'ACTIVE',             // Event in progress
  COMPLETED = 'COMPLETED',       // Finished, settling
  CANCELLED = 'CANCELLED',       // Cancelled
  ARCHIVED = 'ARCHIVED'          // Historical record
}

Event Wallet Ledger

typescript
class EventWallet {
  private ledger: Transaction[] = [];
  
  recordEntryFee(userId: UserId, amount: Money): void {
    this.ledger.push({
      type: 'CREDIT',
      source: 'ENTRY_FEE',
      from: userId,
      amount,
      timestamp: new Date()
    });
  }
  
  distributePrize(winnerId: UserId, prize: Money): void {
    this.ledger.push({
      type: 'DEBIT',
      source: 'PRIZE_PAYOUT',
      to: winnerId,
      amount: prize,
      timestamp: new Date()
    });
  }
  
  getBalance(): Money {
    return this.ledger.reduce((sum, tx) => {
      return tx.type === 'CREDIT' 
        ? sum + tx.amount 
        : sum - tx.amount;
    }, 0);
  }
  
  auditTrail(): LedgerEntry[] {
    // Immutable audit trail for compliance
    return this.ledger.map(tx => Object.freeze(tx));
  }
}

6. Payment Processing

Overview

User creates payment for booking/event/consumption, system generates PIX QR code or tokenizes card, processes payment via gateway, receives webhook confirmation, updates wallet, and generates receipt.

Actors

  • User: Paying customer
  • Frontend: Payment UI
  • Payment Service: Payment orchestration
  • Payment Gateway: External processors (Stripe, Mercado Pago, PagSeguro)
  • Wallet Service: Internal credits
  • Booking Service: Related booking
  • Notification Service: Receipts and confirmations

Payment Gateway Abstraction

typescript
interface PaymentGateway {
  createPayment(params: CreatePaymentParams): Promise<PaymentResult>;
  checkStatus(paymentId: string): Promise<PaymentStatus>;
  refund(paymentId: string, amount: Money): Promise<RefundResult>;
  validateWebhook(signature: string, payload: any): boolean;
}

class StripeGateway implements PaymentGateway {
  async createPayment(params: CreatePaymentParams): Promise<PaymentResult> {
    const charge = await this.stripe.charges.create({
      amount: params.amount * 100, // Convert to cents
      currency: 'brl',
      source: params.cardToken,
      description: params.description,
      metadata: params.metadata
    });
    
    return {
      providerPaymentId: charge.id,
      status: this.mapStatus(charge.status),
      paidAt: charge.paid ? new Date(charge.created * 1000) : null
    };
  }
  
  validateWebhook(signature: string, payload: any): boolean {
    return this.stripe.webhooks.constructEvent(
      payload,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  }
}

class MercadoPagoGateway implements PaymentGateway {
  async createPayment(params: CreatePaymentParams): Promise<PaymentResult> {
    const payment = await this.mercadopago.payment.create({
      transaction_amount: params.amount,
      payment_method_id: 'pix',
      payer: {
        email: params.email,
        identification: {
          type: 'CPF',
          number: params.cpf
        }
      }
    });
    
    return {
      providerPaymentId: payment.body.id,
      status: this.mapStatus(payment.body.status),
      qrCode: payment.body.point_of_interaction?.transaction_data?.qr_code,
      qrCodeString: payment.body.point_of_interaction?.transaction_data?.qr_code_base64
    };
  }
}

// Factory pattern for gateway selection
class PaymentGatewayFactory {
  static create(method: PaymentMethod): PaymentGateway {
    switch (method) {
      case PaymentMethod.CREDIT_CARD:
      case PaymentMethod.DEBIT_CARD:
        return new StripeGateway();
        
      case PaymentMethod.PIX:
      case PaymentMethod.BOLETO:
        return new MercadoPagoGateway();
        
      default:
        throw new Error(`Unsupported payment method: ${method}`);
    }
  }
}

Webhook Security

typescript
class WebhookValidator {
  validateMercadoPago(signature: string, payload: string): boolean {
    const expectedSignature = crypto
      .createHmac('sha256', process.env.MP_WEBHOOK_SECRET)
      .update(payload)
      .digest('hex');
    
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expectedSignature)
    );
  }
  
  validateStripe(signature: string, payload: string): boolean {
    try {
      stripe.webhooks.constructEvent(
        payload,
        signature,
        process.env.STRIPE_WEBHOOK_SECRET
      );
      return true;
    } catch (err) {
      return false;
    }
  }
  
  validateSource(ipAddress: string, provider: PaymentProvider): boolean {
    const allowedIPs = {
      MERCADO_PAGO: ['200.98.202.0/24', '52.67.0.0/16'],
      STRIPE: ['3.18.12.0/22', '3.130.192.0/22']
    };
    
    return this.ipInRange(ipAddress, allowedIPs[provider]);
  }
}

Idempotency Implementation

typescript
class PaymentService {
  async createPayment(params: CreatePaymentParams, idempotencyKey: string): Promise<Payment> {
    // Check if payment already exists
    const existing = await this.paymentRepo.findByIdempotencyKey(idempotencyKey);
    
    if (existing) {
      // Return existing payment (idempotent)
      return existing;
    }
    
    // Create new payment
    const payment = Payment.create({
      ...params,
      idempotencyKey
    });
    
    await this.paymentRepo.save(payment);
    
    return payment;
  }
}

// Usage with retry logic
class PaymentClient {
  async createPayment(params: CreatePaymentParams): Promise<Payment> {
    const idempotencyKey = crypto.randomUUID();
    
    let retries = 3;
    while (retries > 0) {
      try {
        return await this.api.post('/payments', params, {
          headers: {
            'Idempotency-Key': idempotencyKey
          }
        });
      } catch (error) {
        if (error.status >= 500 && retries > 1) {
          retries--;
          await this.sleep(1000 * (4 - retries)); // Exponential backoff
        } else {
          throw error;
        }
      }
    }
  }
}

Summary

These sequence diagrams provide comprehensive documentation of the six critical flows in the Sport Tech Club system:

  1. Booking Creation: Real-time availability → selection → payment → confirmation
  2. Queue Management (Ganha-Fica): Dynamic allocation → match formation → winner stays logic
  3. Check-in Process: Location validation → access control → session tracking
  4. Match & Gamification: Score resolution → XP award → level-up → achievements
  5. Event Mode: Creation → activation → separate billing → finalization
  6. Payment Processing: Multiple methods → gateway integration → webhooks → receipts

Key Architectural Patterns Used

  • Pessimistic Locking: Prevent race conditions in bookings
  • Event-Driven Architecture: Decoupled communication via events
  • CQRS: Separate read/write models for performance
  • Event Sourcing: Immutable audit trail (Wallet, Matches)
  • Saga Pattern: Distributed transactions across services
  • Idempotency: Safe retries for payment operations
  • WebSocket: Real-time updates for queue and matches
  • Anti-Corruption Layer: Clean integration with external systems

Error Handling Strategy

All flows include:

  • Validation: Business rule validation before state changes
  • Transactions: ACID guarantees for critical operations
  • Rollback: Automatic rollback on failures
  • Retry Logic: Exponential backoff for transient failures
  • Graceful Degradation: Fallback mechanisms
  • Audit Logging: Complete traceability
  • User Feedback: Clear error messages

Version Control:

VersionDateChangesAuthor
1.0.02026-01-09Initial comprehensive diagramsAtlas (Claude Opus 4.5)

Related Documentation:


"The devil is in the details, but so is salvation." — Mies van der Rohe