android10 / Android-CleanArchitecture

This is a sample app that is part of a series of blog posts I have written about how to architect an android application using Uncle Bob's clean architecture approach.
Apache License 2.0
15.49k stars 3.32k forks source link

JVM dependencies in the domain layer? #253

Open mjordan-gpfw opened 6 years ago

mjordan-gpfw commented 6 years ago

One of my motivations for clean architecture is to have a use case / entity that is prepared for cross platform deployment. I'd like to eventually be able to use Kotlin to either deploy use-cases to a React Native like app using Kotlin/JS, a typical Android app with Kotlin/JVM, or typical iOS using Kotlin/Native.

That means use case / entity can not only be absent of Android dependencies, but JVM dependencies also. Therefore no RxJava to lean on for threading and Observable reactive api.

Is anyone else thinking about this problem and come up with a good pattern for connecting the Observable in the data layer to the Subscriber in the UI when there's no RxJava in the domain?

Or do you think going this far with clean arch (avoiding jvm dependencies) is premature?

brianguertin commented 6 years ago

Yes, when I write my business logic and entities, I avoid all dependencies on 3rd party frameworks (Android, RxJava, etc.).

This is actually a core tenant of clean architecture, It's listed as rule 1 on this post: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

"Independent of Frameworks. The architecture does not depend on the existence of some library of feature laden software. This allows you to use such frameworks as tools, rather than having to cram your system into their limited constraints."

I don't think Bob would approve of the way the UseCase in this project has a hard dependency on RxJava (and neither would I).

rettababy commented 6 years ago

Thanks guys 2 b honest I have absolutely No idea what I'm looking at so help 4 sure appreciated

ghost commented 6 years ago

The only solution I can think of is using a callback interface instead of Rx. @brianguertin Can you tell about how you write your business logic and entities?

brianguertin commented 6 years ago

@Hinaka There are lots of good approaches with different pros/cons.

Let's assume we like the overall asynchronous architecture and just want to abstract RxJava and threading knowledge out of the use cases, you can easily do so like this:

interface SuccessListener<ResultT> {
  void onSuccess(ResultT result);
}
interface ErrorListener {
  void onError(Throwable t);
}
interface UseCase<ParamT, ResultT> {
  void execute(ParamT param, SuccessListener<ResultT> onSuccess, ErrorListener onError);
}

class RxJavaExecutor {
   <ResultT> Single<ResultT> execute(UseCase<ResultT, ParamT> useCase, ParamT param) {
        // Wrap execute call in a `Single` (Excercise left to the reader...)
    }
}
brianguertin commented 6 years ago

Another approach is to make your use-cases single-threaded, which makes them even cleaner, and then only the executor needs to care about threading / listeners / etc..

interface UseCase<ParamT, ResultT> {
  ResultT execute(ParamT param) throws Throwable;
}

class RxJavaExecutor {
   <ResultT> Single<ResultT> execute(UseCase<ResultT, ParamT> useCase, ParamT param) {
        // Executes useCase on a background thread, and wraps call in a `Single` (Excercise left to the reader...)
    }
}

This approach will start to look better if you use Kotlin's coroutines, which help you keep your logic on one thread while easily doing async operations.