@startuml team-class-diagram title CR-Core — Team domain (class diagram) skinparam classAttributeIconSize 0 hide empty members ' === Common abstractions === package "fr.luc.crcore.util.common" { interface Identifiable { + getId(): UUID } interface Named { + getName(): String } interface ScoreHolder { + getScore(name): int + hasScore(name): boolean + getScores(): Map + getTotalScore(): int + addScore(name, delta): int + setScore(name, value): int + resetScore(name): boolean + resetAllScores(): void } abstract class AbstractEntity { - id: UUID + AbstractEntity(id: UUID) + getId(): UUID + equals(o: Object): boolean + hashCode(): int } interface "Repository" as Repository { + save(entity: T): T + findById(id: UUID): Optional + findAll(): Collection + delete(id: UUID): boolean } AbstractEntity ..|> Identifiable } ' === Team domain === package "fr.luc.crcore.features.team" { enum TeamRole { LEADER MEMBER -- + isLeader(): boolean } enum TeamVisibility { PUBLIC PRIVATE -- + isPublic(): boolean + isPrivate(): boolean } enum TeamColor { RED BLUE GREEN YELLOW AQUA LIGHT_PURPLE GOLD WHITE BLACK DARK_BLUE DARK_GREEN DARK_AQUA DARK_RED DARK_PURPLE DARK_GRAY GRAY -- + getChatColor(): ChatColor + getDyeColor(): DyeColor + getDisplayName(): String } class TeamMember { - role: TeamRole - joinedAt: Instant + getPlayerId(): UUID + getRole(): TeamRole + getJoinedAt(): Instant + isLeader(): boolean + withRole(role: TeamRole): TeamMember } class Team { - name: String - tag: String - color: TeamColor - leaderId: UUID *(nullable)* - visibility: TeamVisibility - members: Set - scores: Map - spawnPoint: Location -- + Team(id, name, tag, color) ' leaderless, PRIVATE + Team(id, name, tag, color, visibility) ' leaderless + Team(id, name, tag, color, leaderId) ' with leader, PRIVATE + Team(id, name, tag, color, leaderId, visibility) -- + getName(): String + getTag(): String + getColor(): TeamColor + getLeaderId(): Optional + getLeader(): Optional + hasLeader(): boolean + isLeader(playerId): boolean + getVisibility(): TeamVisibility + setVisibility(v): void + isPublic(): boolean + getMembers(): Set + getMember(playerId): Optional + hasMember(playerId): boolean + size(): int + addMember(playerId): TeamMember + removeMember(playerId): boolean + transferLeadership(newLeaderId): void ' strict: chef→chef + setLeader(newLeaderId): void ' permissive: assign -- + getScore(name): int + hasScore(name): boolean + getScores(): Map + getTotalScore(): int + addScore(name, delta): int + setScore(name, value): int + resetScore(name): boolean + resetAllScores(): void -- + getSpawnPoint(): Optional + hasSpawnPoint(): boolean + setSpawnPoint(loc): void + clearSpawnPoint(): void -- # newMember(playerId, role): TeamMember } class TeamRanking <> { + rank: int + team: Team + score: int } interface TeamRepository { + findByName(name: String): Optional + findByTag(tag: String): Optional + findByMember(playerId: UUID): Optional } class InMemoryTeamRepository { - teams: Map } interface TeamService { + createTeam(name, tag, color): Team ' leaderless, PRIVATE + createTeam(name, tag, color, visibility): Team ' leaderless + createTeam(name, tag, color, leaderId): Team + createTeam(name, tag, color, leaderId, visibility): Team + dissolveTeam(teamId): boolean + addMember(teamId, playerId): boolean + removeMember(teamId, playerId): boolean + joinTeam(teamId, playerId): boolean + transferLeadership(teamId, newLeaderId): boolean ' strict: chef→chef + setLeader(teamId, newLeaderId): boolean ' permissive (admin) + setVisibility(teamId, visibility): void -- + addScore(teamId, name, delta): int + setScore(teamId, name, value): int + getScore(teamId, name): int + resetScore(teamId, name): boolean + resetAllScores(teamId): void -- + getRankingByScore(name): List + getGlobalRanking(): List + getTopRankingByScore(name, limit): List + getTopGlobalRanking(limit): List -- + setSpawnPoint(teamId, loc): void + clearSpawnPoint(teamId): void + getSpawnPoint(teamId): Optional -- + getTeam / getTeamByName / getTeamByTag / getTeamOfPlayer + getAllTeams(): Collection } class TeamServiceImpl { - repository: TeamRepository -- # newTeam(...): Team # newRanking(rank, team, score): TeamRanking # rank(scoreFn): List -- # validateName(name): void # validateTag(tag): void # validateLeader(leaderId): void # validateJoinable(team, playerId): void -- # onBeforeSave(team): void # onAfterCreate(team): void # onBeforeDissolve(team): void # onAfterDissolve(team): void # onMemberAdded(team, member): void # onMemberRemoved(team, playerId): void # onPlayerJoined(team, member): void # onLeadershipTransferred(team, oldId, newId): void # onVisibilityChanged(team, oldV, newV): void # onScoreChanged(team, name, oldV, newV): void # onSpawnPointChanged(team, oldLoc, newLoc): void } class TeamException class TeamAlreadyExistsException class TeamNotFoundException class TeamAccessException TeamMember --|> AbstractEntity Team --|> AbstractEntity Team ..|> Named Team ..|> ScoreHolder TeamRepository --|> Repository InMemoryTeamRepository ..|> TeamRepository TeamServiceImpl ..|> TeamService Team "1" o-- "1..*" TeamMember : members Team --> TeamColor : color Team --> TeamVisibility : visibility TeamMember --> TeamRole : role TeamRanking --> Team TeamServiceImpl o--> TeamRepository TeamService ..> TeamRanking : produces TeamException --|> RuntimeException TeamAlreadyExistsException --|> TeamException TeamNotFoundException --|> TeamException TeamAccessException --|> TeamException } @enduml