Skip to content

Commit

Permalink
Merge pull request #82 from monstar-lab-oss/refactor/modules-core-des…
Browse files Browse the repository at this point in the history
…ignsystem

Refactor/modules core designsystem
  • Loading branch information
levinzonr authored Dec 12, 2023
2 parents cec9fff + 215b45f commit 7275971
Show file tree
Hide file tree
Showing 68 changed files with 382 additions and 166 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/github-actions
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Build and Test

on:
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Setup Java
uses: actions/setup-java@v1
with:
java-version: 17

- name: Spotless Check
run: ./gradlew spotlessCheck

- name: Detekt Check
run: ./gradlew detekt

- name: Run Tests
run: ./gradlew test
52 changes: 21 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,38 +49,28 @@ After installing, creating new features in the correct template structure should



## Project Structure
Android template project has three top-level packages: `core`, `designsystem` and `features`. Overall package structure looks like following
- `core`
- `extensions`
- `injection`
- `network`
- `persistence`
- `ui`
- `designsystem`
- `components`
- `theme`
- `features`
- `nstack`
- `feature1`
- `data`
- `domain`
- `injection`
- `ui`

### `core` package
Core package is meant to be Domain-agnostic. It means that it should never reference anything project specific. Instead, it serves as a home for components that can be part of any project. This includes extensions on Kotlin Classes, Utility classes, Base classes, Compose layouts and Compose Effects and Modifiers that are behavioural rather then UI emitting

### `designsystem` package
The design system package contains the building blocks for your application's UI. The main thing you will find here is `Theme`. This template is using custom `Theme` provider for Jetpack Compose while having `Material Theme` as its foundation. This allows to tweak `Colors` and `Typography` so it reflects actual Design System that is used on the project and supply custom properties like `Dimensions`

Another part of the design system are components. Examples of `designsystem` components are Buttons, TopBars, TextFields and so on. The template provides `AppButton` `AppTextField` and `AppTopBar` that you can modify and tweak and use throughout the project. [Learn more about design system here](./docs/DESIGN_SYSTEM.md)


### `features` package
Features package contains the most important part of any project. A Feature typically contains all the necessary code(and resources + assets if migrated to multi module) required to implement a specific application functionality, such as a login screen, shopping cart, or authentication. It may also have its own sets of dependencies, like third-party libraries or other features. [Each feature is following Clean Architecture Principles and has a three-layer split](#architecture)
## Project Structure & Modules
Android template project has a modular structure and has following mopdules: `:core`, `:designsystem` and `:app`. The intention is to make this template as flexible as possible by introducing shared modules while having the all the features reside in `:app` module. In case project grows, this allows to easily decouple features from `:app` module and/or introduce new features as part of separate module.

![](./resources/moduels.svg)

### `core` module
Core module is meant to be Domain-agnostic. It means that it should never reference anything project specific. Instead, it serves as a home for components that can be part of any project. This includes extensions on Kotlin Classes, Utility classes, Base classes, Compose layouts and Compose Effects and Modifiers that are behavioural rather then UI emitting


### `designsystem` module
The design system module contains the building blocks for your application's UI. The main thing you will find here is `Theme`. This template is using custom `Theme` provider for Jetpack Compose while having `Material Theme` as its foundation. This allows to tweak `Colors` and `Typography` so it reflects actual Design System that is used on the project and supply custom properties like `Dimensions`

Another part of the design system are components. Examples of `designsystem` components are Buttons, TopBars, TextFields and so on. The template provides `AppButton` `AppTextField` and `AppTopBar` that you can modify and tweak and use throughout the project.


### `app` module
This is a main modules that contains feature packages - the meat of any project. A Feature typically contains all the necessary code, resources and assets required to implement a specific application functionality, such as a login screen, shopping cart, or authentication. It may also have its own sets of dependencies, like third-party libraries or other features.

#### Feature modules
While the template doesn't really have feature modules and every feature is part of the `:app` module, it is adviced to make use of feature modules in case the you see there is good candidate for it. It will especially benefit mid to large sized projects under active development

Each feature (module or not) is following [Clean Architecture Principles and has a three-layer split](#architecture)

## Architecture
Template implements [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) and follows [best practices](https://developer.android.com/topic/architecture) provided by Google with some tweaks here and there
Expand All @@ -95,7 +85,7 @@ The domain layer contains the application's business logic. This layer should on
The data layer is where the actual interactions happen between different data sources. This layer “implements” parts of the Domain layer and communicates with the APIs, Databases, and other services and SDKs.


![](/docs/assets/arch.svg)
![](/resources/arch.svg)


## Flavors
Expand Down
30 changes: 10 additions & 20 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

@file:Suppress("UnstableApiUsage")
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
@Suppress("DSL_SCOPE_VIOLATION")
Expand All @@ -9,6 +8,7 @@ plugins {
alias(libs.plugins.ksp)
alias(libs.plugins.hilt)
id("dk.nstack.translation.plugin")
alias(libs.plugins.convention.android.application)
}

val nStackKey = "LqWLm621BwIxNRzdrei88pKhIIEI2EE8ni8r"
Expand All @@ -21,14 +21,11 @@ translation {
}

android {
compileSdk = 34
namespace = "com.monstarlab"
flavorDimensions += "default"
defaultConfig {
manifestPlaceholders += mapOf("appId" to nStackAppId, "apiKey" to nStackKey)
applicationId = "com.monstarlab"
minSdk = 23
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -63,28 +60,17 @@ android {
buildConfigField("String", "API_URL", "\"https://reqres.in/api/\"")
}
}
buildFeatures {
viewBinding = true
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(JavaVersion.VERSION_17.toString()))
}
}


packaging {
resources.excludes.add("META-INF/versions/9/previous-compilation-data.bin")
}
}

kotlin {
jvmToolchain(17)
}

configurations {
create("devDebugImplementation")
}
Expand All @@ -109,6 +95,8 @@ dependencies {
// Compose
implementation(platform(libs.android.compose.bom))
implementation(libs.bundles.android.compose.core)
implementation(libs.android.compose.material)
implementation(libs.android.compose.material.windowsize)
implementation(libs.android.activity.compose)
implementation(libs.android.lifecycle.viewmodel.compose)
implementation(libs.bundles.google.accompanist)
Expand All @@ -132,4 +120,6 @@ dependencies {
testImplementation(libs.bundles.test)
androidTestImplementation(libs.bundles.android.test)

implementation(project(":core"))
implementation(project(":designsystem"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.monstarlab.features.buildconfig

import com.monstarlab.BuildConfig
import com.monstarlab.core.config.AppFlavour
import com.monstarlab.core.config.AppVersion
import com.monstarlab.core.config.BuildConfiguration
import com.monstarlab.core.config.NetworkConfig
import javax.inject.Inject

class AppBuildConfiguration @Inject constructor() : BuildConfiguration {
override val flavour: AppFlavour
get() {
return when (BuildConfig.FLAVOR) {
"development" -> AppFlavour.Development
"staging" -> AppFlavour.Staging
"production" -> AppFlavour.Production
else -> throw IllegalStateException(
"Flavour ${BuildConfig.FLAVOR} is not supported",
)
}
}
override val version: AppVersion = AppVersion(
versionName = BuildConfig.VERSION_NAME,
buildNumber = BuildConfig.VERSION_CODE,
)
override val networkConfig: NetworkConfig = NetworkConfig(BuildConfig.API_URL)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.monstarlab.features.buildconfig

import com.monstarlab.core.config.BuildConfiguration
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
interface BuildConfigurationModule {

@Binds
@Singleton
fun bindBuildConfiguration(appBuildConfiguration: AppBuildConfiguration): BuildConfiguration
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.monstarlab.features.nstack.domain.usecase

import com.monstarlab.core.coroutines.IoDispatcher
import com.monstarlab.core.extensions.suspendRunCatching
import com.monstarlab.core.injection.IoDispatcher
import dk.nodes.nstack.kotlin.NStack
import dk.nodes.nstack.kotlin.models.AppOpenData
import javax.inject.Inject
Expand Down
1 change: 1 addition & 0 deletions build-logic/convention/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
28 changes: 28 additions & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
plugins {
`kotlin-dsl`
}

group = "com.monstarlab.buildlogic"


dependencies {
compileOnly(libs.android.gradle.plugin)
compileOnly(libs.kotlin.gradle.plugin)
}

gradlePlugin {
/**
* Register convention plugins so they are available in the build scripts of the application
*/
plugins {
register("conventionAndroidLib") {
id = "convention.android.library"
implementationClass = "com.monstarlab.convention.AndroidLibConventionPlugin"
}

register("conventionAndroidApp") {
id = "convention.android.application"
implementationClass = "com.monstarlab.convention.AndroidAppConventionPlugin"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.monstarlab.convention

import com.android.build.api.dsl.ApplicationExtension
import com.monstarlab.convention.extensions.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure

class AndroidAppConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.configure<ApplicationExtension> {
compileSdk = AndroidConfiguration.COMPILE_SDK
defaultConfig {
targetSdk = AndroidConfiguration.TARGET_SDK
minSdk = AndroidConfiguration.MIN_SDK
}

buildFeatures {
compose = true
buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = target.rootProject.libs.findVersion("compose_compiler").get().toString()
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.monstarlab.convention


internal object AndroidConfiguration {
const val COMPILE_SDK = 34
const val TARGET_SDK = 34
const val MIN_SDK = 23
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.monstarlab.convention

import com.android.build.gradle.LibraryExtension
import com.monstarlab.convention.extensions.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure

class AndroidLibConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
extensions.configure<LibraryExtension> {
compileSdk = AndroidConfiguration.COMPILE_SDK
defaultConfig {
minSdk = AndroidConfiguration.MIN_SDK
}
buildFeatures {
compose = true
buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = target.rootProject.libs.findVersion("compose_compiler").get().toString()
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.monstarlab.convention.extensions

import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.getByType


internal val Project.libs: VersionCatalog get() =
extensions.getByType<VersionCatalogsExtension>().named("libs")
4 changes: 4 additions & 0 deletions build-logic/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configureondemand=true
13 changes: 13 additions & 0 deletions build-logic/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

include(":convention")
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ buildscript {
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.convention.android.library) apply false
alias(libs.plugins.convention.android.application) apply false
alias(libs.plugins.detekt) apply false
alias(libs.plugins.spotless)
alias(libs.plugins.hilt) apply false
Expand Down
1 change: 1 addition & 0 deletions core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
Loading

0 comments on commit 7275971

Please sign in to comment.