I'm creating an Android app and want to comply to clean architecture.
For example, I have an activity which has a presenter which creates use cases. In that inner layer, I have a repository interface (which is known by the use cases) which is implemented by a concrete repository, lets call it repsoitoryImpl (which is not known by the uses cases). What I did in previous projects was to create the presenter and the repositoryImpl in the activity, and pass the repositoryImpl as a repository to the presenter. The presenter can then, whenever there is an action coming from the activity (e.g. a button press) create a new use case and pass the repository to it.
This works, but a) the constructors of the use cases can become very long and b) the UI has knowledge of all other "outer" things, e.g. the repositoryImpl. So I thought DI to the rescue! And started to try out Dagger 2. However, my current solution does not seem to be "correct". What I would have liked is that I can just have an #inject annotated repository in a usecase and a repositoryImpl gets injected. However I found that, at the beginning of the "injection chain" I have to call inject() on the dagger component. In most of the examples, this is done in the activity. But then I would have to inject the presenter in the activity and the usecase into the presenter to be able to inject things into the use case. Is this correct? The problem is that I want to create the use cases dynamically with different parameters and not inject them.
So my current solution is to have the dagger "AppComponent" as a static field in the Android Application class and then in my use cases I call
Application.component.inject(this)
which allows me to inject things in the use case. But then the use cases have a dependency to dagger which doesn't comply to clean architecture. Because framework dependencies should only appear in the outer layer.
Is there a common solution to this problem? Am I understanding something wrong?
As u already pointed out in clean architecture use cases must not know about DI frameworks - even decorating use cases with framework specific attributes would be a smell.
As discussed here: How to handle UseCase Interactor constructors that have too many dependency parameters in DDD w/ Clean Architecture? having too many constructor parameters is usually an indicator that the use case is "doing too much". U should consider splitting them.
Furthermore the interfaces used by a use case to access "the details" (repository, external services and systems) should be designed in a way that they are most convenient for the use case. That means instead of having multiple repository interfaces and multiple service interfaces passed to a use case u could consider using façade pattern and design one or two interfaces which are more convenient for the use cases which then "aggregate" the work with the different repositories/services. This will also reduce the number of parameters passed to the constructor.
According to clean architecture the "composition" of ur application happens in the "main component" - a class living in the frameworks circle. There are objects are created and injected. If u want to create use cases dynamically u could have a factory pattern.
Related
I have an app with the following architecture:
Navigator is a custom class that holds the NavController
Cooridnator holds the Navigator
Cooridnator tells the Navigator to "start" the framgent and passes the ViewModel to it
Navigator asks NavController to navigateTo a NavDirections and provides the required arguments (using Safe-Args)
Now the issue here is that if I want to send the ViewModel as argument, it needs to be Parcelable and all of its underlying classes as well (which would make most of my code Parcelable, and that's not really needed).
So is there a way to do this without making everything Parcelable or using Dagger ? (Don't like Dagger as it adds too much complexity to the code...)
I would be okay with having a lateinit field in the Fragment and setting it manually but can't seem to access the Fragment from NavDirections
Any idea on how I could do this ?
First of all: what you are passing in safe args is "data" while your viewmodel is logic. Which means your data can change over the time (one of examples would be to become outdated) but as long as viewmodel is unchanged, it's logic would stay. Thus passing viewmodel itself does not make sense to me - best you can is to pass its snapshot of state, but I doubt that's what you want.
So yes, you should be using DI and there are alternatives to dagger complexity. You can experiment with koin (because I see kotlin in your tags list), some basic outline of what it can is here https://shorturl.at/bflFL (medium). You can also experiment with Hilt as what appears to be simplified alternative to Dagger, for android world.
In the Using Dagger in your Android app codelab tutorial they use an activity scoped regular class that acts as a ViewModel like so
#ActivityScope
class RegistrationViewModel #Inject constructor(val userManager: UserManager) {
...
}
That makes ViewModel injection by Dagger very simple but won't we loose anyting if we don't derive from the architecture components ViewModel class?
In general, the code labs are related to some topic and they try to explain only this topic. Here it is Dagger, not Architecture Components. Yes, you can lose some functionality, but if they still can make their point - it does not matter.
Also if they make the app work with only plain java objects it means that they don't need the extra functionality from the ViewModel, they wrote less code so even better.
I also want to point out that the explanation that "you are losing ViewModel.onCleared" is "the smallest problem". What is the "main feature" of the VM is that you can share the same instance through the life cycle of the same Activity/Fragment or that you can share it between different Activity/Fragment.
And onCleared is something that should be used with caution because it means in some situations that you are trying to clear a reference to a thing you should not be holding in the first place.
I'm using data binding in my android project, also i'm using dagger 2 for DI.
basically for setting content view with data binding i need to do something like this :
LayoutClass layoutClass = DataBindingUtil.setContentView(Activity, Layout);
I'm providing that layoutClass in dagger module and injecting it to my activity. the question is, is this a good practice ?
Technically you're defining a circle-reference with this. You're just not warned, because setting up the graph requires you to be pro-active about this.
The dependencies would look like activity -> layout -> activity while you provide the module with an activity explicitly. Additionally you're modifying the activity with DataBindingUtil.setContentView() and therefore provide a dependency to the activity, which actually is a property of the activity itself.
So, never provide any UI with Dagger. Especially not to an activity.
So, what I am trying to accomplish is to ensure that I have only one instance per scope in Dagger2.
Default Singleton scope already works that way. No matter on how many places you inject same object, let's call it GlobalInstance, method GlobalInstance provideGlobalInstance() that constructs it will be called once and only once.
On the other side, if I define custom scope, for example #SessionScope and inside some SessionModule I make method User provideUser(), that method (and, consequentially, new User() constructor) will be called as many times as I am injecting User. No matter if I use the same module instance every time, User provideUser() is being called for every #Inject User mUser I have in my code, resulting with multiple instances, instead of one scope-limited "singleton".
Is there some clear way to achieve it, using regular Dagger api. One way to do it is to have lazy getters inside the module class, but it is not very clean way to do it.
Please note that #Singleton scope is functionally equivalent to any other custom scope you define.
It means that you could have two flavors of #Provides methods in SessionModule:
#Provides #SessionsScope - provides "session singletons" (more on this later)
#Provides - provides new object on each injection
Please note that the term "singleton" have some ambiguity when we talk about Dagger, therefore I prefere to use term "scoped objects". When scoped object injected with Dagger for the first time, the component caches its instance and returns it on each subsequent injections PERFORMED BY THE SAME COMPONENT.
For more information you can read this post: Android Dagger 2 Scopes Demistified
For example, let's say my Rest adapter created with Retrofit lives inside Application class.
I would love to get it inside the Activity, so I write the following code:
public class MainActivity extends Activity {
#Inject MyRestAdapter mRestAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((GlobalApplication) getApplication()).getComponent().inject(this);
}
}
Granted, it will make the job done. But...
How is this different from calling getApplication(), and then explicitly yank the MyRestAdapter to MainActivity? Yes, Dagger 2 will simplify the setup by automatically getting everything to the Activity, but you still need to explicitly tell from where you need these dependencies, and that, if I understand correctly, defeats the whole purpose of DI. Am I right to say that Dagger 2 is "semi-automated service locator", or it's just the tutorials that misled me, and there is correct way to inject dependencies with Dagger 2 into the View or Activity from Application?
I've been experimenting with Dagger and it definitely seems to blur the lines between service locator and dependency injection. This is at least true when used with Android activities. With the current version of Dagger, it is possible to write AndroidInjection.inject(this) in an activity's onCreate method. That's basically like saying "find all the services I need and inject them into me." So Dagger is a combination of a some central/global service locator that knows where to get the services and an injector that knows where (i.e. which instance variables) to put those services in the activity. It seems that Android activities force reliance on some kind of singleton/global object.
There is no 1 "purpose of DI" but definitely one of them is to separate the configuration from what actually requests the configured objects. The idea is that your higher-level objects, such as Activitys in Android, can request all of the objects it needs without worrying about where they come from, how they are constructed, and any semantics about their relationships. Similar to how an Activity doesn't deal with drawing text to the screen (and instead delegates that to a TextView[0]), DI helps keep your objects from knowing too much that is not relevant to the actual logic they need to perform.
Inherent in the "semi-automated service locator" you describe is static analysis and error handling. As applications become larger, it becomes even harder to get manual-DI correct. Dagger helps make your code less error-prone (and less tedious to maintain).
Consider the case where you have an internal version of your app for employees, where you log lots of information about how they use the app to make sure you can identify any issues. In your actual product, however, you don't want to track personally identifiable information like that when it's not necessary. Now, your MainActivity needs an AnalyticsLogger - which one should it get? The more cases you have, the easier it is for Dagger to help piece things together than for you to do it yourself.
[0] which delegates to a Paint object