Reasons for using multiple modules
For engineering projects developed using Java technology, whether data processing system or Web site, with the continuous development of the project, the continuous refinement and addition of requirements, more and more codes in the engineering project and more and more complex package structure, various problems will be encountered in the progress of the project:
1) The codes of different aspects are coupled with each other. At this time, when a system has a problem, it is difficult to locate the cause of the problem. Even if the problem is located, it is difficult to correct the problem. More problems may be introduced when correcting the problem.
2) Various codes are concentrated in an overall structure. It is difficult for new developers to have an intuitive feeling about the overall project, which increases the cost of novice intervention in development. A developer familiar with the whole project is required to maintain the structure of the whole project (usually it is difficult to do when the project is large and the development time is long).
3) The code boundary that developers are responsible for themselves or others is very vague, which is the most easily encountered in complex projects. As a result, developers can easily modify the code that others are responsible for, and the code owner does not know it, so responsibility tracking is very troublesome.
Splitting a complex project into multiple modules is an important way to solve the above problems. The division of multiple modules can reduce the coupling between codes (from class level coupling to jar package level coupling). Each module can be self explanatory (through module name or module document). The module also standardizes the division of code boundaries, Developers can easily determine what they are responsible for through modules.
example
All real projects managed by Maven should be divided into modules, and each module corresponds to a POM xml. They are related to each other through inheritance and aggregation (also known as multi module). So why? We clearly import Eclipse into N projects after developing a project and dividing modules, which will bring complexity and inconvenience to the development.
To explain why, suppose there is such a project, a very common Java Web application. In this application, we have several layers:
- Dao layer is responsible for database interaction and encapsulates Hibernate interaction classes.
- The Service layer handles business logic and puts some Service interfaces and implementation related beans.
- The Web layer is responsible for interacting with the client, mainly including some Action classes of structures.
Correspondingly, in a project, we will see some package names:
- org.myorg.app.dao
- org.myorg.app.service
- org.myorg.app.web
- org.myorg.app.util
In this way, the framework of the whole project is clear, but with the progress of the project, you may encounter the following problems:
- This application may need a foreground and a background management side (web or swing). You find that most Daos, some service s, and most util s are available in two applications. You have encountered such a problem several times a week.
- pom. The dependency list in XML is getting longer and longer for reuse. However, since there is only one project (WAR) at present, you have to create a new project to rely on this WAR, which becomes very disgusting, because configuring the dependency on WAR in Maven is far less simple and clear than relying on JAR, and you don't need org. XML at all myorg. app. web. Someone modified the dao, submitted it to svn and accidentally failed the build. You are writing the code of the service and found that the compilation can only continue until the dao is repaired. Many people are modifying it. Later, you don't know who needs which dependency. Gradually, many unnecessary dependencies are introduced. There is even a dependency with multiple versions.
- Building the whole project takes longer and longer. Although you just work in the web layer all the time, you have to build the whole project.
- For a module, such as util, you only want some experienced people to maintain it. However, in this case, every developer can modify it, which leads to the code quality of key modules not meeting your requirements.
We will find that there is actually no design pattern principle: "high cohesion, low coupling". Although we divide the hierarchy by package name, and you will also say that the dependencies of these packages are one-way, and there is no ring dependency of packages. This is good, but not enough, because at the build level, everything is coupled together. Therefore, we need to use Maven to partition modules.
A simple Maven module structure is as follows:
---- app-parent |-- pom.xml (pom) | |-- app-util | |-- pom.xml (jar) | |-- app-dao | |-- pom.xml (jar) | |-- app-service | |-- pom.xml (jar) | |-- app-web |-- pom.xml (war)
In the above simple diagram, there is an app parent that aggregates many child projects (APP util, APP Dao, APP service, APP WEB). Each project, whether father or son, contains a pom XML file. Also note that the packaging type of each item is indicated in parentheses. If the parent item is pom, it can only be pom. The subproject has jar or war. Specific consideration shall be given according to its contents.
The dependencies of these modules are as follows:
app-dao --> app-util
app-service --> app-dao
app-web --> app-service
Pay attention to the transitivity of dependencies (mostly transitive, unless you configure a special dependency scope). App Dao depends on app util, and app service depends on app Dao, so app service also depends on app util. Similarly, APP web depends on app Dao and app util.
Replacing package level division with project level division can bring us the following benefits:
- It is convenient for reuse. If you need app Dao and app service for a new swing project, you can add dependencies on them. You no longer need to rely on a WAR. Some modules, such as app util, can gradually evolve into a basic tool class library for all projects. This is one of the most important purposes of modularity.
- Since you have now divided modules, the configuration of each module is in its own POM XML, you don't have to find your own configuration in a chaotic and complex POM.
- If you only work on app Dao, you no longer need to build the whole project. Just run the mvn command in the app Dao directory to build, which can save time, especially when the project is more and more complex and the build is more and more time-consuming.
- Some modules, such as app util, are dependent on everyone, but you don't want to modify them for everyone. Now you can come out of this project structure and make another project. svn is only accessible to specific people, but still provides jar s for others.
- The multi module Maven project structure supports some of Maven's more interesting features (such as dependency management), which will be discussed later.
POM configuration
Next, let's discuss the details of POM configuration. In fact, it's very simple. First look at the POM of APP parent xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.myorg.myapp</groupId> <artifactId>app-parent</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>app-util</module> <module>app-dao</module> <module>app-service</module> <module>app-web</module> </modules> </project>
Maven's coordinates GAV (groupId, artifactId, version) are configured here, which are necessary. The special thing is that the packaging here is pom. The packaging of all projects with sub modules is pom. If packaging is not configured, its default value is jar, which means Maven will type the project into a jar package.
The important part of this configuration is modules. The sub modules included in the example include app util, APP Dao, APP service and app war. When Maven builds app parent, it will sort out a build order according to the interdependence of sub modules, and then build in turn.
This is the general configuration required for a parent module. Next, let's see that sub modules conform to the configuration and inherit the parent module.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <parent> <artifactId>app-parent</artifactId> <groupId>org.myorg.myapp</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>app-util</artifactId> <dependencies> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.4</version> </dependency> </dependencies> </project>
The app util module inherits the app parent module, so the POM declares a reference to the app parent from the beginning, which is implemented through the Maven coordinate GAV. As for the project app util itself, it does not declare a complete GAV. Here we only see the artifact ID. There is nothing wrong with this POM. groupId and version inherit from the parent module by default. In fact, the child module inherits everything from the parent module, including dependencies, plug-in configuration, and so on.
Let's see how Maven build s the whole project. We run mvn clean install in the app parent root directory, and there will be something like this at the end of the output:
... [INFO] [war:war] [INFO] Packaging webapp [INFO] Assembling webapp[app-web] in [/home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT] [INFO] Processing war project [INFO] Webapp assembled in[50 msecs] [INFO] Building war: /home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT.war [INFO] [install:install] [INFO] Installing /home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT.war to /home/juven/.m2/repository/org/myorg/myapp/app-web/1.0-SNAPSHOT/app-web-1.0-SNAPSHOT.war [INFO] [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary: [INFO] ------------------------------------------------------------------------ [INFO] app-parent ............................................ SUCCESS [1.191s] [INFO] app-util .............................................. SUCCESS [1.274s] [INFO] app-dao ............................................... SUCCESS [0.583s] [INFO] app-service ........................................... SUCCESS [0.593s] [INFO] app-web ............................................... SUCCESS [0.976s] [INFO] ------------------------------------------------------------------------ [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4 seconds [INFO] Finished at: Sat Dec 27 08:20:18 PST 2008 [INFO] Final Memory: 3M/17M [INFO] ------------------------------------------------------------------------
Note the Reactor Summary. The whole project is built according to the order we want. Maven intelligently arranges the order of APP util, APP Dao, APP service and app web according to our dependency configuration.
Finally, you can find the file app-web-1.0-snapshot. In the app Web / target directory War, open the war package and see commons-lang-2.4 in the / WEB-INF/lib directory Jar and the corresponding jar packages of APP util, APP Dao and app service. Maven automatically handles the packaging for you, and introduces the corresponding jar files according to your dependency configuration.
Using multi module Maven configuration can help the project divide modules, encourage reuse, prevent POM from becoming too large, facilitate the construction of a module instead of building the whole project every time, and make the special control for a module more convenient. This paper also gives a practical configuration example to show how to use Maven to configure a multi module project.
Problems in maven multi module splitting
Mode of module division
The module division is mainly based on the single responsibility and coupling of the program. If multiple modules are used to plan the whole project at the early stage of project establishment, the principle of single responsibility should be the primary consideration, that is, the division according to hierarchy in the general sense (similar to the above example), If you start from an already complex project, you should not only consider the single responsibility when dividing modules. The single responsibility will cause a large number of sub modules, resulting in bloated pom files and difficult to identify. If coupling is taken into account, you should merge closely related modules, reduce the number of modules and improve practicability.
Abstraction of public dependencies
Dependency configurations in parent are mainly public dependencies, such as log, apache commons, spring, etc. How to define whether a dependency belongs to public? In general, if this dependency is dependent on more than two-thirds of the sub modules, it can be recognized as a public dependency. In addition, some configurations of the dependency (such as version number) can be declared in the form of pom attribute in parent, so that only one change can be made when upgrading some dependencies (very similar to #define in C language)
Existence and extinction of modules
The division of modules is not invariable. The existence of modules is to facilitate maintenance and improve production efficiency. If some modules are unreasonable and affect the development efficiency, these modules need to be considered again. Generally, this situation occurs either because the module is too large or because of the fragmentation of the module. For the former, it is necessary to split more modules to improve reuse and remove redundancy, while for the latter, it is necessary to merge some modules with high coupling as appropriate.
Sub module of sub module
This situation often means that the project itself should be divided into multiple projects, and multiple projects can inherit the POM of the same parent XML this is mainly to facilitate the unified construction of multiple projects. Therefore, sub modules of sub modules should be avoided.
Supplement:
The core idea of multi module is that each module is regarded as an independent sub project, and each sub project should not depend on each other, so as to achieve low coupling. Of course, the basic module must be relied on. For example, s is the basic module for storing database connections and tools. A, B and C are modules of different businesses, and there is no dependency between them. Then we can only play the increments of S+A, S+B and S+C when developing and packaging. Regardless of modules, each package must be an increment of S+A+B+C.