You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In this learning journey you will learn about modularization, and the modularization strategy used
4
-
to create the modules in the Now in Android app.
3
+
In this learning journey you will learn about the modularization strategy used
4
+
to create modules in the Now in Android app. For the theory behind modularization, check out
5
+
[the official guidance](https://developer.android.com/topic/modularization).
5
6
7
+
**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.
6
8
7
-
## Overview
8
-
9
-
Modularization is the practice of breaking the concept of a monolithic, one-module codebase into
10
-
loosely coupled, self contained modules.
11
-
12
-
13
-
### Benefits of modularization
14
-
15
-
This offers many benefits, including:
16
-
17
-
**Scalability** - In a tightly coupled codebase, a single change can trigger a cascade of
18
-
alterations. A properly modularized project will embrace
19
-
the [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) principle. This
20
-
in turn empowers the contributors with more autonomy while also enforcing architectural patterns.
21
-
22
-
**Enabling work in parallel** - Modularization helps decrease version control conflicts and enables
23
-
more efficient work in parallel for developers in larger teams.
24
-
25
-
**Ownership** - A module can have a dedicated owner who is responsible for maintaining the code and
26
-
tests, fixing bugs, and reviewing changes.
27
-
28
-
**Encapsulation** - Isolated code is easier to read, understand, test and maintain.
29
-
30
-
**Reduced build time** - Leveraging Gradle’s parallel and incremental build can reduce build times.
31
-
32
-
**Dynamic delivery** - Modularization is a requirement
33
-
for [Play Feature Delivery](https://developer.android.com/guide/playcore/feature-delivery) which
34
-
allows certain features of your app to be delivered conditionally or downloaded on demand.
35
-
36
-
**Reusability** - Proper modularization enables opportunities for code sharing and building multiple
37
-
apps, across different platforms, from the same foundation.
38
-
39
-
40
-
### Modularization pitfalls
41
-
42
-
However, modularization is a pattern that can be misused, and there are some gotchas to be aware of
43
-
when modularizing an app:
44
-
45
-
**Too many modules** - each module has an overhead that comes in the form of increased complexity of
46
-
the build configuration. This can cause Gradle sync times to increase, and incurs an ongoing
47
-
maintenance cost. In addition, adding more modules increases the complexity of the project’s Gradle
48
-
setup, when compared to a single monolithic module. This can be mitigated by making use of
49
-
convention plugins, to extract reusable and composable build configuration into type-safe Kotlin
50
-
code. In the Now in Android app, these convention plugins can be found in
51
-
the [`build-logic` folder](https://github.com/android/nowinandroid/tree/main/build-logic).
52
-
53
-
**Not enough modules** - conversely if your modules are few, large and tightly coupled, you end up
54
-
with yet another monolith. This means you lose some benefits of modularization. If your module is
55
-
bloated and has no single, well defined purpose, you should consider splitting it.
56
-
57
-
**Too complex** - there is no silver bullet here. In fact it doesn’t always make sense to modularize
58
-
your project. A dominating factor is the size and relative complexity of the codebase. If your
59
-
project is not expected to grow beyond a certain threshold, the scalability and build time gains
60
-
won’t apply.
61
-
62
-
63
-
## Modularization strategy
64
-
65
-
It’s important to note that there is no single modularization strategy that fits all projects.
66
-
However, there are general guidelines that can be followed to ensure you maximize its benefits and
67
-
minimize its downsides.
68
-
69
-
A barebone module is simply a directory with a Gradle build script inside. Usually though, a module
70
-
will consist of one or more source sets and possibly a collection of resources or assets. Modules
71
-
can be built and tested independently. Due to Gradle's flexibility there are few constraints as to
72
-
how you can organize your project. In general, you should strive for low coupling and high cohesion.
73
-
74
-
***Low coupling** - Modules should be as independent as possible from one another, so that changes
75
-
to one module have zero or minimal impact on other modules. They should not possess knowledge of
76
-
the inner workings of other modules.
77
-
78
-
***High cohesion** - A module should comprise a collection of code that acts as a system. It should
79
-
have clearly defined responsibilities and stay within boundaries of certain domain knowledge. For
80
-
example,
81
-
the [`core:network` module](https://github.com/android/nowinandroid/tree/main/core/network) in Now
82
-
in Android is responsible for making network requests, handling responses from a remote data
83
-
source, and supplying data to other modules.
84
-
85
-
86
-
## Types of modules in Now in Android
9
+
## Module types
87
10
88
11
```mermaid
89
12
graph TB
@@ -95,7 +18,7 @@ graph TB
95
18
:core:network[network]:::android-library
96
19
:core:ui[ui]:::android-library
97
20
end
98
-
subgraph :feature
21
+
subgraph :feature
99
22
direction TB
100
23
:feature:topic[topic]:::android-feature
101
24
:feature:foryou[foryou]:::android-feature
@@ -142,30 +65,29 @@ visualizing dependencies between modules.
142
65
143
66
The Now in Android app contains the following types of modules:
144
67
145
-
* The `app` module - contains app level and scaffolding classes that bind the rest of the codebase,
146
-
such as `MainActivity`, `NiaApp` and app-level controlled navigation. A good example of this is
147
-
the navigation setup through `NiaNavHost` and the bottom navigation bar setup
148
-
through `TopLevelDestination`. The `app` module depends on all `feature` modules and
149
-
required `core` modules.
68
+
### The `app` module
69
+
This contains app level and scaffolding classes that bind the rest of the codebase, such as
70
+
`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.
150
71
151
-
*`feature:` modules - feature specific modules which are scoped to handle a single responsibility
152
-
in the app. These modules can be reused by any app, including test or other flavoured apps, when
153
-
needed, while still keeping it separated and isolated. If a class is needed only by one `feature`
154
-
module, it should remain within that module. If not, it should be extracted into an
155
-
appropriate `core` module. A `feature` module should have no dependencies on other feature
156
-
modules. They only depend on the `core` modules that they require.
72
+
### Feature modules
73
+
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:
157
74
158
-
*`core:` modules - common library modules containing auxiliary code and specific dependencies that
159
-
need to be shared between other modules in the app. These modules can depend on other core
160
-
modules, but they shouldn’t depend on feature nor app modules.
75
+
*`api` - contains navigation keys
76
+
*`impl` - contains everything else
161
77
162
-
* Miscellaneous modules - such as `sync`, `benchmark` and `test` modules, as well
163
-
as `app-nia-catalog` - a catalog app for displaying our design system quickly.
78
+
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.
164
79
80
+
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.
165
81
166
-
## Modules
82
+
### Core modules
83
+
These are common library modules containing auxiliary code and specific dependencies that
84
+
need to be shared between other modules in the app. These modules can depend on other core
85
+
modules, but they shouldn’t depend on feature nor app modules.
86
+
87
+
### Miscellaneous modules
88
+
For example, `sync`, `benchmark` and `test` modules, as well as `app-nia-catalog` - a catalog app for displaying our design system quickly.
167
89
168
-
Using the above modularization strategy, the Now in Android app has the following modules:
90
+
## Examples
169
91
170
92
<table>
171
93
<tr>
@@ -186,15 +108,28 @@ Using the above modularization strategy, the Now in Android app has the followin
186
108
</td>
187
109
</tr>
188
110
<tr>
189
-
<td><code>feature:1,</code><br>
190
-
<code>feature:2</code><br>
111
+
<td><code>feature:1:api,</code><br>
112
+
<code>feature:2:api</code><br>
113
+
...
114
+
</td>
115
+
<td>Navigation keys and functions that other features can use to navigate to this feature.<br><br>
116
+
For example: The <code>:topic:api</code> module exposes a <code>Navigator.navigateToTopic</code> function that the
117
+
<code>:interests:impl</code> module uses to navigate from the <code>InterestsScreen</code> to the <code>TopicScreen</code> when
118
+
a topic is clicked.
119
+
</td>
120
+
<td><code>TopicNavKey</code>
121
+
</td>
122
+
</tr>
123
+
<tr>
124
+
<td><code>feature:1:impl,</code><br>
125
+
<code>feature:2:impl</code><br>
191
126
...
192
127
</td>
193
128
<td>Functionality associated with a specific feature or user journey. Typically contains UI components and ViewModels which read data from other modules.<br>
194
129
Examples include:<br>
195
130
<ul>
196
-
<li><a href="https://github.com/android/nowinandroid/tree/main/feature/topic"><code>feature:topic</code></a> displays information about a topic on the TopicScreen.</li>
197
-
<li><a href="https://github.com/android/nowinandroid/tree/main/feature/foryou"><code>feature:foryou</code></a> which displays the user's news feed, and onboarding during first run, on the For You screen.</li>
131
+
<li><a href="https://github.com/android/nowinandroid/tree/main/feature/topic/impl"><code>feature:topic:impl</code></a> displays information about a topic on the TopicScreen.</li>
132
+
<li><a href="https://github.com/android/nowinandroid/tree/main/feature/foryou/impl"><code>feature:foryou:impl</code></a> which displays the user's news feed, and onboarding during first run, on the For You screen.</li>
198
133
</ul>
199
134
</td>
200
135
<td><code>TopicScreen</code><br>
@@ -283,11 +218,12 @@ Using the above modularization strategy, the Now in Android app has the followin
283
218
</tr>
284
219
</table>
285
220
221
+
## Dependency graphs
286
222
Each module has its own `README.md` file containing a module graph (e.g. [`:app` module graph](../app/README.md#module-dependency-graph)).
287
223
When modules dependencies change, module graphs are automatically updated by the [Build.yaml](../.github/workflows/Build.yaml) workflow.
288
224
You can also manually update the graphs by running the `graphUpdate` task.
289
225
290
-
## Modularization in Now in Android
226
+
## Further considerations
291
227
292
228
Our modularization approach was defined taking into account the “Now in Android” project roadmap, upcoming work and new features. Additionally, our aim this time around was to find the right balance between overmodularizing a relatively small app and using this opportunity to showcase a modularization pattern fit for a much larger codebase, closer to real world apps in production environments.
0 commit comments