in Modular Architecture

App Monolítica vs App Modular ⚙️

En esta entrada compartiré los desafíos a los que me enfrentado al convertir una aplicación monolítica a una modular en iOS.

El inicio

Toda app comienza de forma monolítica. Esto es o un solo Proyecto, o un Workspace con un solo proyecto. 

Cómo sé si mi app es monolítica? Probablemente tu proyecto luzca así:

A medida que tu app crece cada feature, cada dependencia que agregas los tiempos de compilación se elevan, no hay límites definidos al estar todo en un solo proyecto. 

En este contexto, es fundamental modularizar tu gran código base, en componentes pequeños, independientes y testeables.

Sí.

Pero Qué es Arquitectura Modular?

La programación modular es un paradigma de programación que consiste en dividir un programa en módulos o subprogramas con el fin de hacerlo más legible y manejable.

Se presenta históricamente como una evolución de la programación estructurada para solucionar problemas de programación más grandes y complejos de lo que esta puede resolver.

Beneficios:

  • Escalamiento:

Separar el código en grupos por funcionalidades es fundamental para poder escalar.

  • División de equipos: 👨‍💻 👩‍💻

Al utilizar frameworks, módulos estamos aislando funcionalidades.

Varios desarrolladores o equipos asignados a determinado módulo de la app.

Individualmente cada equipo puede usar su propia arquitectura

Aún si no maneja una guía de estilo de desarrollo, cada equipo podría tener su estilo de codificación, incluso puedes hacer pruebas de nuevas arquitecturas orientadas solo a determinada feature.

  • Tiempos de compilación más rápidos. 🚀

Al trabajar en un módulo específico solo compilas las dependencias de ese módulo.

  • Tiempo de desarrollo más rápido.

No solo al compilar, puedes acceder directamente a determinada pantalla que estés modificando sin pasar por todas las pantallas de la aplicación principal.

  • Mantenimiento: Agregar una nueva feature es realmente fácil.
  • Testing: Se pueden hacer pruebas cada feature individualmente. La UI aún debe ser testeada, pero los test de integración se hacen más sencillos.

Luego de hacer un refactoring, ahora la app luce así:

Para el proyecto de ejemplo utilicé Cocoa Pods, pero también es aplicable a Swift Package Manager o Carthage.

Modulos en Detalle:

  • Networking: 

Manejo de las conexiones con los servicios.

Por ejemplo aquí podría una dependencia de tercero, por ejemplo Alamofire, Moya, etc y sólo pertenecería a este módulo.

  • Shared:

Para compartir Entidades de dominio, Casos de Use, Repositorios comunes a los demás módulos. Incluso se podría disgregar un poco más en un Módulo Core y otro Common.

  • Account:

Contiene el manejo del login, logout y todo lo concerniente a acciones que dependen de un usuario autenticado.

  • KeyChainStorage

Para guardar y recuperar valores del KeyChain.

  • AiringToday, PopularShows, SearchShows

En este caso los separé de acuerdo a cada escena de la vista principal de la aplicación.

  • ShowDetails

Una vista que es llamada desde distintos módulos. Creció tanto que decidí aislarla en un módulo individual.

  • Persistence

Contiene los casos de uso y Repositorios para guardar para persistir una entidad.

  • RealmPersistence

Aquí hago las implementaciones del Módulo Persistence. Solo aquí agrego la dependencia de Realm.

En caso necesite migrar a CoreData, remplazo este módulo por uno “CoreDataPersistence”

  • UI:

Todos lo concerniente a la parte visual de la app, aquí incluyo Colores, Fuentes, componentes visuales comunes.

Posibles inconvenientes a los que te enfrentes:

  • Bundle

Cuando trabajas con un solo proyecto, es probable que para referencias a un Storyboard, nib o image lo hagas de esta forma:

let nibName = UINib(nibName: "AiringTodayCollectionViewCell", bundle: nil)

UIImage(named: "calendar")

static func instantiateViewController(_ bundle: Bundle? = nil)

Cuando trabajes con módulos para referenciar al Bundle del módulo en al que pertenece

let nib = UINib(nibName: identifier, bundle: Bundle(for: T.self))

internal class SharedModule { 
  static let bundle = Bundle(for: SharedModule.self) 
}

public extension UIImage { 
  convenience init?(name: String) { 
    self.init(named: name, in: SharedModule.bundle, compatibleWith: .none) 
  } 
}
  • Dependencias Circulares 🚫

Los módulos dependen de otros y de Librerías de terceros, pero la dependencia mutua no está permitida

Si te encuentras en el caso de que el Módulo A dependa del Módulo B y viceversa, Cocoa Pods te lanzará una error.

Para estos casos es mejor repensar la forma cómo interactúan tus módulos.

Un salida para esto es exponer las dependencias de un Módulo mediante un protocolo y hacer que este módulo sea independiente.

  • Módulo Shared demasiado extenso 🤦‍♂️

Talvés por sentido común tendamos a poner todo en el módulo Share, lo que haría que este crezca sin control, lo que nos llevaría al punto inicial, ya no tendríamos una App monolítica, pero en su lugar un Módulo Shared demasiado extenso.

Cada vez que una parte de la aplicación o en general de un módulo empiece a crecer y se pueda abstraer, pues lo extraigo a un módulo independiente.

  • Control de acceso 📁

Mantener el control de acceso por defecto, Swift maneja internal y deberíamos usar private o fileprivate en la mayoría de los casos.

Solo en el caso de punto de entrada a un módulo o cuando realmente sea necesario exponer nuestras interfaces como public.

Conclusión.

Está claro todos los beneficios que conlleva modularizar una aplicación, puedes tener a varios desarrolladores trabajando en paralelo en varias parte de aplicación de forma independiente.

Incluso si tu app aún no es tan grande, puede crecer muy rápido en un futuro cercano, por lo que puedes aprovechar sus beneficios desde ahora.

Puedes revisar un proyecto final modularizado aquí:

Referencias:

https://engineering.depop.com/scaling-up-an-ios-app-with-modularisation-8cd280d6b2b8

https://academy.realm.io/posts/modular-ios-apps/

https://medium.com/kinandcartacreated/modular-ios-strangling-the-monolith-4a6843a28992

https://tech.olx.com/modular-architecture-in-ios-c1a1e3bff8e9

https://blog.gojekengineering.com/1-app-18-products-a-journey-from-monolith-to-a-microapps-codebase-8ea30d070148

Write a Comment

Comment