Clean architecture android app github
New version available written in Kotlin:
This is a sample app that is part of a blog post I have written about how to architect android application using the Uncle Bob’s clean architecture approach.
Architectural reactive approach
Here are some useful Gradle/adb commands for executing this example:
- ./gradlew clean build — Build the entire example and execute unit and integration tests plus lint check.
- ./gradlew installDebug — Install the debug apk on the current connected device.
- ./gradlew runUnitTests — Execute domain and data layer tests (both unit and integration).
- ./gradlew runAcceptanceTests — Execute espresso and instrumentation acceptance tests.
Here you can download and install the java codestyle. https://github.com/android10/java-code-styles
About
This is a sample app that is part of a series of blog posts I have written about how to architect an android application using Uncle Bob’s clean architecture approach.
Источник
Clean architecture android app github
Clean Architecture on Android: the Teamwork.com way!
The purpose of this repository is to showcase, with a very simple (but hopefully clear) sample Android project, how we implement Uncle Bob’s Clean Architecture in our applications. This is not a working demo app: the only purpose of the classes in the project is to demonstrate how the dependency graphs work with the configuration explained below, and to illustrate which dependencies are typically involved in this type of architecture.
Given that broad nature of the topic and the amount of implementation details necessary to implement a working production project, we have simplified our example as much as possible and focused solely on the following areas:
- Module structure: each architecture layer has its own module, following closely the Clean principles and naming.
- Separation of layers: how to configure Gradle making use of api / implementation to hide unwanted dependencies
- Dependency Injection: how to set up Dagger 2 for a multi-module environment, but still ensuring the above points
There is no such thing as «the best architecture» when it comes to mobile applications: the best architecture approach for a project (or team) always depends on a series of factors and assumptions.
Our solution is based on specific requirements, and, although it might not be the silver bullet for every project, it works well and could help you define your own architecture or, at least, inspire you to think about it a bit more.
We came up with our solution (and we iteratively try to improve it) based on the following items:
- Software is our craft. We aim for our applications to be fast, as bug-free as possible and always suiting our customers’ needs: the only way to achieve that is to ensure the quality and maintainability of our code through the use of best practices.
- We believe in code reusability. Modularising components is the only way to ensure that our code is reusable across products, maximise our bandwidth as a team and ensure that bug fixes are promptly delivered to all of our clients.
- Our applications are big. Most of our applications are complex, with non-trivial logic and a significant amount of screens and use cases: structuring our code in a formal and clear way is essential.
- Our applications should endure time. We don’t like technical debt, and we don’t like rewriting the same software, using the same technologies, only because that code is broken.
- We need to scale fast and make new developers onboarding smooth. Using a shared, well-defined architecture helps new developers in the team, who should then be able to get into the codebase faster and contribute to it from the get-go.
Listed below, a quick description of each module and a class diagram with their relationships.
The following diagram illustrates the above mentioned modules relationships in this sample project. In order to support feature modules and (if properly configured) Instant Apps, the project’s view/presentation layer is split into three modules; this is not a requirement and it can be avoided for small projects.
Module | Description | Module dependencies (direct or indirect) |
---|---|---|
entity | Business entities (the Entity layer in Clean) | No dependencies |
data-bridge | «Bridge» module only used for the initialization of the Data layer. Prevents implementation details in the data layer from being accessible in the app module. | data , data-access , entity |
data-access | The Data Access layer, interfaces for the business layer to access the data layer | entity |
data | The Data layer, which includes networking, caching and data delivery for the business layer to manipulate. Exposes via Dagger the DataRepo dependencies to the business layer | data-access , entity |
business | Business layer, contains interactors and business logic (which can then exposed to the presentation layer if necessary). | data-access , entity |
app-core | Core, base module for the view and presentation layer. Contains themes, styles, resources, strings and components that are used across apps and feature modules. | business , entity |
app-feature1 | View and presentation module for a «big» feature. This can be then extracted to use with Instant Apps if desired | app-core , business , entity |
app | View and presentation layers for the application module | app-core , app-feature1 , business , entity , data-bridge |
Google Android Architecture Samples
Google has done a very good job at producing a set of code examples in their Android Architecture Blueprints repository. We took inspiration from it (especially from the todo-mvp-clean, todo-mvp-dagger and dagger-android branches), but found that the examples are quite simple and not suited for more complex applications. More specifically:
- It is well suited for small projects, but the «monolith module» approach doesn’t scale well for medium/large applications
- The package-based separation of layers cannot be enforced at compile-time and is therefore very error-prone (especially when working in a big team)
- It is only a partial implementation of Clean: there is no real separation between presentation and business layer (presenters and use cases)
- It does not allow sharing code across applications, nor it is suitable for feature modules or Instant Apps
Our Gradle modules use Dagger (and its Android extension) for dependency injection. As an architectural choice to ensure encapsulation and enforce layer boundaries, the modules at lower layers do not have access at compile time to the higher layers except its closest dependency (see graph — i.e., the presentation layer can only access the business layer, not the data(-access) layer).
Any exception to this rule must be explicitly declared and made available through a provision method in a public component. Dagger doesn’t work well with this kind of requirement out of the box when using Subcomponents, since it needs to have access at compile time to all of the implementation classes to build the dependency graph (which is what we want to avoid in the first place).
The sample project doesn’t cover other useful Dagger features such scopes and «feature» components; however, both can be easily plugged into our core project structure.
The following diagram illustrates the dependencies between components in our sample project. Notice how all dependency/inheritance arrows point to the business layer. The entity layer does not need a component as it mainly comprises pure entity objects and business logic.
In order to allow using Dagger with our encapsulation constraints, we ensure that:
Each layer owns its Dagger component
Each Dagger Component is internal , and it is created and initialized within the module itself, so that each dependency graph is only fully visible inside the module. This guarantees encapsulation and allows us to declare both classes and the bound interfaces as internal if we don’t want to provide access to them outside of the module. Modules and dependencies are, by default, only accessible by components in the same layer.
Each layer’s Dagger component inherits a public plain interface
This interface only includes the dependencies that we want to expose outside of the module, e.g.:
By doing so, we also encapsulate the usage of Dagger within the module itself, without forcing external «client code» to use the framework, and simplifying injecting a mock of the whole component for testing when needed.
Dependencies between layers are fully managed by Dagger
Each layer which has a direct dependency to a component from another layer, will declare so in its Dagger component as a component dependency:
Dagger has recently introduced component factories, which allow (sub)components to provide an interface, annotated with @Component.Factory (or @Subcomponent.Factory ). The interface provides a single function, which contains dependencies (modules, components or any other) that the Component requires at dependency graph creation. We use component factories to pass the components which are dependencies in the layer we are initialising, along with other classes that might be passed on from lower level layers (e.g. the application Context ) with @BindsInstance.
Note: initialization code is ugly! The sample provides the simplest way to kick off the dependency graphs for each component and trigger initialization of dependencies that require it at application startup. Each project could require a different approach, the only requirement here is to follow the same layer initialisation order shown below.
The trigger for the initialization process is, as usual, the Application.onCreate() method. In order to provide layer-specific initialization on each module, the sample provides a SampleBusinessApplication abstract class in the business layer, and a SampleApplication class, usually in the application module. These classes provide callbacks to initialize the layers’ components (in this order):
The data-bridge module
In order to fulfill the desired level of encapsulation dictated by Clean Architecture, the data layer is not directly accessible from other layers (and modules), and it is used by the business layer through the data-access layer. The data-bridge only purpose is to temporarily «break» the dependency inversion rule at initialization time to provide a DataBridgeInitializer ; this is accessed by the application module to call to the data layer and trigger the Dagger dependency graph initialization for DataComponent .
- data layer through the data-bridge module: DataBridgeInitializer calls to DataLayerInitializer , which executes the component factory’s create() method for DataComponent and sets the singleton instance into DataComponent.INSTANCE and DataAccessComponent.INSTANCE (for access from the business layer)
- business layer: BusinessLayerInitializer , called by SampleBusinessApplication , which executes the component factory’s create() method for BusinessInternalComponent and sets the singleton instance into BusinessInternalComponent.INSTANCE ( DataAccessComponent.INSTANCE is passed to create() )
- presentation/view layer: initializeAppComponent(businessComponent) is called, and the ApplicationComponent.create() factory method is executed Once all the Dagger dependency graphs are created, the application can then move on to the rest of its initialization process.
Dependency Injection: example
Note: this section is intentionally verbose and requires you to go through the code while reading. You can probably skip it if you are already familiar with Dagger.
We have three separate public Dagger Component s in our codebase: ApplicationComponent (view/presentation layer), BusinessComponent and DataAccessComponent . These are declared in the corresponding layer’s module to make sure that the Dagger annotation processor and compiler have access to all the required dependencies from the generated provider classes.
Let’s take our Feature2DetailsPresenter example and follow its dependencies from the bottom-up in the architecture hierarchy:
Источник
Clean architecture android app github
PowerGit app with clean architecture
Applying Uncle Bobs’ clean architecture + MVI pattern to build a powerful github client app — PowerGit.
This project also demonstrates how to use latest techniques (architecture components, RxJava, dagger 2 dependency injection) to fast-develop a robust application (testable, maintainable, extendable, easy to maintain, support offline mode and lanscape mode. )
Read wiki to understand about the architecture, codebase, contribution guide and more.
Your contributions are welcome
This project is under developing and there are so many features still need to be implemented. Don’t hesitate to create a pull request even it’s a small bug fixing or implementing a whole feature, or at least an issue report.
First you need to understand the architecture principle. If you haven’t read or don’t have time to read Uncle Bob’s book, I recommend reading these articles to know what is «domain centric» system which is the begining idea of clean architecture.
Read the contribution instructions for more info and requirements
Credits to FastHub
This project focuses on android application architecture, so, to faster my work, I used some resources and helper classes of Fasthub open source project. Thank k0shk0sh so much for his amazing app!
Licensed under the the GPL-3.0 license.
See the LICENSE file for the whole license text.
Источник