├── .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 |
228 |
229 |
--------------------------------------------------------------------------------
/.idea/dictionaries/rl98880.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | kategory
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | 1.8
38 |
39 |
40 |
41 |
42 |
43 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
--------------------------------------------------------------------------------