Skip to content

Commit 1b3ad03

Browse files
v1
feature: - graphql - retrofit (second option) - chucker - jetpack compose
0 parents  commit 1b3ad03

File tree

208 files changed

+355382
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

208 files changed

+355382
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/.gradle/
2+
/.gradle/**

README.md

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Welcome to My Architecture Design!
2+
3+
Untuk mengukur seberapa baik sebuah arsitektur, kita membutuhkan base line (Standar minimum) dan sebuah alat untuk mengukur.
4+
Base tersebut adalah **Alur Dependensi dan Alur Kerja** dan alat ukur tersebut adalah **Prinsib-prinsib pembuatan software**
5+
Arsitektur software adalah tentang Flow Dependensi dan Flow Runtime. Hal-hal selain itu adalah preferensi saja.
6+
7+
Disini kita akan "**Back to Basic**" dengan konsern sepenuhnya pada **Flow dependensi** dan **Flow runtime**.
8+
9+
Saya memiliki prinsib sederhana; "Jika rumit berarti salah". Jadi kita akan membuatnya sedikit lebih mudah dengan menghadirkan sebuah module baru pada gradle project ,
10+
yaitu : **Provider** dan **Igniter**
11+
12+
Ide utama dari proyek ini adalah:
13+
1. Menghilangkan Horizontal dependency sepenuhnya, dengan memunculkan module baru yakni **Provider** dan **Igniter**.
14+
2. Menghilangkan state dari data layer, dengan memaksimalkan functional pattern pada data layer.
15+
3. Me-lokalisasi side effect. Kita harus membatasi module yang bisa melakukan side effect dan sebisa mungkin menghilangkannya.
16+
17+
# Project Arsitektur
18+
19+
Projek arsitektur mencakup keseluruhan arsitektur dari gradle project kita. Tidak segala hal bisa
20+
kita lakukan dengan gradle project akan tetapi **Pengecualian adalah hal yang buruk** jika kita
21+
tidak membatasinya. Jadi kita akan membatasi pengecualian tersebut hanya 2 hal.
22+
23+
1. App Module adalah bagian dari Igniter. Sehingga App Module dapat berperilaku seperti Igniter
24+
module, akan tetapi tidak boleh memprovide Module apapun sebagaimana module Igniter.
25+
2. Straight dependensi ke Core Module.
26+
27+
**Circular dependensi** terjadi dikarenakan kita mengizinkan "**Horizontal Dependency**". Jadi kita tidak akan mengijinkan adanya Horizontal dependensi pada module ini.
28+
29+
1. Sebuah module tidak boleh depend ke module lain kecuali Provider.
30+
2. Sebuah module, tidak perlu mengetahui keberadaan Module Lain, selain keberadaan module Provider.
31+
32+
## Provider
33+
34+
Sebagai mana namanya, Provider bertugas untuk memprovide module berdasarkan kontrak module pada
35+
Provider. Sebagai contoh, module tersebut bisa sebuah **API**, **Aktivitas**, **Widget** dan lain
36+
sebagainya.
37+
38+
### Runtime Flow
39+
40+
```mermaid
41+
graph LR
42+
A[Module 1] -- payload --> B((Provider)) -- payload --> C[Module 2]
43+
```
44+
45+
Setiap module bisa memanggil module lain melalui kontrak yang sudah dideklarasikan di dalam
46+
Provider. Selanjutnya, **Igniter** akan memprovide module berdasarkan kontrak yang di inginkan,
47+
dan **Module 1** tidak perlu tau dari mana dia mendapatkan **Module 2** tersebut. Tentusaja sebuah
48+
module harus memprovide atau meng-injeksikan module tersebut ke **Igniter** sebelumnya, untuk
49+
kemudian module tersebut dapat ditemukan oleh **Provider**.
50+
51+
### Dependency Flow
52+
53+
```mermaid
54+
graph LR
55+
A[Module 1] --> B((Provider))
56+
C[Module 2] --> B
57+
D[Module 3] --> B
58+
B --> E{{Core}}
59+
```
60+
61+
Seluruh module (yang masuk dalam kategori runtime module) hanya depend ke 1 Module, yakni Provider
62+
Module. Sementara Provider Module depend ke **Core Module**. Dalam arsitektur ini **Core Module**
63+
adalah sebuah pengecualian, dikarenakan Core Module memiliki sangat banyak komponent dan kebanyakan
64+
berukuran sangat kecil. Oleh Karena itu, akan jauh lebih mudah jika semua module yang membutuhkan
65+
core **"Straight Depend" ke Module Core**, dan semua module tetap harus **mengakses Core Module
66+
melalui Provider**.
67+
68+
## Igniter
69+
70+
Sebagaimana namanya, Igniter adalah sebuah module yang bertugas untuk menjadi sumbu api. Igniter
71+
perlu depend ke semua module, dan **meng-interaksikan** Module-module, API, Widget dll, yang diprovide
72+
setiap Module.
73+
74+
**Application** perlu **diprovide oleh Igniter**. Dan Igniter perlu menghubungkan seluruh Module ke satu sumbu api pada
75+
Application class.
76+
77+
## Overview arsitektur
78+
79+
Anda bisa melihat overview arsitektur pada file arsitektur-overview.pdf
80+
81+
# Internal Module Arsitektur
82+
83+
Kita mengenal MVVM , Clean Architectur, tapi apakah aplikasi front-end perlu se rumit itu? Menurut
84+
saya, Tidak.
85+
86+
Sebagai front-end developer, saya tidak pernah menemukan kemungkinan yang begitu rumit.
87+
88+
Aplikasi front-end hanya terdiri dari 2 hal yakni **UI** dan **Data**. Seberapa rumitkah itu.
89+
90+
## UI
91+
92+
UI adalah module level tinggi, penuh dengan side-effect, lifecycle, configuration changes dan lain
93+
sebagainya. Module ini memang cukup rumit, oleh karena itu, prinsib SOLID dan Dependency injection
94+
akan sangat membantu kita. Tidak ada hal yang istimewa yang perlu saya sampaikan, gunakan ViewModel
95+
dan Injector seperti Koin atau Dagger.
96+
97+
**Note**: Sekalipun UI penuh dengan side effect, kita tidak boleh hanya memakluminya. Melainkan kita harus sebisa mungkin menghilangkannya.
98+
Dalam pemrograman android imperativ, saya biasanya hanya mengijinkan side effect terjadi di Controller (Fragment atau Activity class).
99+
100+
1. Tidak boleh ada side effect dimanapun selain pada controller module (Fragment Activity, Service dll). Misalnya saja sekalipun viewmodel dan adapter termasuk di dalam presentation module, mereka tidak boleh melakukan side effect karena tidak termasuk ke dalam kategori Controller Class.
101+
2. Class dengan side effect tidak boleh melakukan "**Cross Responsibility Side Effect**"; Misal, Fragment mengimplementasikan kontrak A dan B, implementasi interface kontrak A melakukan side effect dengan menyimpan data di variable, dan implementasi interface kontrak B menggunakan data yang tersimpan tersebut, ini termasuk dalam **Cross Responsibility Side Effect**. Sangat di sarankan untuk module B, harus memiliki interface untuk menerima data tersebut secara langsung. dan Kontrak A boleh mengirimkan data ke Kontrak B dengan dependensi ke Kontrak B. Atau lebih baik lagi, Kontrak A menyediakan getter untuk data tersebut dan Kontrak B depend ke getter tersebut sesuai dengan **Dependency Inversion** principle.
102+
103+
## Data
104+
105+
Disini bagian menariknya. Data layer tidak memerlukan side-effect, tidak perlu memperdulikan
106+
lifecycle dan configuration changes (rotasi, dark mode, dll). Module ini sangat sederhana, jadi
107+
kenapa kita tidak membuatnya menjadi sederhana?
108+
109+
Dalam contoh dalam project ini. Data module tidak perlu mematuhi SOLID principle. Akan tetapi kita
110+
akan memaksimalkan **Functional Pattern** sebisa mungkin. Saya masih menemukan beberapa kesulitan
111+
dalam hal support teknologi, dan dokumentasi, akan tetapi, lihat seberapa sederhana itu.
112+
113+
1. Data module, tidak boleh memiliki state, dia hanya peduli dengan apa data yang diminta, dan
114+
memberikannya atau memberikan Error / Exception / Either.left apapun yang kalian gunakan.
115+
2. Data module hanya memprovide data dari Source, memberikan data atau Error jika data tidak
116+
ditemukan. Sedangkan bagaimana source berperilaku bukanlah konsern dari Data module.
117+
3. Repository API -> Source. Tidak rumit bukan. Dengan monoid chain hal ini dapat menjadi sangat
118+
sederhana, hanya memerlukan 2 step saja.

app/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

app/build.gradle.kts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
plugins {
2+
alias(libs.plugins.android.application)
3+
alias(libs.plugins.kotlin.android)
4+
}
5+
6+
android {
7+
compileSdk = 33
8+
9+
defaultConfig {
10+
applicationId = "com.stefanus_ayudha.basearchitecture"
11+
minSdk = 28
12+
targetSdk = 32
13+
versionCode = 1
14+
versionName = "1.0"
15+
16+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
17+
vectorDrawables {
18+
useSupportLibrary = true
19+
}
20+
}
21+
22+
buildTypes {
23+
release {
24+
isMinifyEnabled = true
25+
proguardFiles(
26+
getDefaultProguardFile("proguard-android-optimize.txt"),
27+
"proguard-rules.pro"
28+
)
29+
}
30+
}
31+
compileOptions {
32+
sourceCompatibility = JavaVersion.VERSION_1_8
33+
targetCompatibility = JavaVersion.VERSION_1_8
34+
}
35+
kotlinOptions {
36+
jvmTarget = "1.8"
37+
}
38+
buildFeatures {
39+
compose = true
40+
}
41+
composeOptions {
42+
kotlinCompilerExtensionVersion = "1.1.1"
43+
}
44+
packagingOptions {
45+
resources {
46+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
47+
}
48+
}
49+
}
50+
dependencies {
51+
api(project(":control:igniter"))
52+
}

app/proguard-rules.pro

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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.kts.
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.stefanus_ayudha.basearchitecture
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import androidx.test.platform.app.InstrumentationRegistry
5+
import org.junit.Assert.*
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
/**
10+
* Instrumented test, which will execute on an Android device.
11+
*
12+
* See [testing documentation](http://d.android.com/tools/testing).
13+
*/
14+
@RunWith(AndroidJUnit4::class)
15+
class ExampleInstrumentedTest {
16+
@Test
17+
fun useAppContext() {
18+
// Context of the app under test.
19+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20+
assertEquals("com.stefanus_ayudha.basearchitecture", appContext.packageName)
21+
}
22+
}

app/src/main/AndroidManifest.xml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.stefanus_ayudha.basearchitecture">
4+
5+
<application
6+
android:name="com.stefanus_ayudha.control.igniter.Application"
7+
android:allowBackup="true"
8+
android:dataExtractionRules="@xml/data_extraction_rules"
9+
android:fullBackupContent="@xml/backup_rules"
10+
android:icon="@mipmap/ic_launcher"
11+
android:label="@string/app_name"
12+
android:roundIcon="@mipmap/ic_launcher_round"
13+
android:supportsRtl="true"
14+
android:theme="@style/Theme.BaseArchitecture">
15+
<activity
16+
android:name="com.stefanus_ayudha.modsample.splash.ui.activity.SplashActivity"
17+
android:exported="true"
18+
android:label="@string/app_name"
19+
android:theme="@style/Theme.BaseArchitecture">
20+
<intent-filter>
21+
<action android:name="android.intent.action.MAIN" />
22+
23+
<category android:name="android.intent.category.LAUNCHER" />
24+
</intent-filter>
25+
26+
<meta-data
27+
android:name="android.app.lib_name"
28+
android:value="" />
29+
</activity>
30+
31+
<activity
32+
android:name="com.stefanus_ayudha.modsample.todolist.ui.activity.TodoActivity"
33+
android:exported="true" />
34+
<activity
35+
android:name="com.stefanus_ayudha.modsample.pokemon.ui.activity.PokemonActivity"
36+
android:exported="true" />
37+
</application>
38+
39+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:aapt="http://schemas.android.com/aapt"
3+
android:width="108dp"
4+
android:height="108dp"
5+
android:viewportWidth="108"
6+
android:viewportHeight="108">
7+
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
8+
<aapt:attr name="android:fillColor">
9+
<gradient
10+
android:endX="85.84757"
11+
android:endY="92.4963"
12+
android:startX="42.9492"
13+
android:startY="49.59793"
14+
android:type="linear">
15+
<item
16+
android:color="#44000000"
17+
android:offset="0.0" />
18+
<item
19+
android:color="#00000000"
20+
android:offset="1.0" />
21+
</gradient>
22+
</aapt:attr>
23+
</path>
24+
<path
25+
android:fillColor="#FFFFFF"
26+
android:fillType="nonZero"
27+
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
28+
android:strokeWidth="1"
29+
android:strokeColor="#00000000" />
30+
</vector>

0 commit comments

Comments
 (0)