diff --git a/docs/ModularizationLearningJourney.md b/docs/ModularizationLearningJourney.md index 5cda61b69..179ec1bec 100644 --- a/docs/ModularizationLearningJourney.md +++ b/docs/ModularizationLearningJourney.md @@ -1,89 +1,12 @@ # Modularization learning journey -In this learning journey you will learn about modularization, and the modularization strategy used -to create the modules in the Now in Android app. +In this learning journey you will learn about the modularization strategy used +to create modules in the Now in Android app. For the theory behind modularization, check out +[the official guidance](https://developer.android.com/topic/modularization). +**IMPORTANT:** Every module has a dependency graph in its README ([example for the app module](https://github.com/android/nowinandroid/tree/main/app)) which can be useful for understanding the overall structure of the project. -## Overview - -Modularization is the practice of breaking the concept of a monolithic, one-module codebase into -loosely coupled, self contained modules. - - -### Benefits of modularization - -This offers many benefits, including: - -**Scalability** - In a tightly coupled codebase, a single change can trigger a cascade of -alterations. A properly modularized project will embrace -the [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) principle. This -in turn empowers the contributors with more autonomy while also enforcing architectural patterns. - -**Enabling work in parallel** - Modularization helps decrease version control conflicts and enables -more efficient work in parallel for developers in larger teams. - -**Ownership** - A module can have a dedicated owner who is responsible for maintaining the code and -tests, fixing bugs, and reviewing changes. - -**Encapsulation** - Isolated code is easier to read, understand, test and maintain. - -**Reduced build time** - Leveraging Gradle’s parallel and incremental build can reduce build times. - -**Dynamic delivery** - Modularization is a requirement -for [Play Feature Delivery](https://developer.android.com/guide/playcore/feature-delivery) which -allows certain features of your app to be delivered conditionally or downloaded on demand. - -**Reusability** - Proper modularization enables opportunities for code sharing and building multiple -apps, across different platforms, from the same foundation. - - -### Modularization pitfalls - -However, modularization is a pattern that can be misused, and there are some gotchas to be aware of -when modularizing an app: - -**Too many modules** - each module has an overhead that comes in the form of increased complexity of -the build configuration. This can cause Gradle sync times to increase, and incurs an ongoing -maintenance cost. In addition, adding more modules increases the complexity of the project’s Gradle -setup, when compared to a single monolithic module. This can be mitigated by making use of -convention plugins, to extract reusable and composable build configuration into type-safe Kotlin -code. In the Now in Android app, these convention plugins can be found in -the [`build-logic` folder](https://github.com/android/nowinandroid/tree/main/build-logic). - -**Not enough modules** - conversely if your modules are few, large and tightly coupled, you end up -with yet another monolith. This means you lose some benefits of modularization. If your module is -bloated and has no single, well defined purpose, you should consider splitting it. - -**Too complex** - there is no silver bullet here. In fact it doesn’t always make sense to modularize -your project. A dominating factor is the size and relative complexity of the codebase. If your -project is not expected to grow beyond a certain threshold, the scalability and build time gains -won’t apply. - - -## Modularization strategy - -It’s important to note that there is no single modularization strategy that fits all projects. -However, there are general guidelines that can be followed to ensure you maximize its benefits and -minimize its downsides. - -A barebone module is simply a directory with a Gradle build script inside. Usually though, a module -will consist of one or more source sets and possibly a collection of resources or assets. Modules -can be built and tested independently. Due to Gradle's flexibility there are few constraints as to -how you can organize your project. In general, you should strive for low coupling and high cohesion. - -* **Low coupling** - Modules should be as independent as possible from one another, so that changes - to one module have zero or minimal impact on other modules. They should not possess knowledge of - the inner workings of other modules. - -* **High cohesion** - A module should comprise a collection of code that acts as a system. It should - have clearly defined responsibilities and stay within boundaries of certain domain knowledge. For - example, - the [`core:network` module](https://github.com/android/nowinandroid/tree/main/core/network) in Now - in Android is responsible for making network requests, handling responses from a remote data - source, and supplying data to other modules. - - -## Types of modules in Now in Android +## Module types ```mermaid graph TB @@ -95,7 +18,7 @@ graph TB :core:network[network]:::android-library :core:ui[ui]:::android-library end - subgraph :feature + subgraph :feature direction TB :feature:topic[topic]:::android-feature :feature:foryou[foryou]:::android-feature @@ -142,30 +65,29 @@ visualizing dependencies between modules. The Now in Android app contains the following types of modules: -* The `app` module - contains app level and scaffolding classes that bind the rest of the codebase, - such as `MainActivity`, `NiaApp` and app-level controlled navigation. A good example of this is - the navigation setup through `NiaNavHost` and the bottom navigation bar setup - through `TopLevelDestination`. The `app` module depends on all `feature` modules and - required `core` modules. +### The `app` module +This contains app level and scaffolding classes that bind the rest of the codebase, such as +`MainActivity`, `NiaApp` and app-level controlled navigation. A good example of this is the navigation setup through `NiaNavHost` and the bottom navigation bar setup through `TopLevelDestination`. The `app` module depends on all `feature` modules and required `core` modules. -* `feature:` modules - feature specific modules which are scoped to handle a single responsibility - in the app. These modules can be reused by any app, including test or other flavoured apps, when - needed, while still keeping it separated and isolated. If a class is needed only by one `feature` - module, it should remain within that module. If not, it should be extracted into an - appropriate `core` module. A `feature` module should have no dependencies on other feature - modules. They only depend on the `core` modules that they require. +### Feature modules +These are feature-specific modules that handle a single responsibility in the app. For example, the `ForYou` feature handles all content and UI state for the "ForYou" screen. Feature modules aren't gradle modules themselves, they are split into two submodules: -* `core:` modules - common library modules containing auxiliary code and specific dependencies that - need to be shared between other modules in the app. These modules can depend on other core - modules, but they shouldn’t depend on feature nor app modules. +* `api` - contains navigation keys +* `impl` - contains everything else -* Miscellaneous modules - such as `sync`, `benchmark` and `test` modules, as well - as `app-nia-catalog` - a catalog app for displaying our design system quickly. +This approach allows features to navigate to other features by using the target feature's navigation keys. A feature's `api` and `impl` modules can be used by any app, including test or other flavoured apps. If a class is needed only by one feature module, it should remain within that module. If not, it should be placed into an appropriate `core` module. +A feature's `api` module should not depend on another feature's `api` or `impl` module. A feature's `impl` should only depend on another featur's `api` module. Both submodules should only depend on the `core` modules that they require. -## Modules +### Core modules +These are common library modules containing auxiliary code and specific dependencies that + need to be shared between other modules in the app. These modules can depend on other core + modules, but they shouldn’t depend on feature nor app modules. + +### Miscellaneous modules +For example, `sync`, `benchmark` and `test` modules, as well as `app-nia-catalog` - a catalog app for displaying our design system quickly. -Using the above modularization strategy, the Now in Android app has the following modules: +## Examples
feature:1,- feature:2+ | feature:1:api,+ feature:2:api+ ... + |
+ Navigation keys and functions that other features can use to navigate to this feature. + For example: The :topic:api module exposes a Navigator.navigateToTopic function that the
+ :interests:impl module uses to navigate from the InterestsScreen to the TopicScreen when
+ a topic is clicked.
+ |
+ TopicNavKey
+ |
+
feature:1:impl,+ feature:2:impl... |
Functionality associated with a specific feature or user journey. Typically contains UI components and ViewModels which read data from other modules. Examples include:
|
TopicScreen@@ -283,11 +218,12 @@ Using the above modularization strategy, the Now in Android app has the followin |