Skip to content

// project

The KMP toolkit family

Three decoupled repos so a new app starts at "write the feature".

The brief

The KMP toolkit family is three decoupled repos (kmp-toolkit, kmp-build-logic and kmp-app-template) instead of one "platform" repo, so that using one of them never means dragging the other two along. None of the three were designed up front: each exists because a second consumer needed something the first one already had, and extracting it once was cheaper than copy-pasting it again. The family is vendored into Doori, PaymentsLab-KMP, Candidai, Gaddi and this portfolio's own Compose Multiplatform twin via Gradle includeBuild, so a fix or a version bump lands once and every consumer picks it up on its own schedule.

What shipped

  • kmp-toolkit: 46 modules, each extracted the moment a second consumer needed the same logic, never designed as a "platform" up front, from the mvi-core base all four apps share to modules like device-integrity and a chart renderer still finding their first consumer.
  • kmp-build-logic: 18 convention plugins. The AGP / Kotlin / Compose / test / lint / Firebase / Room / Koin setup written once and applied with one line.
  • kmp-app-template, the app shape the toolkit slots into: one shared Compose UI, a wired Splash → Login → Home nav scaffold, thin Android + Desktop shells, and a customizer.sh that renames the whole project in one command.
  • Consumed by Doori (16 of its 52 modules), PaymentsLab-KMP (31 of its 48), Candidai and Gaddi. The composition is the proof the extraction was real, not a library nobody uses.
Kotlin MultiplatformGradle convention pluginsCompose MultiplatformMIT

// case study

The short version

The problem

Four separate consumer apps each needed the same MVI core, the same AGP/Kotlin/Compose build setup and the same starting app shape: building any of that per-app again would be exactly the copy-paste a platform team exists to prevent.

The decision

Split into three decoupled repos instead of one "platform" repo (kmp-toolkit, kmp-build-logic, kmp-app-template) so using one never drags the other two along, and extract a module only the moment a second consumer actually needs it, never ahead of demand.

The result

Five independent consumers (Doori, PaymentsLab-KMP, Candidai, Gaddi and this portfolio's own Compose Multiplatform twin) now vendor the family via Gradle includeBuild, which is the actual proof the extraction paid off rather than a library nobody uses.

5 repos vendoring this family

Go deeper

Analyst view: The KMP toolkit family draws on 4 stack entries and links to 5 outside sources.

46

modules · kmp-toolkit

18

convention plugins

5

repos vendoring this family

20

payment provider modules

// design notes

How it works

kmp-toolkit: 46 modules, extracted, never designed

The library repo, 46 modules, each pulled out the moment a second consumer needed the same logic rather than sketched in ahead of demand. All four native apps (Doori, Gaddi, PaymentsLab-KMP and Candidai) build on the same eight: typed Result, the (State, Event) → Effects mvi-core base, common, network, on-device AI plus its ai-testing harness, the design system and llm-chat, three of which (Result, network, llm-chat) this portfolio's own Compose Multiplatform twin substitutes too, the narrower slice a résumé app actually needs. Security is one substitution short of universal: Doori, PaymentsLab-KMP and Candidai run it, Gaddi doesn't. Past that the toolkit gets consumer-specific: Doori alone runs the location tracking module, settings, app-shell and the operation-log offline-outbox; Gaddi alone runs feedback and bots-policy, the generic ISMCTS search shell its own AI engine is actually built from; PaymentsLab-KMP alone runs the payments-api contract plus its provider gateway modules. Still finding a first consumer: device-integrity, biometric, secure-store, auth, netlog, a screen-state store (ScreenState/DecisionEngine, a different module from Doori's outbox) and a chart renderer.

kmp-build-logic: the setup written once

18 convention plugins live in this repo. AGP, Kotlin, Compose, test, lint, Firebase, Room and Koin configuration for a module is one line (apply the plugin) instead of a build.gradle.kts a new module has to get right from scratch. This is the other half of the shared foundation: the composite build every consumer app pulls in for its module wiring.

kmp-app-template: the shape a new app starts from

The third repo is the app shape the toolkit and build logic slot into: one shared Compose UI, a wired Splash → Login → Home nav scaffold, thin Android and Desktop shells, and a customizer.sh script that renames the whole project in one command. A new app starts at "write the feature", not at "stand up the module graph".

The composition is the proof

Doori consumes 16 of its 52 modules from the toolkit; PaymentsLab-KMP consumes 31 of its 48; Candidai and Gaddi draw on the same foundation. This portfolio's own Compose Multiplatform twin is built on kmp-app-template too, which is the reason that project's write-up can say the template carries a real four-target app rather than a hello-world, the same claim this family makes about itself, checked by a fifth independent consumer.

One MVI contract, four apps

Doori, PaymentsLab-KMP, Candidai and Gaddi are not four isolated demos: they share a build-wiring contract and a unidirectional-state contract, both written once in this family and pulled in as composite builds rather than re-derived per app. The discipline the toolkit exists to enforce is exactly what a platform team is supposed to bring to a codebase at scale: one seam, reused, instead of the same decision made differently four times.

// architecture

How it's built

Three repos, one seam each

Module adoption, generated from the measured graph

// under the hood

Tech stack

kmp-toolkit

  • typed Result
  • MVI ViewModel core (State, Event) → Effects
  • network + security
  • on-device AI seam
  • device-integrity
  • operation-log offline-outbox (Doori)
  • screen-state store (ScreenState/DecisionEngine, no consumer app yet)
  • settings
  • app-shell
  • llm-chat
  • secrets vault pattern
  • bots-policy (Gaddi's ISMCTS shell)
  • 20 payment provider modules

kmp-build-logic

  • AGP
  • Kotlin
  • Compose
  • test + lint
  • Firebase
  • Room
  • Koin

kmp-app-template

  • Shared Compose UI
  • Splash → Login → Home nav scaffold
  • Android + Desktop shells
  • customizer.sh

Distribution

  • Gradle includeBuild
  • MIT license

// adoption

Which app substituted which module

ModuleDooriGaddiPaymentsLab-KMPCandidaiPortfolio twin
result●●●●●
common●●●●·
mvi-core●●●●·
network●●●●●
security●·●●·
biometric·····
secure-store·····
device-integrity·····
settings●····
auth·····
netlog·····
designsystem●●●●·
ai●●●●·
ai-mlkit●●●··
designsystem-wallet-gms●·●··
ai-testing●●●●·
llm-chat●●●●●
feedback·●···
location●····
app-shell●····
app-shell-location-gms●····
payments-api··●··
offline-outbox●····
store·····
bots-policy·●···
charts·····
stripe··●··
upi-intent··●··
cashfree··●··
applepay·····
googlepay··●··
omise··●··
razorpay··●··
square··●··
hosted-webview··●··
flutterwave··●··
paystack··●··
paytm··●··
stripe-connect··●··
cash··●··
nmi··●··
peach··●··
mobile-money··●··
mpesa··●··
wallet··●··
xendit··●··

Every catalog module, one node each

See how everything connects →