Arquitectura limpia en el contexto del desarrollo multiplataforma

Hola a todos Recientemente, se han escrito bastantes artículos sobre el tema de la arquitectura limpia. Es decir, una arquitectura limpia que le permite escribir aplicaciones que sean convenientes para mantener y probar. Puede leer sobre la arquitectura pura en sí misma en artículos tan maravillosos como: Conceptos erróneos de Arquitectura limpia o Arquitectura limpia , por lo que no veo ninguna razón para repetir lo que ya se ha escrito.

Para comenzar, permítanme presentarme, mi nombre es Kakushev Rasul. Dio la casualidad de que simultáneamente estaba involucrado en el desarrollo nativo en iOS y Android, así como en el desarrollo de código de back-end para aplicaciones móviles en Navibit. Todavía es una empresa poco conocida, que se está preparando para ingresar al mercado para la venta de materiales de construcción. Tenemos un equipo muy pequeño y, por lo tanto, el desarrollo de aplicaciones móviles recae por completo en mis hombros (aún no demasiado profesionales).

En mi trabajo, a menudo tengo que hacer una aplicación en iOS y Android, y como comprenderán, debido a las diferencias en las plataformas, a menudo tengo que escribir la misma funcionalidad varias veces. Lleva mucho tiempo, y hace un tiempo, cuando conocí la arquitectura limpia, se me ocurrió lo siguiente: el kotlin y los lenguajes rápidos son bastante similares, pero las plataformas son diferentes, pero en la arquitectura limpia hay una capa de dominio que no está vinculada a la plataforma. , pero contiene pura lógica de negocios. ¿Qué sucederá si solo toma la capa de dominio completa de Android y la transfiere a iOS, con cambios mínimos?

Bueno, concebido, hecho. Comencé la transferencia. Y de hecho, la idea resultó ser mayormente cierta. Juzga por ti mismo. Por ejemplo, aquí hay un interactor en kotlin y swift:

Kotlin (Android)

class AuthInteractor @Inject internal constructor(private val authRepository: AuthRepository, private val profileRepository: ProfileRepository) { fun auth(login: String, password: String, cityId: Int): Single<Auth> = authRepository.auth(login.trim { it <= ' ' }, password.trim { it <= ' ' }, cityId, cloudToken) fun restore(login: String, password: String, cityId: Int, confirmHash: String): Single<AuthInfo> = authRepository.restore(login.trim { it <= ' ' }, password.trim { it <= ' ' }, cityId, confirmHash) fun restore(password: String, confirmHash: String): Single<AuthInfo> = authRepository.restore(password.trim { it <= ' ' }, confirmHash) fun getToken(): String = authRepository.checkIsAuth() fun register(login: String, family: String, name: String, password: String, cityId: Int, confirmHash: String): Single<AuthInfo> = authRepository.register(login.trim { it <= ' ' }, family.trim { it <= ' ' }, name.trim { it <= ' ' }, password.trim { it <= ' ' }, cityId, confirmHash) fun checkLoginAvailable(login: String): Single<LoginAvailable> = authRepository.checkLoginAvailable(login) fun saveTempCityInfo(authCityInfo: AuthCityInfo?) = authRepository.saveTempCityInfo(authCityInfo) fun checkPassword(password: String): Single<AuthInfo> = authRepository.checkPassword(password) fun auth(auth: Auth) { authRepository.saveToken(auth.token!!) profileRepository.saveProfile(auth.name!!, auth.phone!!, auth.location!!) } companion object { const val AUTH_ERROR = "HTTP 401 Unauthorized" } } 

Swift (iOS):

 class AuthInteractor { public static let AUTH_ERROR = "HTTP 401 Unauthorized" private let authRepository: AuthRepository private let profileRepository: ProfileRepository private let cloudMessagingRepository: CloudMessagingRepository init(authRepository: AuthRepository, profileRepository: ProfileRepository, cloudMessagingRepository: CloudMessagingRepository) { self.authRepository = authRepository self.profileRepository = profileRepository self.cloudMessagingRepository = cloudMessagingRepository } func auth(login: String, password: String, cityId: Int) -> Observable<Auth> { return authRepository.auth(login: login.trim(), password: password.trim(), cityId: cityId, cloudMessagingToken: cloudMessagingRepository.getCloudToken()) } func restore(login: String, password: String, cityId: Int, confirmHash: String) -> Observable<AuthInfo> { return authRepository.restore(login: login.trim(), password: password.trim(), cityId: cityId, confirmHash: confirmHash) } func restore(password: String, confirmHash: String) -> Observable<AuthInfo> { return authRepository.restore(password: password.trim(), confirmHash: confirmHash) } func getToken() -> String { return authRepository.checkIsAuth() } func register(login: String, family: String, name: String, password: String, cityId: Int, confirmHash: String) -> Observable<AuthInfo> { return authRepository.register(login: login.trim(), family: family.trim(), name: name.trim(), password: password.trim(), cityId: cityId, confirmHash: confirmHash) } func checkLoginAvailable(login: String) -> Observable<LoginAvailable> { return authRepository.checkLoginAvailable(login: login) } func saveTempCityInfo(authCityInfo: AuthCityInfo?) { authRepository.saveTempCityInfo(authCityInfo: authCityInfo) } func checkPassword(password: String) -> Observable<AuthInfo> { return authRepository.checkPassword(password: password) } func auth(auth: Auth) { authRepository.saveToken(token: auth.token) profileRepository.saveProfile(name: auth.name, phone: auth.phone, location: auth.location) } } 

O aquí hay un ejemplo de cómo se ven las interfaces de repositorio en diferentes plataformas:

Kotlin (Android)

 interface AuthRepository { fun auth(login: String, password: String, cityId: Int, cloudMessagingToken: String): Single<Auth> fun register(login: String, family: String, name: String, password: String, cityId: Int, confirmHash: String): Single<AuthInfo> fun restore(login: String, password: String, cityId: Int, confirmHash: String): Single<AuthInfo> fun restore(password: String, confirmHash: String): Single<AuthInfo> fun checkLoginAvailable(login: String): Single<LoginAvailable> fun sendCode(login: String): Single<CodeCheck> fun checkCode(hash: String, code: String): Single<CodeConfirm> fun checkIsAuth(): String fun saveToken(token: String) fun removeToken() fun notifyConfirmHashListener(confirmHash: String) fun getResendTimer(time: Long): Observable<Long> fun checkPassword(password: String): Single<AuthInfo> fun saveTempCityInfo(authCityInfo: AuthCityInfo?) fun saveTempConfirmInfo(codeConfirmInfo: CodeConfirmInfo) fun getTempCityInfo(): AuthCityInfo? fun getConfirmHashListener(): Observable<String> fun getTempConfirmInfo(): CodeConfirmInfo? } 

Swift (iOS):

 protocol AuthRepository { func auth(login: String, password: String, cityId: Int, cloudMessagingToken: String) -> Observable<Auth> func register(login: String, family: String, name: String, password: String, cityId: Int, confirmHash: String) -> Observable<AuthInfo> func restore(login: String, password: String, cityId: Int, confirmHash: String) -> Observable<AuthInfo> func restore(password: String, confirmHash: String) -> Observable<AuthInfo> func checkLoginAvailable(login: String) -> Observable<LoginAvailable> func sendCode(login: String) -> Observable<CodeCheck> func checkCode(hash: String, code: String) -> Observable<CodeConfirm> func checkIsAuth() ->String func saveToken(token: String) func removeToken() func notifyConfirmHashListener(confirmHash: String) func getResendTimer(time: Int) -> Observable<Int> func checkPassword(password: String) -> Observable<AuthInfo> func saveTempCityInfo(authCityInfo: AuthCityInfo?) func saveTempConfirmInfo(codeConfirmInfo: CodeConfirmInfo) func getTempCityInfo() -> AuthCityInfo? func getConfirmHashListener() -> Observable<String> func getTempConfirmInfo() -> CodeConfirmInfo? } 

La situación es similar con la capa de presentación, ya que los presentadores y las interfaces de visualización en ambas plataformas son iguales. Por lo tanto, gracias a dicha transferencia, mi velocidad de desarrollo casi se duplicó, porque debido al hecho de que las capas de dominio y presentación ya están completamente formadas en ambas plataformas, sigue siendo una tarea pequeña conectar bibliotecas específicas y modificar las capas de interfaz de usuario y de datos.

Gracias por leer hasta el final. Espero que este artículo beneficie a los desarrolladores móviles que se dedican al desarrollo nativo. Todo lo mejor

Source: https://habr.com/ru/post/es419173/


All Articles