La decision arquitectonica de Admin Finance se centra en maximizar la reutilizacion de codigo entre plataformas. El resultado es un proyecto donde el 100% del codigo de dominio y datos es compartido entre Android y Desktop, con solo la capa de presentacion y la inyeccion de dependencias siendo especificas de cada plataforma.

Estructura de Directorios

composeApp/
├── src/commonMain/kotlin/com/masbits/studio/
│   ├── presentation/        # UI (Composables, ViewModels) [COMUN]
│   ├── domain/              # Models, Use Cases [COMUN]
│   ├── data/                # Repositories, Database, Sync [COMUN]
│   ├── di/                  # Koin modules [PARCIALMENTE COMUN]
│   └── utils/               # Helpers [COMUN]
├── src/androidMain/kotlin/  # Android-specific [ANDROID]
│   ├── MainActivity.kt
│   └── di/
│       ├── AndroidDI.kt
│       └── PresentationModule.kt
└── src/jvmMain/kotlin/      # Desktop-specific [DESKTOP]
    ├── main.kt
    └── di/
        ├── DesktopDI.kt
        └── PresentationModule.kt

Capa de Dominio

La capa de dominio contiene todos los modelos de dominio y la logica de negocio pura, sin dependencias de plataforma:

  • Transaction — Modelo principal con tipo (INCOME, EXPENSE, TRANSFER), categoria, monto, fecha y opcionales empresa y notas
  • Company — Gestion de multiples empresas para multi-tenancy
  • ExpenseCategory — Categorias de gastos con colores para visualizacion
  • Invoice / VATEntry — Documentos para exportacion de IVA
  • DailyHourSettings — Configuracion de horas diarias trabajadas

Los modelos utilizan serializacion Kotlin para la sincronizacion P2P y son completamente independientes de la plataforma subyacente.

Capa de Datos

La capa de datos implementa el Repository Pattern, abstraendo completamente el almacenamiento subyacente:

SQLDelight como Fuente de Verdad

SQLDelight genera tipos de datos a partir de archivos .sq definidos por el desarrollador. Esto proporciona:

  • Tipos de datos compilados y seguros desde la base de datos
  • Migrationes versionadas y validadas en compilacion
  • Queries tipadas sin riesgo de errores de string

Repositorios Implementados

  • TransactionRepository — CRUD completo, filtrado por fecha, calculo de totales por tipo
  • VATRepository — Gestion de facturas y entradas de IVA
  • DeductionRepository — Deducciones fiscales por empresa
  • DailyHourSettingsRepository — Preferencias de horas diarias
  • PreferencesManager — Preferencias generales de la app (tema, configuracion)

Sincronizacion P2P

El sistema de sincronizacion se implementa como una capa de datos adicional:

  • SyncService — Orquestador principal que gestiona la conexion, deteccion de dispositivos y ejecucion de sincronizaciones
  • SyncProtocol — Protocolo personalizado de comunicacion basado en TCP
  • SyncMessage — Tipos de mensajes: DISCOVER, DISCOVER_ACK, SYNC_REQUEST, SYNC_DATA, SYNC_ACK, ERROR, CLOSE
  • SyncMerger — Algoritmo de fusion basado en timestamps para resolver conflictos en campos modificados de transacciones

Capa de Presentacion

La presentacion utiliza Jetpack Compose con arquitectura MVVM:

  • ViewModels — TransactionViewModel, CompanyViewModel, SettingsViewModel, etc.
  • StateFlow — Estado reactivo con estados de carga, error y datos
  • Composables — Componentes reutilizables organizados por dominio: charts, transactions, companies, export, settings

Aunque la mayor parte de los Composables esta en commonMain, algunos componentes especificos de plataforma (como la actividad principal en Android o el window en Desktop) requieren implementaciones separate.

Inyeccion de Dependencias con Koin

Koin gestiona todo el ciclo de vida de las dependencias:

DataModule (commonMain)

Define todas las dependencias de la capa de datos y dominio:

// Database
single { AppDatabase(driver = SqlDriver(...)) }
single { TransactionRepositoryImpl(database = get()) }
single { VATRepositoryImpl(database = get()) }
// ... mas repositorios

// Sync single { SyncService(...) } single { SyncMerger(...) }

PresentationModule (platform-specific)

Define los ViewModels y la inicializacion de Koin por plataforma:

// androidMain - AndroidDI.kt
class AndroidDI : KoinAndroidContext { ... }
// jvmMain - DesktopDI.kt
fun startDesktopDI() { startKoin { ... } }

Diagrama de Dependencias

┌─────────────┐     ┌─────────────┐
│ Presentation │────▶│   Domain    │
│  (Compose)  │     │  (Models)   │
└──────┬──────┘     └──────┬──────┘
       │                   │
       ▼                   ▼
┌─────────────┐     ┌─────────────┐
│   Koin DI   │────▶│   Data      │
│  (Koin)     │     │  (SQLDelight│
└─────────────┘     │  + TCP Sync)│
                    └─────────────┘

Testing

El proyecto incluye 86+ tests en commonTest que se ejecutan tanto en Android como en Desktop:

  • MockK — Mocking de dependencias en tests
  • Turbine — Testing de Flows/StateFlow
  • SQLDelight JDBC Driver — Driver SQLite JDBC para tests en JVM

Resultados de la Arquitectura

  • Ratio de codigo compartido: 100% en capa de dominio y datos
  • Build dual: APK Android (~13MB) + ejecutable Windows MSI/Portable
  • Testing unificado — Los mismos tests ejecutables en ambos targets
  • Escalabilidad — Nuevas plataformas (iOS, Web) podrian agregar codigo sin modificar el comun