Compare the two trees below. In both cases the goal is to have an application with two independent modules (frontend
and reporting
), and one shared/common module (domain
). The code in frontend
shouldn’t be able to access code in reporting
, and vice versa. Both modules can use the domain
code. Ideally, we would like to check these access rules at build-time.
On the left, there’s a traditional solution using Maven build modules. Each build module has a pretty elaborate pom.xml
, e.g.:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <parent> <artifactId>parent</artifactId> <groupId>org.veripacks.battle</groupId> <version>1.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <name>Veripacks vs Build Modules: Frontend</name> <artifactId>frontend</artifactId> <dependencies> <dependency> <groupId>org.veripacks.battle</groupId> <artifactId>domain</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> </dependencies> </project> |
On the right, on the other hand, we have a much simpler structure with only one build module. Each application module now corresponds to one top-level project package (see also this blog on package naming conventions).
Notice the package-info.java
files. There, using Veripacks, we can specify which packages are visible where. First of all, we specify that the code from top-level packages (frontend
, reporting
and domain
) should be only accessible if explicitly imported, using @RequiresImport
. Secondly, we specify that we want to access the domain
package in frontend
and reporting
using @Import
; e.g.:
1 2 3 4 5 6 | @RequiresImport @Import("org.veripacks.battle.domain") package org.veripacks.battle.frontend; import org.veripacks.Import; import org.veripacks.RequiresImport; |
Now, isn’t the Veripacks approach simpler? :) There is still build-time checking, which is possible by running a simple test (see the README for details). Plus, you can also use other Veripacks features, like @Export
annotations, which is a generalized version of package-private scope, taking into account package hierarchies. There are also other benefits, like trivial sharing of test code (which is kind of hard with Maven), or much easier refactoring (introducing a new application module is a matter of adding a top-level package).
The immediate question that arises is – what about 3rd party libraries? Most probably, we’d like frontend-specific libraries to be accessible only in the frontend
module, and reporting-specific ones in the reporting
module. Well, not supported yet, but good news – that will be the scope of the next Veripacks release :).
You can view the example projects on GitHub.
Adam