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

Can I use this architecture in real project? #55

Closed
sitexa opened this issue Oct 8, 2015 · 13 comments
Closed

Can I use this architecture in real project? #55

sitexa opened this issue Oct 8, 2015 · 13 comments

Comments

@sitexa
Copy link

sitexa commented Oct 8, 2015

This is not an issue.
It's really a clearly defined architecture. A lot of technologies are introduced into the architecture, I 've never used them before, Dagger, RxAndroid, Butternife, etc.
But I am little confused by the fine defined UseCases, for example , GetUserList, GetUserDetails for user usecases. If I want to do user search, I must define SearchUser usecase, I want to do CRUD for many many Objects, I need to write hands of usecases separately.
And, I'm not sure how to pass parameters to the usecases. For example, GetUserDetails, pass the userId by constructor in UserModule. How can I pass a search critera to SearchUser UseCase? A Map? Or a Model? Is there another way instead of constructor?

Thank you very much for any suggestion.

@spirosoik
Copy link

Of course you can but you must use a few tools to make your life easier.

@johnwatsondev
Copy link

@sitexa I have solved this problem. Here is a NOT elegant enough solution.

No callback function. Just pass your criteria through Varargs.

LoginPresenter.java

  private void performLogin(String userId, String pwd){
    Map<String, String> fields = new HashMap<>();
    fields.put("userId", userId);
    fields.put("pwd", pwd);

    userLoginCase.execute(new LoginSubscriber(), fields);
  }

UseCase.java

  protected abstract Observable buildUseCaseObservable (Object...param);

  public void execute(Subscriber useCaseSubscriber, Object...param){
    subscription = buildUseCaseObservable(param)
        .subscribeOn(Schedulers.from(threadExecutor))
        .observeOn(postExecutionThread.getScheduler())
        .lift(new OperatorSynchronousUnsubscribe())
        .subscribe(useCaseSubscriber);
  }

UserLoginCase.java

public class UserLoginCase extends UseCase {

  private final UserRepository userRepository;

  @Inject
  public UserLoginCase(UserRepository userRepository,
      ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
    super(threadExecutor, postExecutionThread);
    this.userRepository = userRepository;
  }

  @Override protected Observable buildUseCaseObservable(Object...param) {
    return this.userRepository.userLogin((Map<String, String>) param[0]);
  }
}

UserRepository.java

public interface UserRepository {
  Observable<User> userLogin(final Map<String, String> fields);
}

UserDataRepository.java

@Singleton public class UserDataRepository implements UserRepository {

  private final UserDataStoreFactory userDataStoreFactory;
  private final UserEntityDataMapper dataMapper;

  @Inject public UserDataRepository(UserDataStoreFactory dataStoreFactory,
      UserEntityDataMapper userEntityDataMapper) {
    this.userDataStoreFactory = dataStoreFactory;
    this.dataMapper = userEntityDataMapper;
  }

  @Override public Observable<User> userLogin(Map<String, String> fields) {
    final UserDataStore userDataStore = this.userDataStoreFactory.createCloudDataStore();
    return userDataStore.userLogin(fields).map(entity -> this.dataMapper.transform(entity));
  }
}

UserDataStore.java

public interface UserDataStore {
  Observable<UserEntity> userLogin(@NonNull final Map<String, String> fields);
}

CloudUserDataStore.java

public class CloudUserDataStore implements UserDataStore {
  private final RestApi restApi;

  public CloudUserDataStore(RestApi restApi) {
    this.restApi = restApi;
  }

  @Override public Observable<UserEntity> userLogin(@NonNull Map<String, String> fields) {
    return restApi.userLogin(fields);
  }
}

RestApi.java

public interface RestApi {
  Observable<UserEntity> userLogin(final Map<String, String> fields);
}

RestApi.java

public class RestApiImpl implements RestApi {
  private static RetrofitAPI RETROFIT_CLIENT;

  @Override public Observable<UserEntity> userLogin(final Map<String, String> fields) {
    return RETROFIT_CLIENT.userLogin(fields).timeout(REQUEST_TIMEOUT, TimeUnit.SECONDS);
  }
}

Please @me if you have any issue. Thanks.

@sitexa
Copy link
Author

sitexa commented Oct 9, 2015

Thank you very much, @johnwatsondev .
It is a good approach I'd like to follow, but I don't know how to write the 'OperatorSynchronousUnsubscribe' class, would you please give that to me ?

Thanks.

@sitexa
Copy link
Author

sitexa commented Oct 9, 2015

@spirosoik , I am glad to have your words.
But my life is not easy if you don't tell me what those tools are and what they can do work for me.

Thank you.

@spirosoik
Copy link

@sitexa on of this example you will see that the data layer uses files. You can use ORMLite for database. Also for the API you can use Retrofit in order to get back Observables as a response.

@johnwatsondev
Copy link

@sitexa It a approach that prevent memory leak. But it has a weakness as konmik said below.
qq20151009-0 2x
Reference:
ReactiveX/RxJava#3148
OperatorSynchronousUnsubscribe

There is a more elegant solution for preventing memory leak in Android. (Recommend)
https://github.com/trello/RxLifecycle

@spirosoik
Copy link

@johnwatsondev cool thanks for the info.

@sitexa
Copy link
Author

sitexa commented Oct 9, 2015

@johnwatsondev , it seems too difficult for me to get the point from the posts you referred. I need do some research on that topic.

@sitexa
Copy link
Author

sitexa commented Oct 9, 2015

@spirosoik , thank you for your reply.
I do use OkHttp/Retrofit in the network layer under the data layer. My point is should I write many many usecases separately for each object, like CRUD.

@johnwatsondev
Copy link

@sitexa I have used this architecture in real project.

As you said I have write some UseCase to do business stuff at the beginning. Eg:UserLoginCase,UserRegisterCase,UserSendVerifyCodeCase,UserForgetPasswordCase etc.

But i found they are belong to User Module.
So i created a UseCaseList which extends UseCase. Here is the SNAPSHOT:

public abstract class UseCaseList extends UseCase {

  //protected Subscription subscription1 = Subscriptions.empty();
  //protected Subscription subscription2 = Subscriptions.empty();
  //protected Subscription subscription3 = Subscriptions.empty();
  //protected Subscription subscription4 = Subscriptions.empty();

  protected Subscription subscription1;
  protected Subscription subscription2;
  protected Subscription subscription3;
  protected Subscription subscription4;

  protected CompositeSubscription mCompositeSubscription = new CompositeSubscription();

  protected UseCaseList(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
    super(threadExecutor, postExecutionThread);
  }

  protected abstract Observable buildUseCaseObservable1(Object... param);
  protected abstract Observable buildUseCaseObservable2(Object... param);
  protected abstract Observable buildUseCaseObservable3(Object... param);
  protected abstract Observable buildUseCaseObservable4(Object... param);

  @Override public void execute(Subscriber useCaseSubscriber, Object... param) {
    super.execute(useCaseSubscriber, param);
    mCompositeSubscription.add(subscription);
  }

  public void execute1(Subscriber UseCaseSubscriber, Object... param) {
    subscription1 = buildUseCaseObservable1(param).subscribeOn(Schedulers.from(threadExecutor))
        .observeOn(postExecutionThread.getScheduler())
        .lift(new OperatorSynchronousUnsubscribe())
        .subscribe(UseCaseSubscriber);
    mCompositeSubscription.add(subscription1);
  }

  public void execute2(Subscriber UseCaseSubscriber, Object... param) {
    subscription2 = buildUseCaseObservable2(param).subscribeOn(Schedulers.from(threadExecutor))
        .observeOn(postExecutionThread.getScheduler())
        .lift(new OperatorSynchronousUnsubscribe())
        .subscribe(UseCaseSubscriber);
    mCompositeSubscription.add(subscription2);
  }

  public void execute3(Subscriber UseCaseSubscriber, Object... param) {
    subscription3 = buildUseCaseObservable3(param).subscribeOn(Schedulers.from(threadExecutor))
        .observeOn(postExecutionThread.getScheduler())
        .lift(new OperatorSynchronousUnsubscribe())
        .subscribe(UseCaseSubscriber);
    mCompositeSubscription.add(subscription3);
  }

  public void execute4(Subscriber UseCaseSubscriber, Object... param) {
    subscription4 = buildUseCaseObservable4(param).subscribeOn(Schedulers.from(threadExecutor))
        .observeOn(postExecutionThread.getScheduler())
        .lift(new OperatorSynchronousUnsubscribe())
        .subscribe(UseCaseSubscriber);
    mCompositeSubscription.add(subscription4);
  }

  @Override public void unsubscribe() {
    //super.unsubscribe();
    if (mCompositeSubscription.hasSubscriptions()) {
      mCompositeSubscription.unsubscribe();
    }
  }
}

Then My UserUseCaseList can extend UseCaseList and override buildUseCaseObservable method.

We can package some similar UseCase or in same Business Module together in my opinion. My code is just a simple idea.

I have realized that code is not elegant. It can be replace with Queue or other Data Structure. Maybe you could give me some refactor suggestion.

You can argue with me by email or QQ (503548433).

Thanks in advance!

@sitexa
Copy link
Author

sitexa commented Oct 10, 2015

@johnwatsondev , added your QQ.

@sitexa sitexa closed this as completed Oct 11, 2015
@alexandru-calinoiu
Copy link

@johnwatsondev what is QQ?

@johnwatsondev
Copy link

@alexandru-calinoiu Sorry about that.
QQ is a chinese IM software.

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

No branches or pull requests

4 participants