Los niveles del laboratorio te dan los ladrillos. Los proyectos te obligan a construir una casa. No hagas los seis: elige uno por fase y llévalo hasta el final.
Cómo elegir
| Si quieres… | Haz… |
|---|---|
| afianzar Compose y UI | Proyecto 1 |
| dominar dominio y tests | Proyecto 2 |
| practicar Android de verdad | Proyecto 3 |
| aprender KMP | Proyecto 4 |
| aprender backend Kotlin | Proyecto 5 |
| demostrar que sabes trabajar en un repo ajeno | Proyecto 6 |
Regla: un proyecto terminado vale más que cuatro empezados. “Terminado” significa: compila, tiene tests, tiene README y puedes explicarlo en 3 minutos.
Proyecto 1 — Portar una pantalla Flutter a Compose
Objetivo: traducir a Compose una pantalla real de una app Flutter tuya (de trabajo o personal) sin copiar la estructura de Flutter.
Alcance mínimo
Una pantalla con lista y detalle, o formulario con validación.
Tres estados:
Loading,Content,Empty/Error.Navegación entre lista y detalle.
@Previewpara cada estado.Un test de UI o de ViewModel.
Accesibilidad básica:
contentDescription, tamaños táctiles, contraste.
Restricciones
Nada de lógica de negocio dentro de
@Composable.El estado se modela con un
data classinmutable.Los eventos suben; el estado baja.
No uses
rememberpara estado que deba sobrevivir a navegación.
Archivos de referencia en Kontor
feature/accounts/src/main/kotlin/com/rubengmlp/kontor/feature/accounts/AccountsScreen.ktfeature/accounts/src/main/kotlin/com/rubengmlp/kontor/feature/accounts/AccountsViewModel.ktcore/designsystem/src/main/kotlin/com/rubengmlp/kontor/core/designsystem/components/KontorComponents.kt
Entregable
Código en un módulo nuevo o en una carpeta
experiments/de Kontor.Capturas o GIF de los tres estados.
README con: qué pantalla, qué decisiones tomaste, qué cambió respecto a Flutter.
Tests verdes.
Criterios de aceptación
[ ] La UI no conoce el repositorio.
[ ] El ViewModel no conoce Compose.
[ ] Hay un estado por caso, no cinco booleanos.
[ ] Los previews no necesitan backend.
[ ] El test falla si rompes el estado vacío.
[ ] Puedes explicar
remembervsViewModelsin mirar.
Tiempo estimado: 8–12 horas.
Stretch: animaciones, tema oscuro, layout para tablet, screenshot tests.
Proyecto 2 — Librería de dominio bancario (JVM)
Objetivo: construir un núcleo de dominio testeable, sin Android, que podría compartirse con KMP.
Alcance mínimo
Moneycon céntimos, suma, resta y formato local.Ibancon normalización y validación de checksum.TransferRequest,TransferValidationyTransferValidator.Accountcon saldo y saldo disponible.Un caso de uso
SubmitTransferque devuelvaResult<TransferReceipt>.20 tests, incluidos bordes: cero, negativo, IBAN inválido, saldo insuficiente, referencia larga.
Restricciones
Nada de
Doublepara dinero.Nada de
!!.Los errores esperados se modelan como datos, no como excepciones.
El dominio no depende de Room, Retrofit ni Android.
Dónde construirlo
Nuevo módulo
projects/banking-domaindentro dekotlin-lab, o un repo aparte.Reutiliza los niveles 3, 4 y 7 (el
Ibancon smart constructor) como punto de partida.
Entregable
README con el modelo y un diagrama pequeño.
Tests verdes.
Un ADR de una página: “por qué céntimos y no
BigDecimalen la API”.
Criterios de aceptación
[ ]
Moneyno se puede construir con unDoubleaccidentalmente.[ ] El validador devuelve todos los motivos, no solo el primero.
[ ] El caso de uso es independiente de la UI.
[ ] Hay tests de límites, no solo del camino feliz.
[ ] Puedes explicar por qué
Resultno es lo mismo que una excepción.
Tiempo estimado: 6–10 horas.
Stretch: property-based testing con Kotest, serialización JSON, módulo KMP.
Proyecto 3 — Kontor: transferencias programadas
Objetivo: añadir una feature completa a Kontor: programar una transferencia para una fecha futura, listarla y cancelarla.
Alcance mínimo
Modelo
ScheduledTransferencore/domain.Entidad y DAO en
core/data/local.Migración de Room.
Repositorio y caso de uso.
ScheduledTransfersViewModelconUiState.Pantalla Compose con lista, estado vacío y cancelación.
Validación: fecha futura, importe positivo, saldo suficiente, IBAN válido.
Tests de dominio, ViewModel y DAO.
Navegación desde la pantalla de transferencias.
Archivos de referencia
core/domain/src/main/kotlin/com/rubengmlp/kontor/core/domain/model/Transfer.ktcore/data/src/main/kotlin/com/rubengmlp/kontor/core/data/local/TransferDao.ktcore/data/src/main/kotlin/com/rubengmlp/kontor/core/data/local/KontorDatabase.ktfeature/transfer/src/main/kotlin/com/rubengmlp/kontor/feature/transfer/TransferViewModel.ktapp/src/main/kotlin/com/rubengmlp/kontor/navigation/Routes.kt
Restricciones
La transferencia programada no mueve dinero al crearse.
Cancelar no borra el historial: marca estado
CANCELLED.La UI no calcula validaciones; usa el validador de dominio.
La fecha se guarda como
Instanto epoch millis, no comoString.
Entregable
Feature funcionando en el emulador.
Migración probada.
Tests verdes.
Nota de 10 líneas: qué harías distinto en producción (idempotencia, reintentos, WorkManager, backend autoritativo).
Criterios de aceptación
[ ] No se puede programar en el pasado.
[ ] Cancelar una transferencia ya ejecutada falla con un error claro.
[ ] La lista reacciona a cambios de Room.
[ ] El ViewModel no usa
GlobalScope.[ ] Hay un test que cubre la migración o el DAO.
[ ] La pantalla tiene estados loading/empty/content/error.
Tiempo estimado: 12–18 horas.
Stretch: recurrencia mensual, notificaciones, confirmación biométrica, idempotency key.
Proyecto 4 — Kotlin Multiplatform: núcleo compartido
Objetivo: compartir Money e IbanValidator entre Android y JVM sin duplicar lógica.
Alcance mínimo
Módulo
shared-coreconcommonMain.Money,IbanValidatory unTransferValidatoren común.Tests en
commonTest.Consumo desde un pequeño programa JVM.
Consumo desde Kontor o desde un módulo Android de prueba.
README con el grafo de dependencias.
Restricciones
commonMainno puede usar APIs de Android ni de JVM.Si necesitas fecha/hora, usa
kotlinx-datetimeo pasa el dato desde fuera.No compartas UI en esta fase; comparte dominio.
Entregable
Módulo compilando para JVM y Android.
Tests comunes verdes.
Explicación de
expect/actualy de cuándo no usarlo.
Criterios de aceptación
[ ] El dominio compartido no importa
android.*.[ ] Los tests se ejecutan en JVM.
[ ] Kontor o el programa JVM usan el módulo real, no una copia.
[ ] Puedes explicar qué código no merece la pena compartir.
Tiempo estimado: 10–16 horas.
Stretch: Ktor client compartido, SQLDelight, publicación local del módulo.
Proyecto 5 — API Ktor de cuentas y transferencias
Objetivo: construir el backend que Kontor merece, con tests y errores bien modelados.
Alcance mínimo
GET /accountsGET /accounts/{id}POST /transfersGET /transfers/{id}DTOs con
kotlinx.serialization.Validación de IBAN, importe y saldo.
Errores con código y mensaje:
400,404,409,422.Repositorio en memoria.
Tests con
testApplication.README con ejemplos
curl.
Restricciones
No devuelvas
200con un error dentro.No uses
Doublepara dinero.El servidor valida otra vez; no confía en el cliente.
Los tests no dependen de una base de datos real.
Entregable
API ejecutable con
./gradlew run.Colección de ejemplos o
curl.Tests verdes.
Nota sobre idempotencia y concurrencia.
Criterios de aceptación
[ ] Un importe negativo devuelve
422.[ ] Un IBAN inválido devuelve
422.[ ] Una transferencia sin saldo devuelve
409.[ ] Un id inexistente devuelve
404.[ ] Hay un test por código de error.
[ ] El README se puede seguir sin preguntarte nada.
Tiempo estimado: 8–14 horas.
Stretch: autenticación, idempotency key, paginación, OpenAPI, PostgreSQL.
Proyecto 6 — Contribución open source
Objetivo: demostrar que puedes entrar en un código que no escribiste, entenderlo y mejorarlo.
Alcance mínimo
Encuentra un issue pequeño en un repo Kotlin/Android (documentación, bug acotado, test que falta).
Reproduce el problema.
Escribe un test que falle.
Haz el cambio mínimo.
Abre un PR con descripción, evidencia y limitaciones.
Dónde buscar
Issues con etiqueta
good first issueen repos de Kotlin, Ktor, Compose, Room o librerías que uses.No empieces por un repo enorme si es tu primera contribución.
Evita issues sin respuesta durante meses.
Entregable
Enlace al issue y al PR.
Nota: qué entendiste mal al principio, cómo lo depuraste, qué aprendiste del maintainer.
Aunque el PR no se mergee, el análisis cuenta.
Criterios de aceptación
[ ] El test falla antes del cambio.
[ ] El cambio es pequeño y explicable.
[ ] No mezclas refactors no relacionados.
[ ] La descripción del PR explica el “antes” y el “después”.
[ ] Respetas el estilo y las convenciones del repo.
Tiempo estimado: 10–20 horas.
Stretch: revisar el PR de otra persona, escribir documentación, participar en una discusión de diseño.
README de portfolio
Cada proyecto debe tener un README con estas secciones:
# Nombre
## Qué hace
Dos frases. Sin marketing.
## Demo
GIF, captura o enlace.
## Arquitectura
Módulos, capas y flujo de datos. Un diagrama vale más que 20 líneas.
## Decisiones
- Por qué céntimos y no Double.
- Por qué Room es la fuente de verdad.
- Por qué el ViewModel no conoce Compose.
## Tests
Qué cubren y qué no.
## Cómo ejecutarlo
Comandos exactos.
## Límites
Qué no hace, qué harías con más tiempo, qué es demo y qué es producción.
## Qué aprendí
Tres cosas concretas, no “aprendí mucho”.
Cómo contarlo en una entrevista
Usa este guion de 3 minutos:
Problema: “Kontor necesitaba transferencias programadas y no había forma de cancelarlas.”
Diseño: “Modelé el estado como sealed, guardé la fecha como Instant y usé Room como fuente de verdad.”
Decisión difícil: “Elegí no ejecutar la transferencia en el cliente; el backend sería autoritativo.”
Prueba: “Tengo tests de dominio, ViewModel y DAO; el de cancelación cubre el caso de estado inválido.”
Límite: “No implementé reintentos ni idempotencia real; lo siguiente sería una idempotency key y WorkManager.”
Si no puedes contar el límite, no has entendido el proyecto.