
How often do you get into this situation?
fun Document.isAccessible(tenantId: Int, userId: Int): Boolean
if (doc.isAccessible(userId, tenantId)) ... // Error or security breachI hope – never – because you always add parameter names to function calls and also have 100% test coverage.
Still, maintaining hundreds of indistinguishable val id: Int properties is frustrating and inevitably leads to mistakes.
There’s a nice trick to solve this problem once and for all.
@JvmInline // Required for JVM world
value class TenantId(val value: Int)Now you have a custom type for your Int id with almost no overhead. (“Almost” because Kotlin still might box value class in case if it’s used as a type parameter or a nullable value)
Now the dangerous code above will just fail to compile:
if (doc.isAccessible(userId, tenantId)) ...
// Argument type mismatch: actual type is 'UserId', but 'TenantId' was expected.To make it easier to convert Ints to custom classes use helpers like:
fun Int.toTenantId() = TenantId(this)It appeared to be so helpful that now I also use custom value classes for UUIDs and Strings:
@JvmInline
value class DocumentReference(val value: String)
data class Document(
val ref: DocReference,
...
)
@JvmInline
value class ExternalResourceId(val value: UUID)
data class ExternalResource(
val id: ExternalResourceId,
...
)You can go further and make your data framework understand this custom types. For example jOOQ:
class TenantIdConverter : AbstractConverter<Int, TenantId>(Int::class.java, TenantId::class.java) {
override fun from(v: Int?): TenantId? = v?.toTenantId()
override fun to(tenantId: TenantId?): Int? = tenantId?.value
}
// Then in jooq configuration
forcedType {
includeExpression = "tenant\\.id"
userType = "org.example.TenantId"
converter = "org.example.TenantIdConverter"
}
forcedType {
includeExpression = ".*\\.tenant_id"
userType = "org.example.TenantId"
converter = "org.example.TenantIdConverter"
}Now you could save and fetch your TenantId directly.
Moving away from primitives is a big step towards Domain-Driven Design and Hexagonal Architecture which in my opinion is essential for any enterprise project!