Skip to content

Best practice to pass dynamic parameters to an UseCase? #32

Open
@rockerhieu

Description

@rockerhieu

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?

Activity

changed the title [-]Best practice to pass parameter to an UseCase?[/-] [+]Best practice to pass dynamic parameters to an UseCase?[/+] on Jul 5, 2015
android10

android10 commented on Aug 10, 2015

@android10
Owner

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.

spirosoik

spirosoik commented on Sep 10, 2015

@spirosoik

@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):

  1. 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);
  }
}
  1. Observer to get the changes and re-initialises the Activity dagger module.
  2. 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 @PerFragment also)
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?

spirosoik

spirosoik commented on Sep 13, 2015

@spirosoik

@android10 please when you have free time it will be nice to give us a better approach by your side.

spirosoik

spirosoik commented on Oct 27, 2015

@spirosoik

@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?

MehdiChouag

MehdiChouag commented on Nov 28, 2015

@MehdiChouag

@spirosoik Does the solution example in your last comment works nice with your needs ?

spirosoik

spirosoik commented on Nov 29, 2015

@spirosoik

@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.

MehdiChouag

MehdiChouag commented on Nov 29, 2015

@MehdiChouag

@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]);
  }
}
spirosoik

spirosoik commented on Nov 29, 2015

@spirosoik

@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.

MehdiChouag

MehdiChouag commented on Nov 29, 2015

@MehdiChouag

@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.

spirosoik

spirosoik commented on Nov 29, 2015

@spirosoik

@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.

22 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @Dharmendra10@spirosoik@jamolkhon@sebastien-tiscar@rockerhieu

        Issue actions

          Best practice to pass dynamic parameters to an UseCase? · Issue #32 · android10/Android-CleanArchitecture