Value Class Identifier
Every record's id is a Kotlin inline value class wrapping a UUID string, making identifiers type-safe at compile time and free at runtime.
MfFactionId, MfPlayerId, MfLawId, MfGateId, MfLockedBlockId, MfDuelId, MfFactionRoleId, MfApprovalRequestId, MfFactionRelationshipId — all nine identifiers in the plugin are @JvmInline value class wrappers around a String, with no exceptions.
What it buys
At compile time these are distinct types: a function taking an MfFactionId cannot be handed an MfPlayerId, even though both are strings underneath. In a codebase where nearly every method signature takes at least one identifier, that is the difference between a compiler error and a bug that only appears when two UUIDs get swapped.
At runtime, Kotlin erases the wrapper. An MfFactionId in a local variable is a String — no allocation, no indirection. The safety is free.
Where the boundary lives
Each id class owns its own conversions, which keeps the awkward casts in one place:
MfFactionId.generate()produces a fresh random UUID string.MfPlayerId.fromBukkitPlayer()and.toBukkitPlayer()bridge to Bukkit'sOfflinePlayer, so the rest of the plugin can pass a player identity around without depending on the Bukkit API.
That second pair is the more interesting one: it means the domain model refers to players by a value it owns, not by a Bukkit object whose lifecycle the server controls.
The catch
Value classes and Java interoperation do not always agree, which is why domain classes are peppered with @get:JvmName("getId"). Without it, Kotlin mangles the getter name for a value-class-typed property, and Java callers — including expansions written in Java such as Fiefs — cannot see it. The annotation is the tax for keeping the public API usable from Java.
Related
The unwrapping to a plain column value happens in the Repository Pattern implementations, so the database sees only strings.