---
title: "Room no Kotlin Multiplatform: Banco Compartilhado em 2026 | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/room-kotlin-multiplatform-2026/"
markdown_url: "https://kotlin.dev.br/blog/room-kotlin-multiplatform-2026.MD"
description: "Veja como usar Room no Kotlin Multiplatform em 2026 com commonMain, KSP, BundledSQLiteDriver e exemplos práticos para Android e iOS."
date: "2026-04-23"
author: "Karina Melo"
---

# Room no Kotlin Multiplatform: Banco Compartilhado em 2026 | Kotlin Brasil

Veja como usar Room no Kotlin Multiplatform em 2026 com commonMain, KSP, BundledSQLiteDriver e exemplos práticos para Android e iOS.


Durante muito tempo, falar em persistência local no **Kotlin Multiplatform** significava cair quase sempre em SQLDelight, wrappers próprios ou soluções diferentes para cada plataforma. Em 2026, esse cenário mudou bastante: o **Room** amadureceu seu suporte a KMP e já virou uma alternativa concreta para times Android que querem compartilhar entidades, DAOs e parte importante da camada de dados com iOS e outras plataformas.

Neste artigo, vamos ver como o Room funciona no mundo multiplataforma, quando faz sentido adotá-lo, como configurar `commonMain`, qual o papel dos drivers SQLite e quais cuidados você precisa ter para não tratar KMP como “Android com nomes diferentes”.

Se você ainda está construindo a base do assunto, vale revisar antes nosso [guia de Kotlin Multiplatform Mobile](/guias/guia-kotlin-multiplatform-mobile/), o [tutorial de KMP](/tutoriais/kotlin-multiplatform-tutorial/) e o artigo sobre o [futuro do Kotlin Multiplatform](/blog/futuro-kotlin-multiplatform/).

## O que mudou para o Room no Kotlin Multiplatform?

A principal novidade é que o **Room 2.7+** passou a oferecer suporte real para **Kotlin Multiplatform**, permitindo definir em `commonMain` boa parte da estrutura que antes ficava presa ao Android:

- entidades (`@Entity`);
- interfaces DAO (`@Dao`);
- definição do banco (`@Database`);
- queries compartilhadas;
- retorno reativo com `Flow`.

Isso aproxima muito a experiência de quem já conhece Room no Android. Em vez de abandonar completamente o conhecimento acumulado do ecossistema Jetpack, você reaproveita conceitos, anotações e parte da ergonomia.

Para times brasileiros que já mantêm app Android em Kotlin e querem evoluir para iOS com mais compartilhamento, esse é um salto importante. É o mesmo tipo de ganho gradual que comentamos em [Kotlin para Android](/blog/kotlin-para-android/) e em comparações como [Kotlin Multiplatform vs Flutter](/comparacoes/kotlin-multiplatform-vs-flutter/).

## Quando vale usar Room em KMP?

A resposta curta é: **quando seu time já tem afinidade com o ecossistema AndroidX e quer reduzir a distância entre Android e KMP**.

### Room em KMP faz mais sentido quando:

- a equipe já domina Room no Android;
- a camada de dados precisa ser compartilhada entre Android e iOS;
- você quer continuar usando entidades e DAOs com uma API familiar;
- o projeto já segue arquitetura com repositórios e `Flow`;
- existe interesse em reduzir soluções paralelas entre plataformas.

### Talvez não seja a melhor escolha quando:

- o projeto já está consolidado com SQLDelight e sem dores reais;
- a equipe precisa de suporte muito específico fora do modelo Room;
- a base multiplataforma ainda é experimental e muda demais;
- você quer o mínimo possível de acoplamento com AndroidX.

Como quase toda decisão arquitetural, isso depende mais do contexto do que de hype. O mesmo raciocínio vale quando discutimos [monólito modular com Kotlin e Spring](/blog/monolito-modular-kotlin-spring-2026/) ou [gRPC com Kotlin](/blog/grpc-kotlin-coroutines-tutorial-2026/): a ferramenta certa é a que reduz complexidade real.

## Dependências e plugins

Um setup moderno de Room com KMP pode começar assim:

```kotlin
// shared/build.gradle.kts
plugins {
    kotlin("multiplatform")
    id("com.google.devtools.ksp")
    id("androidx.room")
}

kotlin {
    androidTarget()
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        commonMain.dependencies {
            implementation("androidx.room:room-runtime:2.8.4")
            implementation("androidx.sqlite:sqlite-bundled:2.6.2")
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
        }
    }
}

dependencies {
    add("kspAndroid", "androidx.room:room-compiler:2.8.4")
    add("kspIosX64", "androidx.room:room-compiler:2.8.4")
    add("kspIosArm64", "androidx.room:room-compiler:2.8.4")
    add("kspIosSimulatorArm64", "androidx.room:room-compiler:2.8.4")
}

room {
    schemaDirectory("$projectDir/schemas")
}
```

Dois pontos merecem atenção:

1. **KSP por target** — em KMP, você precisa configurar o compilador do Room para cada alvo relevante.
2. **Schema versionada** — salvar schemas continua sendo uma boa prática para evolução do banco e revisão de migrações.

Se você já acompanha o tema de tooling, aproveite também nosso conteúdo sobre [KSP em Kotlin](/blog/kotlin-ksp-guia-completo/) e [Gradle Version Catalog](/blog/gradle-version-catalog-kotlin-2026/), porque ambos ajudam a manter essa configuração organizada.

## Estruturando o banco em `commonMain`

Aqui está a grande virada conceitual: em vez de deixar entidade e DAO presas ao Android, você move o núcleo do banco para `commonMain`.

### Entidade compartilhada

```kotlin
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "artigos")
data class ArtigoEntity(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    val slug: String,
    val titulo: String,
    val categoria: String,
    val favorito: Boolean = false,
)
```

### DAO compartilhado

```kotlin
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow

@Dao
interface ArtigoDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun salvar(item: ArtigoEntity)

    @Query("SELECT * FROM artigos ORDER BY titulo ASC")
    fun listarTodos(): Flow<List<ArtigoEntity>>

    @Query("SELECT * FROM artigos WHERE favorito = 1")
    fun listarFavoritos(): Flow<List<ArtigoEntity>>

    @Query("UPDATE artigos SET favorito = :favorito WHERE slug = :slug")
    suspend fun atualizarFavorito(slug: String, favorito: Boolean)
}
```

### Banco compartilhado

```kotlin
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.ConstructedBy

@Database(
    entities = [ArtigoEntity::class],
    version = 1,
    exportSchema = true,
)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun artigoDao(): ArtigoDao
}
```

Essa API deixa a modelagem muito mais alinhada com o restante do código compartilhado. Para projetos que já usam [Flow](/tutoriais/kotlin-flow-tutorial/) e camadas reativas, o encaixe é natural.

## O papel do `@ConstructedBy` e do construtor esperado

Em KMP, o Room precisa de uma forma multiplataforma para inicializar a implementação gerada do banco. Por isso aparece o padrão com `expect object`:

```kotlin
import androidx.room.RoomDatabaseConstructor

@Suppress("KotlinNoActualForExpect")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
    override fun initialize(): AppDatabase
}
```

Esse padrão parece estranho à primeira vista, mas segue a mesma lógica do `expect/actual` que você já encontra no ecossistema KMP. Se quiser reforçar esse conceito, vale passar pelo nosso glossário sobre [expect/actual](/glossario/expect-actual/).

## Drivers SQLite: por que eles importam tanto?

No Android puro, muita gente quase nunca pensa profundamente no driver do banco, porque a plataforma já entrega quase tudo de forma implícita. No KMP, isso muda. Você precisa escolher como o Room conversa com SQLite em cada alvo.

### Opção recomendada: `BundledSQLiteDriver`

Na maioria dos casos, a melhor escolha é o **`BundledSQLiteDriver()`**, porque ele entrega comportamento mais consistente entre plataformas.

```kotlin
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import kotlinx.coroutines.Dispatchers

fun criarBanco(builder: androidx.room.RoomDatabase.Builder<AppDatabase>): AppDatabase {
    return builder
        .setDriver(BundledSQLiteDriver())
        .setQueryCoroutineContext(Dispatchers.IO)
        .build()
}
```

Essa consistência é valiosa porque reduz diferenças inesperadas entre Android e iOS. Em times menores, isso economiza bastante tempo de investigação.

### Alternativas por plataforma

Dependendo do cenário, você também pode trabalhar com drivers específicos, como:

- `AndroidSQLiteDriver` no Android;
- `NativeSQLiteDriver` no iOS.

Mas, para a maior parte dos casos novos, o driver bundled tende a simplificar a vida.

## Criando o builder por plataforma

Mesmo com o banco definido em `commonMain`, o caminho físico do arquivo ainda depende da plataforma. Por isso, o builder costuma ser criado localmente em cada target.

### Android

```kotlin
import android.content.Context
import androidx.room.Room

fun provideDatabaseBuilder(context: Context): androidx.room.RoomDatabase.Builder<AppDatabase> {
    val dbFile = context.applicationContext.getDatabasePath("kotlin_brasil.db")

    return Room.databaseBuilder<AppDatabase>(
        context = context.applicationContext,
        name = dbFile.absolutePath,
    )
}
```

### iOS

```kotlin
import androidx.room.Room
import platform.Foundation.NSHomeDirectory

fun provideDatabaseBuilder(): androidx.room.RoomDatabase.Builder<AppDatabase> {
    val dbPath = NSHomeDirectory() + "/Documents/kotlin_brasil.db"

    return Room.databaseBuilder<AppDatabase>(
        name = dbPath,
    )
}
```

A ideia é simples: a estrutura do banco é compartilhada, mas o local do arquivo ainda é responsabilidade de cada ambiente.

## Repositório compartilhado com Flow

Com o DAO em `commonMain`, seu repositório pode ficar bastante limpo:

```kotlin
import kotlinx.coroutines.flow.Flow

class ArtigoRepository(
    private val dao: ArtigoDao,
) {
    fun listarFavoritos(): Flow<List<ArtigoEntity>> {
        return dao.listarFavoritos()
    }

    suspend fun salvarArtigo(slug: String, titulo: String, categoria: String) {
        dao.salvar(
            ArtigoEntity(
                slug = slug,
                titulo = titulo,
                categoria = categoria,
            )
        )
    }

    suspend fun alternarFavorito(slug: String, favorito: Boolean) {
        dao.atualizarFavorito(slug, favorito)
    }
}
```

Se a sua UI Android usa Compose e a iOS usa bindings próprios, ambas podem consumir a mesma regra de persistência. Esse é justamente o tipo de compartilhamento que torna o KMP interessante no mundo real.

## Diferenças importantes em relação ao Room Android clássico

Aqui está um ponto essencial: **Room em KMP não é só o Room Android transportado de plataforma**. Existem diferenças práticas.

### 1. `Flow` tende a ser a escolha natural

Em código compartilhado, `Flow` funciona melhor do que abordagens ligadas ao Android, como `LiveData`.

### 2. Algumas APIs Android-only não existem

Recursos específicos do Android podem não estar disponíveis da mesma forma no ambiente multiplataforma.

### 3. Queries cruas e transações exigem atenção

Dependendo do caso, você vai usar APIs específicas do modelo KMP, inclusive para acesso por conexões de leitura e escrita.

### 4. Testes precisam cobrir mais de um ambiente mental

Mesmo que você não rode todos os testes em todas as plataformas o tempo todo, vale validar que sua modelagem não assume detalhes exclusivos do Android.

Esse cuidado é parecido com o que discutimos em [Compose Multiplatform 1.10.6](/blog/compose-multiplatform-1-10-6-2026/) e em [Kotlin Multiplatform vs React Native](/comparacoes/kotlin-multiplatform-vs-react-native/): o segredo é respeitar o que é compartilhável e o que continua sendo específico de plataforma.

## Exemplo de uso na camada de apresentação

Vamos imaginar um ViewModel compartilhado para artigos favoritos:

```kotlin
class FavoritosViewModel(
    private val repository: ArtigoRepository,
) : ViewModel() {
    val favoritos = repository.listarFavoritos()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = emptyList(),
        )

    fun alternarFavorito(slug: String, favorito: Boolean) {
        viewModelScope.launch {
            repository.alternarFavorito(slug, favorito)
        }
    }
}
```

Num app Android com Compose, isso encaixa muito bem com telas declarativas. Num projeto iOS, a mesma base compartilhada continua útil, desde que a camada de apresentação esteja preparada para consumir esse estado.

## Boas práticas para usar Room em KMP

Se você está avaliando adoção, estas recomendações ajudam bastante:

### 1. Comece com uma feature simples

Não migre toda a persistência do app de uma vez. Pilote com favoritos, cache de artigos, onboarding ou configurações locais.

### 2. Prefira modelos pequenos e estáveis

Evite entidades gigantes logo no começo. Quanto menor a superfície, mais fácil validar o comportamento multiplataforma.

### 3. Padronize o uso de `Flow`

Se a estratégia da equipe já é reativa, manter essa consistência facilita integração com UI e testes.

### 4. Versione schemas desde o início

Mesmo em projeto novo, deixe a evolução do banco previsível.

### 5. Documente limites da camada compartilhada

Nem toda necessidade de banco precisa estar em `commonMain`. Às vezes é melhor compartilhar 80% com qualidade do que forçar 100% e aumentar atrito.

## Room em KMP substitui SQLDelight?

Não necessariamente. O que mudou é que agora existe uma opção mais forte dentro do universo AndroidX. Em alguns times, isso vai tornar o Room a escolha natural. Em outros, SQLDelight continuará fazendo mais sentido.

O ganho real é ter escolha madura. E isso é ótimo para o ecossistema Kotlin em 2026.

## Conclusão

O suporte do **Room ao Kotlin Multiplatform** é uma das evoluções mais práticas do ecossistema recente. Ele não transforma KMP em solução mágica, mas reduz bastante a fricção para equipes Android que querem compartilhar persistência local sem abandonar ferramentas familiares.

Se o seu time já domina Room, trabalha com `Flow` e está levando KMP mais a sério, estudar essa stack faz muito sentido agora. Para muita gente, ela representa o caminho mais confortável entre o mundo Android tradicional e uma arquitetura realmente multiplataforma.

E esse talvez seja o principal valor da novidade: permitir que a adoção de KMP aconteça de forma mais incremental, com menos reinvenção e mais reaproveitamento de conhecimento já consolidado. Para quem trabalha com persistência em outras linguagens, vale comparar como <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust usa SQLx e Diesel para acesso a banco com type safety em tempo de compilação</a> — uma filosofia parecida com o que o Room oferece no ecossistema Kotlin.

## Perguntas Frequentes

## Room funciona em Kotlin Multiplatform de verdade?

Sim. As versões recentes do Room já oferecem suporte a KMP, permitindo compartilhar entidades, DAOs e definição do banco em `commonMain`, com builders específicos por plataforma.

## Preciso usar KSP com Room no KMP?

Sim, na prática o KSP continua sendo parte importante do setup, e normalmente você precisa configurar os targets relevantes para geração de código.

## Qual driver SQLite usar no Room multiplataforma?

Na maioria dos casos novos, o `BundledSQLiteDriver()` é a escolha mais simples e previsível, porque mantém o comportamento mais consistente entre plataformas.

## Room em KMP é melhor que SQLDelight?

Não existe resposta universal. Para times já acostumados com AndroidX e Room, a adoção pode ser mais natural. Para outros cenários, SQLDelight ainda pode ser a melhor opção.

## O banco fica 100% compartilhado entre Android e iOS?

A estrutura principal pode ser compartilhada, mas o builder e o caminho físico do arquivo continuam específicos por plataforma. Ou seja: o núcleo compartilha bastante, mas nem tudo é idêntico.
