ant2009
ant2009

Reputation: 22486

Best practice for handling null types in classes when mapping classes together

I am using Kotlin 1.30. I have the following entity classes that will be populated from the API. And some of the properties could be null from the API so I have declared them using the safe null type.

However, I will map this entity class to my domain model class. And just wondering what is the best way to handle the null types?

I have 2 ideas on how to do this when I map the classes.

  1. declare all the equivalent properties as safe null types
  2. use the elivs operator to return either a empty string or a emptylist

In the following snippet I am using the elvis operator. Just wondering what is the best practice for this?

class LoginResponseDomainMapperImp : LoginResponseDomainMapper {
    override fun map(entity: LoginResponseEntity): LoginResponse {
        return LoginResponse(
            entity.token ?: "",
            mapUsers(entity.user),
            mapEnterprises(entity.enterprises ?: emptyList()),
            mapVendors(entity.vendors ?: emptyList()))
    }

    private fun mapUsers(userEntity: UserEntity?): User {
        return User(
            userEntity?.id,
            userEntity?.email,
            userEntity?.firstName,
            userEntity?.lastName,
            userEntity?.phone,
            userEntity?.address,
            userEntity?.dob,
            userEntity?.customer,
            userEntity?.enterpriseIds ?: emptyList(),
            userEntity?.vendorIds ?: emptyList())
    }

    private fun mapEnterprises(enterprisesEntity: List<EnterprisesEntity>): List<Enterprises> {
        val enterpriseList = mutableListOf<Enterprises>()

        enterprisesEntity.forEach {
            val enterprise = Enterprises(
                it.id,
                it.enterpriseName,
                it.typeLabel,
                it.country,
                it.imageId,
                it.managers,
                it.members,
                it.stripe,
                it.locations)

            enterpriseList.add(enterprise)
        }

        return enterpriseList.toList()
    }

    private fun mapVendors(vendorsEntity: List<VendorsEntity>): List<Vendors> {
        val vendorList = mutableListOf<Vendors>()

        vendorsEntity.forEach {
            val vendor = Vendors(
                it.id,
                it.vendorName,
                it.typeLabel,
                it.userRole,
                it.latitude,
                it.longitude,
                it.partner,
                it.country,
                it.imageId,
                it.stripe)

            vendorList.add(vendor)
        }

        return vendorList.toList()
    }
}

Entity class that will populate from the API, so any of these could be null

data class LoginResponseEntity(
    @SerializedName("token") val token: String?,
    @SerializedName("user") val user: UserEntity?,
    @SerializedName("enterprises") val enterprises: List<EnterprisesEntity>?,
    @SerializedName("vendors") val vendors: List<VendorsEntity>?)

data class UserEntity(
    @SerializedName("id") val id: String?,
    @SerializedName("email") val email: String?,
    @SerializedName("first_name") val firstName: String?,
    @SerializedName("last_name") val lastName: String?,
    @SerializedName("phone") val phone: String?,
    @SerializedName("address") val address: String?,
    @SerializedName("dob") val dob: String?,
    @SerializedName("customer") val customer: String?,
    @SerializedName("enterprise_ids") val enterpriseIds: List<String>?,
    @SerializedName("vendor_ids") val vendorIds: List<String>?)

data class EnterprisesEntity(
    @SerializedName("id") val id: String?,
    @SerializedName("enterprise_name") val enterpriseName: String?,
    @SerializedName("type_label") val typeLabel: String?,
    @SerializedName("referral_code") val referralCode: String?,
    @SerializedName("country") val country: String?,
    @SerializedName("image_id") val imageId: String?,
    @SerializedName("managers") val managers: List<String>?,
    @SerializedName("members") val members: List<String>?,
    @SerializedName("stripe") val stripe: Boolean,
    @SerializedName("locations") val locations: List<String>?)

data class VendorsEntity(
    @SerializedName("id") val id: String?,
    @SerializedName("vendor_name") val vendorName: String?,
    @SerializedName("type_label") val typeLabel: String?,
    @SerializedName("user_role") val userRole: String?,
    @SerializedName("latitude") val latitude: Float,
    @SerializedName("longitude") val longitude: Float,
    @SerializedName("partner") val partner: Boolean,
    @SerializedName("country") val country: String?,
    @SerializedName("image_id") val imageId: String?,
    @SerializedName("stripe") val stripe: Boolean)

Data model class in the domain, Its it better to declare them all safe null types?

data class LoginResponse(
    val token: String,
    val user: User?,
    val enterprises: List<Enterprises>,
    val vendors: List<Vendors>)

data class User(
    val id: String?,
    val email: String?,
    val firstName: String?,
    val lastName: String?,
    val phone: String?,
    val address: String?,
    val dob: String?,
    val customer: String?,
    val enterpriseIds: List<String>,
    val vendorIds: List<String>)

data class Enterprises(
    val id: String,
    val enterpriseName: String,
    val typeLabel: String,
    val country: String,
    val imageId: String,
    val managers: List<String>,
    val members: List<String>,
    val stripe: Boolean,
    val locations: List<String>)

data class Vendors(
    val id: String,
    val vendorName: String,
    val typeLabel: String?,
    val userRole: String,
    val latitude: Float,
    val longitude: Float,
    val partner: Boolean,
    val country: String?,
    val imageId: String,
    val stripe: Boolean)

Upvotes: 0

Views: 509

Answers (2)

Willi Mentzel
Willi Mentzel

Reputation: 29844

First of all there are no safe null types in Kotlin. A variable is either nullable or not.

If the API delivers null values, either by absence or by setting them explicitely null, your data classes should reflect that in the way that those variables are nullable (question mark ? right after the data type).

The moment you map those data classes to your entities (domain model) you should handle the null case properly. Since it is a valid case, that the API serves null values, which you expect, you should handle that case by assigning a default value.

Using the elvis operator is just a way to deal with nullable types elegantely, but if you use it, remains your choice.

Upvotes: 2

Omar Mainegra
Omar Mainegra

Reputation: 4184

If you declare all the properties as nullable that's not much better than Java, even when you access them with the null safety operator. If your JSON properties have null value, that means that in your business logic they don't always have a value, and is your responsibility to handle that, by fallback to a default value (i.e. emptyList() or ""), or maybe something more complicated like redirecting to a sign in screen.

Upvotes: 1

Related Questions