orderId?.takeIf { it.isNotBlank() }
?.toIntOrNull()
?.let { httpClient.getPartialOrder(it) }
?.toDomainObject()
?.let { dbClient.findFullOrder(it) }
?.takeIf { it.status == Status.PENDING }
?.apply { logger.info("Processing order $id") }
?.let { dbClient.updateStatus(it.id, Status.PROCESSING) }
?.apply { this.total = this.price + billingService.calculateTax(this) }
?.also { order -> emailService.sendConfirmation(order) }
?.let { dbClient.finalizeOrder(it) }
?: logger.error("Order processing skipped or invalid ID")
}
This is a problem. Yes, the code works, it's arguably kotlinic, and it's brief. A strong Kotlin programmer will understand what is occurring at each line. However, it's unintuitive, especially to someone new to Kotlin. Even a senior Kotlin coder might require context to understand each line, especially if the variables and methods aren't named well.
Without Googling, try to remember which scope function returns its lambda result and which returns the original context object? At what point is the variable referred to as it a String, an Int, a FullOrder, or a database-friendly FullOrderDomain? What is even the purpose of cramming all of this logic into just one expression?
Just because you can doesn't mean you should. Let's look at each problem, line by line.