{"id":13608273,"url":"https://github.com/aminography/WorldCitiesApp","last_synced_at":"2025-04-12T14:32:36.840Z","repository":{"id":46573410,"uuid":"338760259","full_name":"aminography/WorldCitiesApp","owner":"aminography","description":"This example project demonstrates a clean way to architect an Android application.","archived":false,"fork":false,"pushed_at":"2021-10-05T14:42:50.000Z","size":5291,"stargazers_count":31,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-06T16:12:31.847Z","etag":null,"topics":["android","clean-architecture","coroutines","dagger2","deeplink","dynamic-feature-module","jetpack-android","kotlin","mvvm","prefix-search","radix-tree"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aminography.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-14T08:27:08.000Z","updated_at":"2024-11-18T17:17:10.000Z","dependencies_parsed_at":"2022-09-10T11:02:13.273Z","dependency_job_id":null,"html_url":"https://github.com/aminography/WorldCitiesApp","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aminography%2FWorldCitiesApp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aminography%2FWorldCitiesApp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aminography%2FWorldCitiesApp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aminography%2FWorldCitiesApp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aminography","download_url":"https://codeload.github.com/aminography/WorldCitiesApp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248581352,"owners_count":21128154,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","clean-architecture","coroutines","dagger2","deeplink","dynamic-feature-module","jetpack-android","kotlin","mvvm","prefix-search","radix-tree"],"created_at":"2024-08-01T19:01:25.780Z","updated_at":"2025-04-12T14:32:33.709Z","avatar_url":"https://github.com/aminography.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"# Android app architecture in a clean way\n\nThis example project demonstrates a clean way to architect an Android application.\nFurther details will be explained in a [Medium Post](https://medium.com/@aminography/android-app-architecture-in-a-clean-way-91e8b86e4b6f).\n\nThis app consists of three screens with a simple flow of UI:\n- A filterable list of world cities\n- Viewing a city on map\n- Exploring github users of the city\n\n\u003cbr/\u003e\n\n### :warning: **DISCLAIMER**\n\nIn case of large-scale projects, containing dozens of features, the exact architecture of this project might not be effective.\nIt would be better to apply `Clean Architecture` to each feature independently, which leads to higher levels of cohesion and shorter compile-time.\n\n\u003cbr/\u003e\n\nTable of Contents\n-----------------\n- [Main Characteristics](#main-characteristics)\n- [Architecture Overview](#architecture-overview)\n  - [Gradle Modules](#gradle-modules)\n  - [Architecture Layers](#architecture-layers)\n  - [Module Dependencies](#module-dependencies)\n- [Dependency Injection](#dependency-injection)\n- [Third-Party Libraries](#third-party-libraries)\n- [License](#license)\n\n\u003cbr/\u003e\n\nMain Characteristics\n--------------------\n\n| What | With |\n| --- | :---: |\n| Layered architecture | `Clean Architecture` |\n| Separation of concerns ([`SoC`](https://en.wikipedia.org/wiki/Separation_of_concerns)) | `Gradle` modularization |\n| Lifecycle-aware presentation architecture | `MVVM` pattern |\n| Navigation | `Jetpack Navigation Component` |\n| Feature independence | `Dynamic Feature Module` |\n| Inversion on control ([`IoC`](https://en.wikipedia.org/wiki/Inversion_of_control)) | `Dagger2` |\n| Concurrent programming | `Coroutines` |\n| Reactive programming | `Flow` and `LiveData` |\n| Indexing a large amount of records (~210k) + retrieval time **\u003c** 10ms | `RadixTree` |\n| Pagination | `Paging3` |\n\n\u003cbr/\u003e\n\nArchitecture Overview\n---------------------\nThe architecture of the app follows the well-known `Clean Architecture` guidelines to make the business rules as separated as possible.\nIt consists of 3 main layers: `Data`, `Domain`, and `Presentation` that will be explained in the following.\n\n\u003cbr/\u003e\n\n##  Gradle Modules\nThe overall structure of the codebase is organized into 3 categories of gradle modules.\n\n- `architecture`: modules that establish the `Clean Architecture` layers.\n- `common`: modules that provide common components and foundations for `architecture` modules.\n- `features`: modules that implement separate features of the application.\n\nRegardless of two test-related modules (*i.e.* `sharedTest` and `sharedAndroidTest`), there are 11 gradle modules. (4 pure `Kotlin` and 7 `Android` module)\n\nEach module follows single responsibility principle, results in higher cohesion.\nIn addition, modules are decoupled, so they know only each other's interface, which increases their maintainability and testability.\n\n\u003cbr/\u003e\n\n![](/static/modules.png)\n\n\u003cbr/\u003e\n\n##  Architecture Layers\nThe overall architecture is organized into 3 main layers:\n\n\u003cbr/\u003e\n\n![](/static/layers.svg)\n\n\u003cbr/\u003e\n\n### 1. Presentation\nThe presentation layer contains the `app` and features, which are all `Android` application modules.\nIt presents user interface by navigating between different features provided by feature modules.\nFeatures are implemented in `Dynamic Feature Modules` and they will be delivered at install time.\nIt allows us to separate their code and resources from the base `app` module.\nIn fact, the `app` module aggregates architectural modules for the features, in addition holds base and navigation-related classes.\n\n### 2. Domain\nThe domain layer contains business rules and entities, which are pure `Kotlin` modules.\nThe `domain` module provides the business logic via use-case classes and defines abstraction of repositories to be implemented by the `data` module.\nIn addition, each use-case object can act as a bridge between `app` and `data` modules.\nSo, this is the only possible way for the `app` and it descendant features to collaborate with the `data` module.\n\n### 3. Data\nAs the aim of the data layer is to deal with local or remote sources of data, it needs to interact with the framework.\nTherefor, it is an `Android` library module.\nThe concrete objects are created and delivered to the `domain` by the `dagger`.\nIt is worth noting that all of the concrete classes in this module are `internal`, so they cannot be exposed to dependant modules, like the `app`.\n\n- The only reason for having a dependency between `app` and `data` is accessing to dagger modules and components located in the `data`, which is required for building the dependency graph by the dagger.\n\n\u003cbr/\u003e\n\n## Module Dependencies\nThe diagram below shows the dependency graph of gradle modules.\n\n\u003cbr/\u003e\n\n![](/static/modules.svg)\n\n\u003cbr/\u003e\n\nDependency Injection\n--------------------\n`dagger2` is a powerful tool to establish Inversion of control (`IoC`) in the architecture design.\nUsing scopes, we can manage the lifetime of objects provided by dagger components.\nIn this project, scopes are categorized into 3 levels:\n\n- **App-level** scope (*i.e.* `AppScope`) is used for the objects whose lifetime is equal to the app.\nSo, they are instantiated once and used through whole app, like the `applicationContext`.\n\n- **Foundation-level** scopes define the lifetime of the foundational objects, like the `Retrofit` object.\nThey can live as long as the app lives or shorter than that.\n\n- **Feature-level** scope (*i.e.* `FeatureScope`) is used for specifying the lifetime of object that should live as long as a feature lives.\n\n\u003cbr/\u003e\n\n![](/static/scopes.svg)\n\n\u003cbr/\u003e\n\nThird-Party Libraries\n-------------------\n- [Dagger2](https://dagger.dev)\n- [Navigation Component](https://developer.android.com/guide/navigation)\n- [Paging3](https://developer.android.com/topic/libraries/architecture/paging/v3-overview)\n- [Retrofit](https://square.github.io/retrofit)\n- [Gson](https://github.com/google/gson)\n- [RadixTree](https://github.com/aminography/RadixTree)\n- [Glide](https://bumptech.github.io/glide)\n- [Google Maps](https://developers.google.com/maps/documentation/android-sdk/overview)\n- [JUnit 5](https://junit.org/junit5/docs/current/user-guide)\n- [MockK](https://mockk.io)\n\n\u003cbr/\u003e\n\nLicense\n--------\n```\nCopyright 2021 Amin Hassani.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faminography%2FWorldCitiesApp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faminography%2FWorldCitiesApp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faminography%2FWorldCitiesApp/lists"}