Overview
MiTensión follows the MVVM (Model-View-ViewModel) architecture pattern, which provides clear separation of concerns and makes the codebase maintainable, testable, and scalable.Model
Data layer with Room database, entities, DAOs, and repositories
View
UI layer built with Jetpack Compose components
ViewModel
Business logic that connects Model and View using Kotlin Flow
Architecture Layers
Data Layer (Model)
The data layer manages the app’s data sources and business entities.Components:
AppDatabase- Room database singletonMedicion- Entity representing blood pressure measurementsMedicionDao- Data Access Object for database queriesMedicionRepository- Abstraction layer between data sources and ViewModels
The database uses the singleton pattern to ensure only one instance exists throughout the app lifecycle.
Repository Pattern
The repository acts as a single source of truth and abstracts data sources from the ViewModel.Benefits:
- Decouples data sources from business logic
- Makes unit testing easier
- Provides a clean API for ViewModels
- Allows for future data source changes without affecting ViewModels
ViewModel Layer
ViewModels hold UI state and handle business logic. They survive configuration changes (like screen rotations).
ViewModels use
viewModelScope for coroutine operations, which automatically cancels when the ViewModel is cleared.Data Flow
The data flows through the architecture in a unidirectional manner:- User Input Flow
- Data Retrieval Flow
- User enters blood pressure values in
MedicionScreen - View calls
viewModel.onSistolicaChanged()orviewModel.onDiastolicaChanged() - ViewModel updates
MedicionUiState - View recomposes with new state
- User clicks “Guardar” button
- View calls
viewModel.guardarMedicion() - ViewModel validates input and calls
repository.insertarMedicion() - Repository calls
medicionDao.insertar() - Room persists data to SQLite database
- ViewModel emits success event via
SharedFlow - View collects event and shows Toast message
Dependency Injection
MiTensión uses manual dependency injection without a DI framework:- No additional dependencies required
- Simple and easy to understand
- Full control over object creation
- Good for small to medium projects
For larger projects, consider using Hilt or Koin for automated dependency injection.
State Management
UI State
MiTensión usesState<T> and MutableState<T> for UI state management:
Events
One-time events (like showing Toast messages) useSharedFlow:
Database Observations
Room queries returnFlow<T> for reactive data:
Threading Model
MiTensión uses Kotlin coroutines for asynchronous operations:Main Thread
- UI rendering
- State updates
- User interactions
Background Threads
- Database operations (Room)
- Suspend functions
- Repository calls
Room automatically handles threading - you don’t need to specify
Dispatchers.IO for database operations.Additional ViewModels
CalendarioViewModel
The Calendar screen uses a simpler state management approach for displaying monthly summaries. Location:app/src/main/java/com/fxn/mitension/ui/viewmodel/CalendarioUiState.kt
State Definition:
fechaSeleccionada- Currently selected date for month navigationresumenMensual- Map of day number to daily summary with period averagesanioMes- Computed property for year/month display
flatMapLatest to automatically reload data when the month changes:
The Map structure (
Map<Int, ResumenDiario>) enables O(1) lookup when rendering calendar cells, significantly improving performance for 30-31 day grids.DiaDetalleViewModel
The Day Detail screen displays all measurements for a specific day, grouped by period. Location:app/src/main/java/com/fxn/mitension/ui/viewmodel/DiaDetalleViewModel.kt
State Definition:
SavedStateHandle:
- Calculates day timestamp range from date parameters
- Queries measurements for that day
- Groups by period using
obtenerPeriodoParaTimestamp() - Exposes as StateFlow for UI observation
Testing Strategy
Unit Testing ViewModels
Repository Testing
Best Practices
ViewModel
- Keep business logic in ViewModel
- Use
viewModelScopefor coroutines - Expose immutable state to View
- Never pass Context to ViewModel
Repository
- Single source of truth
- Abstract data sources
- Use suspend functions for one-shot operations
- Return Flow for observable data
View/Compose
- Keep UI components stateless when possible
- Hoist state to appropriate level
- Use
rememberfor expensive operations - Collect flows safely
Data Layer
- Use Room for local persistence
- Define clear entity relationships
- Write efficient queries
- Use DAOs for database access
Related Documentation
Data Model
Explore the Room database schema and entities
UI Components
Learn about Jetpack Compose components