Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layer for Managers and Services that require Android Context #151

Open
CROSP opened this issue May 23, 2016 · 23 comments
Open

Layer for Managers and Services that require Android Context #151

CROSP opened this issue May 23, 2016 · 23 comments

Comments

@CROSP
Copy link

CROSP commented May 23, 2016

Hi, I am trying to migrate to Clean Architecture.
I have application which is made in standard way, I tried to decouple, but still a lot of stuff happens in Activities and Fragments.
Example is quite simple, mostly applications are much more complex so it is much harder to decouple all highly coupled components inside application.

I have just faced with one problem is Managers and Services that requires ApplicationContext or ActivityContext.

For example I have LocationManager which is responsible for getting location, but it requires context, at least to get Service and to listen to lifecycle of Activity.
I think this manager could go to Data layer, but this is undesirable dependency.

I really don't want to put this login into presentation layer. I firmly believe that Presentation layer should be just UI and ONLY UI nothing more, unfortunately Android SDK is build in a way that we have God Object - Activity but I still wanna to get rid of anything except use cases in Presentation layer.

I see some ways how this can be solved :

  1. Add new layer or sublayer which will be responsible for handling all Context related stuff and call it for example Service or Lifecycle layer and make it tight to context lifecycle, also in order to keep things abstract interface with common lifecycle methods can be created (onCreate, onDestroy ...)
    and injected via dependency injection tool like our beloved Dagger 2
  2. Without separate layer there is only one way is to add Android dependency to some layer. Presentation layer is already coupled to Android Context, but again UI should be UI nothing more, consume, update, and request data through Use cases from domain layer.

Furthermore one important thing to note that LocationManager is great example of publisher/subscriber pattern so it can be easily adopted to RxJava.

All things considered, I would be grateful for any thoughts, advice on this problem.

@Trikke
Copy link

Trikke commented May 24, 2016

See the following discussions on similar matters:

#127
#115
#47

@CROSP
Copy link
Author

CROSP commented May 24, 2016

@Trikke thank you for answer I have already read this posts. I feel that your Infrastructure layer is the same idea as mine.
Anyway in any specific platform we have UI framework and CORE framework, so anyway we will have two layers dependent on specific platform. Maybe it is worth to create separate layer which will be responsible for all stuff like GCM notifications, Location ... It will be referenced by domain layer as repository data layer is being referenced by domain layer now.
We will have separate layer for data and separate layer for lets say device specific data provided by services.
In case of Android and iOS it is location, notifications, camera photos, music player ...
What do you think about this.

P.S. Please write me a letter crospdevel@gmail.com

@Trikke
Copy link

Trikke commented May 25, 2016

Well, you are getting the hang of the general idea of it! A few remarks :

On any specific platform (iOS,Android,Windows,..) you'll always be stuck with a framework that interfaces with everything on the platform, from UI to Networking to Data Access. The point of an architecture is that we create an idea that works for al those platforms with minimal effort. Because all that stuff like location, gcm, ... doesn't have anything to do with data that is important in your app, it doesn't belong in the Data layer but needs a seperate layer. A lot of people make the mistake of just copying this example repo and start putting everything in the Data layer. Only put data in the Data layer that is important to your app

So, like you said, an Infrastructure layer to handle platform services and reside on the same "level" as the Data layer, seen below.

cc53f294-fbe8-11e5-864e-c87c68d19ed5

You can even see that the above image contains "external services" and "devices" on that outer level, which is what you are talking about.

@CROSP
Copy link
Author

CROSP commented May 26, 2016

@Trikke thank you for complete answer. I got an idea. One more thing I am interested in.
My application handles user session. I have Session Manager which is responsible for maintaining user session through app lifecycle. All my managers are stored in ApplicationContext, and injected with the help of Dagger, better than global variables, but still a lot of separate layers are mixed in one place. When any REST request is made I access context (Application) and get SessionManager and custom interceptor access current session and gets token and other credentials required for REST call.

So my question is, how Session , UserAuthorization should be handled correctly in terms of Clean Architecture approach. I think data layer should not be responsible for that, I feel need of new layer like SecurityLayer, but in such case it will become Layer apocalypse soon. On the other hand this is a sort of use case like AuthenticateUserUseCase or GetUserCredentialsUseCase.

What do you think about this ?

Thanks.

@Trikke
Copy link

Trikke commented May 26, 2016

Well, the first question should always be, do i really need a context here and why? Since i don't know how your SessionManager works, i can only guess. So the first thing to do is see if you can modify your Managers to work without.

You shouldn't be creating layers for any type of generalised action. Stuff like Security is actually a cross-cutting concern. This means that it is a topic that doesn't belong in a specific layer.

So your Session logic and Authorization logic belongs in many layers. For example :

  • Part of user authorisation is a login screen, and this is presentation logic
  • The bit where you check the details and pass them on to the data layer, or infrastructure layer belongs in the business layer.
  • If you store your Session/User locally, this belongs in the data layer.

The hard part for you seems to be that Authorization seems tightly coupled with your REST service. So it would seem logical to have the Session Manager (preferably without a Context) in the same layer as your REST service. This can be data, or infrastructure.

@CROSP
Copy link
Author

CROSP commented May 28, 2016

Thanks a lot man. You are really good at architecture.
Sorry I haven't described better my SessionManager .
Btw, this picture doesn't corresponds to clean architecture described and used in this repository. Business logic is the core of all system, but on the picture is default 3-layered architecture. Am I right ?
I think that SessionManager should belong to DataLayer, as far as I really don't care about tokens, passwords and other stuff in UI, I mean that this is just simple Use Case to Auth user or to Logout.
And in case of session, I think the following way will suit good enough for clean architecture approach.

  1. Put all tokens, passwords related stuff into DataLayer or Infrastructure layer. Infrastructure because we need to read saved credentials from SharedPreferences and this is platform specific storage. But on the other hand all databases are platform specific, and in general Java sockets are platform specific, Java is also platform. So I have question is it okay if Data layer knows about platform specific stuff ?
  2. Create specific use cases on the Use case/Business layer. For example AuthUserUseCase , LogoutUserUseCase , these use cases are essential part of all application, this is core, but on the other hand I don't care what specific security mechanism is used, so I can swap it easily.
  3. UI layer will just request auth, logout according to user actions. Furthermore Infrastructure layer also can request to clear preferences when app was closed (Application class instances was destroyed).

Consider that UI , Data and Infrastructure layer will be on the same layer (ring outermost) based on this architecture. And in the middle will be Use Case layer or Business.
Data flow will be following. UI (AuthRequest) ---> UseCase (AuthUserUseCase) ---> Data(ValidateCredentials) or Infrastructure (Application context is going to be destroyed) ---> UseCase (LogoutUser) ---> DataLayer( Clear Preferences, close connections, files ..).

One thing I am worrying about is that there are three layers dependent on platform (UI, Infrastructure, Data) is it okay ?

I would be grateful for your thoughts.

@Trikke
Copy link

Trikke commented May 30, 2016

Yeah, that's true, it's a more traditional diagram, i was just using it to show that there is also stuff that applies to all "layers". Things like Security isn't something you can just put in one layer and keep it there. It's going to be present in all "layers".

In general, your idea of data flow seems correct, so stick with that. From an architectural standpoint, keep the following in mind :

  • UI just cares about the button being pressed and a response, it doesn't care where the action goes
  • Business only cares about what needs to happen, not how. So, "perform auth logout" calls "clear local auth details", "close connections" and "delete auth files", but i don't care how it happens.
  • Data (or infrastructure) only cares about how stuff happens. So there is functionality to do the above 3 functions, and here we care on how we implement it.

On the point of "depending on a platform": you'll always be writing platform specific code. Doesn't matter if it is Java, Android, or iOS. The choice of platform depends on which machines you'll want to run your app. So now you're writing an Android specific app, so it must know about the Android platform. You can only abstract so much before it becomes ridiculous :)

@CROSP
Copy link
Author

CROSP commented May 30, 2016

@Trikke thanks again. It's pleasure for me to discuss such topic with you.
So in general Business layer operates with interfaces only.
Another example I think can be on the Business layer is collecting appropriate view (representation of data), fro instance instead of combining multiple data entities Company and Company Group, business layer will return CompanyView (where all required fields will be set) and Presentation layer will simply display data without doing business logic.
Rule of thumb Business layer is core and it should be like cross-platform, all you need to do on new platform is to implement required interfaces. But Business layer will be the same.
Business layer is just coordinator.
I agree that Security will be present in each layer but represented differently (login screen, request, encrypting) , am I right ?

@Trikke
Copy link

Trikke commented May 31, 2016

No problem, i'm happy to help people curious to learn :)

So yes, the business/domain layer is the core of your app. It's also the central part of the diagram of Clean Architecture. The domain layer operates with interfaces to connect to the outer layers. But it can also have concrete implementations for it's business logic. Like your example, you can have a concrete Factory that takes 2 data entities and create a new one to be passed to Presentation. So don't think that the domain layer only exists of Use Cases.

Here's an extract about this

As you move inwards the level of abstraction increases. The outermost circle is low level concrete detail. As you move inwards the software grows more abstract, and encapsulates higher level policies. The inner most circle is the most general.

Rule of thumb is that if i ask you to see what your app does, you only need to show the domain layer to me. If i wanted to then see how you are doing everything around that, i'd need the rest of the layers.

And yes, Security is something that is represented differently in each layer, but is present in all. This is why it's a cross cutting concern.

Please keep up on reading on these matters, even other stuff then Clean Architecture, there's always something to be learned.

@CROSP
Copy link
Author

CROSP commented May 31, 2016

@Trikke thanks again, this is really good stuff to dive deep into. I always like more architectural approaches rather than feature-driven. I am going to refactor my application completely according to Clean architecture principles and I will share results.
Maybe you can suggest some references to read more about this ? I think this would be useful for everyone interested in this topic.

@Trikke
Copy link

Trikke commented Jun 16, 2016

@CROSP : Anything from Uncle Bob (like his articles 1, 2). There's also a few youtube videos from talks of his.

@CROSP
Copy link
Author

CROSP commented Jun 21, 2016

@Trikke
Thanks, I have already seen them. If I found something interesting I will share. But in general Uncle Bob architecture is great, and can be applied not only for building Android Apps, but I think whole software system.

@android10
Copy link
Owner

@Trikke Kudos man for all the light shaded on this and other discussions.

Just something else to keep in mind when it comes to dealing with android components:

I do not want my domain logic to know anything about any strong dependency with the framework.

@CROSP
Copy link
Author

CROSP commented Jul 10, 2016

@android10 thank you for answer. Do you mean that we should use wrappers around framework components anyway.
Let's consider our outer ring will include one more layer suggested by @Trikke - Infrastructure

It will include some specific features like GPS, Proximity Sensors, Compass or whatever, furthermore it can be as you mention UI Thread , but this is more specific to internals and related to most of today platforms with GUI.

As suggested in Uncle Bob's book Clean Code to use my own wrappers around APIs that makes it easier to test, migrate and maintain.

But I have one question is it ok if Domain Logic will talk to this Infrastructure layer directly through wrappers only ? Or it requires some more abstraction in order to avoid tight coupling

For example I have interface that should be implemented on outer Infrastructure layer - GeolocationProvider.

So in my Business/Domain/Use Case layer I will have something like this GetCurrentLocationUseCase that can be called by Presenter layer for instance and this call should be redirected again to outer layer Infrastructure through GeolocationProvider interface.

Am I right ?

Or it would be better to initiate communication between DataLayer and Infrastructure Layer directly and only ask Data Layer when geolocation is required an it will return latest location ?

Please share your thoughts.

@CROSP
Copy link
Author

CROSP commented Jul 10, 2016

I have just realized that I am still confused a little bit about diagram.
To make everything clear completely, I will show flow on a diagram.

As I understand the Data Layer is the outermost ring, so our Domain Layer accesses the layer above it. So what about Dependency Rule ? Where I am wrong ? In case of Data Layer is below Domain Layer this is the same well-known N-tier architecture.
clean-architecture-android-15-6238

@Trikke
Copy link

Trikke commented Jul 11, 2016

So in my Business/Domain/Use Case layer I will have something like this GetCurrentLocationUseCase that can be called by Presenter layer for instance and this call should be redirected again to outer layer Infrastructure through GeolocationProvider interface.

Am I right ?

Or it would be better to initiate communication between DataLayer and Infrastructure Layer directly and only ask Data Layer when geolocation is required an it will return latest location ?

You were right with your first suggestion. The Data Layer is just for storing data that is central to your app. It's on the same "level" in Clean architecture as the Infrastructure Layer, so you don't call it through the Data Layer.

@CROSP
Copy link
Author

CROSP commented Jul 12, 2016

@Trikke thanks again for your response. what about previous picture, does that make any sense ? Or this is not right ?

@Trikke
Copy link

Trikke commented Jul 14, 2016

Seems about right.

  1. UI to Use Case
  2. Use Case gets data through gateway from datasource
  3. data is returned
  4. pass it back up to UI

The same discussion went on in here, i hope you'll find some more useful info in there.

@CROSP
Copy link
Author

CROSP commented Jul 15, 2016

@Trikke thanks, again, I am still finding a lot of new to learn about this architecture. I have just realised one important thing, with the help of your explanation from adjacent topic you provided.
Our core business logic is like an engine in auto or cpu in computer. Not very good comparison, but I will try to explain. If we take CPU with ARM, to have such device there are some interfaces, schematics that should be implemented in order to run ARM CPU. Every CPU has specific buses, max memory available memory amount and some other rules down to motherboard material, socket. The CPU is core of a computer, in order to use CPU we have to implement some essential interfaces. so CPU defines some rules (interfaces) and others should implement and conform to this rules.
The same in this architecture to use app logic, you have to implement a bunch of interfaces and operate only through interfaces on the business layer and real implementation can be injected through dependency injection tools and you can swap them left and right.

One weird thing for me and I can guess for other developers is dependency rule. It seems according to the @android10 articles pretty the same as 3-layered architecture.

Lower layer should know nothing about upper layer.
UI-> Business Rules (Controller) -> Database

And in our case it seems for me that business layer knows everything about upper layers.
For instance, I need to get all users information

  1. UI layer (outer) makes request to Presenter
  2. Presenter directs request to Business layer
  3. Business layer has interface to retrieve data, for instance Data Layer and Infrastructure layer
  4. Business layer makes call to the outer layer. so it knows about outer layers like Data Layer and Infrastructure.
  5. All outer layers should implement interfaces located in Business Layers, like User Repository, GeoLocationProvider in order to allow Business layer to call real implementations.
  6. So the flow is following UI->Presenter->Business/Domain->Data Layer -> Result back to Business layer -> Result back to Presenter -> Update UI
    Here is the question, what about dependency rule it seems that core Business Layer knows about, not really knows about specific implementations, but it provides interfaces for others to implement.

Please explain this question.
Thanks

@CROSP
Copy link
Author

CROSP commented Jul 15, 2016

I have just played a little bit with @android10 project and logged data flow to make it more clear.
Here is my forked repo with log statements https://github.com/CROSP/Android-CleanArchitecture
And the most interesting part is log output

D/CLEAN_ARCHITECTURE: 1. I am View in UI outer layer. User has just clicked on load data button
D/CLEAN_ARCHITECTURE: 2. I am UserList Presenter in Presenter layer (second) . I got the request to load user data
I/System.out: 3. I am GetUserList use case in Domain Layer. I am trying to get some data from data layer
I/System.out: 4. I am users repository in Data Layer. I know where to get user list and you should not care about this
I/System.out: 5. I am GetUserList use case in Domain Layer. I have just got observable (data) from data layer
D/CLEAN_ARCHITECTURE: 6. I am UserList Presenter in Presenter layer (second) . I have just got user data from domain layer
D/CLEAN_ARCHITECTURE: 7. I am UserListFragment Presenter in UI (outer layer) . I am rendering user list 

So it seems that it is true about violating Dependency rule lower layers know about higher layers.

Please share your thoughts and explanation

@Trikke
Copy link

Trikke commented Jul 18, 2016

Hey @CROSP , it seems you're confusing the flow of data with dependencies. In the linked discussion above there was already an explanation about this. See this post and then this other post for the entire explanation. The amount of love those post got seem to indicate they explained this issue well.

@CROSP
Copy link
Author

CROSP commented Jul 18, 2016

@Trikke , thanks, exactly, I have just looked more precisely to the dependencies and it seems that I got it.
Dependency rule is still here and applied correctly.
Data depends on domain, because we have declared interfaces for the Repositories here.
Presentation depends on domain and data.
Here is one more question, Uncle Bob didn't mention anything about same-layer dependencies , I mean does this make any sense when there are dependencies between components on the same layer ?
In case of this repo, it mostly used in dagger dependency component.
But will it be ok if I swap data layer implementation with completely different, that only implements interfaces from domain layer ? I think no.
I feel like it should be another layer or something else that coordinates and satisfies all dependencies inside the project, like Wireframe in VIPER architecture on iOS.
Also in case of this repo, it seems that presentation layer does too much.
What do you think about that ?

@enuoCM
Copy link

enuoCM commented Aug 26, 2016

@Trikke I followed your Infrastructure idea. I put AlarmManager, Log, Receiver, Service, AudioManager etc to the Infrastructure layer. Is this OK? https://github.com/enuoCM/DE-MVP-Clean/tree/master/app/src/main/java/com/xixicm/de/infrastructure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants