El sistema requiere dos aplicaciones en el frontend:
| Criterio | React Native | Flutter | Kotlin Multiplatform |
|---|---|---|---|
| Madurez | 10+ años (2015) | 7 años (2018) | 4 años (2020) |
| Comunidad | Muy Grande (118K+ stars) | Grande (165K+ stars) | Pequeña (7.8K+ stars) |
| Lenguaje | JavaScript/TypeScript | Dart | Kotlin |
| Curva de Aprendizaje | Baja (JS conocido) | Media (Dart nuevo) | Media-Alta |
| Performance | Buena (bridge nativo) | Excelente (compiled) | Excelente (nativo) |
| Paquetes Disponibles | 50,000+ | 30,000+ | 5,000+ |
| Empresas que lo Usan | Meta, Uber, Airbnb, Microsoft | Google, Alibaba, BMW | JetBrains, Netflix, VMware |
| Hot Reload | ✓ Sí | ✓ Sí (ultra rápido) | ✓ Sí (limitado) |
| Acceso APIs Nativas | Mediante bridge | Via Platform Channels | Directo (expect/actual) |
| Tamaño de App | 25-30 MB | 15-20 MB | 10-15 MB |
| Tiempo de Desarrollo | Rápido | Rápido | Medio |
| Costo de Contratación | Bajo (devs JS abundantes) | Medio (devs Dart escasos) | Alto (devs Kotlin especializados) |
| UI/UX | Componentes nativos | Material + Cupertino consistente | Totalmente nativo |
| Ideal Para | Equipos JS, MVP rápido | Apps visuales, UI consistente | Apps nativas complejas |
| Fortalezas | Debilidades |
|---|---|
|
|
| Oportunidades | Amenazas |
|---|---|
|
|
| Fortalezas | Debilidades |
|---|---|
|
|
| Oportunidades | Amenazas |
|---|---|
|
|
| Fortalezas | Debilidades |
|---|---|
|
|
| Oportunidades | Amenazas |
|---|---|
|
|
Para el proyecto bancario BP, se recomienda React Native por las siguientes razones estratégicas:
El documento menciona que JavaScript es el lenguaje principal del equipo. React Native permite:
graph TB
SharedLogic["SHARED BUSINESS LOGIC
(NPM Package / Monorepo)
• Validación de formularios
• Formateo de datos
• Utilidades comunes
• Constantes y tipos TypeScript
• Lógica de negocio
• GraphQL queries"]
SPA["SPA WEB
(React.js)
• React Router
• Tailwind CSS
• Web APIs"]
Mobile["APP MÓVIL
(React Native)
• React Navigation
• Native Base
• Native APIs"]
SharedLogic --> SPA
SharedLogic --> Mobile
style SharedLogic fill:#E8F5E9,stroke:#4CAF50,stroke-width:3px,rx:15,ry:15
style SPA fill:#E3F2FD,stroke:#2196F3,stroke-width:3px,rx:15,ry:15
style Mobile fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
Reutilización estimada: 60-70% del código
| Aspecto | React Native | Flutter | Kotlin MP |
|---|---|---|---|
| Desarrollo MVP | 3-4 meses | 4-5 meses | 6-8 meses |
| Onboarding de Devs | 1 semana | 3-4 semanas | 6-8 semanas |
| Prototipado | Muy rápido | Rápido | Lento |
| Perfil | Disponibilidad | Salario Promedio (USD/año) |
|---|---|---|
| React Native Developer | Alta (abundantes) | $60,000 - $90,000 |
| Flutter Developer | Media (escasos) | $70,000 - $100,000 |
| Kotlin MP Developer | Baja (muy escasos) | $85,000 - $120,000 |
| Concepto | React Native | Flutter | Kotlin MP |
|---|---|---|---|
| Desarrollo Inicial | $120,000 | $150,000 | $200,000 |
| Salarios Team (3 años) | $540,000 | $630,000 | $810,000 |
| Mantenimiento | $90,000 | $100,000 | $120,000 |
| Capacitación | $10,000 | $30,000 | $60,000 |
| TOTAL 3 AÑOS | $760,000 | $910,000 | $1,190,000 |
| Componente | Tecnología | Justificación |
|---|---|---|
| Framework Base | React Native 0.73+ | Última versión estable con nueva arquitectura Fabric |
| Navegación | React Navigation 6 | Estándar de facto, APIs declarativas, TypeScript support |
| Estado Global | Redux Toolkit + RTK Query | Gestión de estado predecible + data fetching optimizado |
| GraphQL Client | Apollo Client | Caché inteligente, subscriptions, integración React hooks |
| UI Components | NativeBase o React Native Paper | Componentes accesibles y bien diseñados |
| Biometría | react-native-biometrics | Soporte Face ID, Touch ID, fingerprint |
| Almacenamiento Seguro | react-native-keychain | Keychain (iOS) / Keystore (Android) para tokens |
| Push Notifications | @react-native-firebase/messaging | Integración con Firebase Cloud Messaging |
| Analytics | Firebase Analytics | Tracking de eventos, funnels, retención |
| Crash Reporting | Sentry | Error tracking en producción con source maps |
| Testing | Jest + React Native Testing Library | Unit + integration tests con mocking robusto |
| E2E Testing | Detox | Tests end-to-end en dispositivos reales/emuladores |
| Code Quality | ESLint + Prettier + TypeScript | Linting, formateo automático, type safety |
| Componente | Tecnología | Justificación |
|---|---|---|
| Framework Base | React.js 18+ | Mismo ecosistema que móvil, concurrent rendering |
| Navegación | React Router 6 | Routing declarativo con data loading |
| Estado Global | Redux Toolkit + RTK Query | Mismo stack que móvil = reutilización |
| GraphQL Client | Apollo Client | Mismo que móvil = reutilización de queries |
| UI Framework | Tailwind CSS + Headless UI | Utility-first CSS, componentes accesibles |
| Build Tool | Vite | Build ultra-rápido, HMR instantáneo |
| Testing | Vitest + React Testing Library | Tests rápidos con misma API que Jest |
mobile-app/
├── src/
│ ├── components/ # Componentes reutilizables
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ ├── Card.tsx
│ │ └── AccountCard.tsx
│ │
│ ├── screens/ # Pantallas de la app
│ │ ├── Auth/
│ │ │ ├── LoginScreen.tsx
│ │ │ ├── RegisterScreen.tsx
│ │ │ └── BiometricSetupScreen.tsx
│ │ ├── Home/
│ │ │ ├── HomeScreen.tsx
│ │ │ └── AccountDetailScreen.tsx
│ │ ├── Transfers/
│ │ │ ├── TransferScreen.tsx
│ │ │ └── ConfirmTransferScreen.tsx
│ │ └── Profile/
│ │ └── ProfileScreen.tsx
│ │
│ ├── navigation/ # Configuración de navegación
│ │ ├── RootNavigator.tsx
│ │ ├── AuthNavigator.tsx
│ │ └── MainNavigator.tsx
│ │
│ ├── services/ # Llamadas API y lógica de negocio
│ │ ├── api/
│ │ │ ├── apollo-client.ts
│ │ │ ├── auth.service.ts
│ │ │ └── transfer.service.ts
│ │ └── biometric/
│ │ └── biometric.service.ts
│ │
│ ├── store/ # Redux store
│ │ ├── store.ts
│ │ ├── slices/
│ │ │ ├── authSlice.ts
│ │ │ ├── accountSlice.ts
│ │ │ └── transferSlice.ts
│ │ └── api/
│ │ └── apiSlice.ts # RTK Query
│ │
│ ├── hooks/ # Custom React hooks
│ │ ├── useAuth.ts
│ │ ├── useBiometric.ts
│ │ └── useTransfer.ts
│ │
│ ├── utils/ # Utilidades
│ │ ├── formatters.ts
│ │ ├── validators.ts
│ │ └── constants.ts
│ │
│ ├── types/ # TypeScript types
│ │ ├── auth.types.ts
│ │ ├── account.types.ts
│ │ └── api.types.ts
│ │
│ └── graphql/ # GraphQL queries y mutations
│ ├── queries/
│ │ ├── accounts.gql.ts
│ │ └── transactions.gql.ts
│ └── mutations/
│ └── transfer.gql.ts
│
├── android/ # Código nativo Android
├── ios/ # Código nativo iOS
├── shared-logic/ # Lógica compartida con web
│ ├── validators/
│ ├── formatters/
│ └── constants/
│
└── __tests__/ # Tests
├── unit/
├── integration/
└── e2e/
flowchart TD
Start([Usuario abre app]) --> CheckSession{Tiene sesión activa
y biometría habilitada?}
CheckSession -->|Sí| RequestBiometric[Solicita Face ID / Touch ID]
CheckSession -->|No| LoginScreen[LoginScreen]
RequestBiometric --> BiometricResult{Biometría exitosa?}
BiometricResult -->|Sí| RecoverToken[Recupera token desde Keychain]
BiometricResult -->|No| BiometricFailed[LoginScreen
con opción de reintentar]
RecoverToken --> ValidateToken[Valida token con backend]
ValidateToken --> TokenValid{Token válido?}
TokenValid -->|Sí| HomeScreen([HomeScreen])
TokenValid -->|No| LoginScreen
LoginScreen --> EnterCredentials[Usuario ingresa credenciales]
EnterCredentials --> PostLogin[POST OAuth 2.0 /login]
PostLogin --> LoginResult{Login exitoso?}
LoginResult -->|Sí| SaveTokens[Guardar tokens en Keychain]
SaveTokens --> AskBiometric[Preguntar si desea
habilitar biometría]
AskBiometric --> HomeScreen
LoginResult -->|No| ShowError[Mostrar error]
ShowError --> LoginScreen
LoginScreen --> ForgotPassword[Olvidé mi contraseña]
style Start fill:#E8F5E9,stroke:#4CAF50,stroke-width:3px,rx:15,ry:15
style HomeScreen fill:#E8F5E9,stroke:#4CAF50,stroke-width:3px,rx:15,ry:15
style LoginScreen fill:#E3F2FD,stroke:#2196F3,stroke-width:3px,rx:15,ry:15
style RequestBiometric fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
style RecoverToken fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
style SaveTokens fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
style ShowError fill:#FFEBEE,stroke:#F44336,stroke-width:3px,rx:15,ry:15
style BiometricFailed fill:#FFEBEE,stroke:#F44336,stroke-width:3px,rx:15,ry:15
Separación clara entre lógica y presentación:
| Tipo | Responsabilidad | Ejemplo |
|---|---|---|
| Container (Smart) |
|
TransferScreenContainer.tsx |
| Presentational (Dumb) |
|
TransferForm.tsx |
Encapsulación de lógica reutilizable:
flowchart TB
subgraph Pages["PAGES (Templates)"]
TransferScreen[TransferScreen.tsx]
end
subgraph Organisms["ORGANISMS"]
TransferForm[TransferForm]
AccountList[AccountList]
TransactionHistory[TransactionHistory]
end
subgraph Molecules["MOLECULES"]
AccountCard[AccountCard]
InputGroup[InputGroup]
TransactionItem[TransactionItem]
end
subgraph Atoms["ATOMS"]
Button[Button]
Input[Input]
Text[Text]
Icon[Icon]
Avatar[Avatar]
end
Pages --> Organisms
Organisms --> Molecules
Molecules --> Atoms
style Pages fill:#E8F5E9,stroke:#4CAF50,stroke-width:3px,rx:15,ry:15
style Organisms fill:#E3F2FD,stroke:#2196F3,stroke-width:3px,rx:15,ry:15
style Molecules fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
style Atoms fill:#FFE5E5,stroke:#E74C3C,stroke-width:3px,rx:15,ry:15
classDiagram
class ReduxStore {
+State Slices
+RTK Query API Cache
}
class StateSlices {
+auth: AuthState
+accounts: AccountsState
+transfers: TransfersState
+ui: UIState
}
class AuthState {
+user: User
+token: string
+isAuthenticated: boolean
}
class AccountsState {
+list: Account[]
+selected: Account
+loading: boolean
}
class TransfersState {
+pending: Transfer[]
+history: Transfer[]
}
class UIState {
+theme: string
+language: string
+notifications: boolean
}
class RTKQuery {
+Automatic caching GraphQL queries
+Invalidación inteligente
+Polling y refetch automático
}
ReduxStore *-- StateSlices
ReduxStore *-- RTKQuery
StateSlices *-- AuthState
StateSlices *-- AccountsState
StateSlices *-- TransfersState
StateSlices *-- UIState
style ReduxStore fill:#E8F5E9,stroke:#4CAF50,stroke-width:3px,rx:15,ry:15
style StateSlices fill:#E3F2FD,stroke:#2196F3,stroke-width:3px,rx:15,ry:15
style RTKQuery fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
| Dato | Storage | Persistencia |
|---|---|---|
| Tokens de autenticación | Keychain (iOS) / Keystore (Android) | Permanente hasta logout |
| Preferencias de usuario | AsyncStorage | Permanente |
| Caché de datos | Redux Persist + AsyncStorage | Configurable (ej: 24 horas) |
| Datos sensibles | Solo en memoria (Redux) | Solo durante sesión activa |
| Técnica | Implementación | Beneficio |
|---|---|---|
| Lazy Loading | React.lazy() para screens pesadas | Reduce bundle inicial en 30-40% |
| Memoization | React.memo, useMemo, useCallback | Evita re-renders innecesarios |
| FlatList Optimization | windowSize, maxToRenderPerBatch, initialNumToRender | Listas de 1000+ items sin lag |
| Image Optimization | react-native-fast-image con caché | Carga de imágenes 3x más rápida |
| Hermes Engine | Motor JS optimizado para React Native | Reducción 50% en tiempo de inicio |
| Code Splitting | Separar código por features | Downloads bajo demanda |
| Métrica | Objetivo | Herramienta de Medición |
|---|---|---|
| Time to Interactive | < 3 segundos | Flipper Performance Monitor |
| Frame Rate | 60 FPS consistente | Flipper / Perf Monitor |
| Bundle Size | < 25 MB (APK/IPA) | Build analytics |
| API Response Time | < 500ms (p95) | Apollo DevTools |
| Crash-free Rate | > 99.5% | Sentry |
| Aspecto | Implementación |
|---|---|
| Almacenamiento de Tokens |
|
| Certificate Pinning |
|
| Jailbreak/Root Detection |
|
| Ofuscación de Código |
|
| Input Validation |
|
| Session Timeout |
|
| Ataque | Prevención |
|---|---|
| Screenshot / Screen Recording | Bloquear capturas en pantallas con datos sensibles |
| Clipboard Hijacking | No copiar datos sensibles al clipboard |
| Deep Link Exploitation | Validar todos los deep links, nunca ejecutar acciones sensibles directamente |
| Reverse Engineering | Ofuscación + detección de debugging + certificate pinning |
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#FFE5E5','primaryTextColor':'#2C3E50','primaryBorderColor':'#E74C3C','lineColor':'#34495E','secondaryColor':'#E3F2FD','tertiaryColor':'#FFF9C4'}}}%%
graph BT
subgraph E2E["E2E Tests (5%)"]
E2E1[Detox]
E2E2[Flujos completos]
E2E3[Login, Transfer]
end
subgraph Integration["Integration Tests (20%)"]
Int1[React Native Testing Library]
Int2[Componentes + Redux]
Int3[Navigation flows]
end
subgraph Unit["Unit Tests (75%)"]
U1[Jest]
U2[Funciones puras]
U3[Utils, validators]
end
Unit --> Integration
Integration --> E2E
style E2E fill:#FFE5E5,stroke:#E74C3C,stroke-width:3px,rx:15,ry:15
style Integration fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
style Unit fill:#E8F5E9,stroke:#4CAF50,stroke-width:3px,rx:15,ry:15
| Tipo | Cobertura Objetivo | Qué se Testea |
|---|---|---|
| Unit Tests | > 80% | Utils, formatters, validators, custom hooks |
| Integration Tests | > 60% | Componentes con Redux, navigation, API calls |
| E2E Tests | Flujos críticos | Login, transferencias, consultas, cambio clave |
| Snapshot Tests | Componentes UI | Prevenir regresiones visuales |
flowchart TD
Start([Developer Push a Git]) --> TriggerCI[Trigger CI
GitHub Actions / GitLab CI]
TriggerCI --> Lint[Lint & Type Check]
Lint --> LintItems[ESLint
TypeScript compiler]
LintItems --> LintCheck{Pasa?}
LintCheck -->|No| BlockPR1[Bloquear PR]
LintCheck -->|Sí| Tests
Tests[Unit & Integration Tests] --> TestItems[Jest - all tests]
TestItems --> TestCheck{Pasa?}
TestCheck -->|No| BlockPR2[Bloquear PR]
TestCheck -->|Sí| Build
Build[Build App] --> BuildItems[Android: gradlew assembleRelease
iOS: xcodebuild archive]
BuildItems --> BuildCheck{Pasa?}
BuildCheck -->|No| NotifyTeam1[Notificar team]
BuildCheck -->|Sí| E2E
E2E[E2E Tests - Detox] --> E2EItems[Run en emuladores]
E2EItems --> E2ECheck{Pasa?}
E2ECheck -->|No| NotifyTeam2[Notificar team]
E2ECheck -->|Sí| DeployBeta
DeployBeta[Deploy to Beta] --> BetaItems[Android: Firebase App Distribution
iOS: TestFlight
Notificar testers]
BetaItems --> QA[Manual QA Approval]
QA --> Production[Deploy to Production]
Production --> ProdItems[Android: Google Play staged rollout
iOS: App Store phased release]
ProdItems --> End([Deployment Completo])
style Start fill:#E8F5E9,stroke:#4CAF50,stroke-width:3px,rx:15,ry:15
style End fill:#E8F5E9,stroke:#4CAF50,stroke-width:3px,rx:15,ry:15
style Lint fill:#E3F2FD,stroke:#2196F3,stroke-width:3px,rx:15,ry:15
style Tests fill:#E3F2FD,stroke:#2196F3,stroke-width:3px,rx:15,ry:15
style Build fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
style E2E fill:#FFF9C4,stroke:#FBC02D,stroke-width:3px,rx:15,ry:15
style DeployBeta fill:#FFE5E5,stroke:#E74C3C,stroke-width:3px,rx:15,ry:15
style Production fill:#FFE5E5,stroke:#E74C3C,stroke-width:3px,rx:15,ry:15
style BlockPR1 fill:#FFEBEE,stroke:#C62828,stroke-width:3px,rx:15,ry:15
style BlockPR2 fill:#FFEBEE,stroke:#C62828,stroke-width:3px,rx:15,ry:15
| Ambiente | Frecuencia | Estrategia |
|---|---|---|
| Beta (Internal) | Diaria | Automatic deployment on merge to develop |
| Beta (External) | Semanal | TestFlight / Firebase App Distribution |
| Production | Bi-semanal | Staged rollout: 10% → 25% → 50% → 100% en 3 días |
| Herramienta | Uso |
|---|---|
| @react-native-community/eslint-plugin | Linting de reglas de accesibilidad |
| Flipper Accessibility Inspector | Debug de issues de accesibilidad |
| Manual Testing | Testing con VoiceOver y TalkBack habilitados |
| Aspecto | Implementación |
|---|---|
| Librería | react-i18next |
| Idiomas Soportados | Español (es), Inglés (en) |
| Detección de Idioma | Automática según configuración del dispositivo |
| Formateo de Números | Intl.NumberFormat para montos |
| Formateo de Fechas | date-fns con locale support |
| Categoría | Eventos | Herramienta |
|---|---|---|
| User Engagement |
|
Firebase Analytics |
| Business Metrics |
|
Firebase Analytics + Custom events |
| Performance |
|
Firebase Performance Monitoring |
| Errors & Crashes |
|
Sentry |
| Concepto | Costo Estimado (USD) |
|---|---|
| Desarrollo MVP (4 meses) | $120,000 |
| Apple Developer Account | $99/año |
| Google Play Developer Account | $25 (único) |
| Firebase (Analytics + Performance) | $0 (Spark plan gratis) |
| Sentry (Crash reporting) | $26/mes (Team plan) |
| CI/CD (GitHub Actions) | $0 (incluido en GitHub) |
La arquitectura frontend propuesta con React Native ofrece:
Decisión final: React Native es la mejor opción considerando el stack actual del equipo, time-to-market, costos y reutilización de código.