Home > Technical Reference > Data Models > Equipment


Equipment Data Models

Complete reference for all equipment-related entities in Archery Apprentice. These models represent physical archery gear, from individual components like risers and limbs to complete bow configurations.

Overview

Total Equipment Entities: 13 Location: shared:database module Package: com.archeryapprentice.database.entities.equipment

Entity Categories

CategoryEntitiesPurpose
Core Bow ComponentsRiser, Limbs, BowStringEssential bow structure
Sighting & AimingSight, SightMarkAiming equipment and settings
StabilizationStabilizer, WeightBalance and vibration control
Arrow Rest SystemPlunger, RestArrow support and tuning
ArrowsArrowComplete arrow specifications
AccessoriesAccessoryGeneric equipment items
ConfigurationBowSetup, BowSetupEquipmentComplete equipment configurations

Design Principles

1. Component-Based Architecture

  • Each component is a separate entity
  • BowSetup aggregates components into configurations
  • Reusable components across multiple setups

2. Versioning

  • BowSetup tracks version changes
  • Equipment changes create new versions
  • Historical accuracy for performance analysis

3. Soft Deletes

  • BowSetup uses isActive flag instead of hard delete
  • Preserves historical data integrity
  • Rounds can always reference original equipment

4. Flexible Relationships

  • One-to-many: Equipment → BowSetup
  • Optional foreign keys allow partial configurations
  • Junction table (BowSetupEquipment) for future flexibility

Core Bow Components

Riser Entity

The riser is the central body of the bow, to which limbs and accessories attach.

File: RiserEntity.kt Table: Riser

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"Hoyt", "Win&Win", "SF Archery"
modelStringYesModel name"Formula X", "Inno CXT", "Axiom+"
lengthString?NoRiser length"25 inches", "27 inches"
materialString?NoConstruction material"Carbon", "Aluminum", "Wood"

Usage Example

val riser = RiserEntity(
    brand = "Hoyt",
    model = "Formula X",
    length = "25 inches",
    material = "Carbon"
)

Relationships

Used In: BowSetup (via BowSetup.riserId) Cascade: ON DELETE SET NULL (BowSetup.riserId becomes null if Riser deleted)


Limbs Entity

Limbs are the flexible arms of the bow that store and release energy.

File: LimbsEntity.kt Table: Limbs

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"Hoyt", "Win&Win", "Border"
modelStringYesModel name"Formula X-Act", "Wiawis NS-G"
poundageString?NoDraw weight"38 lbs", "42 lbs", "36#"
limbLengthString?NoLimb size"Long", "Medium", "Short"

Usage Example

val limbs = LimbsEntity(
    brand = "Hoyt",
    model = "Formula X-Act",
    poundage = "38 lbs",
    limbLength = "Long"
)

Relationships

Used In: BowSetup (via BowSetup.limbsId) Cascade: ON DELETE SET NULL

Important: Changing limbs in a BowSetup creates a new version (equipment versioning).


BowString Entity

The bowstring connects the limbs and transfers energy to the arrow.

File: BowStringEntity.kt Table: BowString

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"BCY", "Brownell", "Angel"
modelStringYesString type/model"8125G", "X99", "Dyneema"
strandCountInt?NoNumber of strands16, 18, 20
servingMaterialString?NoCenter serving material"BCY 3D", "Halo"

Usage Example

val bowString = BowStringEntity(
    brand = "BCY",
    model = "8125G",
    strandCount = 18,
    servingMaterial = "BCY 3D"
)

Relationships

Used In: BowSetup (via BowSetup.bowStringId) Cascade: ON DELETE SET NULL


Sighting & Aiming

Sight Entity

The sight is the aiming device mounted on the riser.

File: SightEntity.kt Table: Sight

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"Shibuya", "Axcel", "Sure-Loc"
modelStringYesModel name"Ultima RC II", "Achieve XP"
materialString?NoConstruction material"Carbon", "Aluminum"

Usage Example

val sight = SightEntity(
    brand = "Shibuya",
    model = "Ultima RC II",
    material = "Carbon"
)

Relationships

Used In:

  • BowSetup (via BowSetup.sightId)
  • SightMark (via SightMark.sightId)

Cascade:

  • BowSetup: ON DELETE SET NULL
  • SightMark: ON DELETE CASCADE

SightMark Entity

SightMark stores distance-specific sight settings for a Sight.

File: SightMarkEntity.kt Table: SightMark

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
sightIdLongYesForeign key → Sight(id)5
distanceStringYesShooting distance"30 meters", "50 yards"
markValueStringYesSight setting"5.2", "7.8", "105 clicks"
notesString?NoAdditional notes"Windy conditions", "Indoor"

Usage Example

// Sight marks for 70m Olympic round
val sightMarks = listOf(
    SightMarkEntity(
        sightId = 5L,
        distance = "70 meters",
        markValue = "2.5",
        notes = "Outdoor, calm wind"
    ),
    SightMarkEntity(
        sightId = 5L,
        distance = "50 meters",
        markValue = "5.8",
        notes = "Outdoor"
    ),
    SightMarkEntity(
        sightId = 5L,
        distance = "30 meters",
        markValue = "9.2",
        notes = "Indoor/outdoor"
    )
)

Relationships

Parent: Sight (Many-to-One)

  • Foreign Key: SightMark.sightId → Sight.id
  • Cascade: ON DELETE CASCADE (deleting Sight deletes all its SightMarks)

Queries

// Get all sight marks for a sight
@Query("SELECT * FROM SightMark WHERE sightId = :sightId ORDER BY distance ASC")
suspend fun getSightMarksForSight(sightId: Long): List<SightMarkEntity>
 
// Find sight mark for specific distance
@Query("SELECT * FROM SightMark WHERE sightId = :sightId AND distance = :distance")
suspend fun getSightMarkForDistance(sightId: Long, distance: String): SightMarkEntity?

Stabilization

Stabilizer Entity

Stabilizers reduce vibration and balance the bow.

File: StabilizerEntity.kt Table: Stabilizer

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"Doinker", "Shrewd", "Axcel"
modelStringYesModel name"Platinum", "Atlas", "Achieve"
lengthString?NoStabilizer length"30 inches", "12 inches"
weightString?NoTotal weight"8 oz", "120 grams"
straightnessRatingString?NoStraightness tolerance"+/- 0.001", "0.002"

Usage Example

val stabilizer = StabilizerEntity(
    brand = "Doinker",
    model = "Platinum",
    length = "30 inches",
    weight = "8 oz",
    straightnessRating = "+/- 0.001"
)

Relationships

Used In: BowSetup (via BowSetup.stabilizerId) Cascade: ON DELETE SET NULL


Weight Entity

Additional balance weights attached to stabilizer or bow.

File: WeightEntity.kt Table: Weight

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"Shrewd", "Doinker", "Generic"
modelStringYesModel/type"1 oz weight", "3/4 oz", "2 oz donut"
ouncesDouble?NoWeight in ounces1.0, 0.75, 2.0

Usage Example

val weight = WeightEntity(
    brand = "Shrewd",
    model = "1 oz weight",
    ounces = 1.0
)

Relationships

Used In: BowSetup (via BowSetup.weightId) Cascade: ON DELETE SET NULL


Arrow Rest System

Plunger Entity

The plunger (pressure button) fine-tunes arrow flight.

File: PlungerEntity.kt Table: Plunger

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"Beiter", "Shibuya", "Avalon"
modelStringYesModel name"DX Plunger", "Dual Click"
adjustmentString?NoAdjustment type"Micro-adjust", "Click adjust"

Usage Example

val plunger = PlungerEntity(
    brand = "Beiter",
    model = "DX Plunger",
    adjustment = "Micro-adjust"
)

Relationships

Used In: BowSetup (via BowSetup.plungerId) Cascade: ON DELETE SET NULL


Rest Entity

The rest supports the arrow before release.

File: RestEntity.kt Table: Rest

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"Hoyt", "AAE", "Cavalier"
modelStringYesModel name"Super Rest", "Free Flyte"
typeString?NoRest type"Magnetic", "Flipper", "Blade"

Usage Example

val rest = RestEntity(
    brand = "Hoyt",
    model = "Super Rest",
    type = "Magnetic"
)

Relationships

Used In: BowSetup (via BowSetup.restId) Cascade: ON DELETE SET NULL


Arrows

Arrow Entity

Complete arrow specifications including point and nock.

File: ArrowEntity.kt Table: Arrow

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesArrow manufacturer"Easton", "Carbon Express", "Victory"
modelStringYesArrow model"X10", "Protour", "VAP"
spineString?NoArrow stiffness"500", "600", "350"
lengthString?NoShaft length"29 inches", "30.5 inches"
weightString?NoTotal arrow weight"420 grains", "380 gr"
diameterString?NoShaft diameter"5.5mm", "4mm"
arrowPointArrowPoint?NoEmbedded point dataSee ArrowPoint structure
arrowNockArrowNock?NoEmbedded nock dataSee ArrowNock structure

Embedded Objects

ArrowPoint:

data class ArrowPoint(
    val brand: String,      // e.g., "Easton"
    val model: String,      // e.g., "X10 Point"
    val weight: String      // e.g., "120 grains"
)

ArrowNock:

data class ArrowNock(
    val brand: String,      // e.g., "Easton"
    val model: String,      // e.g., "G Nock"
    val size: String?       // e.g., "Large", "Small"
)

Usage Example

val arrow = ArrowEntity(
    brand = "Easton",
    model = "X10",
    spine = "500",
    length = "29.5 inches",
    weight = "420 grains",
    diameter = "5.5mm",
    arrowPoint = ArrowPoint(
        brand = "Easton",
        model = "X10 Point",
        weight = "120 grains"
    ),
    arrowNock = ArrowNock(
        brand = "Easton",
        model = "G Nock",
        size = "Large"
    )
)

Type Converters

@TypeConverter
fun fromArrowPoint(point: ArrowPoint?): String? =
    point?.let { Json.encodeToString(it) }
 
@TypeConverter
fun toArrowPoint(value: String?): ArrowPoint? =
    value?.let { Json.decodeFromString(it) }
 
@TypeConverter
fun fromArrowNock(nock: ArrowNock?): String? =
    nock?.let { Json.encodeToString(it) }
 
@TypeConverter
fun toArrowNock(value: String?): ArrowNock? =
    value?.let { Json.decodeFromString(it) }

Relationships

Used In: BowSetup (via BowSetup.arrowId) Cascade: ON DELETE SET NULL


Accessories

Accessory Entity

Generic equipment items (tabs, chest guards, quivers, etc.)

File: AccessoryEntity.kt Table: Accessory

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
brandStringYesManufacturer"AAE", "Beiter", "Easton"
modelStringYesModel/type"Elite Tab", "Chest Guard", "Hip Quiver"
typeString?NoAccessory category"Tab", "Chest Guard", "Quiver", "Other"

Usage Example

val accessory = AccessoryEntity(
    brand = "AAE",
    model = "Elite Tab",
    type = "Tab"
)

Relationships

Used In: BowSetup (via BowSetup.accessoryId) Cascade: ON DELETE SET NULL


Bow Configuration

BowSetup Entity

The BowSetup entity represents a complete equipment configuration, aggregating all components.

File: BowSetupEntity.kt Table: BowSetup Total Fields: 15

Fields

FieldTypeRequiredDescriptionExample
idLongYesPrimary key, auto-increment1, 2, 3
nameStringYesSetup name"Competition Setup", "Practice Bow"
descriptionString?NoSetup notes"38# limbs, 30\" stabilizer"
versionIntYesVersion number (auto-increment)1, 2, 3
isActiveBooleanYesSoft delete flagtrue (active), false (deleted)
riserIdLong?NoForeign key → Riser(id)1
limbsIdLong?NoForeign key → Limbs(id)2
sightIdLong?NoForeign key → Sight(id)3
stabilizerIdLong?NoForeign key → Stabilizer(id)4
plungerIdLong?NoForeign key → Plunger(id)5
restIdLong?NoForeign key → Rest(id)6
bowStringIdLong?NoForeign key → BowString(id)7
arrowIdLong?NoForeign key → Arrow(id)8
weightIdLong?NoForeign key → Weight(id)9
accessoryIdLong?NoForeign key → Accessory(id)10

Versioning Behavior

Automatic Version Increment:

  • Version starts at 1 when created
  • Any equipment change increments version
  • Changing limbs: version 1 → 2
  • Changing stabilizer: version 2 → 3
  • Rounds reference exact version for historical accuracy

Example:

// Initial setup (version 1)
val setup = BowSetupEntity(
    name = "My Setup",
    version = 1,
    isActive = true,
    riserId = 1L,
    limbsId = 2L
)
 
// Change limbs (version 2)
val updatedSetup = setup.copy(
    version = 2,
    limbsId = 3L  // New limbs
)
 
// Round created with version 2 always references that configuration

Soft Delete Behavior

Why Soft Delete?

  • Preserve historical data integrity
  • Rounds can always reference original equipment
  • Equipment performance history maintained

How It Works:

// "Delete" a setup (soft delete)
bowSetupDao.updateActive(setupId, isActive = false)
 
// Setup still exists in database, but isActive = false
// Won't appear in active equipment lists
// Historical rounds still reference it

Usage Example

val bowSetup = BowSetupEntity(
    name = "Competition Setup 2025",
    description = "38# Hoyt Formula X, 30\" Doinker stab",
    version = 1,
    isActive = true,
    riserId = 1L,      // Hoyt Formula X riser
    limbsId = 2L,      // Hoyt 38# limbs
    sightId = 3L,      // Shibuya Ultima sight
    stabilizerId = 4L, // Doinker 30" stabilizer
    plungerId = 5L,    // Beiter plunger
    restId = 6L,       // Hoyt Super Rest
    bowStringId = 7L,  // BCY 8125G string
    arrowId = 8L,      // Easton X10 arrows
    weightId = 9L,     // 1oz weights
    accessoryId = 10L  // AAE Elite Tab
)

Relationships

Children: All equipment entities

  • Foreign keys to 10 equipment types
  • All cascade ON DELETE SET NULL
  • Partial configurations allowed (nullable foreign keys)

Used By: Round (via Round.bowSetupId and Round.bowSetupVersion)

Queries

// Get active bow setups
@Query("SELECT * FROM BowSetup WHERE isActive = 1 ORDER BY name ASC")
suspend fun getActiveSetups(): List<BowSetupEntity>
 
// Get all versions of a setup
@Query("SELECT * FROM BowSetup WHERE name = :name ORDER BY version ASC")
suspend fun getSetupVersions(name: String): List<BowSetupEntity>
 
// Get setup with all equipment (requires JOINs)
@Query("""
    SELECT BowSetup.*, Riser.*, Limbs.*, Sight.*
    FROM BowSetup
    LEFT JOIN Riser ON BowSetup.riserId = Riser.id
    LEFT JOIN Limbs ON BowSetup.limbsId = Limbs.id
    LEFT JOIN Sight ON BowSetup.sightId = Sight.id
    WHERE BowSetup.id = :setupId
""")
suspend fun getSetupWithEquipment(setupId: Long): BowSetupWithEquipment

BowSetupEquipment Entity

Junction table for flexible setup-equipment relationships (future use).

File: BowSetupEquipmentEntity.kt Table: BowSetupEquipment

Fields

FieldTypeDescription
setupIdLongForeign key → BowSetup(id)
equipmentIdLongGeneric equipment ID
equipmentTypeStringType of equipment (e.g., “RISER”, “LIMBS”)

Purpose

Currently not actively used in favor of direct foreign keys in BowSetup. Preserved for potential future features:

  • Multiple stabilizers
  • Multiple accessories
  • Dynamic equipment combinations

Equipment Lifecycle

Creating Equipment

// 1. Create individual components
val riser = riserDao.insert(RiserEntity(brand = "Hoyt", model = "Formula X", ...))
val limbs = limbsDao.insert(LimbsEntity(brand = "Hoyt", model = "X-Act", ...))
val sight = sightDao.insert(SightEntity(brand = "Shibuya", model = "Ultima", ...))
 
// 2. Create bow setup
val setup = bowSetupDao.insert(BowSetupEntity(
    name = "My Setup",
    version = 1,
    isActive = true,
    riserId = riser,
    limbsId = limbs,
    sightId = sight
))

Updating Equipment

// Changing equipment creates new version
val currentSetup = bowSetupDao.getById(setupId)
val newVersion = currentSetup.version + 1
 
bowSetupDao.update(currentSetup.copy(
    version = newVersion,
    limbsId = newLimbsId  // Changed limbs
))
 
// Old rounds still reference old version
// New rounds will use new version

Deleting Equipment

// Soft delete (preferred)
bowSetupDao.updateActive(setupId, isActive = false)
// Setup hidden from active lists but preserved
 
// Hard delete component (if not in any setup)
riserDao.delete(riserId)
// Only safe if no BowSetup references it
// Otherwise, BowSetup.riserId will be set to NULL (ON DELETE SET NULL)

Equipment Performance Tracking

Linking Equipment to Rounds

// When creating a round
val round = RoundEntity(
    roundName = "Practice",
    bowSetupId = currentSetup.id,
    bowSetupVersion = currentSetup.version,
    ...
)

Querying Performance by Equipment

// Get all completed rounds for a setup
@Query("""
    SELECT * FROM Round
    WHERE bowSetupId = :setupId
    AND status = 'COMPLETED'
    ORDER BY completedAt DESC
""")
suspend fun getRoundsForSetup(setupId: Long): List<RoundEntity>
 
// Calculate average score for setup
@Query("""
    SELECT AVG(totalScore) as avgScore
    FROM Round
    WHERE bowSetupId = :setupId
    AND status = 'COMPLETED'
""")
suspend fun getAverageScoreForSetup(setupId: Long): Double

Equipment Version Comparison

// Compare performance across versions
@Query("""
    SELECT version, AVG(totalScore) as avgScore, COUNT(*) as roundCount
    FROM Round
    WHERE bowSetupId = :setupId
    AND status = 'COMPLETED'
    GROUP BY bowSetupVersion
    ORDER BY version ASC
""")
suspend fun getPerformanceByVersion(setupId: Long): List<VersionPerformance>

Multi-Participant Guest Setups

Auto-Generated Guest Setups

When creating multi-participant rounds, temporary setups are auto-generated for guests:

// Round with 2 guests
val round = RoundEntity(
    participants = listOf(
        SessionParticipant("user123", "Chris", isGuest = false, bowSetupId = 5L),
        SessionParticipant("guest1", "Sarah", isGuest = true, bowSetupId = 100L),  // Auto-generated
        SessionParticipant("guest2", "Mike", isGuest = true, bowSetupId = 101L)   // Auto-generated
    )
)
 
// Guest setups created automatically
val guestSetup1 = BowSetupEntity(
    name = "Guest - Sarah (Temp)",
    version = 1,
    isActive = false,  // Not shown in active lists
    riserId = null,    // Minimal data
    limbsId = null
)

Characteristics:

  • Name includes “Guest - {name} (Temp)”
  • isActive = false (hidden from main equipment lists)
  • Minimal equipment data
  • Preserved for historical accuracy
  • Don’t clutter user’s equipment management

Type Converters

Equipment entities use type converters for embedded objects:

// ArrowPoint converter
@TypeConverter
fun fromArrowPoint(point: ArrowPoint?): String? =
    point?.let { Json.encodeToString(it) }
 
@TypeConverter
fun toArrowPoint(value: String?): ArrowPoint? =
    value?.let { Json.decodeFromString(it) }
 
// ArrowNock converter
@TypeConverter
fun fromArrowNock(nock: ArrowNock?): String? =
    nock?.let { Json.encodeToString(it) }
 
@TypeConverter
fun toArrowNock(value: String?): ArrowNock? =
    value?.let { Json.decodeFromString(it) }

Indexes

Strategic indexes for performance:

-- BowSetup indexes
CREATE INDEX idx_bow_setup_active ON BowSetup(isActive);
CREATE INDEX idx_bow_setup_version ON BowSetup(version);
 
-- Foreign key indexes (for JOINs)
CREATE INDEX idx_bow_setup_riser_id ON BowSetup(riserId);
CREATE INDEX idx_bow_setup_limbs_id ON BowSetup(limbsId);
CREATE INDEX idx_bow_setup_sight_id ON BowSetup(sightId);
-- (and other foreign keys)
 
-- SightMark indexes
CREATE INDEX idx_sight_mark_sight_id ON SightMark(sightId);

Scoring:

  • Round - References BowSetup via bowSetupId
  • ArrowScore - Tracks equipment per arrow

Analytics:


Developer Guidelines

Best Practices

1. Always Create Components First

// Good: Create components, then setup
val riserId = riserDao.insert(riser)
val limbsId = limbsDao.insert(limbs)
val setup = BowSetupEntity(riserId = riserId, limbsId = limbsId, ...)
 
// Bad: Reference non-existent components
val setup = BowSetupEntity(riserId = 999L, ...)  // FK constraint violation

2. Use Versioning for Equipment Changes

// Good: Increment version on change
val newVersion = currentSetup.version + 1
val updated = currentSetup.copy(version = newVersion, limbsId = newLimbsId)
 
// Bad: Change equipment without versioning
val updated = currentSetup.copy(limbsId = newLimbsId)  // Version unchanged

3. Soft Delete BowSetups

// Good: Soft delete
bowSetupDao.updateActive(setupId, false)
 
// Bad: Hard delete
bowSetupDao.delete(setupId)  // Breaks historical round references

4. Handle Nullable Foreign Keys

// Good: Check nullability
val riser = setup.riserId?.let { riserDao.getById(it) }
 
// Bad: Assume non-null
val riser = riserDao.getById(setup.riserId)  // NPE if null

Common Queries

Get Complete Setup with Equipment:

data class CompleteSetup(
    val setup: BowSetupEntity,
    val riser: RiserEntity?,
    val limbs: LimbsEntity?,
    val sight: SightEntity?,
    // ... other components
)
 
@Transaction
@Query("SELECT * FROM BowSetup WHERE id = :setupId")
suspend fun getCompleteSetup(setupId: Long): CompleteSetup

Find Setups Using Specific Component:

@Query("SELECT * FROM BowSetup WHERE riserId = :riserId AND isActive = 1")
suspend fun getSetupsUsingRiser(riserId: Long): List<BowSetupEntity>

Database:

Other Data Models:

User Guides:


Quick Reference

Equipment Entity Summary

EntityRequired FieldsOptional FieldsUsed In
Riserbrand, modellength, materialBowSetup
Limbsbrand, modelpoundage, limbLengthBowSetup
BowStringbrand, modelstrandCount, servingMaterialBowSetup
Sightbrand, modelmaterialBowSetup, SightMark
SightMarksightId, distance, markValuenotes-
Stabilizerbrand, modellength, weight, straightnessRatingBowSetup
Weightbrand, modelouncesBowSetup
Plungerbrand, modeladjustmentBowSetup
Restbrand, modeltypeBowSetup
Arrowbrand, modelspine, length, weight, diameter, arrowPoint, arrowNockBowSetup
Accessorybrand, modeltypeBowSetup
BowSetupname, version, isActivedescription, all equipment IDsRound

BowSetup Versioning

When Version Increments:

  • Any equipment field changes (riserId, limbsId, etc.)
  • Description or name changes do NOT increment version

Why Versioning Matters:

  • Rounds reference exact configuration at time of shooting
  • Compare performance before/after equipment changes
  • Historical accuracy for analytics

Soft Delete Workflow

Active Setup (isActive = true)
  ↓
User "deletes" setup
  ↓
Soft Delete (isActive = false)
  ↓
Hidden from active lists
  ↓
Still referenced by historical rounds
  ↓
Can be queried if needed

Next: Tournament Data Models - Tournament and participant entities

0 items under this folder.