Key Takeaways
- Unified Business Logic: Kotlin Multiplatform (KMP) enables writing the complex ONDC protocol logic (discovery, cart, order management) once in a shared
commonMain module and deploying it natively across Android, iOS, and the Web.
- Native UI, Shared Core: This architecture advocates for using the best native UI toolkit for each platform (Jetpack Compose for Android, SwiftUI for iOS, and Next.js/React for Web) while sharing the entire business logic and data layer. This avoids the compromises of non-native UI frameworks.
- Protocol-Centric Architecture: ONDC's success relies on strict adherence to the Beckn protocol. KMP is ideal for modeling these intricate, stateful interactions, ensuring 100% consistency across all user-facing applications and significantly reducing bugs.
- Efficient DevOps: A single monorepo containing the shared KMP module, Android app, iOS app, and Next.js web app simplifies the CI/CD pipeline, enabling parallel builds and unified testing strategies for faster, more reliable releases.
- Future-Proof Stack: By combining KMP's native performance with the scalability of Next.js 15, this blueprint creates a resilient, high-performance, and maintainable solution ready for the scale and complexity of India's digital commerce revolution.
The ONDC Revolution Demands a New Development Paradigm
The Open Network for Digital Commerce (ONDC) is not just another e-commerce platform; it's a foundational shift in how digital commerce operates in India. By unbundling and democratizing the ecosystem, it presents an unprecedented opportunity for businesses to build innovative buyer and seller applications. However, this opportunity comes with a significant technical challenge: how do you build a robust, consistent, and high-performance Buyer App that works flawlessly across the web, Android, and iOS without tripling your development costs and creating engineering silos?
The traditional approach of maintaining three separate codebases (Swift for iOS, Kotlin for Android, JavaScript/TypeScript for Web) is a direct path to technical debt, inconsistent user experiences, and a cripplingly slow time-to-market. In a protocol-driven world like ONDC, where even a minor discrepancy in implementing the state machine for an order can lead to failed transactions, this inconsistency is unacceptable.
This is where a unified codebase strategy, powered by Kotlin Multiplatform (KMP), becomes a decisive competitive advantage. This blueprint outlines the definitive architecture for building a next-generation ONDC Buyer App, leveraging KMP to share 100% of the complex business logic while delivering a truly native user experience on every platform.
While other cross-platform frameworks exist, KMP is uniquely suited for the rigorous demands of an ONDC application. It's not about rendering web views or drawing pixels on a canvas; it's about sharing compiled, native code.
Native Performance, Shared Logic
Unlike frameworks that rely on a JavaScript bridge or a custom rendering engine, KMP compiles your shared Kotlin code into the native format of each target platform:
- JVM Bytecode for Android, integrating seamlessly with the existing Android ecosystem.
- Native Binaries (via LLVM) for iOS, allowing direct interoperability with Swift and Objective-C.
- JavaScript for the Web, enabling the same logic to run in the browser or on the server with Node.js.
This means zero performance overhead. Your ONDC protocol logic, network requests, and database operations run as fast as if they were written natively for that platform because, for all intents and purposes, they are.
Perfect Fit for Protocol-Driven Development
ONDC is governed by the Beckn protocol, a complex set of specifications for discovery, search, select, init, confirm, and tracking. This is the "core domain" of your application. Implementing this intricate logic—handling asynchronous network callbacks, managing multi-provider states, and ensuring data integrity—is the most critical and difficult part of development.
With KMP, you model these data contracts (e.g., Context, Message, Catalog) and stateful workflows once using Kotlin's powerful features like data classes, sealed classes, and coroutines. This single source of truth eliminates the risk of platform-specific implementation errors and ensures your app is always compliant with the ONDC network.
Ecosystem Maturity and First-Party Support
Backed by JetBrains and officially supported by Google for Android development, Kotlin is a first-class citizen in the mobile world. KMP is the natural evolution of this ecosystem. With mature libraries like Ktor for networking, SQLDelight for a shared database, and kotlinx.serialization for JSON parsing, you have a robust, production-ready toolkit to build the entire business logic layer of your application.

Core Architectural Blueprint: The Unified ONDC Buyer App
The architecture is designed around a central, shared KMP module that acts as the brain of the application. The platform-specific code is a thin, presentation-only layer.
The Shared commonMain Module: The Heart of the Operation
This is where all non-UI code resides. It is the single source of truth for your app's behavior and data.
H3: ONDC Protocol & Networking Layer
This layer is responsible for all communication with the ONDC network.
- ONDC API Contracts: Define all ONDC request and response models using Kotlin's
data class with @Serializable annotations. This provides type-safe, compile-time-checked models for your entire application.
- Networking with Ktor: Use the Ktor multiplatform HTTP client to define all network calls (
/search, /select, /init, etc.). Configure a single client in commonMain with shared logic for adding authentication headers (like ONDC's signing keys), logging, and error handling. Ktor automatically uses the appropriate engine for each platform (OkHttp on Android, URLSession on iOS, Fetch API on JS).
- Serialization: Employ
kotlinx.serialization to seamlessly parse JSON responses from ONDC Network Participants into your Kotlin data classes.
// In commonMain: A shared repository making an ONDC search call
class OndcRepository(private val httpClient: HttpClient) {
private val gatewayEndpoint = "https://gateway.ondc.org/search"
suspend fun search(searchRequest: OndcSearchRequest): OndcSearchResponse {
return httpClient.post(gatewayEndpoint) {
contentType(ContentType.Application.Json)
setBody(searchRequest)
// Header logic for signing would go here
}.body()
}
}
H3: Business Logic & State Management
This layer orchestrates the data from the network layer and manages the application's state.
- Shared ViewModels/Presenters: Implement your presentation logic using an MVI or MVVM pattern within
commonMain. These ViewModels will manage UI state, handle user actions, and call the repository layer. Libraries like MOKO MVVM or Decompose can provide multiplatform ViewModel implementations.
- Complex State Logic: The ONDC cart is a prime example. A single order can contain items from multiple sellers, each with its own delivery and fulfillment state. This complex logic is modeled once in the shared module, ensuring a consistent checkout and order tracking experience everywhere.
H3: Persistence with SQLDelight
For caching catalog data, storing user preferences, or enabling offline functionality, SQLDelight is the definitive choice. You write standard SQL schema files in commonMain, and SQLDelight generates type-safe Kotlin APIs to interact with the database. It handles the platform-specific database drivers (SQLite on Android, an in-memory driver for tests, and a native SQLite driver for iOS) automatically.
With the entire backend and business logic in the shared KMP module, the platform-specific work becomes focused purely on building the best possible user interface.
H3: Android with Jetpack Compose
This is the most straightforward integration. Since Jetpack Compose is written in Kotlin, the connection is seamless.
- Your Android
Activity or Fragment will instantiate a ViewModel from the shared KMP module.
- Your Composable functions will observe state changes (e.g., using
StateFlow) exposed by the shared ViewModel and recompose the UI accordingly. User events from the UI (like a button click) simply call functions on the shared ViewModel.
// In your Android app's Composable
@Composable
fun ProductScreen(viewModel: ProductViewModel) { // viewModel is from commonMain
val uiState by viewModel.productState.collectAsState()
// Render UI based on uiState
// ...
Button(onClick = { viewModel.addToCart(uiState.product) }) {
Text("Add to Cart")
}
}
H3: iOS with SwiftUI
For iOS, KMP compiles the shared module into a standard Objective-C framework that can be easily consumed by Swift.
- Coroutines to Swift/Async: To bridge the gap between Kotlin's
suspend functions and Swift's async/await, use a library like KMP-NativeCoroutines. It automatically generates async wrappers for your suspend functions.
- State Observation: The
StateFlow and SharedFlow from your KMP ViewModels can be converted into Swift AsyncStream or Combine Publishers, making them easy to observe from within a SwiftUI view.
- Adapter Layer: A best practice is to create a thin Swift "Adapter ViewModel" that holds a reference to the KMP ViewModel and exposes its state in a SwiftUI-friendly way (e.g., using
@Published properties).
// In your iOS app's SwiftUI View
@MainActor
class ProductObservableObject: ObservableObject {
private let kmpViewModel: ProductViewModel // from the shared KMP framework
@Published var uiState: ProductState?
init() {
self.kmpViewModel = ProductViewModel()
// Observe state changes from the KMP ViewModel
// and update the @Published property
}
func addToCart() {
kmpViewModel.addToCart(product: self.uiState.product)
}
}
H3: Web/PWA with Next.js 15
This is where the architecture becomes truly unified. The same KMP module compiles to JavaScript, allowing you to share logic with your modern web front-end.
- Consuming the Kotlin/JS Module: The KMP toolchain produces a JavaScript module that you can import into your Next.js project.
- Server Components: For initial page loads, you can call functions from your KMP module directly within Next.js 15 Server Components. This allows you to fetch ONDC data on the server, render the initial HTML, and send a fully-formed page to the client for excellent SEO and performance.
- Client Components: For interactive elements, you can use the same KMP module on the client side. The state management logic for the cart, for example, is the exact same code that runs on the Android and iOS apps. This creates an incredibly powerful and consistent Progressive Web App (PWA) experience.

Navigating ONDC-Specific Challenges with KMP
The benefits of this architecture are most apparent when tackling ONDC's unique complexities.
- Real-time Order Tracking: The ONDC network uses webhooks or polling to provide order status updates (
on_status). A WebSocket client implemented with Ktor in commonMain can handle these real-time updates once, and the logic for updating the local database and UI state is automatically shared across all platforms.
- Geolocation and Discovery: Finding nearby sellers requires platform-specific location APIs. KMP's
expect/actual mechanism is perfect for this. You expect a LocationProvider interface in commonMain and then provide the actual implementation using the native location services on Android, iOS, and the browser's Geolocation API for the web target. The business logic just interacts with the common interface.
- Multi-Domain Support: As ONDC expands from retail to mobility, logistics, and financial services, your Buyer App may need to support multiple domains. The core protocol logic remains similar. With KMP, you can structure your shared module to accommodate these domains without duplicating code, allowing you to scale your app's features efficiently.
DevOps and CI/CD for a Unified KMP Project
A unified codebase demands a unified CI/CD pipeline.
- Monorepo Strategy: The entire project—shared module, Android app, iOS app, and Next.js app—should live in a single Git repository. This simplifies dependency management and ensures that a change in the shared logic is tested against all platforms simultaneously.
- CI/CD with GitHub Actions: A typical pipeline would consist of parallel jobs:
- Shared Module Test: A job that runs on a Linux runner to compile and run all the unit tests in the
commonMain module.
- Android Build: A job that builds the Android app, runs its integration tests, and generates a signed APK or AAB.
- iOS Build: A job that runs on a macOS runner, uses fastlane or Xcode Cloud to build the iOS app, run its tests, and archive it for TestFlight or App Store distribution.
- Web Build & Deploy: A job that runs
npm install, npm run build, and deploys the static Next.js output to a provider like Vercel or AWS Amplify.
A pull request is only considered mergeable if all four jobs pass, guaranteeing that no change breaks any platform.

Frequently Asked Questions (FAQ)
Q1: How does KMP compare to React Native or Flutter for an ONDC app?
KMP's primary advantage is in sharing business logic while allowing for 100% native UI. React Native and Flutter force you into their specific UI paradigms. For a complex, protocol-heavy application like an ONDC Buyer App, the most critical part is the business logic's correctness, which is KMP's strength. It also gives you the flexibility to use platform-specific UI components and patterns without fighting a framework, which is crucial for a premium user experience.
Q2: What is the learning curve for an existing native Android/iOS team to adopt KMP?
For an Android team already using Kotlin, the learning curve is minimal. They primarily need to learn about the multiplatform project structure and libraries like Ktor. For an iOS team, the curve involves learning Kotlin syntax (which is very similar to Swift) and understanding how to interoperate with the generated framework. However, since they continue to write UI in SwiftUI, they remain in a familiar environment. It's a significantly lower barrier than having the entire team learn Dart/Flutter.
Q3: How do you handle platform-specific SDK integrations (e.g., payment gateways like UPI) in a KMP architecture?
This is a classic use case for the expect/actual pattern. You expect an interface in commonMain, such as interface PaymentHandler { fun initiateUpiPayment(details: PaymentDetails) }. Then, in the Android source set, you provide the actual implementation that calls the UPI SDK for Android. In the iOS source set, you provide the actual implementation for its respective payment flow. The shared ViewModel simply calls the common interface, completely decoupled from the platform-specific implementation.
Q4: Is the Kotlin/JS target for web production-ready and performant enough for a complex app?
Yes. Kotlin/JS has been stable for years and is used in production by many companies. With recent improvements in the compiler and DCE (Dead Code Elimination), bundle sizes are competitive. When combined with a framework like Next.js for server-side rendering, the performance is excellent. You get the benefits of a type-safe, robust language for your web application's logic without sacrificing performance.
Build Your Next-Generation ONDC Application with Induji Technologies
Architecting a unified, multiplatform ONDC application is a complex undertaking that requires deep expertise in mobile, web, and backend systems. A Kotlin Multiplatform strategy, when executed correctly, can provide a massive strategic advantage, reducing costs, accelerating your roadmap, and delivering a superior product.
Don't navigate the complexities of ONDC alone. The expert team at Induji Technologies specializes in architecting and building high-performance, scalable, and secure enterprise applications using cutting-edge stacks like Kotlin Multiplatform and Next.js. We can help you design a future-proof architecture, build your application from the ground up, and ensure you launch a successful product on the ONDC network.
Ready to architect your ONDC success story? Request a quote from our technical experts today.