Best practice to pass dynamic parameters to an UseCase?
As in the example, usecase's parameters are set via UserModule:
@Module
public class UserModule {
private int userId = -1;
public UserModule() {}
public UserModule(int userId) {
this.userId = userId;
}
@Provides @PerActivity @Named("userDetails") UseCase provideGetUserDetailsUseCase(
UserRepository userRepository, ThreadExecutor threadExecutor,
PostExecutionThread postExecutionThread) {
return new GetUserDetailsUseCase(userId, userRepository, threadExecutor, postExecutionThread);
}
}
But there are some cases when buildUseCaseObservable depends on some dynamic parameters. I tried to create an interface to provide these parameters and let the view (in MVP) implement it and pass them to UserModule. With this approach if the view is a Fragment then I have to re-create UserModule again in the Fragment.
public interface UserIdProvider {
int getUserId();
}
Any suggestion, recommendation?
So in that case, buildUserCaseObservable is no longer useful and you will have to pass them via setter (with defensive code and failing fast in case all the dynamic parameters are not satisfied). I used this approach due to dagger instantiating my objects thus, being able to pass the user id via module constructor parameter.
@android10 yes that's true, but how do you pass dynamics parameters for login process for example? I mean in this case there isn't a predefined parameter but you must setup the submitted details? Which is your approach on this? I know that this is a question but it's really interesting to give us an example.
For this case the Dagger module must be dynamic so I suppose that there is 3 solutions (I think):
- PerFragment modules something like that:
@PerFragment
@Component(dependencies = ApplicationComponent.class, modules = FragmentModule.class)
public interface FragmentComponent {
//Exposed to sub-graphs.
Fragment fragment();
}
@Module
public class FragmentModule {
private final Fragment fragment;
public FragmentModule(Fragment fragment) {
this.fragment = fragment;
}
@Provides @PerFragment Fragment fragment() {
return this.fragment;
}
}
@PerFragment
@Component(dependencies = ApplicationComponent.class, modules = {FragmentModule.class, LoginFragmentModule.class})
public interface LoginFragmentComponent extends FragmentComponent {
void inject(UserLoginFragment userLoginFragment);
}
@Module
public class LoginFragmentModule {
private String username = "";
private String password = "";
public LoginFragmentModule(String username, String password) {
this.username = username;
this.password = password;
}
@Provides @PerFragment @Named("userLogin") UseCase provideUserLoginUseCase(
UserRepository userRepository, ThreadExecutor threadExecutor,
PostExecutionThread postExecutionThread) {
return new UserLogin(username, password,userRepository, threadExecutor, postExecutionThread);
}
}
- Observer to get the changes and re-initialises the Activity dagger module.
- Or to change dynamically the component into the Fragment during the action. For example I can do into the fragment this one as below! Not sure if it will work but with this one we are re-creating the component during the action. Basically this will be done in every case (for
@PerFragmentalso)
DaggerUserComponent.builder()
.applicationComponent(getApplicationComponent())
.activityModule(getActivityModule())
.userModule(new UserModule(username, pass))
.build();
or ????
There is a problem only this because you can't use multiple scopes for example for data mappers. So we must remove scopre for datamappers for example in order to be able to seen into the both scopes! Right? Any other suggestions maybe cleaner than this one?
@android10 please when you have free time it will be nice to give us a better approach by your side.
@android10 I know that is closed this issues but I want to know if our approach is acceptable.
Currently we have multiple forms so for example I have an invite form with multiple attrs.
Let's Say I have InviteActivity InviteView InvitePresenter InviteComponent in their referral package explicitly.
In my InviteModule of dagger I have something like this:
/**
* Dagger module that provides user related collaborators.
*/
@Module public class InviteModule {
private String email;
private String businessName;
public InviteModule() {
}
public InviteModule(String email, String businessName) {
this.email = email;
this.businessName = businessName;
}
@Provides @PerActivity @Named("userLogin") UseCase provideInviteBusinessUseCase(InviteBusiness sendInviteBusiness) {
return new InviteBusiness(this.email, this.businessName, UserRepository, threadExecutor,
postExecutionThread);
}
}
In the InviteComponent I have this
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = {
ActivityModule.class, UserModule.class
})
public interface InviteComponent extends ActivityComponent {
void inject(InviteBusinessFragment inviteBusinesssFragment);
void plus(InviteModule userModule);
}
So on click of the form I am just doing this:
this.getComponent(InviteComponent.class).plus(new InviteModule(textemail, textBusinessName);
in order to use the attributes into the UseCase. Any suggestion for a different approach?
@spirosoik Does the solution example in your last comment works nice with your needs ?
@MehdiChouag It's easier to keep the abstraction as it is with buildUseCaseObservable() but you can use this. Remember that in this method you must pass a Domain model not simple parameters.
protected abstract Observable buildUseCaseObservable(Object...param);
or you can re-create the component during login
DaggerUserComponent.builder()
.applicationComponent(getApplicationComponent())
.activityModule(getActivityModule())
.userModule(new UserModule(user))
.build();
Another way is to use @PerFragment scope
I am suggesting the first solution.
@android10 @lalongooo if you have a better solution than the first one, I would love to listen.
@spirosoik For me the first solution seems to be more appropriate.
Rather using Object, we may using template for parameters, like :
public abstract class UseCase<T> {
...
protected abstract Observable buildUseCaseObservable(@Nullable T... params);
public void execute(Subscriber UseCaseSubscriber, @Nullable T... params) {
mSubscription = buildUseCaseObservable(params).subscribeOn(mExecutionThread.getScheduler())
.observeOn(mPostExecutionThread.getScheduler())
.subscribe(UseCaseSubscriber);
}
...
}
And using it like this :
public class LoginAccountUseCase extends UseCase<String> {
@Override
protected Observable buildUseCaseObservable(String... params) {
return mAccountRepository.login(params[0], params[1]);
}
}
@MehdiChouag Yes of course I just send you the logic. This is depends in your case but as I said before it will be nice to use domain models to use cases, not plain params.
For example I am passing into the UseCase a UserModel which I am transforming this into User domain model or for credentials you can have a different object for this.
@spirosoik Can we avoid to create a UserModel then transforming into a User domain ? And instead directly use the User domain, that wouldn't break dependency rules.
@MehdiChouag Yes there is a great discussion (you are in) on this subject and I will support @android10 on this that each layer must have the View Models and each layer should have its model to deal with in order to avoid coupling between layers. It's clear that you are breaking the dependency.
Let me give my 2 cents here.
@MehdiChouag in reality you are not breaking any dependency rule. The domain layer does not use any model from the presentation layer but the opposite. This would leave the dependency as follows:
Presentation -> Domain
which this is correct according to the principle. The idea of having models for each tier it is to simply isolate completely each layer from each other. Also would allow you to have more specific models in the presentation layer without info that might be required in the domain layer. I guess it all boils down to app requirements.
Regarding the UseCase, I used the following solution instead:
/**
* Interactor to login the user using username and password
*/
public class UserLogin extends UseCase {
private final UserRepository userRepository;
private String password;
private String username;
public UserLogin(ThreadExecutor threadExecutor,
PostExecutionThread postExecutionThread,
UserRepository userRepository) {
super(threadExecutor, postExecutionThread);
this.userRepository = userRepository;
}
/**
* Initializes the interactor with the username and password to use for the authentication.
*
* @param username - username for the user
* @param password - password for the user.
*/
public UserLogin init(@NonNull String username, @NonNull String password) {
this.username = username;
this.password = password;
return this;
}
/**
* Builds the {@link UseCase} observable
* @return an {@link} Observable that will emit the logged in {@link UserProfile}
**/
@Override
public Observable buildUseCaseObservable() {
if (this.username == null || this.password == null) {
throw new IllegalArgumentException("init(username,password) not called, or called with null argument.");
}
return Observable.concat(validate(), this.userRepository.user(this.username, this.password));
}
private Observable validate() {
return Observable.create(new Observable.OnSubscribe<Object>() {
@Override
public void call(Subscriber<? super Object> subscriber) {
if (UserLogin.this.username.isEmpty()) {
subscriber.onError(new InvalidEmailException());
} else if (UserLogin.this.password.isEmpty()) {
subscriber.onError(new InvalidLoginPasswordException());
} else {
subscriber.onCompleted();
}
}
});
}
}
So you would execute it like:
this.userLogin.init("myUser", "myPassword").execute(new UserLoginSubscriber())
@jatago, so init must be abstract in UseCase?
In my case I don't actually include it in the base UseCase as abstract method but do it case by case. There might be some cases in which I required different params and some cases in which I don't require any. I just make sure to throw an early exception if any of the required params for this particular UseCase are not set and good to go.
@jatago what to do with a hard dependency UserLogin in Presentation layer in this case ?
Sorry to interrupt here but this a matter of architecture, I referenced many issues
- execute() can take more parameters than just a subscriber witch means buildUseCaseObservable should be overrided as well to pass the defined parameters ...so the base class is almost useless btw, injecting UseCase instances when the execute() method doesn't have the same signature is kind of ugly I guess
- the subscribtion has to be unsubscribed before calling another execute() with other parameters (if the useCase is the same instance of course) I think a useCase should should only be reused if a pagination is made, nothing more
- creating a dagger module seems the "cleanest" way to me but seriously this is overkill ....still I don't have any answer but the issue is really serious
It would be a good solution pass bundle to buildUseCaseObservable method?
public class SigninInteractor extends BaseInteractor {
public final static String EMAIL_PARAM = "email_param";
public final static String PASSWORD_PARAM = "password_param";
private FirebaseAuth firebaseAuth;
public SigninInteractor(FirebaseAuth firebaseAuth) {
this.firebaseAuth = firebaseAuth;
}
@Override
protected Observable<AuthResult> buildUseCaseObservable(Bundle bundle) {
if(bundle == null){
throw new Error("Invalid Params");
}
return RxFirebaseAuth.signInWithEmailAndPassword(firebaseAuth, bundle.getString(EMAIL_PARAM), bundle.getString(PASSWORD_PARAM));
}
}
Hi all,
I know this thread have closed but I want to share my approach, please leave some comment if possible : In my approach, I create a factory for the UseCase, and when needed, I will create a new UseCase in Presenter from the Factory. For example I have this UseCase :
public class DeleteFeedUseCase extends UseCase {
@NonNull
FeedRepository mRepository;
@NonNull
String mFeedKey;
@Inject
public DeleteFeedUseCase(String feed, FeedRepository repository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
super(threadExecutor, postExecutionThread);
this.mRepository = repository;
this.mFeedKey = feed;
}
@Override
protected Observable buildUseCaseObservable() {
return mRepository.deleteFeed(mFeedKey);
}
}
And this is the Factory :
public class DeleteFeedUseCaseFactory {
private FeedRepository mRepository;
private ThreadExecutor mThreadExecutor;
private PostExecutionThread mPostExecutionThread;
@Inject
public DeleteFeedUseCaseFactory(FeedRepository mRepository, ThreadExecutor mThreadExecutor, PostExecutionThread mPostExecutionThread) {
this.mRepository = mRepository;
this.mThreadExecutor = mThreadExecutor;
this.mPostExecutionThread = mPostExecutionThread;
}
public DeleteFeedUseCase create(String feed){
return new DeleteFeedUseCase(feed, mRepository, mThreadExecutor, mPostExecutionThread);
}
}
And how it is used in presenter
void deleteFeed(FeedModel feedModel, int index){
String key = mKeyStore.get(index);
DeleteFeedUseCase deleteFeedUseCase = mDeleteFeedUseCaseFactory.create(key);
deleteFeedUseCase.execute(new DeleteFeedSubscriber());
unsubscribeOnUnbindView(deleteFeedUseCase);
}
You can check out more detail on my sample https://github.com/roscrazy/Android-RealtimeUpdate-CleanArchitecture
Try this.
public abstract class UseCase<P extends UseCase.RequestValues, Q extends UseCase.ResponseValues> {
private P requestValues;
protected ThreadExecutor threadExecutor;
protected PostExecutionThread postExecutionThread;
private Subscription subscription = Subscriptions.empty();
public UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
this.threadExecutor = threadExecutor;
this.postExecutionThread = postExecutionThread;
}
protected P getRequestValues() {
return requestValues;
}
protected abstract Observable<Q> buildObservable(P requestValues);
public final void executeUseCase(DefaultSubscriber<Q> subscriber, P requestValues) {
this.subscription = checkNotNull(buildObservable(requestValues), "Observable cant be null")
.compose(applySchedulers())
.subscribe(subscriber);
}
public void unsubscribe() {
if(!subscription.isUnsubscribed())
subscription.unsubscribe();
}
protected final <T> Observable.Transformer<T, T> applySchedulers() {
return observable -> observable.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
}
public interface RequestValues {
}
public interface ResponseValues {
}
}
public class GetProfileUseCase extends UseCase<GetProfileUseCase.RequestValues,
GetProfileUseCase.ResponseValues> {
private ProfileRepository profileRepository;
public GetProfileUseCase(ThreadExecutor threadExecutor,
PostExecutionThread postExecutionThread,
ProfileRepository profileRepository) {
super(threadExecutor, postExecutionThread);
this.profileRepository = profileRepository;
}
@Override
protected Observable<ResponseValues> buildObservable(RequestValues requestValues) {
return profileRepository.get(requestValues.getProfileId())
.map(profile -> new ResponseValues(profile));
}
public static final class RequestValues implements UseCase.RequestValues {
private final long profileId;
public RequestValues(long profileId) {
this.profileId = profileId;
}
public long getProfileId() {
return profileId;
}
}
public static final class ResponseValues implements UseCase.ResponseValues {
private final Profile profile;
public ResponseValues(Profile profile) {
this.profile = profile;
}
public Profile getProfile() {
return profile;
}
}
}
Hi @astelmakh, I checked your solution. Could it be any problem with the usecase's subscription if we run the "executeUseCase()" multiple time? Example call "executeUseCase" 5 times immediately.
I re-opened this since it was a pending task and due to the lack of time I could not do it. Thanks for all the input/feedback, here is the commit that allows us to pass dynamic parameters to our use cases: https://github.com/android10/Android-CleanArchitecture/commit/dfd61c29170b74298780ebb7cc991e0b2edbdfe6
I will also write a quick post giving the reasons why I opted for that approach. Feel free to give any other constructive feedback. 😄
I also wrote an article with the arguments behind the design decisions: http://fernandocejas.com/2016/12/24/clean-architecture-dynamic-parameters-in-use-cases/
As usual, any feedback is very welcome. Merry Christmas! 😄 🎄
I know we have few alternative for the dynamic argument for the UseCase but I want to share my approach. This is similar approach like @roscrazy just little changes.
Here is UseCase
public class GetUserUseCase extends UseCase {
private final UserRepository userRepository;
private String userId;
@Inject
public GetUserUseCase(UserRepository userRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
super(threadExecutor, postExecutionThread);
this.userRepository = userRepository;
}
public void setUserId(String userId) {
this.userId = userId;
}
@Override
public Observable buildUseCaseObservable() {
return this.userRepository.getUser(userId);
}
}
Here is the Factory class for the User
@Singleton
public class UserUseCaseFactory {
@Inject
public CartUseCaseFactory(UserRepository userRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
this.userRepository = userRepository;
this.threadExecutor = threadExecutor;
this.postExecutionThread = postExecutionThread;
}
public UseCase provideGetUserDetailUseCase(String userId) {
GetUserUseCase mGetUserUseCase = new GetUserUseCase(userRepository, threadExecutor, postExecutionThread);
mGetUserUseCase.setUserId(userId);
return mGetUserUseCase;
}
}
Inside presenter the factory will provide the UseCase
public void loadUser(String userId) {
getUserUseCase = mUserUseCaseFactory.provideGetUserDetailUseCase(userId);
getUserUseCase.execute(new GetUserSubscriber());
}
@Dharmendra10 We know there are no silver bullets and sharing experiences is awesome. Thanks
Kotlin here.
create an interface (here : Parameters) for holding the params.
create classes (or data classes) which extend the interface to hold the data, as shown below.
interface Parameters
class LoginParams(val userName: String, val password: String) : Parameters
class GetUserDetailsParams(val id: Long): Parameters
Change your base Usecase(here : ObservableUseCase) to take in the Parameter interface to make it generic.
abstract class ObservableUseCase<T, P : Parameters> constructor(
private val postExecutionThread: PostExecutionThread
) {
protected abstract fun buildUseCaseObservable(p: P): Observable<T>
open fun invoke(singleObserver: DisposableObserver<T>, p: P) {
val single = this.buildUseCaseObservable(p)
.subscribeOn(Schedulers.io())
.observeOn(postExecutionThread.scheduler)
}
}
According the changes in the usecase look like this
open class LoginUseCase @Inject constructor(
postExecutionThread: PostExecutionThread,
private val userRepository: UserRepository
) : ObservableUseCase<User, LoginParams>(postExecutionThread) {
override fun buildUseCaseObservable(arguments: LoginParams): Observable<User> {
return userRepository.login(arguments.userName, arguments.password)
}
}
If your usecase doesnot take in any arguments then you can use this.
// This is useful when your use case needs no parameters
class Empty : Parameters
What's the point of having a UseCase interface/base class? What's wrong with a plain class like this:
class LoginUseCase @Inject constructor(private val loginService: LoginService) {
fun login(username: String, password: String): Single<LoginResult> {
...
}
}
What's the point of having a UseCase interface/base class? What's wrong with a plain class like this:
class LoginUseCase @Inject constructor(private val loginService: LoginService) { fun login(username: String, password: String): Single<LoginResult> { ... } }
I use a plain approach like the one you wrote here. It is flexible and works very well for me. But this is mainly because my use cases do not use shared code, which should be placed in a super class. But I guess in really big projects with several developers a more strict approach (with a base class or an interface) can be useful, but I have not encountered this necessity yet.