Domain Model - Sport Tech Club
Visão Geral
Este documento descreve o modelo de domínio completo do Sport Tech Club, organizado em Bounded Contexts seguindo os princípios de Domain-Driven Design (DDD).
Contextos Delimitados (Bounded Contexts)
┌─────────────────────────────────────────────────────────────────────┐
│ Sport Tech Club │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
│ │ Identity & Access│ │ Arena Context │ │ Courts & Alloc │ │
│ └──────────────────┘ └──────────────────┘ └─────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
│ │ Scheduling │ │ Lessons & Coach │ │ Play & Match │ │
│ └──────────────────┘ └──────────────────┘ └─────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
│ │ Events │ │ Access & Presence│ │ Devices & IoT │ │
│ └──────────────────┘ └──────────────────┘ └─────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
│ │ Media & Video │ │Commerce & Billing│ │ Wallet & Value │ │
│ └──────────────────┘ └──────────────────┘ └─────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
│ │Engagement & Game │ │ Sponsorship & Ads│ │ Analytics │ │
│ └──────────────────┘ └──────────────────┘ └─────────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ Shared Kernel │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘1. Identity & Access Context
Objetivo
Gerenciar identidades, autenticação, autorização e controle de acesso para todos os usuários e sistemas.
Aggregates
1.1 Tenant (Aggregate Root)
Descrição: Representa uma organização/arena que utiliza o sistema em modelo multi-tenant.
Atributos:
id: TenantId(Identifier)name: stringsubdomain: string(único)status: TenantStatus(ACTIVE, SUSPENDED, INACTIVE)subscription: SubscriptionInfoconfiguration: TenantConfigurationcreatedAt: DateactivatedAt: Date?
Relacionamentos:
- Possui múltiplas
ArenaTenant(arenas físicas) - Possui múltiplos
UserAccount - Define
Policyde segurança
Invariantes:
- Subdomain deve ser único e válido (DNS-safe)
- Tenant ativo deve ter pelo menos uma arena
- Configuração deve ser válida para o plano contratado
1.2 ArenaTenant
Descrição: Vincula um Tenant a uma Arena física específica.
Atributos:
id: ArenaTenantIdtenantId: TenantIdarenaId: ArenaIdrole: TenantRole(OWNER, OPERATOR, PARTNER)accessLevel: AccessLevelactiveSince: Date
Relacionamentos:
- Pertence a um
Tenant - Referencia uma
Arena(Arena Context)
1.3 UserAccount (Aggregate Root)
Descrição: Conta de usuário no sistema, identidade digital única.
Atributos:
id: UserId(Identifier)tenantId: TenantIdemail: Email(Value Object)username: string?status: AccountStatus(ACTIVE, SUSPENDED, LOCKED, PENDING_VERIFICATION)emailVerified: booleanphoneNumber: PhoneNumber?(Value Object)phoneVerified: booleanmfaEnabled: booleanpasswordExpiresAt: Date?lockedUntil: Date?lastLoginAt: Date?createdAt: Date
Relacionamentos:
- Possui um ou mais
PersonProfile - Possui múltiplos
Credential - Possui múltiplas
Session - Possui múltiplos
Role - Possui
Consentpara políticas de privacidade
Invariantes:
- Email deve ser único por tenant
- Conta suspensa não pode autenticar
- MFA obrigatório para roles administrativos
Comportamentos:
authenticate(credential): AuthResultsuspend(reason): voidactivate(): voidlockAccount(duration): voidenableMFA(): voidverifyEmail(token): void
1.4 PersonProfile
Descrição: Perfil de pessoa física (dados pessoais).
Atributos:
id: PersonProfileIduserId: UserIdfullName: FullName(Value Object)dateOfBirth: Date?gender: Gender?document: Document?(CPF, RG, Passport)address: Address?(Value Object)avatar: MediaAssetId?preferences: UserPreferencescreatedAt: Date
Relacionamentos:
- Pertence a um
UserAccount - Pode ter
PlayerProfile - Pode ter
InstructorProfile - Pode ter
StaffProfile - Pode ter
GuestProfile
Invariantes:
- Pessoa menor de idade requer responsável
1.5 PlayerProfile
Descrição: Perfil específico para jogadores (alunos/atletas).
Atributos:
id: PlayerProfileIdpersonProfileId: PersonProfileIdplayerId: string(código/matrícula)skillLevel: SkillLevel(BEGINNER, INTERMEDIATE, ADVANCED, PROFESSIONAL)preferredHand: Hand(LEFT, RIGHT, AMBIDEXTROUS)playStyle: PlayStyle[]medicalClearance: MedicalClearance?emergencyContact: EmergencyContactmemberSince: Datestatus: PlayerStatus(ACTIVE, INACTIVE, SUSPENDED)
Relacionamentos:
- Pertence a um
PersonProfile - Possui
PlayerProgress(Engagement Context) - Possui
Wallet(Wallet Context) - Participa de
Match(Play Context) - Inscreve-se em
Lesson(Lessons Context)
Invariantes:
- Jogador ativo deve ter atestado médico válido
- Menor de idade requer autorização dos responsáveis
1.6 InstructorProfile
Descrição: Perfil de instrutor/professor.
Atributos:
id: InstructorProfileIdpersonProfileId: PersonProfileIdinstructorCode: stringspecialties: Specialty[](TENNIS, PADEL, BEACH_TENNIS)certifications: Certification[]bio: string?hourlyRate: Money?commissionRate: Percentage?availability: InstructorAvailabilitystatus: InstructorStatushiredAt: Date
Relacionamentos:
- Pertence a um
PersonProfile - Ministra
Lesson(Lessons Context) - Possui
InstructorPayout(Commerce Context)
Invariantes:
- Instrutor ativo deve ter pelo menos uma certificação válida
- Disponibilidade deve respeitar regras trabalhistas
1.7 StaffProfile
Descrição: Perfil de funcionário da arena (não instrutor).
Atributos:
id: StaffProfileIdpersonProfileId: PersonProfileIdemployeeCode: stringdepartment: Department(RECEPTION, MAINTENANCE, MANAGEMENT, OPERATIONS)position: stringhiredAt: Datestatus: EmploymentStatus
Relacionamentos:
- Pertence a um
PersonProfile - Possui
Roleespecíficas
1.8 GuestProfile
Descrição: Perfil temporário para visitantes não cadastrados.
Atributos:
id: GuestProfileIdpersonProfileId: PersonProfileIdguestCode: stringsponsor: UserId?(quem convidou)validUntil: Datepurpose: GuestPurpose(TRIAL, EVENT, ONE_TIME_PLAY)
Relacionamentos:
- Pertence a um
PersonProfile - Possui acesso limitado
Invariantes:
- Convidado não pode reservar quadras sem patrocínio
- Acesso expira automaticamente
1.9 Role (Aggregate Root)
Descrição: Papel/função que agrupa permissões.
Atributos:
id: RoleIdtenantId: TenantIdname: stringdescription: string?type: RoleType(SYSTEM, TENANT, CUSTOM)isActive: booleancreatedAt: Date
Relacionamentos:
- Possui múltiplas
Permission - Atribuído a múltiplos
UserAccount
Invariantes:
- Roles de sistema não podem ser modificadas
- Nome deve ser único por tenant
Comportamentos:
grantPermission(permission): voidrevokePermission(permission): voidhasPermission(permission): boolean
Exemplos de Roles:
SUPER_ADMIN: acesso total ao sistemaARENA_ADMIN: administrador da arenaINSTRUCTOR: professor/instrutorRECEPTIONIST: recepcionistaPLAYER: jogador/alunoGUEST: visitante
1.10 Permission
Descrição: Permissão atômica para executar ação específica.
Atributos:
id: PermissionIdresource: string(ex: "court", "booking", "user")action: string(ex: "create", "read", "update", "delete")scope: PermissionScope(SYSTEM, TENANT, OWN)conditions: Condition[]?(regras condicionais)
Formato: resource:action:scope
Exemplos:
court:create:tenant- criar quadras na arenabooking:read:own- ler próprias reservasuser:update:tenant- atualizar usuários da arenamatch:score:system- pontuar partidas (qualquer)
1.11 Policy (Aggregate Root)
Descrição: Política de segurança e governança.
Atributos:
id: PolicyIdtenantId: TenantIdtype: PolicyType(PASSWORD, SESSION, ACCESS, DATA_RETENTION)rules: PolicyRule[]isActive: booleaneffectiveFrom: DateeffectiveUntil: Date?
Relacionamentos:
- Pertence a um
Tenant - Aplicada em
UserAccounteSession
Exemplos de Políticas:
- Password: comprimento mínimo, complexidade, expiração
- Session: timeout, max concurrent, device binding
- Access: horários permitidos, IPs permitidos, geolocalização
1.12 Credential (Aggregate Root)
Descrição: Credencial de autenticação (abstração para múltiplos tipos).
Atributos Base:
id: CredentialIduserId: UserIdtype: CredentialType(PASSWORD, OAUTH, RFID, QR_CODE, WRISTBAND, BIOMETRIC)status: CredentialStatus(ACTIVE, EXPIRED, REVOKED, SUSPENDED)createdAt: DatelastUsedAt: Date?expiresAt: Date?
Subtipos:
PasswordCredential:
passwordHash: stringsalt: stringalgorithm: HashAlgorithmmustChangeOnNextLogin: booleanpreviousHashes: string[](histórico para evitar reuso)
OAuthCredential:
provider: IdentityProviderproviderUserId: stringaccessToken: string?(encrypted)refreshToken: string?(encrypted)tokenExpiresAt: Date?
RFIDCredential:
tagId: stringtagType: RFIDTagTypeassignedTo: DeviceId?
QRCodeCredential:
code: string(único, rotativo)generatedAt: DatevalidUntil: DateusageCount: numbermaxUsages: number?
WristbandCredential:
wristbandId: stringbatteryLevel: number?assignedAt: Date
Comportamentos:
verify(input): booleanrotate(): void(para QR Code)revoke(): voidrenew(): void
1.13 Session (Aggregate Root)
Descrição: Sessão de autenticação ativa.
Atributos:
id: SessionIduserId: UserIdtoken: string(JWT ou session token)deviceInfo: DeviceInfo(user agent, OS, browser)ipAddress: stringgeoLocation: GeoLocation?createdAt: DatelastActivityAt: DateexpiresAt: Datestatus: SessionStatus(ACTIVE, EXPIRED, TERMINATED)
Relacionamentos:
- Pertence a um
UserAccount - Registra
AuthenticationAttempt
Invariantes:
- Sessão expirada não pode ser renovada
- Usuário pode ter limite de sessões concorrentes
Comportamentos:
refresh(): voidterminate(): voidisExpired(): booleanrecordActivity(): void
1.14 AuthenticationAttempt
Descrição: Registro de tentativa de autenticação (sucesso ou falha).
Atributos:
id: AuthAttemptIduserId: UserId?email: string?credentialType: CredentialTyperesult: AuthResult(SUCCESS, INVALID_CREDENTIALS, LOCKED, MFA_REQUIRED)ipAddress: stringuserAgent: stringtimestamp: DatefailureReason: string?
Relacionamentos:
- Vinculado a
UserAccount(se sucesso) - Gera
Session(se sucesso)
Uso: Auditoria, detecção de ataques brute force, análise de segurança
1.15 AuthorizationGrant
Descrição: Concessão de autorização (OAuth 2.0 / OIDC).
Atributos:
id: GrantIduserId: UserIdclientId: string(aplicação cliente)scope: string[]grantType: GrantType(AUTHORIZATION_CODE, REFRESH_TOKEN, CLIENT_CREDENTIALS)code: string?redirectUri: string?expiresAt: DatecreatedAt: Date
Relacionamentos:
- Pertence a um
UserAccount - Gera
Sessionou access token
1.16 IdentityProvider
Descrição: Provedor de identidade externo (SSO).
Atributos:
id: ProviderIdtenantId: TenantIdtype: ProviderType(GOOGLE, FACEBOOK, APPLE, SAML, OIDC)name: stringclientId: stringclientSecret: string(encrypted)configuration: ProviderConfig(endpoints, scopes)isActive: boolean
Relacionamentos:
- Pertence a um
Tenant - Gera
OAuthCredential
1.17 Consent
Descrição: Consentimento do usuário para políticas (LGPD/GDPR).
Atributos:
id: ConsentIduserId: UserIdpolicyType: PolicyType(TERMS_OF_SERVICE, PRIVACY_POLICY, MARKETING, DATA_SHARING)policyVersion: stringconsentedAt: DateipAddress: stringrevokedAt: Date?
Relacionamentos:
- Pertence a um
UserAccount
Invariantes:
- Consentimento obrigatório para usar o sistema
- Revogação de consentimento crítico pode suspender conta
2. Arena Context
Objetivo
Gerenciar arenas físicas, suas configurações, áreas, zonas de acesso e calendários operacionais.
Aggregates
2.1 Arena (Aggregate Root)
Descrição: Representação de uma arena física (complexo esportivo).
Atributos:
id: ArenaIdname: stringdisplayName: LocalizedTextslug: string(URL-friendly)description: string?address: Address(Value Object)geoLocation: GeoLocation(Value Object)timezone: string(IANA timezone)phoneNumber: PhoneNumberemail: Emailwebsite: string?status: ArenaStatus(ACTIVE, UNDER_CONSTRUCTION, MAINTENANCE, INACTIVE)openedAt: Datecapacity: ArenaCapacity(max concurrent players)features: ArenaFeature[](PARKING, RESTAURANT, SHOP, LOCKER_ROOMS)photos: MediaAssetId[]
Relacionamentos:
- Possui uma
ArenaConfiguration - Possui múltiplas
ArenaArea - Possui múltiplas
AccessZone - Possui um
BusinessCalendar - Possui múltiplas
Court(Courts Context)
Invariantes:
- Arena ativa deve ter pelo menos uma quadra operacional
- Timezone deve ser válida
- Capacidade deve respeitar limites de segurança
Comportamentos:
isOpen(date: Date): booleangetCurrentOccupancy(): numbercanAccommodate(playerCount: number): boolean
2.2 ArenaConfiguration
Descrição: Configurações operacionais da arena.
Atributos:
id: ConfigIdarenaId: ArenaIddefaultCourtDuration: Duration(ex: 1h30)minAdvanceBooking: Duration(ex: 30 minutos)maxAdvanceBooking: Duration(ex: 30 dias)cancellationWindow: Duration(ex: 24 horas)autoReleaseNoShow: Duration(ex: 15 minutos)checkInWindow: TimeRange(ex: -15min a +5min)defaultCurrency: CurrencytaxSettings: TaxSettingspaymentMethods: PaymentMethod[]supportedLanguages: string[]notificationSettings: NotificationSettings
Relacionamentos:
- Pertence a uma
Arena
Invariantes:
- Durações devem ser positivas e lógicas
- Pelo menos um método de pagamento ativo
2.3 ArenaArea
Descrição: Área física dentro da arena (abstração).
Atributos Base:
id: AreaIdarenaId: ArenaIdname: stringtype: AreaType(COURT, HOSPITALITY, EVENT, LOCKER_ROOM, COMMON)description: string?floor: number?capacity: number?status: AreaStatus(AVAILABLE, OCCUPIED, MAINTENANCE, CLOSED)
Subtipos:
CourtArea:
courtId: CourtIdsurface: SurfaceType(SYNTHETIC_GRASS, CEMENT, CLAY)isIndoor: booleanhasLighting: booleanhasRoof: boolean
HospitalityArea:
serviceType: ServiceType(RESTAURANT, BAR, CAFE, LOUNGE)seatingCapacity: numberoperatingHours: OperatingHours
EventArea:
setupType: SetupType(THEATER, BANQUET, COCKTAIL, CUSTOM)equipmentAvailable: Equipment[]
LockerRoomArea:
gender: Gender?(null = unisex)lockerCount: numberhasShowers: booleanhasRestrooms: boolean
Relacionamentos:
- Pertence a uma
Arena - Associada a
AccessZone
2.4 AccessZone
Descrição: Zona lógica de controle de acesso (pode agrupar múltiplas áreas).
Atributos:
id: ZoneIdarenaId: ArenaIdname: stringdescription: string?type: ZoneType(PUBLIC, RESTRICTED, PRIVATE, VIP)areas: AreaId[]accessLevel: AccessLevel(OPEN, MEMBER_ONLY, EVENT_ONLY, STAFF_ONLY)isActive: boolean
Relacionamentos:
- Pertence a uma
Arena - Referencia múltiplas
ArenaArea - Controlada por
ZoneRuleSet - Monitorada por
CheckInPoint(Access Context)
Invariantes:
- Zona VIP requer validação de acesso
- Zona restrita não pode ser pública
2.5 ZoneRuleSet
Descrição: Conjunto de regras de acesso para uma zona.
Atributos:
id: RuleSetIdzoneId: ZoneIdname: stringrules: AccessRule[]effectivePeriod: DateRangepriority: numberisActive: boolean
AccessRule (Value Object):
condition: RuleCondition(ex: "has valid booking", "is VIP member")action: RuleAction(ALLOW, DENY, REQUIRE_APPROVAL)timeRestriction: TimeRange?dayOfWeekRestriction: DayOfWeek[]?
Comportamentos:
evaluate(user: UserId, context: AccessContext): RuleResult
2.6 BusinessCalendar (Aggregate Root)
Descrição: Calendário operacional da arena com horários e feriados.
Atributos:
id: CalendarIdarenaId: ArenaIdtimezone: stringdefaultOperatingHours: OperatingHoursspecialDates: SpecialDate[]
Relacionamentos:
- Pertence a uma
Arena - Possui
OperatingHours - Possui
HolidayCalendar
Comportamentos:
getOperatingHours(date: Date): OperatingHoursisOpen(dateTime: Date): booleanisHoliday(date: Date): booleangetNextAvailableSlot(from: Date): Date?
2.7 OperatingHours
Descrição: Horário de funcionamento (pode variar por dia da semana).
Atributos:
id: OperatingHoursIdcalendarId: CalendarIddayOfWeek: DayOfWeekopenTime: TimecloseTime: TimeisClosed: booleanbreakPeriods: TimeRange[]?(ex: almoço)
Invariantes:
- Horário de abertura deve ser antes do fechamento
- Períodos de pausa não podem sobrepor
2.8 HolidayCalendar
Descrição: Feriados e datas especiais.
Atributos:
id: HolidayCalendarIdcalendarId: CalendarIdyear: numberholidays: Holiday[]
Holiday (Value Object):
date: Datename: stringtype: HolidayType(NATIONAL, REGIONAL, CUSTOM)isRecurring: booleanoperatingHours: OperatingHours?(se opera em horário especial)isClosed: boolean
3. Courts & Allocation Context
Objetivo
Gerenciar quadras, suas características, disponibilidade, e o sistema inteligente de alocação de jogadores para partidas.
Aggregates
3.1 Court (Aggregate Root)
Descrição: Quadra esportiva (tennis, padel, beach tennis).
Atributos:
id: CourtIdarenaId: ArenaIdcode: string(ex: "C1", "PADEL-A")name: stringsport: Sport(TENNIS, PADEL, BEACH_TENNIS, PICKLEBALL)surface: SurfaceType(SYNTHETIC_GRASS, CEMENT, CLAY, SAND)isIndoor: booleanhasLighting: booleanhasRoof: booleancapacity: CourtCapacity(singles: 2, doubles: 4)dimensions: CourtDimensions(length, width em metros)features: CourtFeature[](ELECTRONIC_SCOREBOARD, CAMERA, HIGHLIGHT_BUTTON)status: CourtStatus(AVAILABLE, OCCUPIED, MAINTENANCE, OUT_OF_SERVICE)maintenanceSchedule: MaintenanceSchedule?
Relacionamentos:
- Pertence a uma
Arena - Possui um
CourtProfile - Possui múltiplos
CourtState(histórico de estados) - Possui
CourtAvailability - Possui
CourtRuleSet - Associada a
CourtArea(Arena Context) - Possui
Deviceinstalados (Devices Context)
Invariantes:
- Quadra ocupada não pode aceitar novas alocações
- Quadra em manutenção não aparece como disponível
- Capacidade deve respeitar o esporte
Comportamentos:
isAvailable(timeSlot: TimeRange): booleanoccupy(match: MatchId): voidrelease(): voidscheduleMainte(period: DateRange): void
3.2 CourtProfile
Descrição: Perfil de características para matching de jogadores.
Atributos:
id: CourtProfileIdcourtId: CourtIdskillLevelProfile: SkillLevelProfilegenderProfile: GenderProfileplayStyleProfile: PlayStyleProfilepreferredFormats: MatchFormat[](SINGLES, DOUBLES, MIXED_DOUBLES)ageGroups: AgeGroup[]?
Relacionamentos:
- Pertence a uma
Court
Uso: Sistema de alocação usa este perfil para formar partidas equilibradas
3.3 SkillLevelProfile
Descrição: Perfil de nível técnico aceito na quadra.
Atributos:
allowedLevels: SkillLevel[](ex: [INTERMEDIATE, ADVANCED])mixedLevelPolicy: MixedLevelPolicy(ALLOWED, RESTRICTED, PROHIBITED)maxLevelGap: number?(diferença máxima entre jogadores)
3.4 GenderProfile
Descrição: Perfil de gênero para formação de partidas.
Atributos:
policy: GenderPolicy(MIXED, MALE_ONLY, FEMALE_ONLY, PREFERENCE)mixedDoublesAllowed: boolean
3.5 PlayStyleProfile
Descrição: Perfil de estilo de jogo.
Atributos:
styles: PlayStyle[](RECREATIONAL, COMPETITIVE, TRAINING, PROFESSIONAL)allowMixedStyles: boolean
3.6 CourtState
Descrição: Estado da quadra em um momento específico (snapshot).
Atributos:
id: StateIdcourtId: CourtIdstatus: CourtStatusoccupiedBy: MatchId?temperature: number?humidity: number?lightLevel: number?timestamp: Date
Relacionamentos:
- Pertence a uma
Court
Uso: Histórico de estados, telemetria, analytics
3.7 CourtAvailability
Descrição: Disponibilidade da quadra (agenda).
Atributos:
id: AvailabilityIdcourtId: CourtIddate: Dateslots: AvailabilitySlot[]
AvailabilitySlot (Value Object):
timeRange: TimeRangestatus: SlotStatus(AVAILABLE, BOOKED, BLOCKED, MAINTENANCE)bookingId: BookingId?price: Money?
Comportamentos:
getAvailableSlots(date: Date): AvailabilitySlot[]blockSlot(timeRange: TimeRange, reason: string): voidreleaseSlot(timeRange: TimeRange): void
3.8 CourtRuleSet
Descrição: Regras específicas da quadra.
Atributos:
id: RuleSetIdcourtId: CourtIdaccessRules: AccessRule[](quem pode usar)bookingRules: BookingRule[](restrições de reserva)playRules: PlayRule[](regras de jogo)
Exemplos:
- "Membros VIP têm prioridade"
- "Máximo 2 horas consecutivas por pessoa"
- "Obrigatório uso de calçado apropriado"
3.9 AllocationPolicy (Aggregate Root)
Descrição: Política de alocação automática de jogadores.
Atributos:
id: PolicyIdarenaId: ArenaIdname: stringmode: AllocationMode(FIFO, SKILL_BASED, RANDOM, PRIORITY_BASED)priorityRules: PriorityRule[]eligibilityRules: EligibilityRule[]matchTerminationRules: MatchTerminationRule[]isActive: boolean
Relacionamentos:
- Pertence a uma
Arena - Aplicada a
AllocationQueue
Comportamentos:
evaluate(queue: AllocationQueue): AllocationDecisionprioritize(entries: QueueEntry[]): QueueEntry[]
3.10 AllocationQueue (Aggregate Root)
Descrição: Fila de espera para alocação em quadras.
Atributos:
id: QueueIdcourtId: CourtId?(null = qualquer quadra)createdAt: Dateentries: QueueEntry[]status: QueueStatus(ACTIVE, PAUSED, CLOSED)
Relacionamentos:
- Associada a uma
Court(ou todas) - Contém múltiplos
QueueEntry - Gera
AllocationDecision
Invariantes:
- Fila deve manter ordem baseada em política
- Entrada duplicada do mesmo jogador não permitida
Comportamentos:
addPlayer(player: PlayerId): QueueEntryremovePlayer(player: PlayerId): voidprocessNext(): AllocationDecision?getPosition(player: PlayerId): number
3.11 QueueEntry
Descrição: Entrada de jogador na fila de alocação.
Atributos:
id: EntryIdqueueId: QueueIdplayerId: PlayerIdjoinedAt: Datepriority: number(calculado por regras)skillLevel: SkillLevelpreferredFormat: MatchFormat?maxWaitTime: Duration?status: EntryStatus(WAITING, ALLOCATED, EXPIRED, CANCELLED)
Relacionamentos:
- Pertence a uma
AllocationQueue - Referencia um
PlayerProfile
Comportamentos:
calculatePriority(rules: PriorityRule[]): numberisEligible(court: Court): booleanisExpired(): boolean
3.12 AllocationDecision
Descrição: Decisão de alocação tomada pelo sistema.
Atributos:
id: DecisionIdqueueId: QueueIdcourtId: CourtIdselectedPlayers: PlayerId[]matchFormat: MatchFormatreasoning: string(explicação da decisão)confidence: number(0-1, qualidade do match)decidedAt: Datestatus: DecisionStatus(PENDING_ACCEPTANCE, ACCEPTED, REJECTED, EXPIRED)expiresAt: Date
Relacionamentos:
- Originada de
AllocationQueue - Aloca
Court - Cria
Match(se aceita) (Play Context)
Comportamentos:
accept(players: PlayerId[]): Matchreject(reason: string): voidexpire(): void
3.13 PriorityRule
Descrição: Regra para calcular prioridade na fila.
Atributos:
id: RuleIdname: stringtype: PriorityRuleType(WAIT_TIME, MEMBERSHIP_LEVEL, PLAY_FREQUENCY, SKILL_LEVEL)weight: number(peso na fórmula de prioridade)parameters: Record<string, any>
Exemplos:
WAIT_TIME: +10 pontos por hora de esperaMEMBERSHIP_LEVEL: VIP +50, Premium +30, Basic +10PLAY_FREQUENCY: -5 pontos se jogou nas últimas 2 horas
3.14 EligibilityRule
Descrição: Regra de elegibilidade para alocação.
Atributos:
id: RuleIdname: stringtype: EligibilityRuleType(SKILL_MATCH, MEMBERSHIP_REQUIRED, TIME_BASED, CREDITS_REQUIRED)condition: RuleConditionerrorMessage: string
Comportamentos:
evaluate(player: PlayerProfile, court: Court): boolean
Exemplos:
- "Jogador deve ter nível compatível com a quadra"
- "Requer membro ativo"
- "Deve ter pelo menos 1 crédito disponível"
3.15 MatchTerminationRule
Descrição: Regra para finalizar partidas automaticamente.
Atributos:
id: RuleIdtype: TerminationRuleType(TIME_LIMIT, SCORE_LIMIT, WINNER_STAYS, MANUAL)parameters: RuleParameters
Subtipos:
TimeLimitRule:
maxDuration: Duration(ex: 1h30)warningBefore: Duration(ex: 10 minutos)gracePeriod: Duration(ex: 5 minutos para finalizar)
ScoreLimitRule:
winningScore: number(ex: 6 games)isTiebreakEnabled: booleantiebreakScore: number?(ex: 7 pontos)
WinnerStaysRule:
maxConsecutiveWins: number(ex: 2)rotationPolicy: RotationPolicy(LOSER_OUT, ROTATE_ALL)
ManualOverride:
allowStaffOverride: booleanrequiresReason: boolean
4. Scheduling Context
Objetivo
Gerenciar agendamentos de quadras, aulas, day use e eventos, com políticas de reserva, cancelamento e waitlist.
Aggregates
4.1 Schedule (Aggregate Root)
Descrição: Agenda mestre de uma arena ou recurso.
Atributos:
id: ScheduleIdresourceId: ResourceId(Court, Instructor, Area)resourceType: ResourceTypetimezone: stringeffectivePeriod: DateRange
Relacionamentos:
- Associado a um recurso (Court, Instructor, etc)
- Contém múltiplos
ScheduleSlot - Referenciado por
Booking
Comportamentos:
getAvailableSlots(date: Date): ScheduleSlot[]book(slot: ScheduleSlot, booking: Booking): voidcancelBooking(bookingId: BookingId): voidfindConflicts(timeRange: TimeRange): Booking[]
4.2 ScheduleSlot
Descrição: Slot de tempo específico na agenda.
Atributos:
id: SlotIdscheduleId: ScheduleIdtimeRange: TimeRangestatus: SlotStatus(AVAILABLE, BOOKED, BLOCKED, MAINTENANCE)capacity: number(quantos podem reservar)currentOccupancy: numberprice: Money?bookingIds: BookingId[]
Relacionamentos:
- Pertence a um
Schedule - Pode ter múltiplas
Booking(se capacidade > 1, ex: aulas)
Comportamentos:
isAvailable(): booleanisFull(): booleancanAccommodate(count: number): boolean
4.3 Booking (Aggregate Root)
Descrição: Reserva abstrata (base para diferentes tipos).
Atributos Base:
id: BookingIdtenantId: TenantIdbookingNumber: string(único, legível)type: BookingType(COURT, LESSON, DAY_USE, EVENT)bookedBy: UserIdparticipants: UserId[]scheduleId: ScheduleIdslotId: SlotIdtimeRange: TimeRangestatus: BookingStatus(PENDING, CONFIRMED, CHECKED_IN, COMPLETED, CANCELLED, NO_SHOW)price: MoneypaidAmount: MoneypaymentStatus: PaymentStatus(PENDING, PARTIAL, PAID, REFUNDED)createdAt: DateconfirmedAt: Date?cancelledAt: Date?cancellationReason: string?notes: string?
Subtipos:
CourtBooking:
courtId: CourtIdsport: SportmatchFormat: MatchFormatskillLevel: SkillLevel?isRecurring: booleanrecurrenceRule: RecurrenceRule?
LessonBooking:
lessonId: LessonIdinstructorId: InstructorIdlessonType: LessonType(PRIVATE, GROUP, CLINIC)enrollmentId: ClassEnrollmentId?
DayUseBooking:
accessZones: ZoneId[]includesLockerRoom: booleanguestCount: number
EventBooking:
eventId: EventIdticketType: TicketTypeseatNumber: string?
Relacionamentos:
- Pertence a um
UserAccount(bookedBy) - Associada a um
ScheduleSlot - Possui
PaymentStatus(Commerce Context) - Pode ter
Waitlistentries - Gera
CheckInRecord(Access Context)
Invariantes:
- Reserva confirmada não pode sobrepor outra
- Cancelamento deve respeitar política de cancelamento
- No-show registrado se não houve check-in no prazo
Comportamentos:
confirm(): voidcancel(reason: string): voidcheckIn(time: Date): voidmarkNoShow(): voidreschedule(newSlot: ScheduleSlot): voidcanCancel(at: Date): booleancanReschedule(at: Date): boolean
4.4 BookingPolicy (Aggregate Root)
Descrição: Política de reservas da arena.
Atributos:
id: PolicyIdarenaId: ArenaIdname: stringresourceType: ResourceTypeminAdvanceBooking: DurationmaxAdvanceBooking: DurationmaxConcurrentBookings: numbermaxBookingsPerDay: numbermaxBookingsPerWeek: numberrequiresPaymentUpfront: booleanallowGuestBooking: booleanguestBookingRequiresApproval: booleancancellationPolicy: CancellationPolicynoShowPolicy: NoShowPolicylateArrivalPolicy: LateArrivalPolicyeffectivePeriod: DateRangeisActive: boolean
Relacionamentos:
- Pertence a uma
Arena - Aplicada a
Booking
Comportamentos:
canBook(user: UserId, timeRange: TimeRange): ValidationResultcalculateCancellationFee(booking: Booking, cancelAt: Date): MoneygetPenalty(offense: PolicyOffense): Penalty
4.5 CancellationPolicy
Descrição: Política de cancelamento.
Atributos:
allowCancellation: booleancancellationWindow: Duration(ex: 24 horas antes)feeStructure: CancellationFee[]
CancellationFee (Value Object):
timeBeforeBooking: DurationfeeType: FeeType(PERCENTAGE, FIXED)amount: number
Exemplos:
- Mais de 24h antes: sem taxa
- Entre 24h-12h antes: 50% do valor
- Menos de 12h antes: 100% do valor
4.6 NoShowPolicy
Descrição: Política para falta sem aviso.
Atributos:
penaltyType: PenaltyType(WARNING, FEE, SUSPENSION, CREDIT_LOSS)penaltyAmount: Money?suspensionDuration: Duration?creditsLost: number?warningCountBeforeSuspension: number?resetPeriod: Duration(período para zerar contador)
4.7 LateArrivalPolicy
Descrição: Política para atrasos.
Atributos:
gracePeriod: Duration(ex: 10 minutos)maxLateness: Duration(ex: 20 minutos)afterMaxLatenessAction: Action(TREAT_AS_NO_SHOW, REDUCE_TIME, CANCEL)penaltyForLateness: Penalty?
4.8 RecurrenceRule
Descrição: Regra de recorrência para reservas repetitivas.
Atributos:
pattern: RecurrencePattern(DAILY, WEEKLY, MONTHLY)interval: number(ex: a cada 2 semanas)daysOfWeek: DayOfWeek[]?(para padrão semanal)dayOfMonth: number?(para padrão mensal)endDate: Date?occurrenceCount: number?exceptions: Date[](datas excluídas)
Comportamentos:
generateOccurrences(from: Date, to: Date): Date[]addException(date: Date): void
4.9 Waitlist (Aggregate Root)
Descrição: Lista de espera para reservas esgotadas.
Atributos:
id: WaitlistIdresourceId: ResourceId(Court, Lesson, Event)resourceType: ResourceTypetargetDate: DatetargetTimeRange: TimeRange?entries: WaitlistEntry[]status: WaitlistStatus(ACTIVE, CLOSED, EXPIRED)expiresAt: Date?
Relacionamentos:
- Associada a recurso específico
- Contém múltiplos
WaitlistEntry
Invariantes:
- Entrada duplicada do mesmo usuário não permitida
- Notificação automática quando vaga disponível
Comportamentos:
addEntry(user: UserId, preferences: Preferences): WaitlistEntryremoveEntry(entryId: EntryId): voidnotifyAvailability(slot: ScheduleSlot): voidgetPosition(userId: UserId): number
4.10 WaitlistEntry
Descrição: Entrada individual na waitlist.
Atributos:
id: EntryIdwaitlistId: WaitlistIduserId: UserIdjoinedAt: Dateposition: numberpreferences: WaitlistPreferencesnotificationsSent: numberlastNotifiedAt: Date?status: EntryStatus(WAITING, NOTIFIED, CONVERTED, EXPIRED, CANCELLED)expiresAt: Date?
WaitlistPreferences (Value Object):
flexibleTime: booleanacceptableTimeRanges: TimeRange[]?maxPriceWillingToPay: Money?notificationChannels: Channel[](EMAIL, SMS, PUSH, WHATSAPP)
Relacionamentos:
- Pertence a uma
Waitlist - Referencia um
UserAccount
Comportamentos:
notify(availableSlot: ScheduleSlot): voidconvert(booking: Booking): voidexpire(): void
5. Lessons & Coaching Context
Objetivo
Gerenciar aulas, turmas, planos de aula, instrutores, matrículas e frequência.
Aggregates
5.1 Lesson (Aggregate Root)
Descrição: Aula ou sessão de treinamento.
Atributos:
id: LessonIdarenaId: ArenaIdtype: LessonType(PRIVATE, SEMI_PRIVATE, GROUP, CLINIC, CAMP)sport: Sportlevel: SkillLeveltitle: stringdescription: string?instructorId: InstructorIdcourtId: CourtId?classGroupId: ClassGroupId?(para aulas em turma)schedule: LessonSchedulecapacity: LessonCapacitycurrentEnrollment: numberprice: Moneystatus: LessonStatus(SCHEDULED, IN_PROGRESS, COMPLETED, CANCELLED)createdAt: Date
LessonSchedule (Value Object):
startTime: Dateduration: DurationrecurrenceRule: RecurrenceRule?
LessonCapacity (Value Object):
minStudents: numbermaxStudents: numbercurrentEnrolled: number
Relacionamentos:
- Ministrada por
InstructorProfile - Ocorre em
Court - Pertence a
ClassGroup(se em turma) - Possui
LessonPlan - Possui múltiplas
LessonSession(ocorrências) - Possui múltiplos
ClassEnrollment - Gera
AttendanceRegister
Invariantes:
- Aula privada: max 1-2 alunos
- Aula em grupo: min 4, max 12 alunos
- Instrutor não pode ter duas aulas simultâneas
- Quadra não pode estar reservada para outro fim
Comportamentos:
enroll(student: PlayerId): ClassEnrollmentunenroll(student: PlayerId): voidstart(): voidcomplete(): voidcancel(reason: string): voidisFull(): booleancanStart(): boolean(verifica min de alunos)
5.2 LessonPlan
Descrição: Plano de aula (conteúdo pedagógico).
Atributos:
id: PlanIdlessonId: LessonIdtitle: stringobjectives: string[]warmUp: ActivityDescriptionmainActivities: ActivityDescription[]coolDown: ActivityDescriptionequipment: Equipment[]duration: Durationnotes: string?
ActivityDescription (Value Object):
name: stringdescription: stringduration: Durationdifficulty: DifficultyfocusAreas: string[](ex: "forehand", "serve", "footwork")
Relacionamentos:
- Pertence a uma
Lesson
5.3 LessonSession
Descrição: Ocorrência específica de uma aula (instância).
Atributos:
id: SessionIdlessonId: LessonIdoccurrenceNumber: numberscheduledStart: DatescheduledEnd: DateactualStart: Date?actualEnd: Date?instructorId: InstructorIdcourtId: CourtIdstatus: SessionStatus(SCHEDULED, IN_PROGRESS, COMPLETED, CANCELLED, RESCHEDULED)attendanceRegisterId: AttendanceRegisterId?notes: string?weatherConditions: WeatherConditions?(para outdoor)
Relacionamentos:
- Pertence a uma
Lesson - Ministrada por
InstructorProfile - Possui
AttendanceRegister
Comportamentos:
start(): voidcomplete(notes: string): voidreschedule(newDate: Date): voidcancel(reason: string): void
5.4 ClassGroup (Aggregate Root)
Descrição: Turma fixa de alunos (programa contínuo).
Atributos:
id: ClassGroupIdarenaId: ArenaIdname: string(ex: "Turma Iniciante - Terças 18h")sport: Sportlevel: SkillLevelageGroup: AgeGroup?schedule: GroupSchedule(dias e horários fixos)instructorId: InstructorIdcourtId: CourtIdcapacity: numbercurrentEnrollment: numberstartDate: DateendDate: Date?price: Money(valor mensal)status: ClassGroupStatus(ENROLLING, ACTIVE, COMPLETED, CANCELLED)
GroupSchedule (Value Object):
daysOfWeek: DayOfWeek[]time: Timeduration: DurationlessonsPerWeek: number
Relacionamentos:
- Possui múltiplas
Lesson - Possui múltiplos
ClassEnrollment - Possui
InstructorAssignment
Invariantes:
- Turma não pode iniciar sem mínimo de alunos
- Não pode exceder capacidade máxima
Comportamentos:
enroll(student: PlayerId): ClassEnrollmentunenroll(student: PlayerId, reason: string): voidstart(): voidcomplete(): voidisFull(): boolean
5.5 ClassEnrollment
Descrição: Matrícula de aluno em turma ou aula.
Atributos:
id: EnrollmentIdstudentId: PlayerIdlessonId: LessonId?classGroupId: ClassGroupId?enrolledAt: DatestartDate: DateendDate: Date?status: EnrollmentStatus(ACTIVE, SUSPENDED, COMPLETED, DROPPED, EXPELLED)paymentStatus: PaymentStatusattendanceRate: number(percentual de presença)droppedAt: Date?dropReason: string?
Relacionamentos:
- Pertence a um
PlayerProfile - Vinculada a
LessonouClassGroup - Possui múltiplos
AttendanceEntry
Invariantes:
- Matrícula ativa requer pagamento em dia
- Drop antes do período mínimo pode gerar multa
Comportamentos:
suspend(reason: string, until: Date): voidreactivate(): voiddrop(reason: string): voidcalculateAttendanceRate(): number
5.6 InstructorAssignment
Descrição: Atribuição de instrutor a turma/aula.
Atributos:
id: AssignmentIdinstructorId: InstructorIdlessonId: LessonId?classGroupId: ClassGroupId?assignedAt: DatestartDate: DateendDate: Date?role: InstructorRole(PRIMARY, ASSISTANT, SUBSTITUTE)compensationType: CompensationType(HOURLY, PER_STUDENT, FIXED, COMMISSION)compensationAmount: Moneystatus: AssignmentStatus(ACTIVE, COMPLETED, CANCELLED)
Relacionamentos:
- Vinculado a
InstructorProfile - Vinculado a
LessonouClassGroup
5.7 AttendanceRegister (Aggregate Root)
Descrição: Registro de frequência de uma sessão de aula.
Atributos:
id: RegisterIdlessonSessionId: SessionIddate: DateinstructorId: InstructorIdentries: AttendanceEntry[]completedAt: Date?completedBy: UserId?status: RegisterStatus(OPEN, COMPLETED, LOCKED)
Relacionamentos:
- Pertence a uma
LessonSession - Contém múltiplos
AttendanceEntry
Comportamentos:
markAttendance(student: PlayerId, status: AttendanceStatus): voidcomplete(): voidlock(): void(impede alterações)calculateAttendanceRate(): number
5.8 AttendanceEntry
Descrição: Entrada individual de frequência.
Atributos:
id: EntryIdregisterId: RegisterIdstudentId: PlayerIdstatus: AttendanceStatus(PRESENT, ABSENT, LATE, EXCUSED, EARLY_DEPARTURE)checkInTime: Date?checkOutTime: Date?notes: string?markedBy: UserIdmarkedAt: Date
Relacionamentos:
- Pertence a um
AttendanceRegister - Referencia
PlayerProfile
Documento continua no próximo bloco...
6. Play & Match Context
Objetivo
Gerenciar partidas, formação de times, pontuação, resultados e participação dos jogadores.
Aggregates
6.1 Match (Aggregate Root)
Descrição: Partida esportiva (casual ou competitiva).
Atributos:
id: MatchIdarenaId: ArenaIdcourtId: CourtIdmatchNumber: string(identificador único legível)sport: Sportformat: MatchFormat(SINGLES, DOUBLES, MIXED_DOUBLES)type: MatchType(CASUAL, FRIENDLY, COMPETITIVE, TOURNAMENT, RANKING)ruleSet: MatchRuleSetscheduledStart: DateactualStart: Date?scheduledEnd: DateactualEnd: Date?status: MatchStatus(SCHEDULED, IN_PROGRESS, PAUSED, COMPLETED, CANCELLED, ABANDONED)teams: Team[]scoreboard: Scoreboardresult: MatchResult?hasStake: boolean(partida "valendo")matchStake: MatchStake?videoRecordingId: RecordingSessionId?createdAt: DatecreatedBy: UserId
Relacionamentos:
- Ocorre em
Court - Possui 2+
Team - Possui um
Scoreboard - Possui
MatchResult(ao finalizar) - Possui múltiplos
MatchParticipation - Pode ter
MatchStake - Pode ter
RecordingSession(Media Context) - Pode ter
HighlightClip(Media Context) - Pode ser parte de
Tournament(Engagement Context)
Invariantes:
- Singles: 2 jogadores (1 por time)
- Doubles: 4 jogadores (2 por time)
- Partida completada deve ter resultado
- Partida com stake requer validação de créditos
Comportamentos:
start(): voidpause(reason: string): voidresume(): voidcomplete(): MatchResultabandon(reason: string): voidcancel(reason: string): voidrecordScore(team: TeamId, points: number): voidgetCurrentSet(): numbergetWinner(): Team?
6.2 MatchFormat
Descrição: Formato da partida (Value Object).
Atributos:
type: FormatType(SINGLES, DOUBLES, MIXED_DOUBLES)scoringSystem: ScoringSystem(TRADITIONAL_TENNIS, NO_AD, MATCH_TIEBREAK, PADEL)numberOfSets: number(1, 3, 5)gamesPerSet: number(geralmente 6)tiebreakAt: number?(ex: 6-6)decidingSetType: DecidingSetType(FULL_SET, SUPER_TIEBREAK, MATCH_TIEBREAK)pointsToWinTiebreak: number(geralmente 7)
6.3 MatchRuleSet
Descrição: Regras específicas da partida.
Atributos:
id: RuleSetIdmatchId: MatchIdformat: MatchFormattimeLimit: Duration?warmUpDuration: Duration(ex: 5 minutos)breakBetweenGames: Duration(ex: 90 segundos)breakBetweenSets: Duration(ex: 2 minutos)medicalTimeoutAllowed: booleancoachingAllowed: booleanlineCallSystem: LineCallSystem(HUMAN, HAWK_EYE, SELF_CALL)ballChangeFrequency: number?(a cada X games)
Relacionamentos:
- Pertence a um
Match
6.4 Team
Descrição: Time/equipe na partida.
Atributos:
id: TeamIdmatchId: MatchIdname: string?(ex: "Team A", ou gerado automaticamente)side: Side(TEAM_1, TEAM_2)players: PlayerAssignment[]color: TeamColor?(para identificação visual)
Relacionamentos:
- Pertence a um
Match - Contém múltiplos
PlayerAssignment
Comportamentos:
addPlayer(player: PlayerId, position: Position): voidremovePlayer(player: PlayerId): voidgetScore(): number
6.5 PlayerAssignment
Descrição: Atribuição de jogador a time em posição específica.
Atributos:
id: AssignmentIdteamId: TeamIdplayerId: PlayerIdposition: Position?(PLAYER_1, PLAYER_2 para doubles)side: CourtSide?(DEUCE, AD para doubles)joinedAt: DateleftAt: Date?role: PlayerRole(REGULAR, SUBSTITUTE)
Relacionamentos:
- Pertence a um
Team - Referencia
PlayerProfile
6.6 Scoreboard (Aggregate Root)
Descrição: Placar da partida.
Atributos:
id: ScoreboardIdmatchId: MatchIdcurrentSet: numbercurrentGame: numberserver: TeamIdsets: SetScore[]games: GameScore[]points: PointScorehistory: ScoreEvent[]
SetScore (Value Object):
setNumber: numberteam1Games: numberteam2Games: numberisTiebreak: booleantiebreakScore: TiebreakScore?
GameScore (Value Object):
setNumber: numbergameNumber: numberteam1Points: numberteam2Points: numberisBreakPoint: booleanisGamePoint: booleanisSetPoint: booleanisMatchPoint: boolean
PointScore (Value Object):
team1: string(ex: "0", "15", "30", "40", "AD")team2: string
Relacionamentos:
- Pertence a um
Match - Contém múltiplos
ScoreEvent
Comportamentos:
recordPoint(winnerTeam: TeamId): voidrecordGame(winnerTeam: TeamId): voidrecordSet(winnerTeam: TeamId): voidundo(): void(desfazer último ponto)getCurrentScore(): ScoreSnapshotgetLeader(): TeamId?
6.7 ScoreEvent
Descrição: Evento de pontuação (histórico).
Atributos:
id: EventIdscoreboardId: ScoreboardIdtimestamp: Datetype: ScoreEventType(POINT, GAME, SET, ACE, DOUBLE_FAULT, WINNER, UNFORCED_ERROR)team: TeamIdplayer: PlayerId?setNumber: numbergameNumber: numberscoreAfter: ScoreSnapshotrallyLength: number?(quantidade de toques)shotType: ShotType?(FOREHAND, BACKHAND, VOLLEY, SMASH, SERVE)videoTimestamp: number?(timestamp no vídeo)
Relacionamentos:
- Pertence a
Scoreboard
Uso: Analytics, highlights automáticos, estatísticas de jogo
6.8 MatchResult
Descrição: Resultado final da partida.
Atributos:
id: ResultIdmatchId: MatchIdwinnerTeamId: TeamIdloserTeamId: TeamIdfinalScore: FinalScoreresultType: ResultType(COMPLETED, WALKOVER, RETIREMENT, DISQUALIFICATION, TIME_LIMIT)completedAt: Dateduration: Durationstatistics: MatchStatistics
FinalScore (Value Object):
sets: SetScore[]totalGames: numbertotalPoints: number
MatchStatistics (Value Object):
aces: Record<TeamId, number>doubleFaults: Record<TeamId, number>winners: Record<TeamId, number>unforcedErrors: Record<TeamId, number>breakPointsWon: Record<TeamId, number>breakPointsTotal: Record<TeamId, number>firstServePercentage: Record<TeamId, number>pointsWonOnFirstServe: Record<TeamId, number>
Relacionamentos:
- Pertence a um
Match - Gera
MatchParticipationpara cada jogador
Comportamentos:
calculatePlayerStats(player: PlayerId): PlayerMatchStats
6.9 MatchParticipation
Descrição: Participação de jogador em partida (para histórico).
Atributos:
id: ParticipationIdmatchId: MatchIdplayerId: PlayerIdteamId: TeamIdresult: ParticipationResult(WON, LOST, DRAW)statisticsContribution: PlayerMatchStatsskillLevelBefore: SkillLevelskillLevelAfter: SkillLevel?pointsEarned: number(para ranking)playedAt: Date
PlayerMatchStats (Value Object):
aces: numberdoubleFaults: numberwinners: numberunforcedErrors: numberbreakPointsConverted: number
Relacionamentos:
- Pertence a um
Match - Referencia
PlayerProfile - Atualiza
PlayerProgress(Engagement Context)
6.10 MatchStake
Descrição: Aposta/stake em partida (partida "valendo").
Atributos:
id: StakeIdmatchId: MatchIdtype: StakeType(FRIENDLY_WAGER, CREDITS, POINTS, TOKENS, PRIZE_POOL)currency: ValueUnit(credits, tokens, points, money)amountPerPlayer: Money | numbertotalPot: Money | numberwinnerTakeAll: booleandistribution: PrizeDistribution?(se não winner-take-all)status: StakeStatus(PENDING, LOCKED, COMPLETED, CANCELLED, DISPUTED)paidOut: booleanpaidOutAt: Date?
PrizeDistribution (Value Object):
winnerShare: PercentageloserShare: Percentage?(se houver consolação)
Relacionamentos:
- Pertence a um
Match - Gera
WalletTransactionao finalizar (Wallet Context)
Invariantes:
- Todos jogadores devem ter saldo suficiente antes do início
- Stake travado não pode ser alterado
- Pagamento automático ao resultado
Comportamentos:
lock(): void(travar fundos)distribute(result: MatchResult): voidrefund(reason: string): void(em caso de cancelamento)dispute(reason: string): void
7. Events Context
Objetivo
Gerenciar eventos especiais (torneios internos, sociais, corporativos), participantes, ingressos e políticas de consumo.
Aggregates
7.1 Event (Aggregate Root)
Descrição: Evento especial na arena.
Atributos:
id: EventIdarenaId: ArenaIdtype: EventTypetitle: stringdescription: stringslug: stringstartDate: DateendDate: DateregistrationStartDate: DateregistrationEndDate: Datevenue: EventVenue(áreas utilizadas)capacity: numbercurrentParticipants: numberprice: Money?isPublic: booleanrequiresApproval: booleanstatus: EventStatus(DRAFT, PUBLISHED, REGISTRATION_OPEN, IN_PROGRESS, COMPLETED, CANCELLED)posterImage: MediaAssetId?tags: Tag[]createdAt: DatepublishedAt: Date?
Relacionamentos:
- Ocorre em
Arena - Possui
EventConfiguration - Possui múltiplos
EventParticipant - Possui múltiplos
EventInvitation - Possui múltiplos
EventAccessPass - Define
EventZoneRuleSet - Define
EventConsumptionPolicy - Pode ter
Tournamentassociado (Engagement Context)
Invariantes:
- Evento publicado não pode ser deletado (só cancelado)
- Capacidade não pode ser excedida
- Datas de registro devem estar dentro do período do evento
Comportamentos:
publish(): voidregister(user: UserId): EventParticipantapprove(participant: ParticipantId): voidreject(participant: ParticipantId, reason: string): voidstart(): voidcomplete(): voidcancel(reason: string): voidisFull(): booleancanRegister(user: UserId): ValidationResult
7.2 EventType
Descrição: Tipo de evento (Value Object enum).
Valores:
TOURNAMENT: Torneio esportivoSOCIAL: Evento social (confraternização, happy hour)CORPORATE: Evento corporativoCLINIC: Clínica esportivaCHARITY: Evento beneficenteEXHIBITION: Exibição/demonstraçãoLEAGUE_MATCH: Partida de ligaCHAMPIONSHIP: CampeonatoPARTY: Festa temáticaWORKSHOP: Workshop/palestraOTHER: Outro tipo
7.3 EventConfiguration
Descrição: Configurações específicas do evento.
Atributos:
id: ConfigIdeventId: EventIdcheckInEnabled: booleancheckInStartTime: Date?checkInEndTime: Date?requiresWristband: booleanallowsGuestPlusOne: booleanmaxGuestsPerParticipant: numberconsumptionPolicy: EventConsumptionPolicyzonesAllowed: ZoneId[]parkingIncluded: booleandresscode: string?ageRestriction: AgeRestriction?
Relacionamentos:
- Pertence a um
Event
7.4 EventParticipant
Descrição: Participante inscrito no evento.
Atributos:
id: ParticipantIdeventId: EventIduserId: UserIdregisteredAt: Datestatus: ParticipantStatus(PENDING, APPROVED, REJECTED, CHECKED_IN, ATTENDED, NO_SHOW)ticketType: TicketType?ticketNumber: string?paymentStatus: PaymentStatuscheckedInAt: Date?guestsCount: numberguestNames: string[]?specialRequirements: string?(dietary, accessibility, etc)wristbandId: string?
Relacionamentos:
- Pertence a um
Event - Referencia
UserAccount - Possui
EventAccessPass
Comportamentos:
approve(): voidreject(reason: string): voidcheckIn(): voidissueWristband(wristbandId: string): void
7.5 EventInvitation
Descrição: Convite para evento privado.
Atributos:
id: InvitationIdeventId: EventIdinvitedUserId: UserId?(null = convite aberto)invitedEmail: Email?invitedBy: UserIdcode: string(código único do convite)sentAt: DateexpiresAt: Date?acceptedAt: Date?declinedAt: Date?status: InvitationStatus(PENDING, ACCEPTED, DECLINED, EXPIRED, REVOKED)message: string?
Relacionamentos:
- Pertence a um
Event - Enviado para
UserAccount
Comportamentos:
accept(): EventParticipantdecline(reason: string): voidrevoke(): voidisValid(): boolean
7.6 EventAccessPass
Descrição: Passe de acesso para evento.
Atributos:
id: PassIdeventId: EventIdparticipantId: ParticipantIdpassType: PassType(PARTICIPANT, GUEST, VIP, STAFF, PRESS)qrCode: string(código QR único)accessLevel: AccessLevelzonesAllowed: ZoneId[]validFrom: DatevalidUntil: DateisActive: booleanusageCount: numbermaxUsages: number?lastUsedAt: Date?
Relacionamentos:
- Pertence a um
Event - Atribuído a
EventParticipant - Validado em
CheckInPoint(Access Context)
Comportamentos:
validate(at: Date): ValidationResultuse(): voiddeactivate(): voidcanAccessZone(zone: ZoneId): boolean
7.7 EventZoneRuleSet
Descrição: Regras de acesso a zonas durante evento.
Atributos:
id: RuleSetIdeventId: EventIdzoneId: ZoneIdaccessRules: AccessRule[]capacityOverride: number?(capacidade diferente do normal)timeRestrictions: TimeRange[]?
Relacionamentos:
- Pertence a um
Event - Aplicada a
AccessZone(Arena Context)
7.8 EventConsumptionPolicy
Descrição: Política de consumo em eventos (abstração).
Atributos Base:
id: PolicyIdeventId: EventIdtype: ConsumptionPolicyType(ALL_INCLUSIVE, PAY_PER_ITEM, TICKET_SYSTEM, CREDIT_BASED)
Subtipos:
AllInclusivePolicy:
includedItems: ProductCategory[](ex: food, beverages, soft_drinks)excludedItems: ProductItemId[](ex: premium drinks)quantityLimits: Record<ProductCategory, number>?
PayPerItemPolicy:
priceList: PriceListIdtaxIncluded: booleanpaymentMethods: PaymentMethod[]
TicketSystemPolicy:
ticketsPerParticipant: numberticketValue: MoneyexchangeableFor: ProductCategory[]
CreditBasedPolicy:
creditsPerParticipant: numbercreditValue: Money(valor de 1 crédito)productCosts: Record<ProductItemId, number>(custo em créditos)
Relacionamentos:
- Pertence a um
Event - Aplicada em
Tab(Commerce Context)
8. Access & Presence Context
Objetivo
Gerenciar controle de acesso físico, check-in/check-out, presença e integração com sistemas de controle de acesso.
Aggregates
8.1 AccessControlSystem (Aggregate Root)
Descrição: Sistema de controle de acesso integrado.
Atributos:
id: SystemIdarenaId: ArenaIdvendor: string(ex: "Intelbras", "HID", "Hikvision")model: stringapiEndpoint: stringapiKey: string(encrypted)configuration: SystemConfigurationstatus: SystemStatus(ONLINE, OFFLINE, ERROR, MAINTENANCE)lastSyncAt: Date?
Relacionamentos:
- Pertence a uma
Arena - Controla múltiplos
CheckInPoint - Usa
AccessIntegrationAdapter
Comportamentos:
sync(): voidauthorize(credential: Credential, zone: ZoneId): AuthorizationResultrevokeAccess(userId: UserId): voidgrantTemporaryAccess(userId: UserId, zones: ZoneId[], validUntil: Date): void
8.2 AccessIntegrationAdapter
Descrição: Adaptador para integração com sistema específico.
Atributos:
id: AdapterIdsystemId: SystemIdvendor: stringprotocol: IntegrationProtocol(REST_API, SOAP, MQTT, TCP, WIEGAND)configuration: AdapterConfigurationmappings: FieldMapping[](mapeia campos do sistema externo)
Relacionamentos:
- Pertence a
AccessControlSystem
Comportamentos:
connect(): voiddisconnect(): voidsendCommand(command: AccessCommand): voidparseEvent(event: ExternalEvent): AccessEvent
8.3 CheckInPoint (Aggregate Root)
Descrição: Ponto de check-in/check-out físico.
Atributos Base:
id: CheckInPointIdarenaId: ArenaIdtype: CheckInPointType(COURT, GATE, EVENT, AREA, VIRTUAL)name: stringlocation: Location(descrição física)zoneId: ZoneIddeviceId: DeviceId?(reader, terminal)requiresManualValidation: booleanisActive: boolean
Subtipos:
CourtCheckInPoint:
courtId: CourtIdcheckInRadius: number?(metros, para check-in por geolocalização)
GateCheckInPoint:
direction: Direction(ENTRY, EXIT, BOTH)hasTurnstile: boolean
EventCheckInPoint:
eventId: EventId
Relacionamentos:
- Pertence a uma
Arena - Vinculado a
AccessZone - Conectado a
Device(Devices Context) - Registra
CheckInRecordeCheckOutRecord
Comportamentos:
checkIn(credential: Credential): CheckInRecordcheckOut(credential: Credential): CheckOutRecordvalidateAccess(user: UserId, context: AccessContext): ValidationResult
8.4 CheckInRecord
Descrição: Registro de check-in.
Atributos:
id: RecordIdcheckInPointId: CheckInPointIduserId: UserIdresourceId: ResourceId?(Court, Event, etc)resourceType: ResourceType?credentialType: CredentialTypecredentialId: CredentialIdtimestamp: Dateevidence: PresenceEvidencevalidationResult: ValidationResultbookingId: BookingId?eventId: EventId?
Relacionamentos:
- Gerado por
CheckInPoint - Referencia
UserAccount - Associado a
BookingouEvent - Inicia
PresenceSession
8.5 CheckOutRecord
Descrição: Registro de check-out.
Atributos:
id: RecordIdcheckInRecordId: RecordIdcheckInPointId: CheckInPointIduserId: UserIdtimestamp: Dateevidence: PresenceEvidenceduration: Duration(tempo de permanência)
Relacionamentos:
- Vinculado a
CheckInRecord - Finaliza
PresenceSession
8.6 PresenceSession (Aggregate Root)
Descrição: Sessão de presença contínua de um usuário.
Atributos:
id: SessionIduserId: UserIdarenaId: ArenaIdresourceId: ResourceId?(Court, Area)resourceType: ResourceType?startedAt: DateendedAt: Date?duration: Duration?checkInRecordId: RecordIdcheckOutRecordId: RecordId?evidenceRecords: PresenceEvidenceId[]status: SessionStatus(ACTIVE, ENDED, ABANDONED)
Relacionamentos:
- Iniciada por
CheckInRecord - Finalizada por
CheckOutRecord - Contém múltiplos
PresenceEvidence - Gera
UsageAuditRecord
Comportamentos:
end(): voidabandon(reason: string): voidaddEvidence(evidence: PresenceEvidence): voidgetCurrentDuration(): Duration
8.7 PresenceEvidence
Descrição: Evidência de presença (abstração).
Atributos Base:
id: EvidenceIdsessionId: SessionIduserId: UserIdtimestamp: Datetype: EvidenceType(DEVICE_TAP, QR_SCAN, RFID_SCAN, BIOMETRIC, GEOLOCATION, BLUETOOTH)
Subtipos:
DeviceTap:
deviceId: DeviceIdlocation: Location
QRCodeScan:
code: stringscannedBy: DeviceId
RFIDScan:
tagId: stringreaderId: DeviceIdsignalStrength: number
BiometricEvidence:
biometricType: BiometricType(FINGERPRINT, FACE, IRIS)confidence: number
GeolocationEvidence:
coordinates: GeoLocationaccuracy: number(metros)provider: string(GPS, WiFi, Bluetooth)
Relacionamentos:
- Pertence a
PresenceSession - Capturado por
Device(Devices Context)
8.8 UsageAuditRecord
Descrição: Registro de auditoria de uso.
Atributos:
id: AuditRecordIdtenantId: TenantIduserId: UserIdresourceId: ResourceIdresourceType: ResourceTypeaction: AuditAction(CHECK_IN, CHECK_OUT, ACCESS_GRANTED, ACCESS_DENIED, BOOKING_CREATED, etc)timestamp: DateipAddress: string?deviceInfo: DeviceInfo?metadata: Record<string, any>result: ActionResult(SUCCESS, FAILURE, ERROR)errorMessage: string?
Relacionamentos:
- Referencia
UserAccount - Associado a recurso específico
Uso: Compliance, LGPD, analytics, troubleshooting
Documento continua no próximo bloco...
9. Devices & IoT Context
Objetivo
Gerenciar dispositivos IoT, tablets de quadra, botões de highlight, leitores de acesso, sensores e telemetria.
Aggregates
9.1 Device (Aggregate Root)
Descrição: Dispositivo físico (abstração para diversos tipos).
Atributos Base:
id: DeviceIdarenaId: ArenaIdtype: DeviceType(COURT_TABLET, HIGHLIGHT_BUTTON, ACCESS_READER, SCORE_TERMINAL, SENSOR, CAMERA, WEARABLE)name: stringmodel: stringmanufacturer: stringserialNumber: stringmacAddress: string?ipAddress: string?firmwareVersion: string?status: DeviceStatus(ONLINE, OFFLINE, ERROR, MAINTENANCE, DECOMMISSIONED)lastSeenAt: Date?installedAt: Datelocation: DeviceLocationconfiguration: DeviceConfiguration
Subtipos:
CourtTablet:
courtId: CourtIdscreenSize: number(inches)operatingSystem: string(Android, iOS)appVersion: stringbatteryLevel: number?
HighlightButton:
courtId: CourtIdbuttonType: ButtonType(WALL_MOUNTED, PORTABLE, WEARABLE)batteryPowered: booleanbatteryLevel: number?
AccessReader:
checkInPointId: CheckInPointIdreaderType: ReaderType(RFID, NFC, QR, BARCODE, BIOMETRIC)protocol: ReaderProtocol(WIEGAND, OSDP, TCP_IP)
ScoreTerminal:
courtId: CourtIdhasDisplay: booleandisplaySize: number?inputType: InputType(TOUCH, KEYPAD, REMOTE)
Sensor:
courtId: CourtId?areaId: AreaId?sensorType: SensorType(TEMPERATURE, HUMIDITY, LIGHT, MOTION, OCCUPANCY)measurementUnit: stringmeasurementRange: RangereadingInterval: Duration
Wearable:
wearableType: WearableType(WRISTBAND, WATCH, TAG, SENSOR_PATCH)assignedTo: UserId?
Relacionamentos:
- Pertence a uma
Arena - Pode estar vinculado a
Court,CheckInPoint,Arena Area - Possui
DeviceRegistration - Pode ter
DeviceAssignment(se atribuído a usuário) - Envia
DeviceTelemetry - Recebe
DeviceCommand - Emite
DeviceEvent
Invariantes:
- Serial number deve ser único
- Dispositivo offline não pode executar comandos críticos
- Tablet de quadra deve estar vinculado a uma quadra
Comportamentos:
register(): DeviceRegistrationassign(userId: UserId): DeviceAssignmentunassign(): voidsendCommand(command: DeviceCommand): voidreportTelemetry(data: TelemetryData): DeviceTelemetryupdateFirmware(version: string): voiddecommission(reason: string): void
9.2 DeviceRegistration
Descrição: Registro de dispositivo no sistema.
Atributos:
id: RegistrationIddeviceId: DeviceIdregisteredAt: DateregisteredBy: UserIdactivationCode: string?certificate: string?(para autenticação mTLS)provisioningStatus: ProvisioningStatus(PENDING, PROVISIONED, ACTIVATED, DEACTIVATED)
Relacionamentos:
- Pertence a um
Device
9.3 DeviceAssignment
Descrição: Atribuição de dispositivo a usuário.
Atributos:
id: AssignmentIddeviceId: DeviceIduserId: UserIdassignedAt: DateassignedBy: UserIdreturnedAt: Date?expectedReturnDate: Date?status: AssignmentStatus(ACTIVE, RETURNED, LOST, DAMAGED)notes: string?
Relacionamentos:
- Vincula
DeviceaUserAccount
Comportamentos:
return(condition: DeviceCondition): voidreportLost(): voidreportDamaged(description: string): void
9.4 DeviceTelemetry
Descrição: Dados de telemetria do dispositivo.
Atributos:
id: TelemetryIddeviceId: DeviceIdtimestamp: Datemetrics: Record<string, number>(temperatura, bateria, uso de memória, CPU, etc)status: DeviceStatuserrorCode: string?errorMessage: string?
Exemplos de Métricas:
battery_level: 85 (%)temperature: 23.5 (°C)humidity: 60 (%)light_level: 800 (lux)cpu_usage: 45 (%)memory_usage: 2048 (MB)uptime: 86400 (segundos)
Relacionamentos:
- Pertence a um
Device
Uso: Monitoramento, manutenção preditiva, alertas
9.5 DeviceCommand
Descrição: Comando enviado para dispositivo.
Atributos:
id: CommandIddeviceId: DeviceIdtype: CommandType(REBOOT, UPDATE_FIRMWARE, CHANGE_CONFIG, TRIGGER_ACTION, CAPTURE_DATA)payload: Record<string, any>sentAt: DatesentBy: UserIdstatus: CommandStatus(PENDING, SENT, ACKNOWLEDGED, EXECUTED, FAILED, TIMEOUT)executedAt: Date?result: string?errorMessage: string?
Exemplos de Comandos:
REBOOT: reiniciar dispositivoUPDATE_FIRMWARE: atualizar firmware para versão XCHANGE_CONFIG: alterar configuração (ex: interval de leitura)TRIGGER_ACTION: acionar ação específica (ex: abrir catraca)CAPTURE_DATA: capturar foto/vídeo
Relacionamentos:
- Enviado para
Device
Comportamentos:
acknowledge(): voidmarkExecuted(result: string): voidmarkFailed(error: string): void
9.6 DeviceEvent
Descrição: Evento emitido por dispositivo.
Atributos:
id: EventIddeviceId: DeviceIdtype: DeviceEventType(BUTTON_PRESSED, ACCESS_GRANTED, ACCESS_DENIED, SENSOR_TRIGGERED, ERROR, BATTERY_LOW, OFFLINE, ONLINE)timestamp: Datedata: Record<string, any>severity: EventSeverity(INFO, WARNING, ERROR, CRITICAL)processedAt: Date?processedBy: string?(handler que processou)
Exemplos de Eventos:
BUTTON_PRESSED: botão de highlight pressionado- data:
{ courtId, matchId, timestamp }
- data:
ACCESS_GRANTED: acesso concedido- data:
{ userId, zoneId, credentialType }
- data:
SENSOR_TRIGGERED: sensor de movimento ativado- data:
{ sensorId, reading, threshold }
- data:
BATTERY_LOW: bateria baixa- data:
{ batteryLevel, estimatedRuntime }
- data:
Relacionamentos:
- Emitido por
Device - Pode disparar ações no sistema (ex: criar highlight, registrar acesso)
Uso: Event-driven architecture, automações, notificações
10. Media & Video Context
Objetivo
Gerenciar captação de vídeo, gravações de partidas, highlights automáticos e manuais, associação de mídia a jogadores e partidas.
Aggregates
10.1 MediaAsset (Aggregate Root)
Descrição: Ativo de mídia (abstração para vídeo, imagem, etc).
Atributos Base:
id: MediaAssetIdtenantId: TenantIdtype: MediaType(VIDEO, IMAGE, AUDIO, DOCUMENT)title: string?description: string?fileName: stringfileSize: number(bytes)mimeType: stringstorageProvider: StorageProvider(S3, AZURE, GCS, LOCAL)storagePath: stringurl: string(CDN URL)thumbnailUrl: string?duration: number?(para vídeos, em segundos)dimensions: Dimensions?(width x height)uploadedBy: UserIduploadedAt: DateprocessingStatus: ProcessingStatus(PENDING, PROCESSING, COMPLETED, FAILED)tags: Tag[]isPublic: booleanmetadata: MediaMetadata
Subtipos:
VideoAsset:
resolution: Resolution(1080p, 720p, 4K)framerate: number(fps)codec: stringbitrate: number(kbps)hasAudio: booleanstreamingUrl: string?(HLS/DASH)thumbnailsGenerated: booleanthumbnailsInterval: number?(segundos)
ImageAsset:
resolution: Resolutionformat: ImageFormat(JPEG, PNG, WEBP)colorSpace: ColorSpace(RGB, CMYK)
Relacionamentos:
- Possui múltiplos
MediaAssociation(vinculação a Match, Player, Court, etc) - Pode ser
RecordingSession(se vídeo de partida) - Pode ser
HighlightClip(se highlight)
Invariantes:
- URL deve ser acessível
- Vídeos devem ter thumbnail
Comportamentos:
process(): void(transcodificar, gerar thumbnails)generateThumbnails(): voidassociateTo(entity: EntityId, entityType: EntityType): MediaAssociationmakePublic(): voidmakePrivate(): voidarchive(): voiddelete(): void
10.2 Camera
Descrição: Câmera instalada na arena.
Atributos:
id: CameraIddeviceId: DeviceId(referencia Device)arenaId: ArenaIdcourtId: CourtId?areaId: AreaId?name: stringmanufacturer: stringmodel: stringipAddress: stringrtspUrl: string?(para streaming)resolution: Resolutionframerate: numberhasPTZ: boolean(Pan-Tilt-Zoom)hasNightVision: booleanviewAngle: ViewAngleposition: CameraPosition(descrição da posição)status: CameraStatus(ONLINE, OFFLINE, RECORDING, ERROR)
Relacionamentos:
- Pertence a
Arena - Vinculada a
CourtouArenaArea - Gera
RecordingSession
Comportamentos:
startRecording(): RecordingSessionstopRecording(): voidmoveToPosition(pan: number, tilt: number, zoom: number): void(se PTZ)captureSnapshot(): ImageAsset
10.3 RecordingSession (Aggregate Root)
Descrição: Sessão de gravação de vídeo.
Atributos:
id: SessionIdcameraId: CameraIdmatchId: MatchId?courtId: CourtIdstartedAt: DateendedAt: Date?duration: Duration?status: RecordingStatus(RECORDING, STOPPED, PROCESSING, COMPLETED, FAILED)videoAssetId: MediaAssetId?storageSize: number?(bytes)recordingQuality: Quality(LOW, MEDIUM, HIGH, ULTRA)isAutoRecording: boolean(gravação automática vs manual)triggeredBy: UserId?
Relacionamentos:
- Capturada por
Camera - Associada a
Match(se gravação de partida) - Gera
VideoAsset - Pode gerar múltiplos
HighlightClip
Invariantes:
- Sessão finalizada deve ter vídeo associado
- Duração deve ser positiva
Comportamentos:
stop(): VideoAssetprocess(): voidgenerateHighlights(triggers: HighlightTrigger[]): HighlightClip[]archive(): voiddelete(reason: string): void
10.4 HighlightClip (Aggregate Root)
Descrição: Clip de highlight (momento marcante).
Atributos:
id: ClipIdrecordingSessionId: SessionIdvideoAssetId: MediaAssetIdmatchId: MatchId?title: stringdescription: string?startTime: number(timestamp no vídeo original, segundos)endTime: numberduration: Durationtype: HighlightType(ACE, WINNER, RALLY, MATCH_POINT, FUNNY_MOMENT, CUSTOM)automaticallyGenerated: booleanconfidence: number?(0-1, se gerado por IA)tags: Tag[]associatedPlayers: PlayerId[]createdAt: DatecreatedBy: UserId?views: numberlikes: numbershares: number
Relacionamentos:
- Extraído de
RecordingSession - Referencia
VideoAsset - Associado a
Match - Criado por
HighlightTrigger - Associado a
PlayerProfile(via MediaAssociation)
Comportamentos:
view(): void(incrementar contador)like(userId: UserId): voidunlike(userId: UserId): voidshare(platform: string): voidedit(startTime: number, endTime: number): voidpublish(): voidunpublish(): void
10.5 HighlightTrigger
Descrição: Gatilho para criação de highlight.
Atributos:
id: TriggerIdrecordingSessionId: SessionIdtype: TriggerType(BUTTON_PRESS, APP_ACTION, SCORE_EVENT, AI_DETECTION)timestamp: DatevideoTimestamp: number(timestamp no vídeo, segundos)triggeredBy: UserId?(se manual)deviceId: DeviceId?(se por botão físico)scoreEventId: ScoreEventId?(se por evento de pontuação)metadata: Record<string, any>processed: booleanprocessedAt: Date?resultingClipId: ClipId?
Subtipos:
ButtonTrigger:
deviceId: DeviceId(HighlightButton)buttonLocation: Location
AppTrigger:
userId: UserIdappVersion: stringdeviceType: DeviceType
ScoreEventTrigger:
scoreEventId: ScoreEventIdeventType: ScoreEventType(ACE, WINNER, MATCH_POINT)
AIDetectionTrigger:
detectionType: AIDetectionType(RALLY, EMOTION, ACTION)confidence: numberdetectionModel: string
Relacionamentos:
- Pertence a
RecordingSession - Pode ser originado de
Device(HighlightButton) - Pode ser originado de
ScoreEvent(Play Context) - Gera
HighlightClip
Comportamentos:
process(): HighlightClipcalculateClipBoundaries(): { start: number, end: number }
10.6 MediaAssociation
Descrição: Associação de mídia a entidades do domínio.
Atributos:
id: AssociationIdmediaAssetId: MediaAssetIdentityId: string(Match, Player, Court, etc)entityType: EntityTypeassociationType: AssociationType(PROFILE_PICTURE, MATCH_VIDEO, HIGHLIGHT, EVIDENCE, DOCUMENTATION)associatedAt: DateassociatedBy: UserId?displayOrder: number?(para ordenação)isPrimary: boolean(ex: foto de perfil principal)
Exemplos:
- Vídeo de partida → Match
- Highlight → Player + Match
- Foto de perfil → Player
- Foto de quadra → Court
- Vídeo promocional → Arena
Relacionamentos:
- Vincula
MediaAsseta qualquer entidade - Permite relacionamento many-to-many
11. Commerce & Billing Context
Objetivo
Gerenciar produtos, vendas, consumo, pagamentos, assinaturas, pacotes, créditos, faturas, comissões e pagamentos a instrutores.
Aggregates
11.1 ProductItem (Aggregate Root)
Descrição: Item/produto vendido na arena.
Atributos:
id: ProductItemIdarenaId: ArenaIdsku: string(código único)name: stringdescription: string?category: ProductCategory(COURT_RENTAL, LESSON, EQUIPMENT, FOOD, BEVERAGE, MERCHANDISE, DAY_USE, MEMBERSHIP)type: ProductType(GOOD, SERVICE, DIGITAL)unit: Unit(HOUR, UNIT, LESSON, MONTH)price: MoneytaxProfile: TaxProfileIdisActive: booleanrequiresInventory: booleanstockQuantity: number?minStock: number?imageUrl: string?tags: Tag[]
Relacionamentos:
- Pertence a
Arena - Possui
TaxProfile - Listado em
PriceList - Vendido em
OrderLine - Consumido em
Tab
Invariantes:
- SKU deve ser único por arena
- Produto inativo não pode ser vendido
- Produto com estoque deve controlar quantidade
Comportamentos:
adjustStock(quantity: number, reason: string): voidisAvailable(): booleancalculatePrice(quantity: number): MoneyapplyDiscount(discount: Discount): Money
11.2 PriceList
Descrição: Lista de preços (pode variar por contexto).
Atributos:
id: PriceListIdarenaId: ArenaIdname: stringtype: PriceListType(STANDARD, HAPPY_HOUR, WEEKEND, MEMBER, VIP, EVENT)effectiveFrom: DateeffectiveUntil: Date?prices: ProductPrice[]isActive: boolean
ProductPrice (Value Object):
productItemId: ProductItemIdprice: MoneydiscountPercentage: number?
Relacionamentos:
- Pertence a
Arena - Aplica preços a
ProductItem
Comportamentos:
getPrice(productId: ProductItemId): MoneyisEffective(date: Date): boolean
11.3 TaxProfile
Descrição: Perfil tributário.
Atributos:
id: TaxProfileIdarenaId: ArenaIdname: stringtaxes: Tax[]isActive: boolean
Tax (Value Object):
name: string(ex: "ICMS", "ISS", "Service Fee")type: TaxType(PERCENTAGE, FIXED)rate: number(percentual ou valor fixo)isInclusive: boolean(incluído no preço ou adicional)
Relacionamentos:
- Aplicado a
ProductItem
Comportamentos:
calculate(baseAmount: Money): MoneygetTotalRate(): number
11.4 Order (Aggregate Root)
Descrição: Pedido/ordem de venda.
Atributos:
id: OrderIdarenaId: ArenaIdorderNumber: stringcustomerId: UserIdorderDate: Datelines: OrderLine[]subtotal: MoneytaxAmount: MoneydiscountAmount: Moneytotal: Moneystatus: OrderStatus(DRAFT, CONFIRMED, PAID, PARTIALLY_REFUNDED, REFUNDED, CANCELLED)paymentStatus: PaymentStatus(PENDING, PAID, PARTIAL, REFUNDED)source: OrderSource(POS, ONLINE, MOBILE_APP, KIOSK)notes: string?createdBy: UserIdcreatedAt: Date
Relacionamentos:
- Pertence a
Arena - Cliente:
UserAccount - Contém múltiplos
OrderLine - Possui múltiplos
Payment - Pode gerar
Invoice
Invariantes:
- Ordem confirmada não pode alterar items
- Total deve ser soma de linhas + taxas - descontos
- Pagamento total deve igualar valor da ordem
Comportamentos:
addLine(product: ProductItemId, quantity: number, price: Money): OrderLineremoveLine(lineId: OrderLineId): voidapplyDiscount(discount: Discount): voidconfirm(): voidcancel(reason: string): voidcalculateTotal(): Money
11.5 OrderLine
Descrição: Linha de pedido (item individual).
Atributos:
id: OrderLineIdorderId: OrderIdlineNumber: numberproductItemId: ProductItemIdproductName: string(snapshot)quantity: numberunitPrice: Moneydiscount: Money?taxAmount: Moneytotal: Moneynotes: string?
Relacionamentos:
- Pertence a um
Order - Referencia
ProductItem
Comportamentos:
calculateTotal(): Money
11.6 Tab (Aggregate Root)
Descrição: Comanda de consumo (tipicamente em eventos ou áreas de hospitalidade).
Atributos:
id: TabIdarenaId: ArenaIdtabNumber: stringownerId: UserIdeventId: EventId?(se em evento)zoneId: ZoneId?(zona onde é válida)openedAt: DateclosedAt: Date?lines: TabLine[]subtotal: MoneytaxAmount: MoneyserviceCharge: Money?total: Moneystatus: TabStatus(OPEN, CLOSED, PAID, TRANSFERRED)paymentStatus: PaymentStatusconsumptionPolicy: EventConsumptionPolicyId?
Relacionamentos:
- Pertence a
Arena - Proprietário:
UserAccount - Pode estar associada a
Event - Contém múltiplos
TabLine - Registra
ConsumptionRecord - Possui
Paymentao fechar
Invariantes:
- Comanda fechada não aceita novos itens
- Total deve ser calculado segundo política de consumo do evento
Comportamentos:
addItem(product: ProductItemId, quantity: number): TabLineremoveItem(lineId: TabLineId): voidclose(): voidpay(payment: Payment): voidtransfer(newOwner: UserId): voidsplit(portions: number): Tab[](dividir comanda)
11.7 TabLine
Descrição: Item consumido na comanda.
Atributos:
id: TabLineIdtabId: TabIdlineNumber: numberproductItemId: ProductItemIdproductName: stringquantity: numberunitPrice: Moneytotal: MoneyconsumedAt: DateservedBy: UserId?(staff)
Relacionamentos:
- Pertence a um
Tab - Referencia
ProductItem
11.8 ConsumptionRecord
Descrição: Registro de consumo individual.
Atributos:
id: RecordIdtabId: TabId?userId: UserIdproductItemId: ProductItemIdquantity: numberconsumedAt: Datelocation: Location?zoneId: ZoneId?withinAllowance: boolean(se dentro do permitido pela política)chargeApplied: Money?
Relacionamentos:
- Vinculado a
Tab(se houver) - Referencia
UserAccounteProductItem
Uso: Controle de consumo em eventos all-inclusive, analytics
11.9 ConsumptionZoneAllowance
Descrição: Permissão de consumo em zona específica.
Atributos:
id: AllowanceIduserId: UserIdeventId: EventId?zoneId: ZoneIdallowedCategories: ProductCategory[]quantityLimit: number?valueLimit: Money?validFrom: DatevalidUntil: DatecurrentUsage: numbercurrentValue: Money
Relacionamentos:
- Pertence a
UserAccount - Aplicada em
AccessZone - Definida por
EventConsumptionPolicy
Comportamentos:
canConsume(product: ProductItemId, quantity: number): booleanrecordConsumption(product: ProductItemId, quantity: number): voidgetRemainingAllowance(): number | Money
11.10 Payment (Aggregate Root)
Descrição: Pagamento realizado.
Atributos:
id: PaymentIdarenaId: ArenaIdpayerId: UserIdamount: Moneymethod: PaymentMethodstatus: PaymentStatus(PENDING, PROCESSING, AUTHORIZED, CAPTURED, COMPLETED, FAILED, REFUNDED)transactionId: string?(ID do gateway)gatewayResponse: string?paidAt: Date?refundedAt: Date?refundAmount: Money?relatedOrderId: OrderId?relatedTabId: TabId?relatedInvoiceId: InvoiceId?metadata: Record<string, any>createdAt: Date
Relacionamentos:
- Pago por
UserAccount - Associado a
Order,Tab, ouInvoice - Usa
PaymentMethod
Invariantes:
- Pagamento completado não pode ser editado
- Reembolso não pode exceder valor pago
Comportamentos:
authorize(): voidcapture(): voidcomplete(): voidfail(reason: string): voidrefund(amount: Money, reason: string): void
11.11 PaymentMethod
Descrição: Método de pagamento (Value Object/Enum).
Valores:
CASH: DinheiroCREDIT_CARD: Cartão de créditoDEBIT_CARD: Cartão de débitoPIX: PIXBANK_TRANSFER: Transferência bancáriaWALLET: Carteira digital (da arena)CREDIT_PACKAGE: Pacote de créditosINVOICE: Faturado (pagamento posterior)COMPLIMENTARY: Cortesia
11.12 BillingAccount (Aggregate Root)
Descrição: Conta de cobrança (para clientes corporativos ou mensalidades).
Atributos:
id: BillingAccountIdarenaId: ArenaIdaccountNumber: stringaccountHolder: UserId(empresa ou pessoa)billingAddress: AddresstaxId: string(CNPJ/CPF)paymentTerms: PaymentTerms(ex: NET 30)creditLimit: Money?currentBalance: Moneystatus: AccountStatus(ACTIVE, SUSPENDED, CLOSED)createdAt: Date
Relacionamentos:
- Pertence a
Arena - Titular:
UserAccount - Gera múltiplas
Invoice
Comportamentos:
addCharge(amount: Money, description: string): voidaddCredit(amount: Money, description: string): voidgenerateInvoice(period: DateRange): Invoicesuspend(reason: string): void
11.13 Subscription (Aggregate Root)
Descrição: Assinatura recorrente (mensalidade, plano).
Atributos:
id: SubscriptionIdarenaId: ArenaIdsubscriberId: UserIdplanId: SubscriptionPlanIdstatus: SubscriptionStatus(ACTIVE, PAUSED, CANCELLED, EXPIRED, PAST_DUE)startDate: DateendDate: Date?nextBillingDate: DatebillingCycle: BillingCycle(MONTHLY, QUARTERLY, YEARLY)price: MoneypaymentMethod: PaymentMethodautoRenew: booleancancelledAt: Date?cancellationReason: string?
Relacionamentos:
- Pertence a
Arena - Assinante:
UserAccount - Baseada em
SubscriptionPlan - Gera
Invoicerecorrente
Invariantes:
- Assinatura ativa requer pagamento em dia
- Cancelamento respeitando período de aviso
Comportamentos:
pause(until: Date): voidresume(): voidcancel(reason: string): voidrenew(): voidchangePlan(newPlan: SubscriptionPlanId): void
11.14 SubscriptionPlan
Descrição: Plano de assinatura.
Atributos:
id: PlanIdarenaId: ArenaIdname: stringdescription: stringprice: MoneybillingCycle: BillingCyclebenefits: PlanBenefit[]courtCredits: number?(créditos de quadra inclusos)lessonCredits: number?discountPercentage: number?(desconto em produtos)priorityBooking: booleanguestPasses: number?isActive: boolean
PlanBenefit (Value Object):
type: BenefitType(COURT_CREDIT, LESSON_DISCOUNT, FREE_DAY_USE, PRIORITY_BOOKING)quantity: number?description: string
Relacionamentos:
- Pertence a
Arena - Usado por múltiplas
Subscription
11.15 Package (Aggregate Root)
Descrição: Pacote de créditos/serviços pré-pago.
Atributos:
id: PackageIdarenaId: ArenaIdname: stringdescription: stringprice: Moneycredits: PackageCredit[]validityPeriod: Duration(ex: 90 dias)isTransferable: booleanisRefundable: booleanisActive: boolean
PackageCredit (Value Object):
type: CreditType(COURT_HOUR, LESSON, DAY_USE, GENERAL)quantity: numberrestrictions: CreditRestriction[]?(ex: "apenas dias úteis")
Relacionamentos:
- Pertence a
Arena - Adquirido em
Order - Gera
CreditWallet
11.16 CreditWallet
Descrição: Carteira de créditos de um usuário.
Atributos:
id: WalletIduserId: UserIdpackageId: PackageId?(se veio de pacote)creditType: CreditTypebalance: numberoriginalBalance: numberpurchasedAt: DateexpiresAt: Date?status: WalletStatus(ACTIVE, EXPIRED, DEPLETED)
Relacionamentos:
- Pertence a
UserAccount - Originada de
Package
Comportamentos:
debit(amount: number, reason: string): voidcredit(amount: number, reason: string): voidisExpired(): booleangetAvailableBalance(): number
11.17 Invoice (Aggregate Root)
Descrição: Fatura/Nota fiscal.
Atributos:
id: InvoiceIdarenaId: ArenaIdinvoiceNumber: stringbillingAccountId: BillingAccountId?customerId: UserIdissueDate: DatedueDate: DateperiodStart: Date?periodEnd: Date?lines: InvoiceLine[]subtotal: MoneytaxAmount: Moneytotal: MoneyamountPaid: MoneyamountDue: Moneystatus: InvoiceStatus(DRAFT, ISSUED, SENT, PAID, OVERDUE, CANCELLED, REFUNDED)paidAt: Date?pdfUrl: string?xmlUrl: string?(NFe XML)nfeKey: string?(chave de acesso NFe)
Relacionamentos:
- Pertence a
Arena - Cliente:
UserAccount - Associada a
BillingAccount(se corporativo) - Contém múltiplos
InvoiceLine - Possui
Payment
Comportamentos:
issue(): voidsend(email: Email): voidpay(payment: Payment): voidcancel(reason: string): voidgeneratePDF(): stringgenerateNFe(): string(integração fiscal)
11.18 Refund
Descrição: Reembolso/Estorno.
Atributos:
id: RefundIdarenaId: ArenaIdoriginalPaymentId: PaymentIdamount: Moneyreason: stringrequestedBy: UserIdrequestedAt: DateapprovedBy: UserId?approvedAt: Date?processedAt: Date?status: RefundStatus(REQUESTED, APPROVED, DENIED, PROCESSING, COMPLETED, FAILED)method: RefundMethod(ORIGINAL_METHOD, CREDIT, TRANSFER)
Relacionamentos:
- Associado a
Paymentoriginal - Pode gerar
Paymentnegativo ouCreditWallet
Comportamentos:
approve(approver: UserId): voiddeny(reason: string): voidprocess(): voidcomplete(): void
11.19 CommissionRule
Descrição: Regra de comissionamento (para instrutores, vendedores).
Atributos:
id: RuleIdarenaId: ArenaIdname: stringapplicableTo: CommissionTarget(INSTRUCTOR, SALESPERSON, STAFF)type: CommissionType(PERCENTAGE, FIXED_PER_UNIT, TIERED)rate: number(percentual ou valor)tiers: CommissionTier[]?(se tiered)appliesTo: ProductCategory[]minimumAmount: Money?effectiveFrom: DateeffectiveUntil: Date?isActive: boolean
CommissionTier (Value Object):
from: Money(valor mínimo)to: Money?(valor máximo, null = infinito)rate: number
Relacionamentos:
- Pertence a
Arena - Aplicada a
InstructorProfile,StaffProfile
Comportamentos:
calculate(amount: Money): MoneyisApplicable(date: Date, product: ProductItemId): boolean
11.20 Payout (Aggregate Root)
Descrição: Pagamento/repasse (genérico).
Atributos:
id: PayoutIdarenaId: ArenaIdrecipientId: UserIdrecipientType: RecipientType(INSTRUCTOR, STAFF, VENDOR, PARTNER)amount: Moneyperiod: DateRangeitems: PayoutItem[]status: PayoutStatus(PENDING, APPROVED, PAID, CANCELLED)approvedBy: UserId?approvedAt: Date?paidAt: Date?paymentMethod: PaymentMethodreferenceNumber: string?
PayoutItem (Value Object):
description: stringamount: MoneyreferenceId: string?(Order, Lesson, etc)referenceType: string?
Relacionamentos:
- Pertence a
Arena - Destinatário:
UserAccount(Instructor, Staff)
Comportamentos:
approve(approver: UserId): voidpay(payment: Payment): voidcancel(reason: string): void
11.21 InstructorPayout
Descrição: Pagamento específico para instrutor (herda de Payout).
Atributos (adicionais):
instructorId: InstructorIdlessons: LessonId[]totalHours: numberhourlyRate: Moneycommissions: Moneybonuses: Money?deductions: Money?
Relacionamentos:
- Destinatário:
InstructorProfile - Baseado em
Lessonministradas - Usa
CommissionRule
Documento continua no próximo bloco...
12. Wallet & Value Context
Objetivo
Gerenciar carteiras digitais multi-moeda (dinheiro real, tokens da arena, pontos, créditos), transações, apostas, prêmios, cashback, conversões e políticas de uso.
Aggregates
12.1 Wallet (Aggregate Root)
Descrição: Carteira digital multi-moeda e multi-propósito.
Atributos:
id: WalletIdownerId: WalletOwnerIdownerType: WalletOwnerType(USER, ARENA, APPLICATION, EVENT)walletNumber: string(identificador único)type: WalletType(PERSONAL, CORPORATE, SYSTEM, ESCROW, PRIZE_POOL)balances: WalletBalance[](múltiplas moedas/tokens)status: WalletStatus(ACTIVE, SUSPENDED, FROZEN, CLOSED)createdAt: DatesuspendedAt: Date?suspensionReason: string?
Relacionamentos:
- Pertence a
WalletOwner(User, Arena, Application, Event) - Possui múltiplos
WalletBalance(uma por ValueUnit) - Registra múltiplas
WalletTransaction - Possui
WalletPolicy - Auditada por
WalletAuditTrail
Invariantes:
- Saldo não pode ser negativo (exceto wallets especiais)
- Wallet suspensa não aceita transações
- Cada ValueUnit tem apenas um balance
Comportamentos:
getBalance(valueUnit: ValueUnit): Money | numbercredit(amount: Money | number, valueUnit: ValueUnit, reason: string): WalletTransactiondebit(amount: Money | number, valueUnit: ValueUnit, reason: string): WalletTransactiontransfer(toWallet: WalletId, amount: Money | number, valueUnit: ValueUnit): WalletTransactionfreeze(reason: string): voidunfreeze(): voidcanTransact(amount: Money | number, valueUnit: ValueUnit): boolean
12.2 WalletOwner
Descrição: Proprietário de carteira (abstração).
Valores:
USER: Carteira pessoal de usuárioARENA: Carteira da arena (revenue)APPLICATION: Carteira do sistema (fees, reservas)EVENT: Carteira de evento (prize pool, cashback pool)ESCROW: Carteira de garantia (stakes, apostas)
12.3 WalletBalance
Descrição: Saldo de uma moeda/token específica.
Atributos:
id: BalanceIdwalletId: WalletIdvalueUnit: ValueUnitavailable: Money | number(disponível para uso)locked: Money | number(bloqueado, ex: apostas pendentes)total: Money | number(available + locked)lastTransactionAt: Date?lastTransactionId: TransactionId?
Relacionamentos:
- Pertence a uma
Wallet - Associado a um
ValueUnit
Invariantes:
total = available + lockedavailable >= 0locked >= 0
Comportamentos:
lock(amount: Money | number): voidunlock(amount: Money | number): voidadd(amount: Money | number): voidsubtract(amount: Money | number): void
12.4 WalletTransaction (Aggregate Root)
Descrição: Transação de carteira (abstração para múltiplos tipos).
Atributos Base:
id: TransactionIdwalletId: WalletIdtype: TransactionType(CREDIT, DEBIT, TRANSFER, REWARD, BET, CASHBACK, CONVERSION, EXPIRATION, ADJUSTMENT)valueUnit: ValueUnitamount: Money | numberbalanceBefore: Money | numberbalanceAfter: Money | numberstatus: TransactionStatus(PENDING, PROCESSING, COMPLETED, FAILED, REVERSED)timestamp: Datedescription: stringmetadata: Record<string, any>relatedEntityId: string?(Order, Match, Bet, etc)relatedEntityType: string?initiatedBy: UserId?reversedAt: Date?reversalReason: string?
Subtipos:
CreditTransaction:
source: CreditSource(PURCHASE, REFUND, REWARD, BONUS, TRANSFER_IN, ADMIN_ADJUSTMENT)sourceTransactionId: TransactionId?
DebitTransaction:
destination: DebitDestination(PURCHASE, WITHDRAWAL, TRANSFER_OUT, FEE, EXPIRATION, PENALTY)destinationTransactionId: TransactionId?
TransferTransaction:
fromWalletId: WalletIdtoWalletId: WalletIdfee: Money?
RewardTransaction:
rewardRuleId: RewardRuleIdtriggerEvent: string(ex: "match_completed", "referral", "milestone")multiplier: number?
BetTransaction:
matchId: MatchIdbetAmount: Money | numberpotentialWin: Money | numberodds: number?
CashbackTransaction:
originalTransactionId: TransactionIdcashbackRate: numbercalculatedFrom: Money
ConversionTransaction:
fromValueUnit: ValueUnittoValueUnit: ValueUnitfromAmount: Money | numbertoAmount: Money | numberexchangeRate: numberconversionRuleId: ConversionRuleId
Relacionamentos:
- Pertence a uma
Wallet - Registrada em
TransactionLedger - Pode gerar outra transação (ex: transfer gera crédito + débito)
Invariantes:
- Transação completada é imutável
- Reversão cria nova transação (não altera original)
- Balance before + amount = balance after
Comportamentos:
complete(): voidfail(reason: string): voidreverse(reason: string): WalletTransaction
12.5 ValueUnit
Descrição: Unidade de valor (moeda ou token).
Atributos:
id: ValueUnitIdtype: ValueUnitType(CURRENCY, ARENA_TOKEN, POINT, CREDIT)code: string(ex: "BRL", "USD", "STC_TOKEN", "POINTS")name: stringsymbol: string?(ex: "R$", "ST")decimals: number(casas decimais, ex: 2 para moeda)isActive: boolean
Subtipos:
Currency:
isoCode: string(ISO 4217)country: string
ArenaToken:
arenaId: ArenaIdtokenDefinition: TokenDefinitionIdcanBeTraded: booleancanBeTransferred: boolean
Point:
pointSystem: PointSystem(LOYALTY, RANKING, EXPERIENCE)expirationRule: ExpirationRuleId?
Credit:
creditType: CreditType(COURT_HOUR, LESSON, DAY_USE, GENERAL)expirationRule: ExpirationRuleId?
Relacionamentos:
- Usado em
WalletBalance - Usado em
WalletTransaction - Pode ter
ExchangeRulepara conversão
12.6 TokenDefinition (Aggregate Root)
Descrição: Definição de token customizado da arena.
Atributos:
id: TokenDefinitionIdarenaId: ArenaIdcode: string(ex: "PADDLE_COIN")name: string(ex: "Paddle Coin")symbol: string(ex: "PC")description: string?type: TokenType(UTILITY, REWARD, STAKE)totalSupply: number?(limite de emissão)currentSupply: numbermintable: boolean(pode emitir mais)burnable: boolean(pode destruir)transferable: booleantradeable: boolean(pode trocar entre usuários)pegged: boolean(atrelado a moeda real)pegRatio: number?(1 token = X reais)expirationRule: ExpirationRuleId?rewardRules: RewardRuleId[]isActive: booleanlaunchedAt: Date
Relacionamentos:
- Pertence a
Arena - Gera
ArenaToken(ValueUnit) - Usa
RewardRule,ExpirationRule
Comportamentos:
mint(amount: number, recipient: WalletId): voidburn(amount: number, fromWallet: WalletId): voidcalculateValue(amount: number): Money(se pegged)
12.7 ExchangeRule
Descrição: Regra de conversão entre unidades de valor.
Atributos:
id: RuleIdarenaId: ArenaIdname: stringfromValueUnit: ValueUnittoValueUnit: ValueUnitexchangeRate: numberinverseRate: numberfee: Money | number?feeType: FeeType(PERCENTAGE, FIXED)minAmount: Money | number?maxAmount: Money | number?dailyLimit: Money | number?effectiveFrom: DateeffectiveUntil: Date?isActive: boolean
Exemplos:
- 1 BRL = 10 PADDLE_COINS
- 100 POINTS = 1 COURT_CREDIT
- 1 USD = 5.20 BRL
Relacionamentos:
- Pertence a
Arena - Converte entre
ValueUnit - Usada em
ConversionTransaction
Comportamentos:
convert(amount: Money | number): Money | numbercalculateFee(amount: Money | number): Money | numberisAvailable(date: Date): boolean
12.8 ConversionRule
Descrição: Regra específica de conversão (alias para ExchangeRule com lógica adicional).
Atributos Adicionais:
requiresApproval: booleanautoConvert: boolean(conversão automática)conversionSchedule: Schedule?(ex: converter pontos em créditos mensalmente)
12.9 ExpirationRule
Descrição: Regra de expiração de valores.
Atributos:
id: RuleIdarenaId: ArenaIdname: stringvalueUnit: ValueUnitexpirationType: ExpirationType(FIXED_DATE, DURATION_FROM_ACQUISITION, ROLLING_WINDOW, NEVER)duration: Duration?(se DURATION_FROM_ACQUISITION)fixedDate: Date?(se FIXED_DATE)rollingWindowSize: Duration?(se ROLLING_WINDOW)warningBefore: Duration(avisar antes de expirar)action: ExpirationAction(EXPIRE, CONVERT, EXTEND)convertTo: ValueUnit?(se action = CONVERT)conversionRate: number?isActive: boolean
Exemplos:
- Créditos expiram em 90 dias após compra
- Pontos expiram em 31/12 de cada ano
- Tokens nunca expiram
Relacionamentos:
- Aplicada a
ValueUnit - Pode gerar
WalletTransaction(expiration)
Comportamentos:
isExpired(acquisitionDate: Date, now: Date): booleangetExpirationDate(acquisitionDate: Date): Dateapply(wallet: WalletId, amount: Money | number): void
12.10 RewardRule (Aggregate Root)
Descrição: Regra de recompensa.
Atributos:
id: RuleIdarenaId: ArenaIdname: stringdescription: string?trigger: RewardTrigger(MATCH_COMPLETION, MATCH_WIN, REFERRAL, MILESTONE, PURCHASE, CHECK_IN_STREAK)conditions: RewardCondition[]rewardType: RewardType(FIXED, PERCENTAGE, TIERED, MULTIPLIER)valueUnit: ValueUnitamount: Money | number?percentage: number?tiers: RewardTier[]?maxPerUser: number?(limite por usuário)maxPerPeriod: number?(limite global)effectiveFrom: DateeffectiveUntil: Date?isActive: boolean
RewardTrigger (Value Object):
event: string(nome do evento que dispara)frequency: TriggerFrequency(ONCE, DAILY, WEEKLY, ALWAYS)
RewardCondition (Value Object):
field: string(ex: "match.type", "user.skillLevel")operator: Operator(EQUALS, GREATER_THAN, IN, NOT_IN)value: any
RewardTier (Value Object):
from: numberto: number?rewardAmount: Money | number
Relacionamentos:
- Pertence a
Arena - Aplicada a
ValueUnit - Gera
RewardTransaction
Exemplos:
- Ganhe 10 tokens ao completar uma partida
- Ganhe 5% de cashback em compras acima de R$100
- Convide um amigo e ganhe 50 pontos
Comportamentos:
evaluate(context: RewardContext): RewardResultcalculateReward(context: RewardContext): Money | numbercanApply(user: UserId, context: RewardContext): boolean
12.11 BetRule
Descrição: Regra para apostas em partidas.
Atributos:
id: RuleIdarenaId: ArenaIdname: stringallowedMatchTypes: MatchType[]allowedValueUnits: ValueUnit[]minBetAmount: Money | numbermaxBetAmount: Money | numbermaxBetPerUser: Money | number?houseEdge: number(percentual da casa, ex: 5%)payoutRatio: number(proporção de pagamento, ex: 1.8)lockBeforeStart: Duration(bloquear apostas X minutos antes)requiresBalance: booleanisActive: boolean
Relacionamentos:
- Pertence a
Arena - Aplicada a
Match(Play Context) - Usa
ValueUnit
Comportamentos:
canBet(user: UserId, match: MatchId, amount: Money | number): booleancalculatePayout(betAmount: Money | number, outcome: MatchResult): Money | number
12.12 PrizeRule
Descrição: Regra de prêmios (para torneios, eventos).
Atributos:
id: RuleIdeventId: EventId?tournamentId: TournamentId?name: stringprizePool: Money | numbervalueUnit: ValueUnitdistribution: PrizeDistribution[]minimumParticipants: number?payoutTrigger: PayoutTrigger(AUTOMATIC, MANUAL, SCHEDULED)payoutDelay: Duration?
PrizeDistribution (Value Object):
position: number(1º, 2º, 3º lugar)share: Percentage?(% do prize pool)fixedAmount: Money | number?
Exemplos:
- 1º lugar: 50% do prize pool
- 2º lugar: 30%
- 3º lugar: 20%
Relacionamentos:
- Aplicada a
EventouTournament(Engagement Context) - Gera
WalletTransaction(prêmios)
Comportamentos:
calculatePrize(position: number): Money | numberdistributePrizes(results: TournamentResult[]): void
12.13 TransactionLedger (Aggregate Root)
Descrição: Livro-razão de transações (imutável, append-only).
Atributos:
id: LedgerIdarenaId: ArenaIdentries: LedgerEntry[]createdAt: Date
Relacionamentos:
- Pertence a
Arena - Contém múltiplos
LedgerEntry
Uso: Auditoria financeira, reconciliação, compliance
12.14 LedgerEntry
Descrição: Entrada no livro-razão (dupla entrada contábil).
Atributos:
id: EntryIdledgerId: LedgerIdtransactionId: TransactionIdentryNumber: number(sequencial)timestamp: DatedebitAccount: AccountCodecreditAccount: AccountCodevalueUnit: ValueUnitamount: Money | numberdescription: stringmetadata: Record<string, any>
AccountCode (Value Object):
- Código contábil (ex: "USER_WALLET", "ARENA_REVENUE", "ESCROW", "PRIZE_POOL")
Relacionamentos:
- Pertence a
TransactionLedger - Referencia
WalletTransaction
Invariantes:
- Toda transação gera pelo menos 2 entradas (débito e crédito)
- Soma de débitos = soma de créditos
12.15 WalletPolicy
Descrição: Política de uso da carteira.
Atributos:
id: PolicyIdwalletId: WalletId?(null = política global)arenaId: ArenaIdlimits: WalletLimit[]restrictions: WalletRestriction[]isActive: boolean
WalletLimit (Value Object):
type: LimitType(DAILY_SPENDING, DAILY_WITHDRAWAL, TRANSACTION_SIZE, BALANCE_MAX)valueUnit: ValueUnitamount: Money | number
WalletRestriction (Value Object):
type: RestrictionType(ALLOWED_TRANSACTIONS, ALLOWED_VALUE_UNITS, REQUIRE_APPROVAL)parameters: Record<string, any>
Relacionamentos:
- Aplicada a
Wallet
Comportamentos:
canTransact(transaction: WalletTransaction): ValidationResulthasReachedLimit(limitType: LimitType, current: Money | number): boolean
12.16 WalletAuditTrail
Descrição: Trilha de auditoria da carteira.
Atributos:
id: AuditTrailIdwalletId: WalletIdentries: AuditEntry[]
AuditEntry (Value Object):
timestamp: Dateaction: AuditAction(CREATED, TRANSACTION, BALANCE_ADJUSTED, SUSPENDED, CLOSED)performedBy: UserId?details: stringbeforeState: WalletState?afterState: WalletState?
Relacionamentos:
- Pertence a
Wallet
Uso: Compliance, LGPD, troubleshooting
13. Engagement & Gamification Context
Objetivo
Gerenciar progressão de jogadores, avaliação de habilidades, conquistas, badges, leaderboards, rankings, torneios, desafios e recompensas.
Aggregates
13.1 PlayerProgress (Aggregate Root)
Descrição: Progressão e evolução do jogador.
Atributos:
id: ProgressIdplayerId: PlayerIdsport: SportcurrentLevel: numberexperiencePoints: number(XP)nextLevelThreshold: numbermatchesPlayed: numbermatchesWon: numbermatchesLost: numberwinRate: number(percentual)currentStreak: number(vitórias consecutivas)bestStreak: numbertotalPlayTime: DurationskillRatings: SkillRating[]lastUpdated: Date
SkillRating (Value Object):
skill: SkillType(SERVE, FOREHAND, BACKHAND, VOLLEY, OVERALL)rating: number(0-100)trend: Trend(IMPROVING, STABLE, DECLINING)
Relacionamentos:
- Pertence a
PlayerProfile(Identity Context) - Atualizado por
MatchParticipation(Play Context) - Gera
Achievementquando atinge marcos - Contribui para
Leaderboard
Comportamentos:
recordMatch(participation: MatchParticipation): voidaddExperience(points: number): voidlevelUp(): voidupdateSkillRating(skill: SkillType, newRating: number): voidcalculateWinRate(): number
13.2 SkillAssessment (Aggregate Root)
Descrição: Avaliação formal de habilidades.
Atributos:
id: AssessmentIdplayerId: PlayerIdsport: SportassessedBy: UserId(instrutor ou sistema)assessmentDate: DateassessmentType: AssessmentType(INITIAL, PERIODIC, TOURNAMENT, PROMOTION)skillScores: SkillScore[]overallScore: numbersuggestedLevel: SkillLevelpreviousLevel: SkillLevel?notes: string?videoEvidenceIds: MediaAssetId[]?
SkillScore (Value Object):
skill: SkillTypescore: number(0-10)observations: string?
Relacionamentos:
- Pertence a
PlayerProfile - Realizada por
InstructorProfileou sistema - Pode ter
VideoAssetcomo evidência
Comportamentos:
approve(): voidreject(reason: string): voidpromotePlayer(): void(se sugere nível superior)
13.3 Achievement (Aggregate Root)
Descrição: Conquista/marco alcançado.
Atributos:
id: AchievementIdarenaId: ArenaIdcode: string(único)name: stringdescription: stringcategory: AchievementCategory(MATCHES, SKILLS, SOCIAL, MILESTONES, SPECIAL)type: AchievementType(ONE_TIME, PROGRESSIVE, REPEATABLE)criteria: AchievementCriteriareward: AchievementReward?badgeId: BadgeId?iconUrl: string?rarity: Rarity(COMMON, UNCOMMON, RARE, EPIC, LEGENDARY)points: number(achievement points)isActive: boolean
AchievementCriteria (Value Object):
conditions: Condition[]threshold: number?
AchievementReward (Value Object):
type: RewardType(XP, TOKENS, CREDITS, BADGE)amount: numbervalueUnit: ValueUnit?
Exemplos de Achievements:
- "Primeira Vitória": Ganhe sua primeira partida
- "Maratonista": Jogue 100 partidas
- "Sem Erros": Vença uma partida sem erros não forçados
- "Social": Convide 5 amigos
Relacionamentos:
- Pertence a
Arena - Associada a
Badge - Concedida como
PlayerBadge - Pode gerar
RewardTransaction
Comportamentos:
evaluate(player: PlayerProgress): booleangrant(playerId: PlayerId): PlayerBadge
13.4 Badge
Descrição: Emblema/insígnia.
Atributos:
id: BadgeIdarenaId: ArenaIdcode: stringname: stringdescription: stringcategory: BadgeCategory(ACHIEVEMENT, RANK, EVENT, SPECIAL)imageUrl: stringrarity: RarityissuedCount: number(quantas vezes foi concedido)isActive: boolean
Relacionamentos:
- Pertence a
Arena - Associado a
Achievement - Concedido como
PlayerBadge
13.5 PlayerBadge
Descrição: Badge conquistado por jogador.
Atributos:
id: PlayerBadgeIdplayerId: PlayerIdbadgeId: BadgeIdachievementId: AchievementId?earnedAt: DateearnedFrom: string?(descrição do contexto)isDisplayed: boolean(exibir no perfil)displayOrder: number?
Relacionamentos:
- Pertence a
PlayerProfile - Referencia
Badge - Originado de
Achievement
13.6 Leaderboard (Aggregate Root)
Descrição: Ranking/tabela de líderes.
Atributos:
id: LeaderboardIdarenaId: ArenaIdname: stringdescription: string?sport: Sport?category: LeaderboardCategory(OVERALL, SKILL_LEVEL, AGE_GROUP, MONTHLY, WEEKLY, TOURNAMENT)metric: LeaderboardMetric(WINS, WIN_RATE, XP, POINTS, SKILL_RATING, MATCHES_PLAYED)scope: LeaderboardScope(GLOBAL, ARENA, CLUB, EVENT)period: DateRange?rankings: Ranking[]lastUpdated: DaterefreshInterval: Duration(ex: atualizar a cada 1 hora)isActive: boolean
Ranking (Value Object):
position: numberplayerId: PlayerIdscore: numbertrend: RankTrend(UP, DOWN, STABLE, NEW)previousPosition: number?
Relacionamentos:
- Pertence a
Arena - Classifica
PlayerProfile - Usa
RankingRuleSet
Comportamentos:
update(): voidgetRanking(playerId: PlayerId): Ranking?getTopN(n: number): Ranking[]calculateRankings(): Ranking[]
13.7 RankingRuleSet
Descrição: Regras para cálculo de ranking.
Atributos:
id: RuleSetIdleaderboardId: LeaderboardIdname: stringcomponents: RankingComponent[]tiebreakers: Tiebreaker[]
RankingComponent (Value Object):
metric: RankingMetric(WINS, POINTS, SKILL_RATING)weight: number(peso no cálculo)direction: Direction(HIGHER_IS_BETTER, LOWER_IS_BETTER)
Tiebreaker (Value Object):
order: numbermetric: RankingMetric
Relacionamentos:
- Pertence a
Leaderboard
Comportamentos:
calculate(player: PlayerProgress): numbercompare(player1: PlayerProgress, player2: PlayerProgress): number
13.8 Tournament (Aggregate Root)
Descrição: Torneio competitivo.
Atributos:
id: TournamentIdeventId: EventId?(se parte de evento)arenaId: ArenaIdname: stringdescription: stringsport: Sportformat: TournamentFormat(SINGLE_ELIMINATION, DOUBLE_ELIMINATION, ROUND_ROBIN, SWISS, LADDER)matchFormat: MatchFormatskillLevelRestriction: SkillLevel[]?ageGroupRestriction: AgeGroup[]?maxParticipants: numbercurrentParticipants: numberregistrationFee: Money?prizePool: Money?prizeRule: PrizeRuleId?startDate: DateendDate: Date?registrationDeadline: Datestatus: TournamentStatus(REGISTRATION, DRAW_PENDING, IN_PROGRESS, COMPLETED, CANCELLED)bracketId: TournamentBracketId?
Relacionamentos:
- Pode ser parte de
Event(Events Context) - Pertence a
Arena - Possui
TournamentBracket - Possui múltiplas
TournamentRegistration - Contém múltiplas
TournamentMatch - Pode ter
PrizeRule(Wallet Context)
Invariantes:
- Torneio iniciado não aceita novas inscrições
- Número de participantes deve respeitar formato
Comportamentos:
register(player: PlayerId): TournamentRegistrationgenerateBracket(): TournamentBracketstart(): voidadvance(match: TournamentMatchId): voidcomplete(): voiddistributePrizes(): void
13.9 TournamentBracket
Descrição: Chaveamento do torneio.
Atributos:
id: BracketIdtournamentId: TournamentIdformat: TournamentFormatrounds: BracketRound[]thirdPlaceMatch: booleangeneratedAt: Date
BracketRound (Value Object):
roundNumber: numberroundName: string(ex: "Final", "Semifinal", "Quartas")matches: TournamentMatchId[]
Relacionamentos:
- Pertence a
Tournament - Organiza
TournamentMatch
13.10 TournamentMatch
Descrição: Partida dentro de torneio (estende Match do Play Context).
Atributos Adicionais:
tournamentId: TournamentIdroundNumber: numbermatchNumber: numberbracket: BracketPosition(ex: "A1 vs A2")advancesToMatch: TournamentMatchId?(próxima partida)isBronzeMatch: boolean(disputa de 3º lugar)
Relacionamentos:
- Pertence a
Tournament - Herda de
Match(Play Context) - Posicionada em
TournamentBracket
Comportamentos:
advanceWinner(): void(avançar vencedor para próxima fase)
13.11 TournamentRegistration
Descrição: Inscrição em torneio.
Atributos:
id: RegistrationIdtournamentId: TournamentIdplayerId: PlayerId(ou teamId se torneio de duplas)partnerId: PlayerId?(para doubles)registeredAt: Datestatus: RegistrationStatus(PENDING, CONFIRMED, WITHDRAWN, DISQUALIFIED)seed: number?(cabeça de chave)paymentStatus: PaymentStatuswaiverSigned: booleanemergencyContact: EmergencyContact
Relacionamentos:
- Pertence a
Tournament - Referencia
PlayerProfile
Comportamentos:
confirm(): voidwithdraw(reason: string): voiddisqualify(reason: string): void
13.12 Challenge (Aggregate Root)
Descrição: Desafio entre jogadores.
Atributos:
id: ChallengeIdarenaId: ArenaIdchallengerId: PlayerIdchallengedId: PlayerIdsport: SportmatchFormat: MatchFormatstake: Money | number?(apostado)stakeValueUnit: ValueUnit?message: string?proposedDate: Date?expiresAt: Datestatus: ChallengeStatus(PENDING, ACCEPTED, DECLINED, EXPIRED, COMPLETED)matchId: MatchId?(se aceito e realizado)acceptedAt: Date?declinedAt: Date?completedAt: Date?
Relacionamentos:
- Entre dois
PlayerProfile - Pode gerar
Match(Play Context) - Pode ter
MatchStake(Play Context)
Comportamentos:
accept(): voiddecline(reason: string): voidexpire(): voidscheduleMatch(court: CourtId, time: Date): Matchcomplete(result: MatchResult): void
13.13 Reward
Descrição: Recompensa concedida (registro histórico).
Atributos:
id: RewardIdplayerId: PlayerIdrewardRuleId: RewardRuleIdtype: RewardTypeamount: Money | numbervalueUnit: ValueUnitreason: stringgrantedAt: DategrantedBy: string?(SYSTEM, ADMIN, EVENT)transactionId: TransactionId?
Relacionamentos:
- Concedida a
PlayerProfile - Originada de
RewardRule(Wallet Context) - Pode gerar
WalletTransaction
14. Sponsorship & Ads Context
Objetivo
Gerenciar patrocinadores, contratos de patrocínio, inventário de anúncios, campanhas, veiculação, impressões e relatórios de exposição.
Aggregates
14.1 Sponsor (Aggregate Root)
Descrição: Patrocinador/anunciante.
Atributos:
id: SponsorIdname: stringlegalName: stringtaxId: string(CNPJ/CPF)industry: Industrywebsite: string?logo: MediaAssetId?contactPerson: ContactPersonbillingAddress: Addressstatus: SponsorStatus(ACTIVE, INACTIVE, SUSPENDED)partnerSince: Date
ContactPerson (Value Object):
name: stringemail: EmailphoneNumber: PhoneNumberposition: string
Relacionamentos:
- Possui múltiplos
SponsorshipContract - Possui múltiplas
AdCampaign
14.2 SponsorshipContract (Aggregate Root)
Descrição: Contrato de patrocínio.
Atributos:
id: ContractIdsponsorId: SponsorIdarenaId: ArenaIdcontractNumber: stringtype: SponsorshipType(TITLE, PREMIUM, STANDARD, EVENT, COURT, TOURNAMENT)title: stringdescription: stringvalue: Moneycurrency: CurrencypaymentTerms: PaymentTermsstartDate: DateendDate: DaterenewalTerms: RenewalTerms?deliverables: Deliverable[]status: ContractStatus(DRAFT, ACTIVE, EXPIRED, CANCELLED, RENEWED)signedAt: Date?signedBy: string?documentUrl: string?
Deliverable (Value Object):
type: DeliverableType(LOGO_PLACEMENT, AD_SLOTS, COURT_NAMING, EVENT_SPONSORSHIP)quantity: number?description: stringdeadline: Date?
Relacionamentos:
- Pertence a
Sponsor - Aplicado a
Arena(ou Court, Event) - Define
AdInventory
Comportamentos:
activate(): voidrenew(newEndDate: Date, newValue: Money?): SponsorshipContractcancel(reason: string): voidaddDeliverable(deliverable: Deliverable): void
14.3 AdInventory (Aggregate Root)
Descrição: Inventário de espaços publicitários disponíveis.
Atributos:
id: InventoryIdarenaId: ArenaIdname: stringtype: AdInventoryType(COURT_SCREEN, SCOREBOARD, WALL_BANNER, DIGITAL_BOARD, VIDEO_PRE_ROLL, EMAIL_FOOTER)location: AdLocationcapacity: number(quantos anúncios simultâneos)dimensions: AdDimensions?(para banners físicos)screenResolution: Resolution?(para digitais)format: AdFormat(IMAGE, VIDEO, HTML, PHYSICAL)slots: AdSlot[]pricePerSlot: Moneystatus: InventoryStatus(AVAILABLE, SOLD_OUT, MAINTENANCE)
AdLocation (Value Object):
courtId: CourtId?areaId: AreaId?description: string
AdDimensions (Value Object):
width: numberheight: numberunit: Unit(PIXELS, METERS, INCHES)
Relacionamentos:
- Pertence a
Arena - Contém múltiplos
AdSlot - Pode estar vinculado a
Court,ArenaArea
14.4 AdSlot
Descrição: Slot de tempo/espaço para anúncio.
Atributos:
id: SlotIdinventoryId: InventoryIdtimeRange: TimeRange?(para slots temporais)dayOfWeek: DayOfWeek[]?status: SlotStatus(AVAILABLE, RESERVED, BOOKED, RUNNING, COMPLETED)campaignId: AdCampaignId?reservedUntil: Date?
Relacionamentos:
- Pertence a
AdInventory - Pode estar reservado para
AdCampaign
14.5 AdCampaign (Aggregate Root)
Descrição: Campanha publicitária.
Atributos:
id: CampaignIdsponsorId: SponsorIdarenaId: ArenaIdname: stringdescription: string?startDate: DateendDate: Datebudget: Moneyspent: MoneytargetAudience: TargetAudience?creatives: AdCreativeId[]placementRules: AdPlacementRule[]impressions: number(total de exibições)clicks: number(total de cliques)status: CampaignStatus(DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, CANCELLED)
TargetAudience (Value Object):
ageRange: AgeRange?gender: Gender[]?skillLevels: SkillLevel[]?sports: Sport[]?locations: ZoneId[]?
Relacionamentos:
- Pertence a
Sponsor - Aplicada a
Arena - Possui múltiplos
AdCreative - Possui múltiplas
AdPlacementRule - Reserva
AdSlot - Gera
AdImpression,AdClick
Comportamentos:
start(): voidpause(): voidresume(): voidcomplete(): voidaddCreative(creative: AdCreative): voidgetPerformance(): CampaignPerformance
14.6 AdCreative
Descrição: Peça criativa (banner, vídeo, etc).
Atributos:
id: CreativeIdcampaignId: AdCampaignIdname: stringtype: CreativeType(IMAGE, VIDEO, HTML5, TEXT)format: AdFormatmediaAssetId: MediaAssetId?dimensions: AdDimensionsduration: Duration?(para vídeos)clickUrl: string?altText: string?approvalStatus: ApprovalStatus(PENDING, APPROVED, REJECTED)rejectionReason: string?
Relacionamentos:
- Pertence a
AdCampaign - Referencia
MediaAsset
14.7 AdPlacementRule
Descrição: Regra de veiculação de anúncios.
Atributos:
id: RuleIdcampaignId: AdCampaignIdinventoryType: AdInventoryType[]locations: AdLocation[]?timeRanges: TimeRange[]?daysOfWeek: DayOfWeek[]?frequency: AdFrequencypriority: number(para resolução de conflitos)conditions: PlacementCondition[]?
AdFrequency (Value Object):
type: FrequencyType(CONTINUOUS, INTERVAL, EVENT_TRIGGERED)interval: Duration?(ex: a cada 10 minutos)maxPerDay: number?maxPerUser: number?
PlacementCondition (Value Object):
type: ConditionType(MATCH_IN_PROGRESS, SKILL_LEVEL, USER_PROFILE)parameters: Record<string, any>
Relacionamentos:
- Pertence a
AdCampaign - Aplicada a
AdInventory
14.8 AdImpression
Descrição: Registro de exibição de anúncio.
Atributos:
id: ImpressionIdcampaignId: AdCampaignIdcreativeId: AdCreativeIdinventoryId: InventoryIdslotId: SlotIduserId: UserId?(se identificado)timestamp: Dateduration: Duration?(tempo de exibição)wasViewed: boolean(completamente visualizado)deviceType: DeviceType?location: Location?
Relacionamentos:
- Pertence a
AdCampaign - Exibido em
AdInventory/AdSlot - Visto por
UserAccount(se identificado)
Uso: Métricas, faturamento, relatórios
14.9 AdClick
Descrição: Registro de clique em anúncio.
Atributos:
id: ClickIdimpressionId: ImpressionIdcampaignId: AdCampaignIdcreativeId: AdCreativeIduserId: UserId?timestamp: DateclickUrl: stringreferrer: string?ipAddress: string?
Relacionamentos:
- Originado de
AdImpression - Pertence a
AdCampaign
14.10 ExposureReport
Descrição: Relatório de exposição para patrocinador.
Atributos:
id: ReportIdsponsorId: SponsorIdcampaignId: AdCampaignId?contractId: ContractId?period: DateRangetotalImpressions: numbertotalClicks: numberuniqueUsers: numberclickThroughRate: number(CTR)costPerImpression: Money?costPerClick: Money?audienceBreakdown: AudienceBreakdowngeneratedAt: DategeneratedBy: UserIdreportUrl: string?(PDF)
AudienceBreakdown (Value Object):
byAge: Record<AgeGroup, number>byGender: Record<Gender, number>bySkillLevel: Record<SkillLevel, number>bySport: Record<Sport, number>
Relacionamentos:
- Pertence a
Sponsor - Baseado em
AdCampaignouSponsorshipContract - Agrega
AdImpressioneAdClick
15. Analytics & Intelligence Context
Objetivo
Gerenciar métricas, indicadores, relatórios operacionais e recomendações inteligentes.
Aggregates
15.1 Metric
Descrição: Métrica rastreada (abstração).
Atributos:
id: MetricIdarenaId: ArenaIddefinition: MetricDefinitioncurrentValue: numberunit: stringtimestamp: Datetags: Tag[]
Relacionamentos:
- Baseada em
MetricDefinition - Gera
MetricSnapshot(histórico)
15.2 MetricDefinition
Descrição: Definição de uma métrica.
Atributos:
id: DefinitionIdcode: string(ex: "court_occupancy_rate")name: stringdescription: stringcategory: MetricCategory(OPERATIONAL, FINANCIAL, CUSTOMER, ENGAGEMENT)unit: string(%, hours, BRL, count)aggregation: AggregationType(SUM, AVG, MIN, MAX, COUNT)calculationFormula: string?collectionFrequency: DurationisActive: boolean
Exemplos de Métricas:
court_occupancy_rate: Taxa de ocupação de quadras (%)revenue_per_court_hour: Receita por hora de quadra (BRL)no_show_rate: Taxa de no-show (%)customer_lifetime_value: Valor do tempo de vida do cliente (BRL)nps_score: Net Promoter Score
15.3 MetricSnapshot
Descrição: Snapshot de métrica em momento específico.
Atributos:
id: SnapshotIdmetricDefinitionId: DefinitionIdvalue: numbertimestamp: Dateperiod: DateRange?(período agregado)dimensions: Record<string, string>(ex: courtId, sport)
Relacionamentos:
- Pertence a
MetricDefinition
Uso: Séries temporais, dashboards, alertas
15.4 OccupancyReport
Descrição: Relatório de ocupação de quadras.
Atributos:
id: ReportIdarenaId: ArenaIdperiod: DateRangecourts: CourtOccupancy[]overallOccupancyRate: numberpeakHours: TimeRange[]lowDemandHours: TimeRange[]generatedAt: Date
CourtOccupancy (Value Object):
courtId: CourtIdcourtName: stringtotalHours: numberoccupiedHours: numberoccupancyRate: numberrevenue: Money
Relacionamentos:
- Pertence a
Arena - Analisa
Court(Courts Context) - Usa
Booking(Scheduling Context)
15.5 NoShowReport
Descrição: Relatório de no-shows.
Atributos:
id: ReportIdarenaId: ArenaIdperiod: DateRangetotalBookings: numbernoShows: numbernoShowRate: numberlostRevenue: MoneytopOffenders: NoShowOffender[]generatedAt: Date
NoShowOffender (Value Object):
userId: UserIduserName: stringnoShowCount: numberlastNoShowDate: Date
15.6 RevenueReport
Descrição: Relatório de receita.
Atributos:
id: ReportIdarenaId: ArenaIdperiod: DateRangetotalRevenue: MoneyrevenueByCategory: Record<ProductCategory, Money>revenueByPaymentMethod: Record<PaymentMethod, Money>topProducts: ProductRevenue[]averageTicket: MoneytransactionCount: numbergeneratedAt: Date
ProductRevenue (Value Object):
productId: ProductItemIdproductName: stringquantitySold: numberrevenue: Money
15.7 EventReport
Descrição: Relatório de evento.
Atributos:
id: ReportIdeventId: EventIdattendance: numbernoShows: numberrevenue: MoneyconsumptionBreakdown: Record<ProductCategory, Money>averageConsumptionPerPerson: Moneysatisfaction: number?(NPS ou rating)generatedAt: Date
15.8 DemandForecast
Descrição: Previsão de demanda.
Atributos:
id: ForecastIdarenaId: ArenaIdresourceType: ResourceType(COURT, INSTRUCTOR, AREA)resourceId: ResourceId?forecastPeriod: DateRangepredictions: DemandPrediction[]confidence: number(0-1)model: string(algoritmo usado)generatedAt: Date
DemandPrediction (Value Object):
date: DatetimeRange: TimeRangepredictedDemand: number(0-100%)confidence: number
Uso: Dynamic pricing, sugestão de horários, planejamento de staff
15.9 Recommendation
Descrição: Recomendação inteligente para usuário.
Atributos:
id: RecommendationIduserId: UserIdtype: RecommendationType(BOOKING_SLOT, OPPONENT, COURT, LESSON, PRODUCT)targetId: string(ID da entidade recomendada)targetType: stringscore: number(relevância 0-1)reasoning: string[](motivos da recomendação)expiresAt: Datestatus: RecommendationStatus(PENDING, VIEWED, ACCEPTED, DISMISSED)createdAt: Date
Exemplos:
- "Recomendamos reservar às 18h de terça (histórico de disponibilidade)"
- "Jogue com João (nível compatível, joga no mesmo horário)"
- "Experimente a Quadra 3 (menos disputada, boa iluminação)"
Relacionamentos:
- Destinada a
UserAccount - Pode recomendar
Court,PlayerProfile,ScheduleSlot, etc
16. Shared Kernel
Objetivo
Value Objects, tipos e conceitos compartilhados por múltiplos contextos.
Value Objects
16.1 Money
Descrição: Valor monetário com moeda.
Atributos:
amount: number(decimal)currency: Currency(BRL, USD, etc)
Comportamentos:
add(other: Money): Moneysubtract(other: Money): Moneymultiply(factor: number): Moneydivide(divisor: number): MoneyisZero(): booleanisPositive(): booleanequals(other: Money): booleanformat(): string(ex: "R$ 150,00")
Invariantes:
- Operações só são permitidas entre mesma moeda
- Precisão decimal conforme moeda (geralmente 2)
16.2 Address
Descrição: Endereço físico.
Atributos:
street: stringnumber: stringcomplement: string?neighborhood: stringcity: stringstate: stringcountry: stringpostalCode: string
Comportamentos:
format(): stringformatShort(): string
16.3 GeoLocation
Descrição: Coordenadas geográficas.
Atributos:
latitude: numberlongitude: numberaltitude: number?accuracy: number?(metros)
Comportamentos:
distanceTo(other: GeoLocation): number(em metros)isWithinRadius(center: GeoLocation, radius: number): boolean
16.4 TimeRange
Descrição: Intervalo de tempo.
Atributos:
start: Time(HH:mm)end: Time(HH:mm)
Comportamentos:
duration(): Durationcontains(time: Time): booleanoverlaps(other: TimeRange): booleanmerge(other: TimeRange): TimeRange?
16.5 DateRange
Descrição: Intervalo de datas.
Atributos:
start: Dateend: Date
Comportamentos:
duration(): Durationcontains(date: Date): booleanoverlaps(other: DateRange): booleansplit(interval: Duration): DateRange[]
16.6 LocalizedText
Descrição: Texto internacionalizado.
Atributos:
translations: Record<Locale, string>defaultLocale: Locale
Comportamentos:
get(locale: Locale): stringset(locale: Locale, text: string): void
16.7 Document
Descrição: Documento de identificação.
Atributos:
type: DocumentType(CPF, CNPJ, RG, PASSPORT, DRIVER_LICENSE)number: stringissuingAuthority: string?issuedDate: Date?expiryDate: Date?country: string
Comportamentos:
isValid(): booleanisExpired(): booleanformat(): string
16.8 Tag
Descrição: Tag/etiqueta.
Atributos:
key: stringvalue: string?category: string?
Comportamentos:
toString(): string(ex: "category:value" ou "key")
16.9 AuditTrail
Descrição: Trilha de auditoria.
Atributos:
createdAt: DatecreatedBy: UserIdupdatedAt: Date?updatedBy: UserId?deletedAt: Date?deletedBy: UserId?version: number
16.10 AuditEntry
Descrição: Entrada de auditoria.
Atributos:
timestamp: DateuserId: UserId?action: stringentityType: stringentityId: stringchanges: Record<string, ChangeSet>?metadata: Record<string, any>?
ChangeSet (Value Object):
field: stringoldValue: anynewValue: any
16.11 DomainEvent
Descrição: Evento de domínio (base abstrata).
Atributos:
eventId: string(UUID)eventType: string(nome do evento)occurredAt: DateaggregateId: stringaggregateType: stringversion: numbercausationId: string?(ID do comando que causou)correlationId: string?(ID de correlação para rastreamento)
Exemplos de Eventos:
UserAccountCreatedBookingConfirmedMatchCompletedPaymentProcessedAchievementUnlocked
16.12 Rule
Descrição: Regra de negócio (abstração).
Atributos:
id: RuleIdname: stringdescription: string?condition: Conditionaction: Actionpriority: numberisActive: boolean
Condition (Value Object):
expression: stringparameters: Record<string, any>
Action (Value Object):
type: ActionTypeparameters: Record<string, any>
16.13 RuleSet
Descrição: Conjunto de regras.
Atributos:
id: RuleSetIdname: stringrules: Rule[]evaluationMode: EvaluationMode(ALL, ANY, FIRST_MATCH, PRIORITY)
Comportamentos:
evaluate(context: any): RuleResult[]addRule(rule: Rule): voidremoveRule(ruleId: RuleId): void
16.14 Status
Descrição: Status genérico com transições.
Atributos:
current: stringprevious: string?changedAt: DatechangedBy: UserId?reason: string?
Comportamentos:
canTransitionTo(newStatus: string): booleantransitionTo(newStatus: string, reason: string?): void
16.15 Identifier
Descrição: Identificador único tipado.
Atributos:
value: string(UUID ou outro formato)
Comportamentos:
equals(other: Identifier): booleantoString(): string
Exemplos de Identificadores Tipados:
UserIdArenaIdCourtIdMatchIdBookingId
Conclusão
Este modelo de domínio representa a estrutura completa do Sport Tech Club, organizado em 15 Bounded Contexts seguindo os princípios de Domain-Driven Design.
Características Principais
- 200+ Entidades e Value Objects cobrindo todos os aspectos do negócio
- Separação clara de responsabilidades entre contextos
- Aggregates bem definidos com fronteiras de consistência
- Relacionamentos explícitos entre contextos via IDs
- Invariantes de negócio documentadas
- Comportamentos ricos nas entidades (não apenas CRUDs)
Próximos Passos
- Implementação Gradual: Começar pelos contextos core (Identity, Arena, Courts, Scheduling)
- Event Storming: Mapear eventos de domínio e fluxos de processo
- Context Mapping: Definir integrações entre contextos (ACL, Shared Kernel, etc)
- Ubiquitous Language: Manter glossário vivo e atualizado
- Testes de Domínio: TDD para garantir invariantes e comportamentos
Referências
- Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software
- Vernon, Vaughn. Implementing Domain-Driven Design
- Vernon, Vaughn. Domain-Driven Design Distilled
- Fowler, Martin. Patterns of Enterprise Application Architecture
Documento mantido por: Engenheiro (Arquiteto de Software Sênior)
Última atualização: 2026-01-09
Versão: 1.0