├── .gitignore ├── .idea ├── codeStyleSettings.xml ├── dictionaries │ └── rl98880.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml ├── sonarlint │ └── issuestore │ │ ├── 0 │ │ ├── 5 │ │ │ └── 05efc8b1657769a27696d478ded1e95f38737233 │ │ └── 7 │ │ │ └── 0712df971a99ac4d2fccb8e0fb19f377f3374cca │ │ ├── 2 │ │ └── a │ │ │ ├── 2a45a911a8f1836b0b6c5b758962572012d8f8c3 │ │ │ └── 2afbb999f001938c88fa43fc2ef52abf0f8213e4 │ │ ├── 5 │ │ └── b │ │ │ └── 5bbfa66edb4db3c7c33c5181f43510990d3307f9 │ │ ├── 6 │ │ └── b │ │ │ └── 6b702aa071c88bef08e9b11a23382f99b489c011 │ │ ├── 8 │ │ └── e │ │ │ └── 8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d │ │ ├── a │ │ └── 5 │ │ │ └── a5cc2925ca8258af241be7e5b0381edf30266302 │ │ ├── f │ │ ├── 0 │ │ │ ├── f075e13cd6fc3e6ba571d97acaf5fe8bcb84f25c │ │ │ └── f07866736216be0ee2aba49e392191aeae700a35 │ │ └── 4 │ │ │ └── f4a01d6a4fcb971362ec00a83903fd3902f52164 │ │ └── index.pb └── vcs.xml ├── README.md ├── _config.yml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── rubylich │ │ └── rl98880 │ │ └── cleanarchdomain │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── rubylich │ │ │ ├── cleanarchdomain │ │ │ ├── MainActivity.kt │ │ │ ├── domain │ │ │ │ ├── reposetory │ │ │ │ │ └── PostRepository.kt │ │ │ │ ├── services │ │ │ │ │ ├── AuthenticationService.kt │ │ │ │ │ ├── UserService.kt │ │ │ │ │ └── ValidationService.kt │ │ │ │ └── usecase │ │ │ │ │ ├── GetPostsUseCase.kt │ │ │ │ │ ├── LikePostUseCase.kt │ │ │ │ │ ├── LoginUseCase.kt │ │ │ │ │ └── LogoutUseCase.kt │ │ │ ├── presentation │ │ │ │ ├── FeedPresenter.kt │ │ │ │ ├── LoginPresenter.kt │ │ │ │ └── LogoutPresenter.kt │ │ │ ├── rxerror │ │ │ │ └── RxError.kt │ │ │ └── rxusecase │ │ │ │ └── RxUseCase.kt │ │ │ └── exmple │ │ │ └── Example.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── rubylich │ └── rl98880 │ └── cleanarchdomain │ └── domain │ └── usecase │ └── LoginUseCaseTest.kt ├── build.gradle ├── detekt.yml ├── gradle.properties ├── gradle ├── generated-kotlin-sources.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 227 | 229 | -------------------------------------------------------------------------------- /.idea/dictionaries/rl98880.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | kategory 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 1.8 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/0/5/05efc8b1657769a27696d478ded1e95f38737233: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/0/5/05efc8b1657769a27696d478ded1e95f38737233 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/0/7/0712df971a99ac4d2fccb8e0fb19f377f3374cca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/0/7/0712df971a99ac4d2fccb8e0fb19f377f3374cca -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/2/a/2a45a911a8f1836b0b6c5b758962572012d8f8c3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/2/a/2a45a911a8f1836b0b6c5b758962572012d8f8c3 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/2/a/2afbb999f001938c88fa43fc2ef52abf0f8213e4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/2/a/2afbb999f001938c88fa43fc2ef52abf0f8213e4 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/5/b/5bbfa66edb4db3c7c33c5181f43510990d3307f9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/5/b/5bbfa66edb4db3c7c33c5181f43510990d3307f9 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/6/b/6b702aa071c88bef08e9b11a23382f99b489c011: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/6/b/6b702aa071c88bef08e9b11a23382f99b489c011 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/a/5/a5cc2925ca8258af241be7e5b0381edf30266302: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/a/5/a5cc2925ca8258af241be7e5b0381edf30266302 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/f/0/f075e13cd6fc3e6ba571d97acaf5fe8bcb84f25c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/f/0/f075e13cd6fc3e6ba571d97acaf5fe8bcb84f25c -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/index.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/.idea/sonarlint/issuestore/index.pb -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Clean Architecture Domain layer modeling with [Kotlin](https://kotlinlang.org), [RxJava](https://github.com/ReactiveX/RxJava) and [Arrow](https://github.com/arrow-kt/arrow). 2 | 3 | ### [Website](https://rubylichtenstein.github.io/Domain-Layer-Modeling/) 4 | 5 | #### The project it's all about modeling domain use cases and domain error system with kotlin and RxJava. 6 | 7 | It's intended for those how wre already familiar with Clean Architecture approach. 8 | 9 | To familiarize yourself with the concept I recommend starting 10 | with these great posts. 11 | 12 | 1. [Android-CleanArchitecture](https://github.com/android10/Android-CleanArchitecture) 13 | 2. [applying-clean-architecture-on-android-hands-on](http://five.agency/android-architecture-part-4-applying-clean-architecture-on-android-hands-on/) 14 | 15 | ##### Lets recap the basic concepts of Clean Architecture 16 | 17 | * Keeping the code clean with single responsibly principle. 18 | * Isolation between layers: domain, data and presentation. 19 | * Domain layer is writen in pure java or kotlin and with Inversion of Control principle domain is framework independent. 20 | 21 | ##### The benefits of pure java domain layer: 22 | 23 | * Easy change frameworks (implementations), depend on abstraction and not on implementation. 24 | * Easy share code between platform, since the domain is framework independent and based on abstraction. 25 | * Fester tests, since the domain layer is pure java. 26 | 27 | #### The project demonstrate simple domain layer with 4 use cases 28 | 29 | * [login](https://github.com/RubyLichtenstein/Domain-Layer-Modeling/blob/master/app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/LoginUseCase.kt) 30 | * [logout](https://github.com/RubyLichtenstein/Domain-Layer-Modeling/blob/master/app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/LogoutUseCase.kt) 31 | * [getPosts](https://github.com/RubyLichtenstein/Domain-Layer-Modeling/blob/master/app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/GetPostsUseCase.kt) 32 | * [likePost](https://github.com/RubyLichtenstein/Domain-Layer-Modeling/blob/master/app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/LikePostUseCase.kt) 33 | 34 | 35 | 36 | ## Now, let's dive in to Use Cases structure 37 | 38 | #### Modeling Use Cases With RxJava 39 | Utilizing Reactive types, we'll demonstrate a Use Case Object, with and without a parameter. 40 | 41 | 42 | ### How to create use case 43 | Use case composed from 44 | 45 | 1. Reactive type - (Observable/Flowable/Single/Maybe/Completable) 46 | 2. Data (Optional) - The data which use case will emit 47 | 3. Error (Optional) - Expected use case error and will be sealed class 48 | 4. Parameter (Optional) 49 | 50 | ### Basic use case structure 51 | 52 | ##### With parameter 53 | 54 | T - reactive type 55 | 56 | P - parameter type 57 | 58 | ```kotlin 59 | interface UseCaseWithParam { 60 | 61 | fun build(param: P): T 62 | 63 | fun execute(param: P): T 64 | } 65 | ``` 66 | 67 | ##### Without parameter 68 | ```kotlin 69 | interface UseCaseWithoutParam { 70 | 71 | fun build(): T 72 | 73 | fun execute(): T 74 | } 75 | ``` 76 | ## Use case examples 77 | 78 | #### [Login use case](https://github.com/RubyLichtenstein/Domain-Layer-Modeling/blob/master/app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/LoginUseCase.kt) 79 | 80 | Reactive type: Maybe 81 | 82 | :white_check_mark: Parameter 83 | 84 | :white_check_mark: Error 85 | 86 | :x: Data 87 | 88 | ```kotlin 89 | class LoginUseCase( 90 | private val authenticationService: AuthenticationService, 91 | private val validationService: ValidationService, 92 | private val userService: UserService, 93 | threadExecutor: Scheduler, 94 | postExecutionThread: Scheduler 95 | ) : MaybeWithParamUseCase( 96 | threadExecutor, 97 | postExecutionThread 98 | ) { 99 | 100 | override fun build(param: Param) 101 | : Maybe { 102 | //implementation 103 | } 104 | 105 | data class Param(val email: String, val password: String) 106 | 107 | sealed class Error { 108 | object InvalidEmail : Error() 109 | object InvalidPassword : Error() 110 | object EmailNotExist : Error() 111 | object WrongPassword : Error() 112 | object NoNetwork : Error() 113 | } 114 | 115 | } 116 | ``` 117 | #### [Get posts use case](https://github.com/RubyLichtenstein/Domain-Layer-Modeling/blob/master/app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/GetPostsUseCase.kt) 118 | 119 | ##### Use case type: 120 | 121 | Reactive type: Observable 122 | 123 | :x: Parameter 124 | 125 | :white_check_mark: Error 126 | 127 | :white_check_mark: Data 128 | 129 | ```kotlin 130 | class GetPostsUseCase( 131 | private val postRepository: PostRepository, 132 | private val userService: UserService, 133 | threadExecutor: Scheduler, 134 | postExecutionThread: Scheduler 135 | ) : ObservableWithoutParamUseCase>( 136 | threadExecutor, 137 | postExecutionThread 138 | ) { 139 | 140 | override fun build() 141 | : Observable> { 142 | //implementation 143 | } 144 | 145 | sealed class Error { 146 | object NoNetwork : Error() 147 | object UserNotLogin : Error() 148 | object PostNotFound : Error() 149 | } 150 | 151 | data class Data(val id: String, var text: String) 152 | } 153 | ``` 154 | 155 | ### Modeling domain error system with Kotlin and [Arrow Either](http://arrow-kt.io/docs/datatypes/either/) 156 | 157 | #### The improvements to the regular rx error system 158 | 159 | 1. Separation between expected and unexpected errors 160 | 2. Pattern matching for error state with kotlin sealed classes. 161 | 3. Keep the stream alive in case of expected errors, stop the stream only on unexpected or fatal errors. 162 | 163 | 164 | The implementation is with either stream `Observable>` 165 | and since error is sealed class we can do pattern matching on it. 166 | 167 | The regular rx on error used for unexpected errors only. 168 | 169 | #### Creating either stream 170 | 171 | You can create Either stream in one of the following ways 172 | 173 | 1. Defining Either observable 174 | ```kotlin 175 | class CreateEither { 176 | fun create() { 177 | Observable.just>(Success("Hello")) 178 | .subscribe() 179 | } 180 | } 181 | ``` 182 | 183 | 2. Converting regular stream to either stream with toSuccess/toFailure 184 | 185 | ```kotlin 186 | private fun Observable.toSuccess() = map { Success(it) } 187 | 188 | private fun Observable.toFailure() = map { Failure(it) } 189 | ``` 190 | ```kotlin 191 | class CreateEither { 192 | fun toEither() { 193 | Observable.just("Hello Either") 194 | .toSuccess() 195 | 196 | Observable.just("Hello Either") 197 | .toFailure() 198 | 199 | } 200 | } 201 | ``` 202 | 203 | #### Operating on either stream 204 | 205 | * Fold - applies `success` block if this is a Success or `failure` if this is a Failure. 206 | 207 | ```kotlin 208 | Observable.just>(Success("Hello")) 209 | .filter({ it.isRight() }) 210 | .map { Failure(Exception()) } 211 | .fold({"on failure"},{"on success"}) 212 | ``` 213 | 214 | #### Consuming either stream 215 | ```kotlin 216 | someUseCase 217 | .execute(SomeUseCase.Param("Hello World!")) 218 | .subscribe(object : ObservableEitherObserver { 219 | override fun onSubscribe(d: Disposable) = TODO() 220 | override fun onComplete() = TODO() 221 | override fun onError(e: Throwable) = onUnexpectedError(e) 222 | override fun onNextSuccess(r: SomeUseCase.Data) = showData(r) 223 | override fun onNextFailure(l: SomeUseCase.Error) = onFailure( 224 | when (l) { 225 | SomeUseCase.Error.ErrorA -> TODO() 226 | SomeUseCase.Error.ErrorB -> TODO() 227 | } 228 | ) 229 | }) 230 | ``` 231 | ### Example of consuming use case and handling expect and unexpected errors separately 232 | ```kotlin 233 | class SomePresenter(val someUseCase: SomeUseCase) { 234 | fun some() { 235 | someUseCase 236 | .execute(SomeUseCase.Param("Hello World!")) 237 | .subscribe(object : ObservableEitherObserver { 238 | override fun onSubscribe(d: Disposable) = TODO() 239 | override fun onComplete() = TODO() 240 | override fun onError(e: Throwable) = onUnexpectedError(e) 241 | override fun onNextSuccess(r: SomeUseCase.Data) = showData(r) 242 | override fun onNextFailure(l: SomeUseCase.Error) = onFailure( 243 | when (l) { 244 | SomeUseCase.Error.ErrorA -> TODO() 245 | SomeUseCase.Error.ErrorB -> TODO() 246 | } 247 | ) 248 | }) 249 | } 250 | 251 | private fun onFailure(any: Any): Nothing = TODO() 252 | private fun showData(data: SomeUseCase.Data): Nothing = TODO() 253 | private fun onUnexpectedError(e: Throwable): Nothing = TODO() 254 | } 255 | ``` 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | apply plugin: 'kotlin-kapt' //optional 7 | //apply from: rootProject.file('gradle/generated-kotlin-sources.gradle') //optional 8 | 9 | apply plugin: 'idea' 10 | 11 | idea { 12 | module { 13 | // sourceDirs += files( 14 | // 'build/generated/source/kapt/main', 15 | // 'build/generated/source/kapt/debug', 16 | // 'build/generated/source/kapt/release', 17 | // 'build/generated/source/kaptKotlin/main', 18 | // 'build/generated/source/kaptKotlin/debug', 19 | // 'build/generated/source/kaptKotlin/release', 20 | // 'build/tmp/kapt/main/kotlinGenerated') 21 | // generatedSourceDirs += files( 22 | // 'build/generated/source/kapt/main', 23 | // 'build/generated/source/kapt/debug', 24 | // 'build/generated/source/kapt/release', 25 | // 'build/generated/source/kaptKotlin/main', 26 | // 'build/generated/source/kaptKotlin/debug', 27 | // 'build/generated/source/kaptKotlin/release', 28 | // 'build/tmp/kapt/main/kotlinGenerated') 29 | } 30 | } 31 | //apply plugin: "io.gitlab.arturbosch.detekt-cli" 32 | //buildscript { 33 | // dependencies { 34 | // classpath "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.0.0.RC6-3" 35 | // 36 | // } 37 | //} 38 | 39 | //apply plugin: 'org.junit.platform.gradle.plugin' 40 | 41 | //junitPlatform { 42 | // filters { 43 | // engines { 44 | // include 'spek' 45 | // } 46 | // } 47 | //} 48 | //plugins { 49 | // id "io.gitlab.arturbosch.detekt" version "1.0.0.RC4-6" 50 | //} 51 | 52 | //detekt { 53 | // profile = 'main' 54 | // 55 | // profile('main') { 56 | // input = "$projectDir/src/main/java" 57 | // config = "$projectDir/detekt.yml" 58 | // filters = '.*test.*,.*/resources/.*,.*/tmp/.*' 59 | // output = "$projectDir/build/reports/detekt" 60 | // } 61 | // 62 | // idea { 63 | // path = "$rootDir/.idea" 64 | // codeStyleScheme = "$rootDir/.idea/codeStyleSettings.xml" 65 | // inspectionsProfile = "$rootDir/.idea/inspectionProfiles/Squanchy.xml" 66 | // report = "$projectDir/build/reports/detekt-idea" 67 | // mask = "*.kt," 68 | // } 69 | //} 70 | 71 | 72 | android { 73 | sourceSets { 74 | 75 | // main { 76 | // res.srcDirs = ['resources/main'] 77 | // } 78 | debug { 79 | manifest.srcFile 'AndroidManifest.xml' 80 | 81 | java.srcDirs += files( 82 | 'build/generated/source/kapt/main', 83 | 'build/generated/source/kapt/debug', 84 | 'build/generated/source/kapt/release', 85 | 'build/generated/source/kaptKotlin/main', 86 | 'build/generated/source/kaptKotlin/debug', 87 | 'build/generated/source/kaptKotlin/release', 88 | 'build/tmp/kapt/main/kotlinGenerated') 89 | // generatedS ourceDirs += files( 90 | // 'build/generated/source/kapt/main', 91 | // 'build/generated/source/kapt/debug', 92 | // 'build/generated/source/kapt/release', 93 | // 'build/generated/source/kaptKotlin/main', 94 | // 'build/generated/source/kaptKotlin/debug', 95 | // 'build/generated/source/kaptKotlin/release', 96 | // 'build/tmp/kapt/main/kotlinGenerated') 97 | } 98 | } 99 | compileSdkVersion 26 100 | defaultConfig { 101 | applicationId "com.example.rl98880.cleanarchdomain" 102 | minSdkVersion 23 103 | targetSdkVersion 26 104 | versionCode 1 105 | versionName "1.0" 106 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 107 | } 108 | buildTypes { 109 | release { 110 | minifyEnabled false 111 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 112 | } 113 | } 114 | kapt { 115 | generateStubs = true 116 | } 117 | 118 | } 119 | //plugins { 120 | // id 'io.gitlab.arturbosch.detekt' version '1.0.0.RC6-3' 121 | //} 122 | dependencies { 123 | 124 | implementation fileTree(dir: 'libs', include: ['*.jar']) 125 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 126 | implementation 'com.android.support:appcompat-v7:26.1.0' 127 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 128 | testImplementation 'junit:junit:4.12' 129 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 130 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 131 | 132 | implementation "io.reactivex.rxjava2:rxjava:2.1.9" 133 | 134 | compile 'io.arrow-kt:arrow-core:0.6.1' 135 | compile 'io.arrow-kt:arrow-typeclasses:0.6.1' 136 | compile 'io.arrow-kt:arrow-instances:0.6.1' 137 | compile 'io.arrow-kt:arrow-data:0.6.1' 138 | compile 'io.arrow-kt:arrow-syntax:0.6.1' 139 | kapt 'io.arrow-kt:arrow-annotations-processor:0.6.1' 140 | 141 | compile 'io.arrow-kt:arrow-free:0.6.1' //optional 142 | compile 'io.arrow-kt:arrow-mtl:0.6.1' //optional 143 | compile 'io.arrow-kt:arrow-effects:0.6.1' //optional 144 | compile 'io.arrow-kt:arrow-effects-rx2:0.6.1' //optional 145 | compile 'io.arrow-kt:arrow-effects-kotlinx-coroutines:0.6.1' //optional 146 | compile 'io.arrow-kt:arrow-optics:0.6.1' //optional 147 | 148 | testCompile "com.nhaarman:mockito-kotlin:1.5.0" 149 | testCompile 'com.rubylichtenstein:rxtest:1.0.5' 150 | testCompile "org.mockito:mockito-core:2.15.0" 151 | 152 | // testCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" 153 | // testCompile ('org.jetbrains.spek:spek-api:1.1.5') { 154 | // exclude group: 'org.jetbrains.kotlin' 155 | // } 156 | // testRuntime ('org.jetbrains.spek:spek-junit-platform-engine:1.1.5') { 157 | // exclude group: 'org.junit.platform' 158 | // exclude group: 'org.jetbrains.kotlin' 159 | // } 160 | } 161 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/rubylich/rl98880/cleanarchdomain/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.rl98880.cleanarchdomain 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.example.rl98880.cleanarchdomain", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import com.rubylich.rl98880.cleanarchdomain.R 6 | 7 | class MainActivity : AppCompatActivity() { 8 | 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | setContentView(R.layout.activity_main) 12 | 13 | } 14 | } 15 | 16 | //class ObservableKHK private constructor() 17 | //typealias ObservableKKind = arrow.HK 18 | // 19 | //@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") 20 | //inline fun ObservableKKind.ev(): ObservableK = 21 | // this as ObservableK 22 | 23 | // 24 | //@higherkind 25 | //data class ObservableK(val observable: Observable) : 26 | // ObservableKKind 27 | // 28 | //@higherkind 29 | //data class SingleK(val single: Single) : SingleKKind 30 | // 31 | //@typeclass 32 | //interface Schedul : TC { 33 | // fun observeOn(f: HK, scheduler: Scheduler): HK 34 | // fun subscribeOn(f: HK, scheduler: Scheduler): HK 35 | //} 36 | // 37 | //@instance(ObservableKHK::class) 38 | //interface ObservableSchInstance : 39 | // Schedul { 40 | // override fun observeOn( 41 | // f: HK, 42 | // scheduler: Scheduler 43 | // ): HK = 44 | // ObservableK(f.ev().observable.observeOn(scheduler)) 45 | // 46 | // 47 | // override fun subscribeOn( 48 | // f: HK, 49 | // scheduler: Scheduler 50 | // ): HK = 51 | // ObservableK(f.ev().observable.subscribeOn(scheduler)) 52 | //} 53 | 54 | //@instance(SingleK::class) 55 | //interface ObservableSchInstance : Schedul { 56 | // override fun observeOn( 57 | // f: HK, 58 | // scheduler: Scheduler 59 | // ): HK = 60 | // ObservableK(f.ev().observable.observeOn(scheduler)) 61 | // 62 | // 63 | // override fun subscribeOn( 64 | // f: HK, 65 | // scheduler: Scheduler 66 | // ): HK = 67 | // ObservableK(f.ev().observable.subscribeOn(scheduler)) 68 | //} 69 | 70 | // 71 | //abstract class ListKHK private constructor() { 72 | // abstract fun observeOn( 73 | // hk: HK, 74 | // scheduler: Scheduler 75 | // ): HK 76 | // 77 | // abstract fun subscribeOn( 78 | // hk: HK, 79 | // scheduler: Scheduler 80 | // ): HK 81 | // 82 | //} 83 | // 84 | //typealias ListKKind = arrow.HK 85 | //typealias ListKKindedJ = io.kindedj.Hk 86 | // 87 | //@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") 88 | //inline fun ListKKind.ev(): ListK = 89 | // this as ListK 90 | // 91 | //@higherkind 92 | //data class ListK(val list: List) : ListKKind 93 | // 94 | // 95 | //@typeclass 96 | //interface Functor : TC { 97 | // fun map(fa: HK, f: (A) -> B): HK 98 | //} 99 | // 100 | //@instance(ListK::class) 101 | //interface ListKFunctorInstance : Functor { 102 | // override fun map(fa: HK, f: (A) -> B): ListK { 103 | // return fa.ev().list.map(f).k() 104 | // } 105 | //} 106 | //1 107 | //class StreamHK private constructor() {} 108 | // 109 | //typealias StreamKind = HK 110 | //class ObservableKHK private constructor() 111 | //typealias ObservableKKind = arrow.HK 112 | //typealias ObservableKKindedJ = io.kindedj.Hk 113 | // 114 | //@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") 115 | //inline fun ObservableKKind.ev(): ObservableK = 116 | // this as ObservableK 117 | 118 | //@higherkind 119 | //data class ObservableK(val observable: Observable) 120 | // : ObservableKKind, Functor1> by observable{ 121 | // override fun observeOn( 122 | // hk: ObservableKKind, 123 | // scheduler: Scheduler 124 | // ): HK, T> { 125 | // TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 126 | // } 127 | // 128 | // override fun subscribeOn( 129 | // hk: ObservableKKind, 130 | // scheduler: Scheduler 131 | // ): HK, T> { 132 | // TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 133 | // } 134 | //} 135 | // 136 | 137 | 138 | // 139 | //@higherkind 140 | //data class SequenceKW constructor(val sequence: Sequence) : SequenceKWKind, 141 | // Sequence by sequence { 142 | // 143 | //} 144 | // 145 | // 146 | //@instance(Observable::class) 147 | //interface ListKFunctorInstance : Functor1 { 148 | // override fun observeOn( 149 | // hk: ObservableKKind, 150 | // scheduler: Scheduler 151 | // ): ObservableKKind { 152 | // hk.ev().observable 153 | // } 154 | // 155 | // override fun subscribeOn( 156 | // hk: ObservableKKind, 157 | // scheduler: Scheduler 158 | // ): ObservableKKind { 159 | // 160 | // } 161 | //} 162 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/domain/reposetory/PostRepository.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.domain.reposetory 2 | 3 | import arrow.core.Either 4 | import io.reactivex.Maybe 5 | import io.reactivex.Observable 6 | 7 | /** 8 | * Created by rl98880 on 01/02/2018. 9 | */ 10 | interface PostRepository { 11 | fun getPosts(token: String): Observable> 12 | fun likePost(id: String, token: String): Maybe 13 | class Post(val id: String, val text: String) 14 | 15 | sealed class LikePostError 16 | sealed class FetchPostsError 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/domain/services/AuthenticationService.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.domain.services 2 | 3 | import arrow.core.Either 4 | import io.reactivex.Maybe 5 | import io.reactivex.Single 6 | 7 | /** 8 | * Created by rl98880 on 30/01/2018. 9 | */ 10 | 11 | interface AuthenticationService { 12 | fun login(loginData: LoginRequest): Single> 13 | fun logOut(token: String): Maybe 14 | 15 | data class LoginRequest( 16 | val name: String, 17 | val password: String 18 | ) 19 | 20 | sealed class Error { 21 | object NetworkError : Error() 22 | object EmailNotExist : Error() 23 | object WrongPassword : Error() 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/domain/services/UserService.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.domain.services 2 | 3 | import arrow.core.Either 4 | import io.reactivex.Maybe 5 | import io.reactivex.Single 6 | 7 | /** 8 | * Created by rl98880 on 03/02/2018. 9 | */ 10 | interface UserService { 11 | fun getToken(): Single> 12 | fun setToken(token: String): Maybe 13 | 14 | sealed class Error { 15 | object TokenNotFound : Error() 16 | object CantSaveToken : Error() 17 | } 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/domain/services/ValidationService.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.domain.services 2 | 3 | import io.reactivex.Maybe 4 | 5 | /** 6 | * Created by rl98880 on 03/02/2018. 7 | */ 8 | interface ValidationService { 9 | fun validateEmail(email: String): Maybe 10 | fun validatePassword(password: String): Maybe 11 | 12 | sealed class Error { 13 | sealed class InvalidEmail 14 | sealed class InvalidPassword 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/GetPostsUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.domain.usecase 2 | 3 | import arrow.core.Either 4 | import com.rubylich.cleanarchdomain.domain.reposetory.PostRepository 5 | import com.rubylich.cleanarchdomain.domain.services.UserService 6 | import com.rubylich.cleanarchdomain.rxerror.Failure 7 | import com.rubylich.cleanarchdomain.rxerror.Success 8 | import com.rubylich.cleanarchdomain.rxusecase.ObservableWithoutParamUseCase 9 | import io.reactivex.Observable 10 | import io.reactivex.Scheduler 11 | 12 | /** 13 | * Created by rl98880 on 03/02/2018. 14 | */ 15 | class GetPostsUseCase( 16 | private val postRepository: PostRepository, 17 | private val userService: UserService, 18 | threadExecutor: Scheduler, 19 | postExecutionThread: Scheduler 20 | ) : ObservableWithoutParamUseCase>( 21 | threadExecutor, 22 | postExecutionThread 23 | ) { 24 | 25 | override fun build() 26 | : Observable> { 27 | 28 | return userService.getToken() 29 | .flatMapObservable { 30 | it.fold( 31 | { Observable.just(Failure(Error.UserNotLogin)) }, 32 | { getPosts(postRepository, it) } 33 | ) 34 | } 35 | } 36 | 37 | private fun getPosts(postRepository: PostRepository, token: String) 38 | : Observable>? { 39 | return postRepository.getPosts(token) 40 | .map { 41 | it.fold( 42 | { Failure(Error.PostNotFound) }, 43 | { Success(Data(it.id, it.text)) } 44 | ) 45 | } 46 | } 47 | 48 | sealed class Error { 49 | object NoNetwork : Error() 50 | object UserNotLogin : Error() 51 | object PostNotFound : Error() 52 | } 53 | 54 | data class Data(val id: String, var text: String) 55 | } 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/LikePostUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.domain.usecase 2 | 3 | import com.rubylich.cleanarchdomain.domain.reposetory.PostRepository 4 | import com.rubylich.cleanarchdomain.domain.services.UserService 5 | import com.rubylich.cleanarchdomain.rxusecase.MaybeWithParamUseCase 6 | import io.reactivex.Maybe 7 | import io.reactivex.Scheduler 8 | 9 | /** 10 | * Created by rl98880 on 03/02/2018. 11 | */ 12 | class LikePostUseCase( 13 | private val postRepository: PostRepository, 14 | private val userService: UserService, 15 | threadExecutor: Scheduler, 16 | postExecutionThread: Scheduler 17 | ) : MaybeWithParamUseCase( 18 | threadExecutor, 19 | postExecutionThread 20 | ) { 21 | 22 | override fun build(param: String): Maybe { 23 | return userService.getToken() 24 | .flatMapMaybe { 25 | it.fold( 26 | { Maybe.just(Error.NoUserToken) }, 27 | { likePost(param, it) } 28 | ) 29 | } 30 | } 31 | 32 | private fun likePost( 33 | params: String, 34 | it: String 35 | ): Maybe { 36 | return postRepository.likePost(params, it) 37 | .map { Error.LikeFail } 38 | } 39 | 40 | sealed class Error { 41 | object NoUserToken : Error() 42 | object LikeFail : Error() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/LoginUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.domain.usecase 2 | 3 | import com.rubylich.cleanarchdomain.domain.services.AuthenticationService 4 | import com.rubylich.cleanarchdomain.domain.services.UserService 5 | import com.rubylich.cleanarchdomain.domain.services.ValidationService 6 | import com.rubylich.cleanarchdomain.rxusecase.MaybeWithParamUseCase 7 | import io.reactivex.Maybe 8 | import io.reactivex.Scheduler 9 | 10 | /** 11 | * Created by rl98880 on 30/01/2018. 12 | */ 13 | 14 | class LoginUseCase( 15 | private val authenticationService: AuthenticationService, 16 | private val validationService: ValidationService, 17 | private val userService: UserService, 18 | threadExecutor: Scheduler, 19 | postExecutionThread: Scheduler 20 | ) : MaybeWithParamUseCase( 21 | threadExecutor, 22 | postExecutionThread 23 | ) { 24 | 25 | override fun build(param: Param) 26 | : Maybe { 27 | return with(param) { 28 | Maybe.concat( 29 | validationService.validateEmail(email) 30 | .map { (Error.InvalidEmail) }, 31 | 32 | validationService.validatePassword(password) 33 | .map { (Error.InvalidPassword) }, 34 | 35 | authenticationService 36 | .login(AuthenticationService.LoginRequest(email, password)) 37 | .flatMapMaybe { 38 | it.fold( 39 | { 40 | Maybe.just( 41 | when (it) { 42 | AuthenticationService.Error.EmailNotExist -> Error.EmailNotExist 43 | AuthenticationService.Error.WrongPassword -> Error.WrongPassword 44 | AuthenticationService.Error.NetworkError -> Error.LoginFailed 45 | } 46 | ) 47 | }, 48 | { setToken(userService, it) } 49 | ) 50 | } 51 | ).firstElement() 52 | } 53 | } 54 | 55 | private fun setToken(userService: UserService, token: String) 56 | : Maybe { 57 | return userService.setToken(token) 58 | .map { Error.LoginFailed } 59 | } 60 | 61 | sealed class Error { 62 | object InvalidEmail : Error() 63 | object InvalidPassword : Error() 64 | object EmailNotExist : Error() 65 | object WrongPassword : Error() 66 | object LoginFailed : Error() 67 | } 68 | 69 | data class Param(val email: String, val password: String) 70 | } 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/domain/usecase/LogoutUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.domain.usecase 2 | 3 | import com.rubylich.cleanarchdomain.domain.services.AuthenticationService 4 | import com.rubylich.cleanarchdomain.domain.services.UserService 5 | import com.rubylich.cleanarchdomain.rxusecase.MaybeUseCase 6 | import io.reactivex.Maybe 7 | import io.reactivex.Scheduler 8 | 9 | /** 10 | * Created by rl98880 on 31/01/2018. 11 | */ 12 | class LogoutUseCase( 13 | private val authenticationService: AuthenticationService, 14 | private val userService: UserService, 15 | threadExecutor: Scheduler, 16 | postExecutionThread: Scheduler 17 | ) : MaybeUseCase(threadExecutor, postExecutionThread) { 18 | override fun build(): Maybe { 19 | return userService.getToken() 20 | .flatMapMaybe { 21 | it.fold( 22 | { 23 | Maybe.just( 24 | when (it) { 25 | UserService.Error.TokenNotFound -> { 26 | Error.LogoutError 27 | } 28 | UserService.Error.CantSaveToken -> TODO() 29 | } 30 | ) 31 | }, 32 | 33 | { logout(it) } 34 | ) 35 | } 36 | } 37 | 38 | private fun logout(it: String): Maybe { 39 | return authenticationService 40 | .logOut(it) 41 | .map { Error.LogoutError } 42 | } 43 | 44 | sealed class Error { 45 | object LogoutError : Error() 46 | } 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/presentation/FeedPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.presentation 2 | 3 | import com.rubylich.cleanarchdomain.domain.usecase.GetPostsUseCase 4 | import com.rubylich.cleanarchdomain.domain.usecase.LikePostUseCase 5 | import com.rubylich.cleanarchdomain.rxerror.ObservableEitherObserver 6 | import io.reactivex.MaybeObserver 7 | import io.reactivex.disposables.Disposable 8 | 9 | /** 10 | * Created by rl98880 on 03/02/2018. 11 | */ 12 | class FeedPresenter( 13 | private val fetchPostsUseCase: GetPostsUseCase, 14 | private val likePostUseCase: LikePostUseCase 15 | ) { 16 | fun getPosts() { 17 | fetchPostsUseCase.execute() 18 | .subscribe(object : 19 | ObservableEitherObserver { 20 | override fun onComplete() { 21 | 22 | } 23 | 24 | override fun onError(e: Throwable) { 25 | 26 | } 27 | 28 | override fun onNextSuccess(r: GetPostsUseCase.Data) { 29 | showPost(r) 30 | } 31 | 32 | override fun onNextFailure(l: GetPostsUseCase.Error) { 33 | val error = when (l) { 34 | GetPostsUseCase.Error.Unknown -> TODO() 35 | GetPostsUseCase.Error.NotValidToken -> TODO() 36 | GetPostsUseCase.Error.PostNotFound -> TODO() 37 | } 38 | showFetchPostError() 39 | } 40 | 41 | override fun onSubscribe(d: Disposable) { 42 | 43 | } 44 | } 45 | ) 46 | } 47 | 48 | fun likePost(id: String) { 49 | likePostUseCase.execute(id) 50 | .subscribe( 51 | object : MaybeObserver { 52 | override fun onComplete() { 53 | showLikeSuccess() 54 | } 55 | 56 | override fun onError(e: Throwable) { 57 | 58 | } 59 | 60 | override fun onSuccess(logoutError: LikePostUseCase.Error) { 61 | val res = when (logoutError) { 62 | LikePostUseCase.Error.NoUserToken -> TODO() 63 | LikePostUseCase.Error.LikeFail -> TODO() 64 | } 65 | showLikeFailed() 66 | } 67 | 68 | override fun onSubscribe(d: Disposable) {} 69 | } 70 | ) 71 | } 72 | 73 | private fun showLikeFailed() { 74 | 75 | } 76 | 77 | private fun showLikeSuccess() { 78 | 79 | } 80 | 81 | private fun showFetchPostError() { 82 | 83 | } 84 | 85 | private fun showPost(it: GetPostsUseCase.Data) { 86 | 87 | } 88 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/presentation/LoginPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.presentation 2 | 3 | import com.rubylich.cleanarchdomain.domain.usecase.LoginUseCase 4 | import com.rubylich.cleanarchdomain.domain.usecase.LoginUseCase.Error.InvalidEmail 5 | import com.rubylich.cleanarchdomain.domain.usecase.LoginUseCase.Error.InvalidPassword 6 | import io.reactivex.MaybeObserver 7 | import io.reactivex.disposables.Disposable 8 | 9 | /** 10 | * Created by rl98880 on 31/01/2018. 11 | */ 12 | class LoginPresenter(private val loginUseCase: LoginUseCase) { 13 | 14 | fun login(name: String, password: String) { 15 | loginUseCase 16 | .execute(LoginUseCase.Param(name, password)) 17 | .subscribe( 18 | object : MaybeObserver { 19 | override fun onSuccess(loginError: LoginUseCase.Error) { 20 | val res = when (loginError) { 21 | InvalidPassword -> showInvalidPasswordError() 22 | InvalidEmail -> showInvalidEmailError() 23 | LoginUseCase.Error.EmailNotExist -> TODO() 24 | LoginUseCase.Error.WrongPassword -> TODO() 25 | LoginUseCase.Error.LoginFailed -> TODO() 26 | } 27 | } 28 | 29 | override fun onError(e: Throwable) { 30 | onUnexpectedError(e) 31 | } 32 | 33 | override fun onSubscribe(d: Disposable) { 34 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 35 | } 36 | 37 | override fun onComplete() { 38 | showLoginSuccess() 39 | } 40 | } 41 | ) 42 | } 43 | 44 | private fun showInvalidEmailError() { 45 | 46 | } 47 | 48 | private fun showInvalidPasswordError() { 49 | 50 | } 51 | 52 | private fun onUnexpectedError(e: Throwable) {} 53 | private fun showLoginSuccess() {} 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/presentation/LogoutPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.presentation 2 | 3 | import com.rubylich.cleanarchdomain.domain.usecase.LogoutUseCase 4 | import io.reactivex.MaybeObserver 5 | import io.reactivex.disposables.Disposable 6 | 7 | /** 8 | * Created by rl98880 on 31/01/2018. 9 | */ 10 | class LogoutPresenter(private val logoutUseCase: LogoutUseCase) { 11 | fun logout() { 12 | logoutUseCase 13 | .execute() 14 | .subscribe(object : MaybeObserver { 15 | override fun onComplete() { 16 | showLogoutSuccess() 17 | } 18 | 19 | override fun onError(e: Throwable) { 20 | 21 | } 22 | 23 | override fun onSuccess(logoutError: LogoutUseCase.Error) { 24 | val res = when (logoutError) { 25 | LogoutUseCase.Error.LogoutError -> TODO() 26 | } 27 | showLogoutFail() 28 | } 29 | 30 | override fun onSubscribe(d: Disposable) {} 31 | }) 32 | } 33 | 34 | private fun unknownErrorHandler() {} 35 | 36 | private fun showLogoutSuccess() {} 37 | private fun showLogoutFail() {} 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/rxerror/RxError.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.rxerror 2 | 3 | import arrow.core.Either 4 | import io.reactivex.* 5 | 6 | /** 7 | * Created by rl98880 on 05/02/2018. 8 | */ 9 | 10 | typealias Success = Either.Right 11 | 12 | typealias Failure = Either.Left 13 | 14 | inline fun Either.fold1( 15 | crossinline failed: (A) -> C, 16 | crossinline success: (B) -> C 17 | ): C = this.fold(failed, success) 18 | 19 | fun Single.toSuccess(): Single> = this.map { 20 | Success( 21 | it 22 | ) 23 | } 24 | 25 | fun Single.toFailure(): Single> = this.map { 26 | Failure( 27 | it 28 | ) 29 | } 30 | 31 | interface EitherObserver { 32 | fun onNextSuccess(r: R) 33 | fun onNextFailure(l: L) 34 | 35 | fun onEither(either: Either) { 36 | either.fold(::onNextFailure, ::onNextSuccess) 37 | } 38 | } 39 | 40 | interface EitherSingleObserver { 41 | fun onSuccess(r: R) 42 | fun onFailure(l: L) 43 | 44 | fun onEither(either: Either) { 45 | either.fold(::onFailure, ::onSuccess) 46 | } 47 | } 48 | 49 | interface ObservableEitherObserver 50 | : Observer>, EitherObserver { 51 | override fun onNext(t: Either) = onEither(t) 52 | } 53 | 54 | interface FlowableEitherSubscriber 55 | : FlowableSubscriber>, 56 | EitherObserver { 57 | override fun onNext(t: Either) = onEither(t) 58 | } 59 | 60 | interface MaybeEitherObserver 61 | : MaybeObserver>, 62 | EitherSingleObserver { 63 | override fun onSuccess(t: Either) = onEither(t) 64 | } 65 | 66 | interface SingleEitherObserver 67 | : SingleObserver>, 68 | EitherSingleObserver { 69 | override fun onSuccess(t: Either) = onEither(t) 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/cleanarchdomain/rxusecase/RxUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.cleanarchdomain.rxusecase 2 | 3 | 4 | import io.reactivex.* 5 | 6 | /** 7 | * Created by rl98880 on 28/01/2018. 8 | */ 9 | 10 | abstract class UseCase( 11 | private val threadExecutor: Scheduler, 12 | private val postExecutionThread: Scheduler 13 | ) 14 | 15 | abstract class UseCaseWithoutParam( 16 | threadExecutor: Scheduler, 17 | postExecutionThread: Scheduler 18 | ) : UseCase(threadExecutor, postExecutionThread) { 19 | 20 | abstract fun build(): T 21 | 22 | abstract fun execute(): T 23 | } 24 | 25 | abstract class UseCaseWithParam( 26 | threadExecutor: Scheduler, 27 | postExecutionThread: Scheduler 28 | ) : UseCase(threadExecutor, postExecutionThread) { 29 | 30 | abstract fun build(param: P): T 31 | 32 | abstract fun execute(param: P): T 33 | } 34 | 35 | abstract class CompletableWithParamUseCase( 36 | threadExecutor: Scheduler, 37 | postExecutionThread: Scheduler 38 | ) : UseCaseWithParam( 39 | threadExecutor, 40 | postExecutionThread 41 | ) 42 | 43 | abstract class CompletableUseCase(threadExecutor: Scheduler, postExecutionThread: Scheduler) : 44 | UseCaseWithoutParam( 45 | threadExecutor, 46 | postExecutionThread 47 | ) 48 | 49 | abstract class ObservableWithParamUseCase( 50 | private val threadExecutor: Scheduler, 51 | private val postExecutionThread: Scheduler 52 | ) : UseCaseWithParam, P>( 53 | threadExecutor, 54 | postExecutionThread 55 | ) { 56 | override fun execute(param: P): Observable { 57 | return build(param) 58 | .subscribeOn(threadExecutor) 59 | .observeOn(postExecutionThread) 60 | } 61 | } 62 | 63 | abstract class ObservableWithoutParamUseCase( 64 | private val threadExecutor: Scheduler, 65 | private val postExecutionThread: Scheduler 66 | ) : UseCaseWithoutParam>( 67 | threadExecutor, 68 | postExecutionThread 69 | ) { 70 | override fun execute(): Observable { 71 | return build() 72 | .subscribeOn(threadExecutor) 73 | .observeOn(postExecutionThread) 74 | } 75 | } 76 | 77 | abstract class SingleWithParamUseCase( 78 | threadExecutor: Scheduler, 79 | postExecutionThread: Scheduler 80 | ) : UseCaseWithParam, P>( 81 | threadExecutor, 82 | postExecutionThread 83 | ) { 84 | 85 | } 86 | 87 | abstract class SingleUseCase( 88 | threadExecutor: Scheduler, 89 | postExecutionThread: Scheduler 90 | ) : 91 | UseCaseWithoutParam>( 92 | threadExecutor, 93 | postExecutionThread 94 | ) 95 | 96 | abstract class MaybeWithParamUseCase( 97 | private val threadExecutor: Scheduler, 98 | private val postExecutionThread: Scheduler 99 | ) : UseCaseWithParam, P>( 100 | threadExecutor, 101 | postExecutionThread 102 | ) { 103 | 104 | override fun execute(param: P): Maybe { 105 | return build(param) 106 | .subscribeOn(threadExecutor) 107 | .observeOn(postExecutionThread) 108 | } 109 | } 110 | 111 | abstract class MaybeUseCase( 112 | private val threadExecutor: Scheduler, 113 | private val postExecutionThread: Scheduler 114 | ) : UseCaseWithoutParam>( 115 | threadExecutor, 116 | postExecutionThread 117 | ) { 118 | override fun execute(): Maybe = 119 | build() 120 | .subscribeOn(threadExecutor) 121 | .observeOn(postExecutionThread) 122 | 123 | } 124 | 125 | abstract class FlowableWithParamUseCase( 126 | threadExecutor: Scheduler, 127 | postExecutionThread: Scheduler 128 | ) : UseCaseWithParam, P>( 129 | threadExecutor, 130 | postExecutionThread 131 | ) 132 | 133 | abstract class FlowableUseCase(threadExecutor: Scheduler, postExecutionThread: Scheduler) : 134 | UseCaseWithoutParam>( 135 | threadExecutor, 136 | postExecutionThread 137 | ) 138 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubylich/exmple/Example.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.exmple 2 | 3 | import arrow.core.Either 4 | import com.rubylich.cleanarchdomain.rxerror.Failure 5 | import com.rubylich.cleanarchdomain.rxerror.ObservableEitherObserver 6 | import com.rubylich.cleanarchdomain.rxerror.Success 7 | import com.rubylich.cleanarchdomain.rxusecase.ObservableWithParamUseCase 8 | import io.reactivex.Observable 9 | import io.reactivex.disposables.Disposable 10 | import io.reactivex.schedulers.Schedulers 11 | 12 | /** 13 | * Created by rl98880 on 19/02/2018. 14 | */ 15 | class CreateEither { 16 | fun toEither() { 17 | Observable.just("Hello Either") 18 | .toSuccess() 19 | 20 | Observable.just>(Success("Hello Either")) 21 | .onErrorReturnItem(Failure("sh*t")) 22 | 23 | Observable.just>(Success("Hello")) 24 | .filter({ it.isRight() }) 25 | .map { Failure(Exception()) } 26 | .fold({"on failure"},{"code on success"}) 27 | 28 | 29 | } 30 | } 31 | 32 | private inline fun Observable>.fold( 33 | crossinline fa: (A) -> C, 34 | crossinline fb: (B) -> C 35 | ): Observable = map { it.fold(fa, fb) } 36 | 37 | private fun Observable.toSuccess() = map { Success(it) } 38 | 39 | private fun Observable.toFailure() = map { Failure(it) } 40 | 41 | 42 | class SomeUseCase : 43 | ObservableWithParamUseCase, SomeUseCase.Param>( 44 | threadExecutor = Schedulers.io(), 45 | postExecutionThread = TODO() 46 | ) { 47 | 48 | override fun build(param: Param): Observable> { 49 | TODO() 50 | } 51 | 52 | data class Param(val hello: String) 53 | data class Data(val world: String) 54 | sealed class Error { 55 | object ErrorA : Error() 56 | object ErrorB : Error() 57 | } 58 | } 59 | 60 | class SomePresenter(val someUseCase: SomeUseCase) { 61 | fun some() { 62 | someUseCase 63 | .execute(SomeUseCase.Param("Hello World!")) 64 | .subscribe(object : 65 | ObservableEitherObserver { 66 | override fun onSubscribe(d: Disposable) = TODO() 67 | override fun onComplete() = TODO() 68 | override fun onError(e: Throwable) = onUnexpectedError(e) 69 | override fun onNextSuccess(r: SomeUseCase.Data) = showData(r) 70 | override fun onNextFailure(l: SomeUseCase.Error) = onFailed( 71 | when (l) { 72 | SomeUseCase.Error.ErrorA -> TODO() 73 | SomeUseCase.Error.ErrorB -> TODO() 74 | } 75 | ) 76 | }) 77 | } 78 | 79 | private fun onFailed(any: Any): Nothing = TODO() 80 | private fun showData(data: SomeUseCase.Data): Nothing = TODO() 81 | private fun onUnexpectedError(e: Throwable): Nothing = TODO() 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CleanArchDomain 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/rubylich/rl98880/cleanarchdomain/domain/usecase/LoginUseCaseTest.kt: -------------------------------------------------------------------------------- 1 | package com.rubylich.rl98880.cleanarchdomain.domain.usecase 2 | 3 | import com.rubylich.cleanarchdomain.domain.services.AuthenticationService 4 | import com.rubylich.cleanarchdomain.domain.services.UserService 5 | import com.rubylich.cleanarchdomain.domain.services.ValidationService 6 | import com.nhaarman.mockito_kotlin.mock 7 | import com.rubylich.cleanarchdomain.domain.usecase.LoginUseCase 8 | import com.rubylichtenstein.rxtest.assertions.should 9 | import com.rubylichtenstein.rxtest.extentions.test 10 | import com.rubylichtenstein.rxtest.matchers.complete 11 | import io.reactivex.Maybe 12 | import io.reactivex.Single 13 | import io.reactivex.schedulers.Schedulers 14 | import org.junit.Before 15 | import org.junit.Test 16 | import org.mockito.Mockito 17 | import org.mockito.MockitoAnnotations 18 | 19 | /** 20 | * Created by rl98880 on 11/02/2018. 21 | */ 22 | class LoginUseCaseTest { 23 | /* Given */ 24 | val authenticationServiceMock = mock { 25 | } 26 | 27 | val validationServiceMock = mock { 28 | } 29 | 30 | val userServiceMock = mock { 31 | } 32 | 33 | @Before 34 | fun setUp() { 35 | MockitoAnnotations.initMocks(this) 36 | } 37 | 38 | val loginUseCase: LoginUseCase = 39 | LoginUseCase( 40 | authenticationServiceMock, 41 | validationServiceMock, 42 | userServiceMock, 43 | threadExecutor = Schedulers.trampoline(), 44 | postExecutionThread = Schedulers.trampoline() 45 | ) 46 | 47 | @Test 48 | fun buildUseCase_email_not_valid() { 49 | val token = "some_token" 50 | Mockito.`when`(validationServiceMock.validateEmail(Mockito.anyString())) 51 | .thenReturn(Maybe.empty()) 52 | 53 | Mockito.`when`(validationServiceMock.validatePassword(Mockito.anyString())) 54 | .thenReturn(Maybe.empty()) 55 | 56 | Mockito.`when`(authenticationServiceMock.login(Mockito.any())) 57 | .thenReturn(Single.just(com.rubylich.cleanarchdomain.rxerror.Success(token))) 58 | 59 | Mockito.`when`(userServiceMock.setToken(token)) 60 | .thenReturn(Maybe.empty()) 61 | 62 | loginUseCase.execute(LoginUseCase.Param("", "ruby")) 63 | .test { 64 | it should complete() 65 | } 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | //apply plugin: "io.gitlab.arturbosch.detekt" 3 | 4 | buildscript { 5 | apply from: rootProject.file('gradle/generated-kotlin-sources.gradle') //optional 6 | 7 | ext.kotlin_version = '1.2.21' 8 | repositories { 9 | google() 10 | jcenter() 11 | } 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:3.0.1' 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | // classpath "gradle.plugin.io.gitlab.arturbosch.detekt-cli:detekt-gradle-plugin:1.0.0.RC6-3" 16 | 17 | // classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0' 18 | 19 | // NOTE: Do not place your application dependencies here; they belong 20 | // in the individual module build.gradle files 21 | } 22 | } 23 | //plugins { 24 | // id 'io.gitlab.arturbosch.detekt' 25 | //} 26 | 27 | 28 | allprojects { 29 | repositories { 30 | google() 31 | jcenter() 32 | maven { url 'http://dl.bintray.com/arturbosch/code-analysis' } 33 | // maven { url "http://dl.bintray.com/jetbrains/spek" } 34 | 35 | } 36 | } 37 | 38 | task clean(type: Delete) { 39 | delete rootProject.buildDir 40 | } 41 | -------------------------------------------------------------------------------- /detekt.yml: -------------------------------------------------------------------------------- 1 | autoCorrect: true 2 | 3 | build: 4 | warningThreshold: 1 5 | failThreshold: 1 6 | 7 | potential-bugs: 8 | active: true 9 | NoElseInWhenExpression: 10 | active: false 11 | UnsafeCast: 12 | active: false 13 | UnsafeCallOnNullableType: 14 | active: false 15 | LateinitUsage: 16 | active: false 17 | 18 | exceptions: 19 | active: true 20 | 21 | empty: 22 | active: true 23 | 24 | empty-blocks: 25 | active: false 26 | 27 | complexity: 28 | active: true 29 | LongMethod: 30 | threshold: 20 31 | LongParameterList: 32 | active: false 33 | LargeClass: 34 | threshold: 300 35 | ComplexMethod: 36 | threshold: 10 37 | NestedBlockDepth: 38 | threshold: 5 39 | TooManyFunctions: 40 | active: false 41 | LabeledExpression: 42 | active: false 43 | 44 | code-smell: 45 | active: true 46 | FeatureEnvy: 47 | threshold: 0.5 48 | weight: 0.45 49 | base: 0.5 50 | 51 | formatting: 52 | active: true 53 | useTabs: false 54 | Indentation: 55 | active: true 56 | autoCorrect: true 57 | ConsecutiveBlankLines: 58 | active: true 59 | autoCorrect: true 60 | MultipleSpaces: 61 | active: true 62 | autoCorrect: true 63 | SpacingAfterComma: 64 | active: false 65 | autoCorrect: false 66 | SpacingAfterKeyword: 67 | active: true 68 | autoCorrect: true 69 | SpacingAroundColon: 70 | active: true 71 | autoCorrect: true 72 | SpacingAroundCurlyBraces: 73 | active: true 74 | autoCorrect: true 75 | SpacingAroundOperator: 76 | active: true 77 | autoCorrect: true 78 | TrailingSpaces: 79 | active: true 80 | autoCorrect: true 81 | UnusedImports: 82 | active: true 83 | autoCorrect: true 84 | OptionalSemicolon: 85 | active: true 86 | autoCorrect: true 87 | OptionalUnit: 88 | active: false 89 | autoCorrect: false 90 | ExpressionBodySyntax: 91 | active: true 92 | autoCorrect: true 93 | ExpressionBodySyntaxLineBreaks: 94 | active: false 95 | autoCorrect: false 96 | 97 | performance: 98 | SpreadOperator: 99 | active: false 100 | autoCorrect: false 101 | 102 | style: 103 | active: true 104 | WildcardImport: 105 | active: false 106 | NamingConventionViolation: 107 | active: false 108 | MaxLineLength: 109 | active: false 110 | maxLineLength: 160 111 | 112 | comments: 113 | active: true 114 | CommentOverPrivateMethod: 115 | active: false 116 | CommentOverPrivateProperty: 117 | active: false 118 | UndocumentedPublicClass: 119 | active: false 120 | UndocumentedPublicFunction: 121 | active: false -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/gradle.properties -------------------------------------------------------------------------------- /gradle/generated-kotlin-sources.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/gradle/generated-kotlin-sources.gradle -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubyLichtenstein/Domain-Layer-Modeling/2c4fda9d77935e989074383ef051e3b77ff22a29/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jan 30 18:03:18 IST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------