https://kotlinlang.org logo
#android-architecture
Title
# android-architecture
v

Vishnu Haridas

03/19/2022, 7:50 PM
A question, or more of a request for comments / opinion / recommendations based on the new architecture. Question: In a repository, will you return a value from a read operation call, or will your return nothing and emit the results via an exposed flow? Here's an example (pseudo-code) of a read operation
getNews()
Copy code
class NewsRepository( localSource, remoteSource ){

   val flow: Flow< Resource<NewsItem> > = // exposes the data, updates, and statuses


   // Will you return a Resource<> here...
   suspend fun getNews(): Resource<NewsItem> {
.
      if(success) return Resource.Success(list)
      else return Resource.Error(exception)

   }

   // ...Or return nothing and just emit the result?
   suspend fun getNews(){
      
      if(success) _flowData.emit( Resource.Success(list) )
      else _flowData.emit( Resource.Error(exception) )

   }

}
There can be other functions in the repository that will update the Flow and return nothing, for example:
Copy code
suspend fun removeItem( newsId ){

     // Remove item from the local copy
     list.remove(newsId)

     // Notify listeners
    _flowData.emit( Result.success( list ) )

}
So my feeling is that I should make all the CRUD functions in the repository to return nothing but just update the Flow to avoid confusion. Comments?
k

K Merle

03/20/2022, 5:08 AM
What would be usecase of the class variable
flow
there? If you need a flow, you could use flowBuilder.
v

Vishnu Haridas

03/20/2022, 5:13 AM
Here there will be an exposed
flow: Flow< Resource >
and a backing field
_flowData: MutableStateFlow<Resource>
just like a LiveData.
k

K Merle

03/20/2022, 5:14 AM
I am just not sure why, but you can certainly do it. If there is no reason to do so, I return value inside function.
a

Arun Joseph

03/20/2022, 7:08 AM
We have been using FlowUseCase's along with UseCases for this purpose.
c

curioustechizen

03/21/2022, 9:22 AM
I think of it this way: • If the flow should be triggered only by the current user action then I make the repository method return the flow. Example:
fun performSearch(query: String): Flow<List<SearchResult>>
or
fun scanDevices(): Flow<List<ScanResult>>
• If the flow is something that we want to the UI to always react to, and that depends on different user actions, then I make the repository method return nothing; and have a separate flow that the UI always observes. Example:
fun addTodo()
,
fun deleteTodo()
,
fun editTodo()
- all of these return nothing; there's a separate
val todos: Flow<List<TodoItem>>
👍 1
v

Vishnu Haridas

03/21/2022, 9:30 AM
@curioustechizen Good. Another question arises here. In the latter case where you have a separate Flow for the
List<TodoItem>
, do you keep another Flow for the API status, or do you bundle both into a sealed class / Resource and expose that, like
val todos: Flow< Resource<List<TodoItem>> >
. This is to ensure that the VM is always aware of the latest status - because there may be multiple VMs or VMs being created and destroyed.
c

curioustechizen

03/21/2022, 10:14 AM
In a majority of the cases my repositories are singletons. So the repositories somehow store the latest value (could be a member variable, could be a shared prefs, a database etc). Any VM that subscribes to a flow from the repository gets the latest value. I'm not sure I understand the first part of your question though.
v

Vishnu Haridas

03/21/2022, 10:58 AM
Yes, the repositories are singleton, but I am interested to know how you expose both the data and other API statuses like loading, success, error etc. Any new VM that connects to the repository should be aware of the latest API status. A couple of options: 1. Use separate flows for data and statuses. 2. Use a single flow, wrapping both data and status (like a sealed class like
Resouce<T>
.
c

curioustechizen

03/21/2022, 11:20 AM
Hi Vishnu. Yes that depends on the situation (i.e., depends on what I want to show in the UI). For example, I might want to keep displaying a List even as I try to load more from the backend. Then it would be 2 separate flows. I do advocate for Either-type sealed classes for all repository operations though (i.e., I prefer every method on a repository to return
Either<T, Error>
for single-shot operations or
Flow<Either<T, Error>>
for flows). There do exist micro-libraries to model LCE (Loading, Content, Error) types so you could look into those.
And I have to mention that in projects that I worked on, there exists a domain (UseCase) layer between the VM and the Repository - that would be responsible for combining the "leaf" operations from the Repository into something that is useful for the VM. If you don't have a UseCase layer then maybe some of this advice does not apply to you (or rather maybe you need to adapt these recommendations)
a

Alex Prince

03/21/2022, 1:34 PM
why the flow variable in the repo as opposed to something like a room db behind it? I did something similar with livedata a while ago and, regretted it vs just going the whole way and using room behind the repo, it will expose flows as well for single flow vs double for loading etc, I agree with Kiran Rao above, thats built through your domain / use case, the way I'd do it is, I'd call the repo for the list flow, bind that into the view, and, if you want to say, remove, you'd call a domain command that does the remove, or whatever other change you want to make, and then returns a result (usually unit / errorType for me ) which the view can react to to show error messages and stuff but, that command doesn't return the list, it just updates the database, which then updates the binding.
c

curioustechizen

03/21/2022, 1:38 PM
@Alex Prince True - I did not mean to suggest that a Flow variable is the only way to achieve this. What I meant to show is that the the flow of data can be separate from the operations on the repository. That said, a Room DB is not the only way to model repository state and there are situations where a Flow variable makes sense. For in-memory "state", a flow variable makes sense. Then there are situations like that search example or scan devices example where a flow variable might fit the bill
a

Alex Prince

03/21/2022, 1:40 PM
yea, that totally makes sense, room was just a suggestion on my side too, and you're right, in some cases it can make sense if it's local state that never needs to be maintained / doesn't have to survive lifecycle or something like that, room is definitely overkill
m

Marek Defeciński

03/22/2022, 11:13 AM
appologies wrong window 🙂
10 Views