I have two ViewModels that contain the same function. The function calls an interface/repository to retrieve data from a database, and the function then manipulates the data.
It is my understanding that the repository should only handle the call to the database and the ViewModels should never interact with each other. I also do not want this function residing in both ViewModels since that would have an unnecessary additional call to the database and it just seems to be redundant code to me.
Therefore, what is the best practice to have this function only written once and be accessible to both ViewModels?
Repository:
class PartsScreenImpl : PartsScreenRepository {
private val log = logging("KMLogging Tag")
private val db = Firebase.firestore
private val storage = Firebase.storage
override suspend fun getPartsList(): QuerySnapshot {
val result = db.collection("parts").get()
val doc = result.documents
return result
}
}
Function in question
private fun getPartsList(): List<Part>? {
val result = runBlocking {
val querySnapshot = async {
return@async repository.getPartsList()
}.await()
return@runBlocking querySnapshot
}
val documents = result.documents
var partsList: MutableList<Part>? = null
for (document in documents) {
val partNumber = document.get<String>("part_number")
val partDesc = document.get<String>("part_desc")
val partImage = getImageUrl(partNumber)
val part = Part(partNumber, partDesc, partImage)
if (partsList == null) {
partsList = mutableListOf(part)
} else {
partsList.add(part)
}
}
return partsList
}
Code that calls the function
fun getOrderSummary(order: List<OrderReportData>) : List<OrderPart> {
val partsList = getPartsList()
val orderSummary = mutableListOf<OrderPart>()
for (part in partsList!!) {
val orderCount = order.filter { it.orderPart.number == part.number }
var orderPartQuantityList: List<Double> = emptyList()
try {
orderPartQuantityList = orderCount.map { it.orderPart.qty.toDouble() }
} catch (e: NumberFormatException) {
Log.e("mytags", "Invalid number format", e)
}
val partQuantityDouble = (orderPartQuantityList.sum())
val partQuantity = if (partQuantityDouble.rem(1).equals(0.0)) {
partQuantityDouble.toInt()
} else {
partQuantityDouble.toBigDecimal().setScale(1, RoundingMode.FLOOR).toDouble()
}
val orderPart = OrderPart(part.number, part.desc, partQuantity.toString())
if (orderPart.qty.toFloat() > 0) {
orderSummary += orderPart
}
}
return orderSummary
}
I think what you're looking for is the Domain Layer
From the first snippet:
The domain layer is responsible for encapsulating complex business logic, or simple business logic that is reused by multiple ViewModels. This layer is optional because not all apps will have these requirements. You should only use it when needed-for example, to handle complexity or favor reusability.
Edit: For example (I don't have an IDE currently and i'm not sure if you can return an optional) :
class PartListUseCase(repository: PartsScreenRepository) {
operator fun invoke() : List<Part>? {
return getPartsList()
}
private fun getPartsList() : List<Part>? {
val result = runBlocking {
val querySnapshot = async {
return@async repository.getPartsList()
}.await()
return@runBlocking querySnapshot
}
val documents = result.documents
var partsList: MutableList<Part>? = null
for (document in documents) {
val partNumber = document.get<String>("part_number")
val partDesc = document.get<String>("part_desc")
val partImage = getImageUrl(partNumber)
val part = Part(partNumber, partDesc, partImage)
if (partsList == null) {
partsList = mutableListOf(part)
} else {
partsList.add(part)
}
}
return partsList
}
}