Skip to content

Commit

Permalink
(0.5.0) Refactoring (#12)
Browse files Browse the repository at this point in the history
- Updated modules structure in app module to follow standards with domain/data/presentation structure
- Renamed some classes specific for DI configuration
- Moved ViewModelFactory of app layer to DI package
  • Loading branch information
kotlitecture committed Jun 30, 2024
1 parent 8f01238 commit d3f9334
Show file tree
Hide file tree
Showing 270 changed files with 1,893 additions and 1,896 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ The overall client architecture follows [the recommended guidelines](https://dev
- [Compose Multiplatform Images and resources](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-images-resources.html)
- [Jetpack Compose](https://developer.android.com/develop/ui/compose)
- [Jetpack Navigation](https://developer.android.com/guide/navigation)
- [Jetpack Lifecycle + ViewModel](https://developer.android.com/topic/libraries/architecture/lifecycle)
- [Jetpack Lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle)
- [Jetpack ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel)
- [Material 3 Design](https://m3.material.io)
- [Koin Dependency Injection](https://insert-koin.io)

Expand All @@ -73,8 +74,9 @@ Provides architectural solutions to implement user flows and integrate all compo

It includes:

- **MVVM pattern implementation** - based on the Jetpack ViewModel and Jetpack Lifecycle components.
- **Navigation implementation** - based on the Jetpack Navigation component.
- **MVVM pattern implementation** - based on the [Jetpack ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) and [Jetpack Lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle) components.
- **Navigation implementation** - based on the [Jetpack Navigation](https://developer.android.com/guide/navigation) component.
- **Store implementation** - initially based on [UI State](https://developer.android.com/topic/architecture/ui-layer/stateholders), but more flexible like [Pinia](https://pinia.vuejs.org/core-concepts/).

The included solutions are not mandatory to follow, as all required dependencies are properly included to enable you to use your own patterns with Jetpack components.

Expand Down Expand Up @@ -182,6 +184,6 @@ The generated project will include a similar table in its README.MD file, but wi
| Userflow | Save Theme API | [Link](docs/Userflow/Save%20Theme%20API/overview.md) | - | [Link](docs/Userflow/Save%20Theme%20API/usage.md) |
| Userflow | Change Theme Screen | [Link](docs/Userflow/Change%20Theme%20Screen/overview.md) | - | [Link](docs/Userflow/Change%20Theme%20Screen/usage.md) |
| Userflow | Toggle Theme Button | [Link](docs/Userflow/Toggle%20Theme%20Button/overview.md) | - | [Link](docs/Userflow/Toggle%20Theme%20Button/usage.md) |
| Userflow | Data Loader | [Link](docs/Userflow/Data%20Loader/overview.md) | - | [Link](docs/Userflow/Data%20Loader/usage.md) |
| Userflow | Data Loading Indicator | [Link](docs/Userflow/Data%20Loading%20Indicator/overview.md) | - | [Link](docs/Userflow/Data%20Loading%20Indicator/usage.md) |
| Testing | Kermit | [Link](docs/Testing/Kermit/overview.md) | - | [Link](docs/Testing/Kermit/usage.md) |
| Testing | Napier | [Link](docs/Testing/Napier/overview.md) | - | [Link](docs/Testing/Napier/usage.md) |
17 changes: 8 additions & 9 deletions docs/Dataflow/Basic In-Memory Cache API/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
## Overview

The API can be accessed through:
- `shared.data.datasource.cache.CacheSource` - facade interface at the core module level.
- `app.datasource.cache.AppCacheSource` - decorator class at the app level.
- `shared.data.source.cache.CacheSource` - facade interface at the core module level.
- `app.data.source.cache.AppCacheSource` - decorator class at the app level.

The difference is that the class serves as a **decorator** and can provide extra methods without impacting facade implementations.

Facade **CacheSource** provides the following methods:

- `getState(key: CacheKey<T>, valueProvider: suspend () -> T?): CacheState<T>` - Retrieves the state of a cache entry associated with the specified key.
- `get(key: CacheKey<T>, valueProvider: suspend () -> T?): T?` - Retrieves the value associated with the specified key from the cache.
- `get(key: CacheKey<T>, valueProvider: suspend () -> T?): CacheEntry<T>` - Retrieves the cache entry associated with the specified key.
- `invalidate(type: Class<K>)` - Invalidates all cache entries associated with the specified key type.
- `invalidate(key: K)` - Invalidates the cache entry associated with the specified key.
- `remove(type: Class<K>)` - Removes all cache entries associated with the specified key type.
Expand All @@ -21,22 +20,22 @@ Facade **CacheSource** provides the following methods:

## Example

Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.datasource.ProvidesCacheSource`.
Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.data.CacheSourceModule`.

To start using, just inject it to your DI managed class.

```kotlin
class BasicCacheViewModel(
private val cacheSource: CacheSource = get()
private val cacheSource: CacheSource
) : BaseViewModel() {

val cacheStore = StoreObject<String>()
val cacheState = DataState<String>()

override fun doBind() {
launchAsync {
val cacheKey = SimpleCacheKey()
val cacheState = cacheSource.getState(cacheKey, ::getDateAsFormattedString)
cacheState.changes().collectLatest(cacheStore::set)
val cacheEntry = cacheSource.get(cacheKey, ::getDateAsFormattedString)
cacheEntry.changes().collectLatest(cacheState::set)
}
}

Expand Down
10 changes: 5 additions & 5 deletions docs/Dataflow/Cash App Paging Library/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

## Overview

- DI integration: `app.di.datasource.ProvidesPagingSource`
- Data source: `app.datasource.paging.AppPagingSource`
- DI integration: `app.di.data.PagingSourceModule`
- Data source: `app.data.source.paging.AppPagingSource`
- UI component: `shared.design.component.AppPagingList`

The integration includes an `AppPagingSource` class located in `app.datasource.paging` to facilitate working with the Paging Library.
The integration includes an `AppPagingSource` class located in `app.data.source.paging` to facilitate working with the Paging Library.

## Example

Expand All @@ -15,11 +15,11 @@ The example is part of showcases provided when the feature is included into the

```kotlin
class BasicPagingViewModel(
private val pagingSource: AppPagingSource = get()
private val pagingSource: AppPagingSource
) : BaseViewModel() {

val itemsFlow by lazy {
val pager = pagingSource.getPager { BasicPagingSource() }
val pager = pagingSource.getPager(::BasicPagingSource)
pager.flow.cachedIn(viewModelScope)
}

Expand Down
8 changes: 4 additions & 4 deletions docs/Dataflow/Facade Analytics API/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
## Overview

The API can be accessed through:
- `shared.data.datasource.analytics.AnalyticsSource` - facade interface at the core module level.
- `app.datasource.analytics.AppAnalyticsSource` - decorator class at the app level.
- `shared.data.source.analytics.AnalyticsSource` - facade interface at the core module level.
- `app.data.source.analytics.AppAnalyticsSource` - decorator class at the app level.

The difference is that the class serves as a **decorator** and can provide extra methods without impacting facade implementations.

Expand All @@ -18,13 +18,13 @@ Facade **AnalyticsSource** provides the following methods:

## Example

Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.datasource.ProvidesAnalyticsSource`.
Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.data.AnalyticsSourceModule`.

To start using, just inject any of them to your DI managed class. Recommended to use from `ViewModel` or `Repository` level.

```kotlin
class TemplateViewModel (
private val analyticsSource: AnalyticsSource = get() // AppAnalyticsSource
private val analyticsSource: AnalyticsSource // AppAnalyticsSource
) : BaseViewModel() {

fun onSomeAction() {
Expand Down
8 changes: 4 additions & 4 deletions docs/Dataflow/Facade Config API/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
## Overview

The API can be accessed through:
- `shared.data.datasource.config.ConfigSource` - facade interface at the core module level.
- `app.datasource.config.AppConfigSource` - decorator class at the app level.
- `shared.data.source.config.ConfigSource` - facade interface at the core module level.
- `app.data.source.config.AppConfigSource` - decorator class at the app level.

The difference is that the class serves as a **decorator** and can provide extra methods without impacting facade implementations.

Expand All @@ -20,7 +20,7 @@ Facade **ConfigSource** provides the following methods:

## Example

Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.datasource.ProvidesConfigSource`.
Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.data.ConfigSourceModule`.

However, it is recommended to use decorator methods instead of directly accessing facade methods,
as the latter requires providing an extra parameter `defaultValue`, which might be hidden in the decorator.
Expand All @@ -34,7 +34,7 @@ class AppConfigSource : DelegateConfigSource() {
}

class TemplateViewModel (
private val configSource: AppConfigSource = get() // ConfigSource
private val configSource: AppConfigSource // ConfigSource
) : BaseViewModel() {

private val counter by lazy { AtomicInteger(configSource.getCounterInitialValue()) }
Expand Down
13 changes: 7 additions & 6 deletions docs/Dataflow/Ktor Client/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@

## Overview

The data source is available within the class `shared.data.datasource.http.HttpSource`. An instance of this class can be obtained through dependency injection (DI) as a singleton in `app.di.datasource.ProvidesHttpSource`.
- DI integration: `app.di.data.HttpSourceModule`
- Data source: `shared.data.source.http.HttpSource`

The class provides the next functionality:

- `ktor` - pre-configured HTTP client to work with HTTP through **Ktor API**.
- `client` - pre-configured HTTP client to work with HTTP through **Ktor API**.

## Example

To start using, just inject it to your DI managed class.

```kotlin
class ApiSource(
private val httpSource: HttpSource = get()
private val httpSource: HttpSource
) {

suspend fun getIp(): String {
val ktor = httpSource.ktor
return ktor.get("https://api64.ipify.org").body<String>()
fun getIp(): String {
val client = httpSource.client
return client.get("https://api64.ipify.org").body<String>()
}

}
Expand Down
9 changes: 5 additions & 4 deletions docs/Dataflow/Multiplatform Settings/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
## Overview

The API can be accessed through:
- `shared.data.datasource.keyvalue.KeyValueSource` - facade interface at the core module level.
- `app.datasource.keyvalue.AppKeyValueSource` - decorator class at the app level.
- `shared.data.source.keyvalue.KeyValueSource` - facade interface at the core module level.
- `app.data.source.keyvalue.AppKeyValueSource` - decorator class at the app level.

The difference is that the class serves as a **decorator** and can provide extra methods without impacting facade implementations.

Expand All @@ -22,14 +22,15 @@ Decorator **AppKeyValueSource** also provides the following methods with default

## Example

Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.datasource.ProvidesKeyValueSource`.
Both the **facade** and **decorator** are pre-configured via dependency injection (DI) as singletons in `app.di.data.KeyValueSourceModule`.

To start using, just inject it to your DI managed class.

```kotlin
class TemplateViewModel @Inject constructor(
private val keyValueSource: AppKeyValueSource = get() // KeyValueSource
private val keyValueSource: AppKeyValueSource // KeyValueSource
) : BaseViewModel() {

override fun doBind() {
launchAsync("init settings") {
...
Expand Down
4 changes: 2 additions & 2 deletions docs/Dataflow/SQLDelight/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

## Overview

- Component package: `app.datasource.database.sqldelight`
- DI integration: `app.di.datasource.ProvidesSqlDelightSource`
- Component package: `app.data.source.database.sqldelight`
- DI integration: `app.di.data.SqlDelightSourceModule`

The integration includes the following components:

Expand Down
22 changes: 11 additions & 11 deletions docs/Essentials/Jetpack Navigation/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The API is available under the package `shared.presentation.navigation`.

## Create Destination

To create your own destination, utilize the provided template `app.ui.screen.template.TemplateDestination`.
To create your own destination, utilize the provided template `app.presentation.template.screen_with_args.TemplateDestination`.

```kotlin
object TemplateDestination : NavigationDestination<TemplateDestination.Data>() {
Expand All @@ -33,14 +33,14 @@ object TemplateDestination : NavigationDestination<TemplateDestination.Data>() {

## Register Destination

All app destinations should be registered within an instance of the class `shared.presentation.navigation.NavigationState`.
This instance is already pre-configured in dependency injection (DI) through the `app.di.state.ProvidesNavigationState` class.
All app destinations should be registered within an instance of the class `shared.presentation.navigation.NavigationStore`.
This instance is already pre-configured in dependency injection (DI) through the `app.di.presentation.NavigationModule` class.
Simply place all your destinations within this instance.

```kotlin
val ProvidesNavigationState = module {
val navigationModule = module {
single {
NavigationState(
NavigationStore(
destinations = listOf(
ShowcasesDestination,
TemplateDestination,
Expand All @@ -58,28 +58,28 @@ val ProvidesNavigationState = module {

## Navigate to Destination

Once the navigation is aware of the destinations, you can initiate navigation to them using the `onBack` and `onNext` methods available in the `shared.presentation.navigation.NavigationState` class.
Simply inject `NavigationState` into your `ViewModel` or other dependency injection managed class, and navigate whenever necessary.
Once the navigation is aware of the destinations, you can initiate navigation to them using the `onBack` and `onNext` methods available in the `shared.presentation.navigation.NavigationStore` class.
Simply inject `NavigationStore` into your `ViewModel` or other dependency injection managed class, and navigate whenever necessary.

```kotlin
class HomeViewModel(
private val navigationState: NavigationState
private val navigationStore: NavigationStore
) : BaseViewModel() {

fun onBack() {
navigationState.onBack()
navigationStore.onBack()
}

fun onShowSettings() {
navigationState.onNext(SettingsDestination)
navigationStore.onNext(SettingsDestination)
}

}
```

## Set Initial Destination

When the app is first opened, you need to provide **NavigationState** with an initial destination. It can be done in pre-configured **AppNavigationRouter** class.
When the app is first opened, you need to provide **NavigationStore** with an initial destination. It can be done in pre-configured **AppNavigationRouter** class.

```kotlin
class AppNavigationRouter() {
Expand Down
12 changes: 6 additions & 6 deletions docs/Essentials/Material 3/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

## Overview

- Component package: `app.ui.theme`
- State management: `shared.presentation.theme.ThemeState`
- DI integration: `app.di.state.ProvidesThemeState`
- Component package: `app.presentation.theme`
- State management: `shared.presentation.theme.ThemeStore`
- DI integration: `app.di.presentation.ThemeModule`

This state instance is utilized by `app.ui.theme.AppThemeProvider`, which is pre-defined at the app level to furnish themes for the entire application.
This state instance is utilized by `app.presentation.theme.AppThemeProvider`, which is pre-defined at the app level to furnish themes for the entire application.

```kotlin
@Composable
fun App() = ViewModelProvider(remember { AppViewModelFactory }) {
fun App() = ViewModelProvider(remember(::get)) {
AppThemeProvider {
AppScreen()
}
Expand All @@ -19,7 +19,7 @@ fun App() = ViewModelProvider(remember { AppViewModelFactory }) {

## Change Themes

By default, `ThemeState` is initialized with pre-defined dark and light themes in the `design` module. To edit these themes:
By default, `ThemeStore` is initialized with pre-defined dark and light themes in the `design` module. To edit these themes:

1. Visit the [Material 3 Theme Builder](https://m3.material.io/theme-builder#/custom).
2. Customize the desired color theme.
Expand Down
20 changes: 10 additions & 10 deletions docs/Userflow/Adaptive Navigation/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,36 @@

## Overview

- Component package: `app.feature.navigation`
- DI integration: `app.di.state.ProvidesNavigationBarState`
- State management: `app.feature.navigation.NavigationBarState`
- Pre-configured destinations package: `app.feature.navigation.samples`
- Component package: `app.presentation.navigation`
- DI integration: `app.di.presentation.NavigationBarModule`
- State management: `app.presentation.navigation.NavigationBarStore`
- Pre-configured sample destinations package: `app.presentation.navigation.samples`

## Configuration

Configure your destinations using `ProvidesNavigationBarState`, and if necessary, specify any restricted or allowed destinations which will force navigation to show/hide the navigation bar in some cases.
Configure your destinations using `NavigationBarModule`, and if necessary, specify any restricted or allowed destinations which will force navigation to show/hide the navigation bar in some cases.

```kotlin
val ProvidesNavigationBarState = module {
val navigationBarModule = module {
single {
NavigationBarState(
NavigationBarStore(
pages = listOf(
createPage(
navigationState = get(),
store = get(),
destination = NavigationADestination,
getActiveIcon = { Icons.Filled.WineBar },
getInactiveIcon = { Icons.Outlined.WineBar },
getLabel = { "Page 1" }
),
createPage(
navigationState = get(),
store = get(),
destination = NavigationBDestination,
getActiveIcon = { Icons.Filled.LocalDrink },
getInactiveIcon = { Icons.Outlined.LocalDrink },
getLabel = { "Page 2" }
),
createPage(
navigationState = get(),
store = get(),
destination = NavigationCDestination,
getActiveIcon = { Icons.Filled.Coffee },
getInactiveIcon = { Icons.Outlined.Coffee },
Expand Down
Loading

0 comments on commit d3f9334

Please sign in to comment.