1
00:00:00,000 --> 00:00:05,720
Chapter 15 Putting the Pieces Together

2
00:00:05,720 --> 00:00:12,760
This chapter covers a bells and whistles version of Service Monitor, whether to use modules,

3
00:00:12,760 --> 00:00:19,879
what an ideal module might look like, keeping module declarations clean, comparing the module

4
00:00:19,879 --> 00:00:24,719
system to build tools, OSGI, and microservices.

5
00:00:24,719 --> 00:00:28,440
Now that we've covered pretty much everything there is to know about the module system,

6
00:00:28,440 --> 00:00:30,280
it's time to wrap things up.

7
00:00:30,280 --> 00:00:34,759
In this final chapter, I want to connect the dots and give a few pieces of advice for creating

8
00:00:34,759 --> 00:00:37,720
awesome modular applications.

9
00:00:37,720 --> 00:00:41,480
The first step is to show you an example of how the various features discussed throughout

10
00:00:41,480 --> 00:00:46,320
the book can come together by applying most of them to the Service Monitor application

11
00:00:46,320 --> 00:00:47,320
in Section 15.1.

12
00:00:47,320 --> 00:00:52,360
Then, I'll take a deep dive into a number of more general concerns that will help you

13
00:00:52,360 --> 00:00:57,599
decide whether to even create modules, what to aim for when doing so, and how to carefully

14
00:00:57,599 --> 00:01:03,799
evolve your module declarations so they stay squeaky clean in Section 15.2.

15
00:01:03,799 --> 00:01:08,320
I'll close with a review of the technology landscape surrounding the module system in

16
00:01:08,320 --> 00:01:15,959
Section 15.3 and my vision for Java's modular ecosystem in Section 15.4.

17
00:01:15,959 --> 00:01:20,559
Section 15.1 Adding Bells and Whistles to Service Monitor

18
00:01:20,559 --> 00:01:23,959
Chapter 2 showed the anatomy of the Service Monitor application.

19
00:01:23,959 --> 00:01:29,080
In Section 2.2, you created simple modules that only used plain requires and exports

20
00:01:29,080 --> 00:01:30,080
directives.

21
00:01:30,080 --> 00:01:35,080
Since then, we've not only discussed those in detail, but also explored the module system's

22
00:01:35,080 --> 00:01:37,120
more advanced features.

23
00:01:37,120 --> 00:01:41,919
We've looked at each of them individually, but now I want to put them all together.

24
00:01:41,919 --> 00:01:46,639
To enjoy the Service Monitor application in all its glory, check out the repository's

25
00:01:46,639 --> 00:01:48,279
Features Combined branch.

26
00:01:48,279 --> 00:01:53,599
The following listing contains the declarations for all the modules in Service Monitor.

27
00:01:53,599 --> 00:02:00,360
Listing 15.1, Service Monitor, using advanced features presented throughout the book.

28
00:02:00,360 --> 00:02:05,720
If you compare this listing to Listing 2.2 or look at Figure 15.1, you can see that the

29
00:02:05,720 --> 00:02:10,600
fundamental structure of Service Monitor has stayed pretty much the same, but looking closer,

30
00:02:10,600 --> 00:02:12,800
you can see a number of improvements.

31
00:02:12,800 --> 00:02:15,759
Let's go over them one by one.

32
00:02:15,759 --> 00:02:21,720
Figure 15.1a Figure 15.1b, Comparison of Module Graphs

33
00:02:21,720 --> 00:02:25,360
for the Service Monitor application depending on feature use.

34
00:02:25,360 --> 00:02:31,279
The first variant only uses plain exports and requires directives, a, whereas the second

35
00:02:31,279 --> 00:02:37,160
makes full use of refined dependencies and exports as well as services, b, the basic

36
00:02:37,160 --> 00:02:43,839
variant has been extended to include the same modules and packages as the advanced one.

37
00:02:43,839 --> 00:02:48,039
Section 15.1.1, Diversified Dependencies.

38
00:02:48,039 --> 00:02:53,080
One change that's easy to spot are the requires transitive and requires optional directives.

39
00:02:53,080 --> 00:02:57,320
Although plain requires directives are the right choice in most cases, a significant

40
00:02:57,320 --> 00:03:01,039
portion of dependencies are a little more complicated.

41
00:03:01,039 --> 00:03:06,479
The most obvious case is optional dependencies, where a module uses types from another module

42
00:03:06,479 --> 00:03:10,919
and hence needs to be compiled against it, but the dependency may still be absent at

43
00:03:10,919 --> 00:03:11,919
runtime.

44
00:03:11,919 --> 00:03:17,740
This is exactly the case for Monitor.Statistics and Stats.Fancy, so the dependency is established

45
00:03:17,740 --> 00:03:20,820
with a requires static directive.

46
00:03:20,820 --> 00:03:27,100
The module system will then enforce the presence of Stats.Fancy when compiling Monitor.Statistics,

47
00:03:27,100 --> 00:03:31,500
which makes sense because otherwise compilation would fail, and will add a reads edge from

48
00:03:31,500 --> 00:03:37,240
Monitor.Statistics to Stats.Fancy if the latter made it into the module graph, which also

49
00:03:37,240 --> 00:03:43,539
makes sense because otherwise Monitor.Statistics couldn't access types from Stats.Fancy.

50
00:03:43,539 --> 00:03:48,619
But Stats.Fancy may not make it into the module graph, in which case Monitor.Statistics

51
00:03:48,619 --> 00:03:51,619
has to handle its absence.

52
00:03:51,619 --> 00:03:53,660
Listing 15.2.

53
00:03:53,660 --> 00:03:58,860
Checking whether the optional dependency Stats.Fancy is present.

54
00:03:58,860 --> 00:04:03,580
Optional dependencies are discussed in detail in Section 11.2.

55
00:04:03,580 --> 00:04:08,419
The other case is a little less obvious than optional dependencies, but no less common,

56
00:04:08,419 --> 00:04:10,100
maybe even more so.

57
00:04:10,100 --> 00:04:16,019
The module Monitor.REST, for example, has this method in its public API.

58
00:04:16,019 --> 00:04:18,299
See this code?

59
00:04:18,299 --> 00:04:23,859
But statistics comes from Monitor.Statistics, so any module using REST needs to read statistics

60
00:04:23,859 --> 00:04:28,220
or it can't access statistics and thus can't create a monitor server.

61
00:04:28,220 --> 00:04:33,059
In other words, REST is useless to modules that don't also read statistics.

62
00:04:33,059 --> 00:04:37,420
In the Service Monitor application, this happens surprisingly often.

63
00:04:37,420 --> 00:04:41,899
Every module that requires at least one other module and exports a package ends up being

64
00:04:41,899 --> 00:04:44,140
in that situation.

65
00:04:44,140 --> 00:04:48,179
That's considerably more frequent than out in the wild, and only happens that often because

66
00:04:48,179 --> 00:04:52,619
the modules are so small that almost all of their code is public API.

67
00:04:52,619 --> 00:04:56,559
It would be surprising if they didn't constantly expose their dependencies' types in their

68
00:04:56,559 --> 00:04:58,299
own APIs.

69
00:04:58,299 --> 00:05:02,859
So although this occurs more rarely in practice, you can still expect to see it on a daily

70
00:05:02,859 --> 00:05:04,179
basis.

71
00:05:04,179 --> 00:05:09,019
In the JDK, roughly 20% of the dependencies are exposed.

72
00:05:09,019 --> 00:05:13,619
To not keep users guessing about which other modules they need to require explicitly, which

73
00:05:13,619 --> 00:05:19,700
is cumbersome and bloats module declarations, the module system offers requires transitive.

74
00:05:19,700 --> 00:05:25,100
Because REST requires transitive statistics, any module reading REST also reads statistics,

75
00:05:25,100 --> 00:05:28,459
and thus users of REST are spared the guesswork.

76
00:05:28,459 --> 00:05:33,299
Implied readability is discussed in detail in section 11.1.

77
00:05:33,299 --> 00:05:37,179
In section 15.1.2, reduced visibility.

78
00:05:37,179 --> 00:05:41,940
Another change from the application's original versions in section 2.2 is that its modules

79
00:05:41,940 --> 00:05:44,899
work harder to reduce their API surface.

80
00:05:44,899 --> 00:05:49,820
The updated modules use considerably fewer plain exports directives.

81
00:05:49,820 --> 00:05:55,299
Thanks to services, the observers no longer have to export their implementations.

82
00:05:55,299 --> 00:06:02,220
By using qualified exports, the package monitor.observer.utils in monitor.observer is only accessible to

83
00:06:02,220 --> 00:06:05,859
a selected set of modules.

84
00:06:05,859 --> 00:06:10,399
Monitor.persistence opens its entity package instead of exporting it, thus only making

85
00:06:10,399 --> 00:06:13,059
it available at runtime.

86
00:06:13,059 --> 00:06:17,820
These changes reduce the amount of code that's readily accessible for any random module,

87
00:06:17,820 --> 00:06:21,799
which means developers can change more code inside a module without having to worry about

88
00:06:21,799 --> 00:06:25,059
the effects on downstream consumers.

89
00:06:25,059 --> 00:06:29,899
Reducing the API surface this way is a boon for the maintainability of frameworks and

90
00:06:29,899 --> 00:06:34,820
libraries, but large applications with many modules can also benefit.

91
00:06:34,820 --> 00:06:42,339
Section 11.3 introduces qualified exports, and section 12.2 explores open packages.

92
00:06:42,339 --> 00:06:46,459
Section 15.1.3, decoupled with services.

93
00:06:46,459 --> 00:06:51,600
The only structural change of the module graph, compared to section 2.2, is that monitor no

94
00:06:51,600 --> 00:06:54,700
longer directly depends on the observer implementations.

95
00:06:55,019 --> 00:07:00,940
Instead, it only depends on the module providing the API, monitor.observer, and it uses service

96
00:07:00,940 --> 00:07:03,380
observer factory as a service.

97
00:07:03,380 --> 00:07:08,019
All three implementing modules provide that service with their specific implementations,

98
00:07:08,019 --> 00:07:11,420
and the module system connects the two sides.

99
00:07:11,420 --> 00:07:14,420
This is much more than just an aesthetic improvement.

100
00:07:14,420 --> 00:07:19,260
Thanks to services, it's possible to configure aspects of the application's behavior, which

101
00:07:19,260 --> 00:07:22,739
kinds of services it can observe, at launch time.

102
00:07:22,739 --> 00:07:27,519
New implementations can be added, and obsolete ones can be removed by adding or removing

103
00:07:27,519 --> 00:07:30,779
modules that provide that service.

104
00:07:30,779 --> 00:07:35,660
No changes of monitor are required, and hence the same artifacts can be used without having

105
00:07:35,660 --> 00:07:37,019
to rebuild them.

106
00:07:37,019 --> 00:07:40,760
To learn all about services, check out chapter 10.

107
00:07:40,760 --> 00:07:45,459
Section 15.1.4 loads code at runtime with layers.

108
00:07:45,459 --> 00:07:50,399
Although services allow us to define the application's behavior at launch time, we even went one

109
00:07:50,399 --> 00:07:51,940
step further.

110
00:07:51,940 --> 00:07:56,619
It isn't visible in the module declarations, but by enabling the monitor module to create

111
00:07:56,619 --> 00:08:01,980
new layers, we made it possible for the application to start observing services at runtime, for

112
00:08:01,980 --> 00:08:06,459
which it didn't even have the service observer implementation when it launched.

113
00:08:06,459 --> 00:08:11,540
On demand, monitor will create a new module graph and, together with a new class loader,

114
00:08:11,540 --> 00:08:15,899
load additional classes and update its list of observers.

115
00:08:15,899 --> 00:08:18,059
Section 15.3.

116
00:08:18,059 --> 00:08:22,940
Creating a new layer with the graph created for modules on those paths.

117
00:08:22,940 --> 00:08:27,339
Such behavior is particularly interesting for applications that aren't frequently redeployed

118
00:08:27,339 --> 00:08:30,420
and where restarts are inconvenient.

119
00:08:30,420 --> 00:08:34,580
Complex desktop applications come to mind, but a web backend that runs on the customer's

120
00:08:34,580 --> 00:08:39,820
premises and needs to be comprehensibly configurable could also qualify.

121
00:08:39,820 --> 00:08:45,940
For a discussion of what layers are and how to create them, see section 12.4.

122
00:08:45,940 --> 00:08:51,099
Section 15.1.5 handles dependencies on plain jars.

123
00:08:51,099 --> 00:08:55,340
Another detail that isn't obvious from the module declarations is the modularization

124
00:08:55,340 --> 00:08:58,580
status of Service Monitor's third-party dependency.

125
00:08:58,580 --> 00:09:03,260
Neither the Hibernate version nor the Spark version it uses is modularized yet, and they

126
00:09:03,260 --> 00:09:05,460
still ship as plain jars.

127
00:09:05,460 --> 00:09:09,659
Because explicit modules require them, they need to be on the module path, though, where

128
00:09:09,659 --> 00:09:14,179
the module system turns plain jars into automatic modules.

129
00:09:14,179 --> 00:09:19,099
So although Service Monitor is fully modularized, it can nonetheless depend on non-modularized

130
00:09:19,099 --> 00:09:20,500
jars.

131
00:09:20,500 --> 00:09:25,099
Looking at this from the ecosystem-wide perspective, where the JDK modules sit at the bottom and

132
00:09:25,099 --> 00:09:31,700
application modules are at the top, this is effectively a top-down modularization effort.

133
00:09:31,700 --> 00:09:36,700
Automatic modules in particular are covered in section 8.3, but all of chapter 8 applies

134
00:09:36,700 --> 00:09:37,700
here.

135
00:09:37,700 --> 00:09:41,900
If you want to catch up on modularization strategies, check out section 9.2.


