banking-kata-java
banking-kata-java copied to clipboard
Modularization - Restrict imports in gradle to a specific package to ensure hexagonal architecture boundaries
Hello,
Last weekend I tried to add on the project a plugin in order to banned some imports according to some packages. This can be very useful to check at the build step if we violated boundaries of the hexagonal architecture. For example thanks to this plugin, I can ensure that we can't have imports from infrastructure in domain package.
On top of that, if it's the case, we can add some exclusions to make explicit the todo list of what should be shift and ensure that no new banned imports will be added.
Maven Plugin:
- url : https://github.com/skuzzle/restrict-imports-enforcer-rule
- description : Maven enforcer rule that bans certain imports. Keep your code base clean and free from usage of unwanted classes
Little problem, when I opened the banking-cata-java project, I was surprised because it is not a maven project but a gradle project. And for gradle I don't know how I can use this useful plugin.
- I tried to call maven plugin in gradle but this seems to be weird.
- According to this https://kordamp.org/enforcer-gradle-plugin/#_excludedependencies and this https://github.com/kordamp/enforcer-gradle-plugin, I tried to add a root configuration in settings.gradle but I don't know how I can exclude dependencies for a specific package (may be if you know some groovy tips) and I tried to add several build.gradle project to have specific configuration according to those sub packages but it's not working like this, indeed, a sub gradle project it's like a module maven and we can't apply this to only separate packages (because domain and infra are in the same project).
So if you have an idea on how we can have a similar check by using gradle I will be happy to talk with you :)
see below a maven sample to exclude infra imports in domain package :
<execution>
<id>check-imports</id>
<phase>process-sources</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<RestrictImports>
<groups>
<group>
<reason>Forbid to depend to infrastructure/application (adapters primary/secondary) or external lib in domain</reason>
<basePackages>
<basePackage>com.example.demo.domain.**</basePackage>
</basePackages>
<bannedImports>
<bannedImport>com.example.demo.adapters.primaries.**</bannedImport>
<bannedImport>com.example.demo.adapters.secondaries.**</bannedImport>
<bannedImport>com.fasterxml.jackson.**</bannedImport>
<bannedImport>org.springframework.**</bannedImport>
<bannedImport>io.swagger.**</bannedImport>
<bannedImport>lombok.**</bannedImport>
</bannedImports>
<exclusions>
</exclusions>
</group>
<group>
<reason>Forbid to depend to infrastructure (secondary) in application (primary)</reason>
<basePackages>
<basePackage>com.example.demo.adapters.primaries.**</basePackage>
</basePackages>
<bannedImports>
<bannedImport>com.example.demo.adapters.secondaries.**</bannedImport>
</bannedImports>
<exclusions>
</exclusions>
</group>
<group>
<reason>Limit dependency of demo to the other domains (useful in case of modular monolith)</reason>
<basePackages>
<basePackage>com.example.demo.**</basePackage>
</basePackages>
<bannedImports>
<bannedImport>com.example.business.**</bannedImport>
</bannedImports>
</group>
</groups>
</RestrictImports>
<!-- You could have another rule instance here for restricting further imports -->
</rules>
</configuration>
</execution>
As an alternative solution i would recommend gradles support for multiple components/projects instead of one component with a deep packages structure.
https://docs.gradle.org/current/userguide/structuring_software_products.html#structure_large_projects
I plan to work on this topic, as preparation for an upcoming meetup. Some changes I had made up to now was separate unit tests from integration tests (i.e. separate fast running from slow running), but next I need to actually also modularize the architecture too.
The following is a rough plan of what I'm currently doing (in progress), I see this as the minimal split required: core & adapter-*
@nicolasrosado, @schneidersteve I've added the split via modules core and adapter-*. In this way, all the infrastructure is purely within adapter modules. Interested to hear your thoughts.
@schneidersteve, in my current code, I'm using modules, but it's still all a single build. I saw the link you sent Reflecting software architecture in project structure - "We can represent each of our components as a separate Gradle build." - what do you see as the pros of that approach, compared to just the single-build multi-module approach?