Clean Architecture en SwiftUI: Los conceptos que necesitas entender primero

Seamos honestos: todos hemos estado en esa reunión donde alguien dice "necesitamos Clean Architecture" y nadie se atreve a preguntar qué significa realmente. O peor, has intentado aprenderla y te has encontrado con diagramas de cebollas, flechas por todas partes y ejemplos tan abstractos que no sabes ni por dónde empezar. Hoy vamos a arreglar eso. Vamos a hablar de qué ES Clean Architecture, por qué EXISTE, y por qué debería importarte (o no). Y sí, habrá algo de código, pero primero necesitas entender la filosofía detrás. ## El problema que Clean Architecture intenta resolver Imagina que estás construyendo una casa. ¿Qué pasaría si los cimientos dependieran del tipo de pintura que vas a usar en las paredes? Sería absurdo, ¿verdad? Si quieres cambiar el color, tendrías que reconstruir toda la casa. Pues eso es exactamente lo que hacemos en el software todo el tiempo. ```swift // El problema clásico: Todo mezclado struct VistaProducto: View { var body: some View { Button("Comprar") { // Lógica de negocio mezclada con UI if producto.stock > 0 && usuario.saldo >= producto.precio { // Llamada a API mezclada con todo URLSession.shared.dataTask(with: url) { data, _, _ in // Guardado en base de datos aquí mismo CoreData.save(data) } } } } } ``` ¿Ves el problema? Si mañana: - Cambias de SwiftUI a UIKit → Reescribes todo - Cambias de CoreData a SwiftData → Reescribes todo - Cambias las reglas de negocio → Buscas en 50 archivos dónde están ## ¿Qué es Clean Architecture realmente? Clean Architecture es un conjunto de principios propuesto por Robert C. Martin (Uncle Bob) que busca crear sistemas de software que sean: 1. **Independientes de frameworks**: Tu lógica de negocio no sabe si usas SwiftUI o UIKit 2. **Testeables**: Puedes probar tu lógica sin UI, sin base de datos, sin red 3. **Independientes de la UI**: Puedes cambiar de SwiftUI a lo que sea sin tocar la lógica 4. **Independientes de la base de datos**: Cambiar de CoreData a SQLite no afecta tu lógica 5. **Independientes de agentes externos**: Tu lógica no depende de APIs específicas ### El principio fundamental: La Regla de Dependencia Esta es LA regla más importante de Clean Architecture: > **Las dependencias solo pueden apuntar hacia adentro. Nunca hacia afuera.** ¿Qué significa esto? Imagina círculos concéntricos: ``` ┌─────────────────────────────────────┐ │ UI (SwiftUI) │ ← Capa Externa │ ┌─────────────────────────────┐ │ │ │ Lógica de Aplicación │ │ ← Capa Media │ │ ┌───────────────────────┐ │ │ │ │ │ Lógica de Negocio │ │ │ ← Capa Interna │ │ │ (Lo importante) │ │ │ │ │ └───────────────────────┘ │ │ │ └─────────────────────────────┘ │ └─────────────────────────────────────┘ ``` - El círculo interno (lógica de negocio) no conoce a los externos - Los círculos externos SÍ conocen a los internos - NUNCA al revés ### ¿Por qué círculos? Porque lo más importante está en el centro, protegido. Como un castillo medieval: - **Centro**: El rey (tu lógica de negocio) - **Murallas medias**: Los nobles (casos de uso) - **Murallas externas**: Los soldados (UI, DB, APIs) Los soldados protegen al rey, pero el rey no necesita saber quiénes son los soldados. ## Las capas de Clean Architecture explicadas ### 1. Domain Layer (Capa de Dominio) - El Corazón **¿Qué es?** Es el núcleo de tu aplicación. Aquí viven las reglas de negocio, las entidades, y todo lo que hace única a tu app. **¿Qué contiene?** - **Entidades**: Los conceptos fundamentales de tu negocio - **Reglas de negocio**: La lógica que no cambia aunque cambies de plataforma - **Interfaces/Protocolos**: Los contratos que otros deben cumplir **Características clave:** - NO importa ningún framework - NO sabe nada de UI - NO sabe nada de bases de datos - Es puro Swift/Kotlin/Java/lo que uses **¿Por qué es importante?** Porque estas son las reglas que definen tu negocio. Si haces Uber, aquí está "conectar conductores con pasajeros". Si haces Instagram, aquí está "compartir fotos con seguidores". ```swift // Ejemplo conceptual - NO es sobre implementación struct Usuario { let id: String let email: String // Regla de negocio: Un email válido tiene @ var tieneEmailValido: Bool { email.contains("@") } } // Esto define QUÉ necesitas, no CÓMO obtenerlo protocol RepositorioUsuarios { func obtenerUsuario(id: String) -> Usuario? } ``` ### 2. Data Layer (Capa de Datos) - Los Trabajadores **¿Qué es?** La capa que se encarga de obtener y guardar datos. Es el puente entre tu dominio puro y el mundo exterior sucio. **¿Qué contiene?** - **Implementaciones de Repositorios**: El código real que habla con APIs/DB - **Modelos de Datos**: DTOs, entidades de CoreData, etc. - **Mappers**: Convierten entre modelos de datos y entidades del dominio **Características clave:** - SÍ conoce el Domain Layer - SÍ puede usar frameworks (CoreData, URLSession, etc.) - NO es conocida por el Domain Layer - Implementa las interfaces definidas en Domain **¿Por qué separarlo?** Imagina que tu app guarda datos localmente. Un día decides agregar sincronización con la nube. Con Clean Architecture: 1. Tu dominio no cambia (las reglas siguen siendo las mismas) 2. Solo agregas una nueva implementación del repositorio 3. Puedes tener ambas funcionando (local + nube) 4. Si la nube falla, puedes volver a local sin tocar la lógica Sin esta separación, tendrías que modificar toda tu lógica de negocio para agregar la sincronización. ### 3. Presentation Layer (Capa de Presentación) - La Cara Visible **¿Qué es?** Todo lo relacionado con mostrar información al usuario y capturar sus acciones. **¿Qué contiene?** - **Views**: SwiftUI views, UIViewControllers, etc. - **ViewModels/Presenters**: Preparan los datos para mostrar - **Navegación**: Cómo te mueves entre pantallas - **Formatters**: Convierten datos a strings para mostrar **Características clave:** - SÍ conoce Domain y Data layers - SÍ usa frameworks de UI - Se preocupa de CÓMO se ve, no de QUÉ significa - Puede cambiar completamente sin afectar la lógica ## Los beneficios reales (no el PowerPoint corporativo) ### 1. Testabilidad de verdad Sin Clean Architecture: ```swift // ¿Cómo testeas esto sin UI y sin servidor real? func comprarProducto() { if producto.stock > 0 { APIManager.shared.comprar(producto) { resultado in CoreDataManager.shared.guardar(resultado) self.actualizarUI() } } } ``` Con Clean Architecture: ```swift // Lógica pura, 100% testeable func puedeComprar(producto: Producto, usuario: Usuario) -> Bool { return producto.stock > 0 && usuario.saldo >= producto.precio } ``` ### 2. Cambios sin dolor **Escenario real**: Tu app usa una API REST. Después de 6 meses, el backend team decide cambiar a GraphQL. Sin Clean Architecture: - Tienes llamadas a la API REST en 47 ViewModels diferentes - Cada una parseando JSON de forma distinta - Cambiar significa tocar 47 archivos - Alto riesgo de romper algo - Imposible hacerlo gradualmente Con Clean Architecture: - Tienes UNA implementación de RepositorioAPI - Todas las vistas hablan con el repositorio a través de una interfaz - Cambias solo esa implementación - El resto de la app ni se entera - Puedes tener ambas implementaciones y cambiar con un flag ```swift // La interfaz no cambia protocol RepositorioProductos { func obtenerProductos() async -> [Producto] } // Solo cambias la implementación class RepositorioREST: RepositorioProductos { /* ... */ } class RepositorioGraphQL: RepositorioProductos { /* ... */ } // Un cambio, no 47 let repositorio = useGraphQL ? RepositorioGraphQL() : RepositorioREST() ``` ### 3. Trabajo en equipo real - Ana puede trabajar en las reglas de negocio - Luis puede diseñar las pantallas - María puede implementar la API - Nadie se pisa porque las interfaces están definidas ### 4. Entendimiento del negocio Cuando abres la carpeta Domain, entiendes inmediatamente qué hace la app: ``` Domain/ ├── Entities/ │ ├── Usuario.swift │ ├── Producto.swift │ └── Pedido.swift └── UseCases/ ├── RealizarPedido.swift ├── CancelarPedido.swift └── AplicarDescuento.swift ``` Sin leer una línea de código, ya sabes que es una app de e-commerce. ## Los principios SOLID y Clean Architecture Clean Architecture se basa fuertemente en SOLID, pero no necesitas ser un experto en SOLID para entenderla. Lo importante es entender el principio clave: ### El Principio de Inversión de Dependencias Este es EL principio más importante para Clean Architecture: ```swift // ❌ MAL: La lógica depende de los detalles class LogicaNegocio { let firebase = FirebaseDatabase() // Dependencia directa func procesarDatos() { let datos = firebase.getData() // Acoplado a Firebase } } // ✅ BIEN: Los detalles dependen de la lógica class LogicaNegocio { let baseDatos: ProtocoloBaseDatos // Depende de abstracción func procesarDatos() { let datos = baseDatos.obtenerDatos() // No sabe qué DB es } } ``` ¿Por qué es tan importante? Porque así: - Puedes cambiar Firebase por cualquier otra cosa - Puedes testear con una base de datos falsa - Tu lógica no se rompe si Firebase cambia su API ## Cuándo SÍ usar Clean Architecture ✅ **Úsala cuando:** - La app tiene lógica de negocio compleja - Trabajas en equipo - La app va a crecer y evolucionar - Necesitas tests robustos - Manejas datos críticos (dinero, salud, etc.) - La app vivirá por años ## Cuándo NO usar Clean Architecture ❌ **No la uses cuando:** - Es un prototipo o MVP rápido - La app es extremadamente simple (calculadora, timer) - Trabajas solo y la app no crecerá - El deadline es ayer - No hay lógica de negocio real (solo mostrar datos) **Recuerda**: Clean Architecture es una herramienta, no una religión. Úsala cuando aporte valor. ## Mitos y realidades ### Mito 1: "Clean Architecture es muy compleja" **Realidad**: Los principios son simples. La complejidad viene de sobre-ingeniería innecesaria. ### Mito 2: "Necesitas 100 archivos para empezar" **Realidad**: Puedes empezar con 3 carpetas y crecer según necesites. ### Mito 3: "Es solo para apps enterprise" **Realidad**: Los principios aplican a cualquier app con lógica de negocio. ### Mito 4: "Tienes que seguirla al 100%" **Realidad**: Puedes adaptarla a tus necesidades. No es todo o nada. ## Aclarando conceptos importantes ### ¿Clean Architecture es un patrón específico? No exactamente. Clean Architecture es más bien un conjunto de principios y guías. No te dice "debes tener exactamente estas clases con estos nombres". Te dice "separa las responsabilidades de esta forma". Es como la diferencia entre: - Una receta: "Pon 200g de harina, 2 huevos..." - Un principio de cocina: "Los sabores ácidos realzan los dulces" Clean Architecture es el principio, no la receta. ### ¿Necesito usar todos los conceptos desde el principio? No. Puedes empezar simple: 1. Separa tu lógica de negocio de las vistas 2. Define interfaces claras entre capas 3. Ve agregando abstracciones según las necesites Es un proceso gradual, no un todo o nada. ### La importancia de las interfaces (protocolos) Un concepto clave que a veces no queda claro es el uso de interfaces/protocolos. Son el "contrato" entre capas: ```swift // El Domain define QUÉ necesita, no CÓMO obtenerlo protocol ServicioAutenticacion { func iniciarSesion(email: String, password: String) async throws -> Usuario func cerrarSesion() async throws } // El Data Layer implementa el CÓMO class ServicioAutenticacionFirebase: ServicioAutenticacion { func iniciarSesion(email: String, password: String) async throws -> Usuario { // Implementación específica de Firebase } } class ServicioAutenticacionPropia: ServicioAutenticacion { func iniciarSesion(email: String, password: String) async throws -> Usuario { // Tu propia implementación } } ``` El Domain no sabe ni le importa si usas Firebase, Auth0, o tu propio sistema. Solo sabe que puede pedir autenticación. ### ¿Por qué no simplemente poner todo en una clase? Es tentador hacer esto: ```swift class GestorUsuarios { func login() { // Validar datos // Llamar API // Guardar en base de datos // Actualizar UI } } ``` Pero entonces: - No puedes testear la validación sin la API - No puedes cambiar la API sin tocar la validación - No puedes reusar la validación en otro lugar - Si falla algo, no sabes si es la validación, la API, o la DB Con Clean Architecture, cada parte tiene su lugar y puedes trabajar con ellas independientemente. ## Para reflexionar Clean Architecture no es sobre escribir más código. Es sobre escribir código que puedas entender, mantener y cambiar sin miedo. Pregúntate: - ¿Puedo explicar qué hace mi app sin mencionar SwiftUI o CoreData? - ¿Puedo testear mi lógica sin levantar toda la app? - ¿Qué tan difícil sería cambiar de base de datos? - ¿Un nuevo desarrollador entendería la estructura? Si las respuestas te incomodan, tal vez es hora de considerar Clean Architecture. ## Lo que viene En los próximos artículos veremos: - Cómo implementar cada capa en SwiftUI - Ejemplos prácticos paso a paso - Cómo migrar una app existente - Testing con Clean Architecture - Cuándo y cómo ser pragmático Por ahora, lo importante es que entiendas: 1. **Por qué** existe Clean Architecture 2. **Qué** problema resuelve 3. **Cuándo** usarla (y cuándo no) --- *¿Te gustó este artículo? En el próximo veremos la implementación práctica: cómo estructurar un proyecto SwiftUI con Clean Architecture desde cero.* **El equipo de AprendeSwift.dev** 🎯 PD: Si alguien te dice que DEBES usar Clean Architecture para todo, huye. Si te dice que NUNCA la uses, también huye. La respuesta correcta siempre es "depende" 🤷‍♂️