Recientemente, leí sobre lo importante que es tener una única fuente de verdad (SSOT) al diseñar el backend de una aplicación (repositorio, no backend del lado del servidor). https://developer.android.com/topic/libraries/architecture/guide. html

Al desarrollar una aplicación de suministro de noticias (utilizando la increíble https://newsapi.org/), estoy tratando de aprender más sobre la arquitectura de la aplicación. Sin embargo, no estoy seguro de cómo diseñar la interfaz del repositorio para mi aplicación. Por cierto: estoy usando MVVM para mi capa de presentación. La Vista se suscribe al LiveData de ViewModel. ViewModel se suscribe a las transmisiones de RxJava.

Entonces se me ocurrieron 2 enfoques:

Enfoque 1:

interface NewsFeedRepository {
        fun loadFeed(): Flowable<List<Article>>
        fun refreshFeed(): Completable
        fun loadMore(): Completable
    }

interface SearchArticleRepository {
    fun searchArticles(sources: List<NewsSource>? = null, query: String? = null): Flowable<List<Article>>
    fun moreArticles(): Completable
}

interface BookmarkRepository {
    fun getBookmarkedArticles(): Flowable<List<Article>>
    fun bookmarkArticle(id: String): Completable
}

Este enfoque utiliza principalmente Flowables que emiten datos si los datos correspondientes en el SSOT subyacente (base de datos) cambian (por ejemplo, los datos antiguos se reemplazan con datos nuevos de API, se cargaron más datos desde API, ...). Sin embargo, no estoy seguro de que tenga sentido usar Flowable para SearchArticleRepository#searchArticles(...). Como es como una solicitud / respuesta, tal vez un Single podría ser más intuitivo.

Enfoque 2:

interface NewsFeedRepository {
    fun loadFeed(): Single<List<Article>>
    fun refreshFeed(): Single<List<Article>>
    fun loadMore(): Single<List<Article>>
}

interface SearchArticleRepository {
    fun searchArticles(sources: List<NewsSource>? = null, query: String? = null): Single<List<Article>>
    fun moreArticles(): Single<List<Article>>​
}

interface BookmarkRepository {
    fun getBookmarkedArticles(): Single<List<Article>>
    fun bookmarkArticle(id: String): Single<Article> // Returns the article that was modified. Articles are immutable. ​
}

Este enfoque utiliza solteros en lugar de fluidos. Esto parece muy intuitivo, pero si los datos del SSOT cambian, no se emitirán cambios. En su lugar, se debe volver a realizar una llamada al repositorio. Otro aspecto a tener en cuenta es que ViewModel puede tener que administrar su propio estado. Tomemos el FeedViewModel por ejemplo (pseudocódigo).

class FeedViewModel : ViewModel() {
    // Variables, Boilerplate, ...
    val newsFeed: LiveData<List<Article>>
    private val articles = mutableListOf<Article>()

    fun loadNewsFeed() {
        // ...
        repository.loadFeed()
                   //...
                   // On success, clear the feed and append the loaded articles.
                  .subscribe({articles.clear(); articles.addAll(it)})
        // ...
    } 

    fun loadMore() {
        // ...
        repository.loadMore()
                   //...
                   // On success, append the newly loaded articles to the feed.
                  .subscribe({articles.addAll(it)}) 
        // ...
    }
}

Por lo tanto, esto puede no ser crucial para una aplicación más pequeña como la mía, pero definitivamente puede generar un problema para una aplicación más grande (consulte la administración estatal: http://hannesdorfmann.com/android/arch-components-purist).

Por último, quería saber qué enfoque adoptar y por qué. ¿Existe alguna práctica recomendada? Sé que muchos de ustedes ya han realizado algunos proyectos / aplicaciones de software más grandes y sería realmente increíble si algunos de ustedes pudieran compartir algunos conocimientos conmigo y con otros.

¡Muchas gracias!

2
Elias 17 feb. 2018 a las 12:59

2 respuestas

La mejor respuesta

Prefiero optar por el primer enfoque usando Observable s en lugar de Flowable s en su caso:

interface NewsFeedRepository {
    fun loadFeed(): Observable<List<Article>>
    fun refreshFeed(): Completable
    fun loadMore(): Completable
}

interface SearchArticleRepository {
    fun searchArticles(sources: List<NewsSource>? = null, query: String? = null): Observable<List<Article>>
    fun moreArticles(): Completable
}

interface BookmarkRepository {
    fun getBookmarkedArticles(): Observable<List<Article>>
    fun bookmarkArticle(id: String): Completable
}

No veo ninguna razón por la que deba usar Flowable para este propósito, ya que nunca tendrá problemas relacionados con OOME al verificar los cambios de su repositorio. En otras palabras, para su caso de uso, en mi humilde opinión, la contrapresión no es necesaria en absoluto.

Consulte esta guía oficial que ofrece nos da un consejo de cuándo utilizar un Flowable en lugar de un Observable.

Por otro lado, y no relacionado con la pregunta en sí, tengo serias dudas de cuál es el propósito de los métodos loadMore o moreArticles ya que devuelven un Completable. Sin conocer el contexto, puede parecer que podría refactorizar el nombre del método con un nombre mejor o cambiar el tipo de retorno si hacen lo que parecen hacer por el nombre.

1
GoRoS 17 feb. 2018 a las 13:27

Creo que el primer enfoque es mejor, su repositorio actualizará los datos cada vez que se modifiquen los datos y su modelo de vista se notificará automáticamente y eso es genial, mientras que en su segundo enfoque tiene que llamar al repositorio nuevamente y eso no es una programación realmente reactiva.

Además, suponga que los datos pueden ser cambiados por algo en lugar de cargar más eventos desde la vista, como cuando se agregan nuevos datos al servidor, o alguna otra parte de la aplicación cambia los datos. Ahora, en el primer enfoque, nuevamente obtiene los datos. automáticamente, mientras que por un segundo ni siquiera sabe acerca de los datos modificados y no sabe cuándo volver a llamar al método.

1
Alireza A. Ahmadi 17 feb. 2018 a las 10:15