android10 / Android-CleanArchitecture

This is a sample app that is part of a series of blog posts I have written about how to architect an android application using Uncle Bob's clean architecture approach.
Apache License 2.0
15.52k stars 3.32k forks source link

Where Content Provider and Service would fit? #47

Open BillBosiolis opened 9 years ago

BillBosiolis commented 9 years ago

This is not an issue, just an architectural question...

Let's say that our application has a SQLite database, a Content Provider that exposes this data and an IntentService that is responsible for syncing data by calling the REST service and updating the database.

Where these two components should be placed?

I think The ContentProvider would easily be placed in the data module. An additional Repository implementation in the data module would query the content provider and transform the cursor data into entity objects in order to be consumed by the Use Cases. For the UseCase there's no difference. It will still talk to the interface and it will get back the data it wants. Of course, the proper concrete implementation would be injected via dagger.

The Service could also be placed in the data module.

A new interface could be introduced in the domain, i.e. SyncRepository which will be used by the Use Case. The actual implementation of it (in the data module) will start the service with an Intent.

What do you think?

J-vandenBerg commented 9 years ago

+1

cesards commented 9 years ago

That's actually a very good question.

alexandru-calinoiu commented 9 years ago

The content provider is just another data source for the repository so I agree with your decision.

I will place the service on the presentation layer, is a services folder, it will trigger a use case to sync the data using just a repository implementation. I think that the intent service is just another way of inputing data in the system.

BillBosiolis commented 9 years ago

One of the things that concerns me is that these components cannot run in sequence.

In the current implementation, when the UI asks for data the following take place

In case a Service is used, the workflow could be the following

After that the UI should be informed about the changes and update its contents.

We can accomplish this by using a ContentObserver but in that case, the presentation layer is bound to a concrete data implementation (ContentProvider), so I don't think this is a good idea.

Another option, is that the data layer keeps a reference to the Subscriber (UseCase) and when needed (new changes saved onto the db) informs him again. The UseCase in turn will notify the Presentation's Subscriber which will ask from the View to update.

@alexandru-calinoiu I agree that the Service could be placed in the presentation layer. For instance, the user may need to manually refresh the data, so the Presenter can start the Service.

alexandru-calinoiu commented 9 years ago

I will comment on this part:

the Repository implementation will return the data querying the local database. In addition, it will start the IntentService to fetch new data from the REST service

I don't understand why is a IntentService needed?

The way I've done this in the past is using a reactive, with rxjava, have a Repository implemetation that return Obeservable and subscribe to this, I have an example of this here

BillBosiolis commented 9 years ago

@alexandru-calinoiu I haven't looked at your example in great detail but I would use an IntentService along with a SyncAdapter if I wanted to sync my data regularly without necessarily the need to display the "new" data to the UI at that moment.

sinistance commented 8 years ago

good explaination @BillBosiolis. i think this repo should have a branch that represents how to deal with databases because many of us is using database in everyday project.

connexion2000 commented 8 years ago

And what about CursorLoaders, where do they belong? How should I pass Cursor from data layer (where content provider belongs) to presentation layer (where cursor adapter belongs)?

lenguyenthanh commented 8 years ago

+1

jpventura commented 8 years ago

@BillBosiolis, I try to follow the approach presented at Google I/O 2010 talk "Android REST client applications", which is the same approach used by Udacity Android Nanodegree course.

Basically you can use:

talhakosen commented 8 years ago

+1

RowlandOti commented 8 years ago

I feel this question needs a demonstrated example by code. It is a very important question. If only I could open a bounty for this!

BillBosiolis commented 8 years ago

Hi @RowlandOti , I am still working on a sample. When it will be ready (I hope soon) I'll put a comment here

thomasdsouza commented 8 years ago

+1

sevar83 commented 8 years ago

Hi, I've tried to put a ContentProvider into the data layer. It serves data so I wish to be in "Data". Unluckily my Data layer is quite complex and I wanted to inject the content provider with few dependencies. This cannot be done with the DI components because they are defined in the topmost "Presentation" layer which is invisible from "Data". If I add another component in "Data" layer the whole app is going to separate have DI setups for each gradle module. That doesn't smell good to me. It seems to me that Dagger2 is fundamentally incompatible with gradle build modules, because the DI component should know the injection target and the target should know the component in order to inject itself. This forces the DI component to be in the same build module or lower-level module (domain). Any ideas how to get out of this mess?

jpventura commented 8 years ago

Google made available a sample application that compares MVP implemented using different approaches, including content providers, dagger and clean architecture.

It may be a good place to get some insights for this discussion :-)

sevar83 commented 8 years ago

Thanks, I've taken a look but there's no sample including multi-module build. Maybe with some other DI framework like Toothpick or Tiger it would be easier.

Kevin1466 commented 7 years ago

Hi, @BillBosiolis

Hi @RowlandOti , I am still working on a sample. When it will be ready (I hope soon) I'll put a comment here

How's your sample now? Could you show it to us?

joaocsousa commented 7 years ago

I was also investigating this issue, as I'm using a plain SQLite database implemented in the data layer and I'm not sure where to put the open and close db calls, as Google suggests calling these in the onCreate and onDestroy of the activity using the database. So in the end, the view (the activity) needs to access the data source implementation (not ideal), as per Google's suggestion.

I was thinking of having some generic methods like initialize and cleanup and delegating them all the way through the domain layer until it reaches the data layer and finally the database data source.

In the end, taking a look at the google sample pointed out by @jpventura , I see that they open and close the DB in every DB call. I'm going to go with this solution for now.

In the end I'm not 100% satisfied with the solution I went with, but it'll have to do for now and it's something I can come back to at a later stage.

@sevar83 I just want to point out that you don't need to discard a sample just because it's not a multi module build. Multi module build is a concrete build implementation. In fact I started with a multi module build but I'm thinking of moving to a single module (keeping the package separation).

jpventura commented 7 years ago

@BillBosiolis, after many months, I would dare to say that content provider is the framework implementation of repository pattern (issue #229 and Stack Overflow post).

@joaocsousa, content provider uses the service activator pattern, i.e. the database is opened when the first content resolver is called.

The downside would require an Android VM/device to run your data tests, but I can live with it :-)