Handling larger codebases

The sample application we modularized is very simple and not a representation of most real-world applications. Here are a couple of characteristics in which most applications differ:

  • They have a broader code base that spans multiple projects. These projects may reside in different source locations and may be hooked to a build system. The build of the main application then gathers the right dependencies together to form the final application build.
  • They have many more framework dependencies that have more complex needs. Frameworks such as Spring or Hibernate require access to your application code to do reflection. They might scan your classes for annotations and do various things such as dependency injection and object-relational mapping. In that sense, it is not just your application code that needs access to libraries as automatic modules; even such automatic modules would need access to your application code.

Given such a large Java 8 code base, how do you even begin migrating? Here are some steps that you'd typically follow:

Step 1: Draw module boundaries and create a high-level module map:

In my opinion, modularizing existing code starts with having at least a rough high-level idea of the modules you need and how you plan to split the code base. We've looked at some strategies and tips to help you draw module boundaries in Chapter 9, Module Design Patterns and Strategies. Depending on the complexity of your code, you'll need to either look at the code in entirety or in high-level parts and come up with some module names and interfaces.

Once you have a rough idea about what your modules will be, you can create a module graph that represents the dependencies between these modules. Don't get too involved in the details. This is just a rough sketch and you may be inclined to make changes to either the modules or their relationships as you get into the weeds and start refactoring.

Step 2: Modularize the main application:

Among all the code projects that a large application consists of, there's usually one that can be classified as the main project. It is the one that perhaps starts the execution or the project that is built and deployed as the application. That would be a good place to start. You can follow the steps you learned in this chapter to bring that application over to the module path first.

Step 3: Use the module overrides for special library needs:

If you are using a framework like Spring or Hibernate, you are sure to run into problems when using them as automatic modules. That's because those frameworks typically need access to your code base to reflectively scan your classes for annotations. We know that automatic modules read all resolved modules. So, it technically reads your application modules that could have Spring annotations. However, if your modules do not export the packages, it'll still be unable to access them. You can get around this problem in a couple of ways:

  • Add the opens declaration to the packages that contain such annotations in your module definition so that the libraries have access to reflect on the necessary classes
  • Use the --add-opens command-line arguments to achieve the same result

Step 4: Leverage automatic modules for in-house build artifacts:

There's no reason why you should not use automatic modules even for your own application JARs. Let's say you are migrating a large Maven application with multiple artifact dependencies on other projects that are built in-house (or code that you own). Those in-house artifacts can be added to the module path and converted into automatic modules too. One thing to watch out for here is the split package problem. Since we are dealing with in-house code, there's a good chance that there are package overlaps between JARs. In such cases, you'll need to refactor your code to make sure there are no overlapping packages in JAR files. You can do this refactoring while using an older version of Java too.

Step 5a: Break down the main project into smaller modules:

Again, following the process we used in this chapter, start chunking away pieces of the monolithic module into smaller pieces. Establish clearer dependencies among the smaller modules as you go.

Step 5b: Migrate modules from the leaf up:

In parallel to Step 5a, you can also start migrating projects other than the main project too. Since you've built your module tree, the order of migration of modules becomes clear. You can make your migration significantly easier by ordering the modules you choose to migrate from the leaf of the module dependency tree and work your way toward the top. The ideal candidate for migration is a module that does not have any other application module dependency. Dependency on Java modules is okay though!

For example, let's say this is your target module graph for the code that you plan to achieve after migration. The graph includes just your application modules. Any dependency on platform modules is excluded in this graph:

The first set of modules you should pick for migration are D and E. Once they are done, migrate C, then A and B.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.38.176