Shared Data Layer

Sharing BookNest's Data Layer Across Platforms

The :shared module applies kotlin("multiplatform") and the serialization plugin (2.4.20), declares jvm() and js { nodejs() }, and puts kotlinx.serialization 1.11.0 5,947 in commonMain. Next to a @Serializable data class Book sits what every BookNest client needs:

shared/src/commonMain/.../Catalog.kt: BookNest's portable core (excerpt)Kotlin
// 5.39.5 the part of BookNest every client needs: parse, search, price
class Catalog(source: String) {
  val books: List<Book> = json.decodeFromString<CatalogFile>(source).books
  fun search(query: String): List<Book> = books.filter {
    it.title.contains(query, ignoreCase = true) || it.author.contains(query, ignoreCase = true)
  }
  fun cartTotal(ids: List<Int>): String =
    formatPrice(ids.sumOf { id -> books.first { it.id == id }.price })
}
// No String.format() in common code: it is a JVM API. Cents avoid floating-point display surprises.
fun formatPrice(amount: Double): String {
  val cents = kotlin.math.round(amount * 100).toLong()
  return "$" + (cents / 100) + "." + (cents % 100).toString().padStart(2, '0')
}

One test in commonTest parses three of the sample books and prints what each platform computes. Gradle 19,597 runs it once per target, on the JVM and, after downloading Node.js 2,131 and Yarn 11,798 on the first run, on Node:

Running the shared test on both targetsKotlin
nice -n 10 ./gradlew --no-daemon --max-workers=2 :shared:jvmTest :shared:jsNodeTest
Output
CatalogTest[jvm] > searchAndTotal[jvm] STANDARD_OUT
    JVM 17.0.20.1: [1, 2], total $78.49, raw price 24.0
...
com.example.booknest.shared.CatalogTest.searchAndTotal[js, node] STANDARD_OUT
    JavaScript on Node.js v24.16.0: [1, 2], total $78.49, raw price 24
com.example.booknest.shared.CatalogTest.searchAndTotal[js, node] PASSED

The search, the total and the formatted price agree, but the raw Double does not: Kotlin/JS represents it as a JavaScript number, and 24.0.toString() prints 24 there, as in JavaScript itself. Formatting in shared code keeps an Android and a web client printing the same prices.

The Android app would add an Android target to :shared and depend on it; a fuller move would also share Room 3 234 (Room 3.0) and the Ktor 274,728 client (Ktor Client).