0

I am working on my little OSS project in which I am using Maven as build tool. I split the project into smaller sub-projects to simplify development. Thus I have following structure:

project
+-- main-module
|   |
|   +- pom.xml
|
+-- submodule1
|   |
|   +- pom.xml
|
+ pom.xml

My thought was that main-module should provide interfaces which each submodule should be implementing in order to be plugged into whole application. Therefore submodule1/pom.xml contains compile time dependency reference to main-module. In its turn I also need to be able to test whole application and thus main-module/pom.xml contains test scope dependency reference to submodule1. As the result maven refuses to compile projects saying that they contain cyclic references.

My thought was that maven could first compile classes of main-module as it does not require any compile time dependency on any of submodules, then it using compiled classes of main-module could compile classes of submodule1 and after that compile test classes of main-module (to be able run tests). But seems that maven compiler does not take in account the scope of dependency and I somehow need to work around that.

The only solution I can see is to move away tests from main-module, which doesn't really make sense for me as only that module provides main logic.

My question - is there any other way around this issue except for moving away tests? Or maybe something is wrong with my understanding of how maven-reactor-plugin should work?

Alexey Kamenskiy
  • 2,888
  • 5
  • 36
  • 56
  • 2
    You could move all tests to a project that uses as dependencies the other modules. THe first modules themselves would not have any dependencies, and the test project would only be executed after the first project is already installed in your m2 – Alexandre Santos Aug 01 '14 at 07:38
  • If you move specific implementation details to submodules to decouple your app and add compile time dependencies by having the tests centralized in the main module, you can stay with a monolithic application, as you lose all advantages of decoupling with the disadvantage of submodule overhead. – Smutje Aug 01 '14 at 07:38
  • @AlexandreSantos @Smutje so you guys suggest that I would actually move tests from `main-module` to another submodule? – Alexey Kamenskiy Aug 01 '14 at 07:44
  • It could be on another submodule (if there are no dependencies) or to another project if you still have the dependencies. – Alexandre Santos Aug 01 '14 at 07:45
  • @AlexandreSantos how would I deal then with my CI implementation? (I am using Travis CI + automatic deployment when I create tagged commit). Each submodule is also in a separate Git repo, in this case I would need to put them all together in one repo otherwise I lose the advantage of CI and CD approaches. – Alexey Kamenskiy Aug 01 '14 at 07:49
  • You would loose nothing. Just the tests, which should already be marked as test would go to a different project. Everything else CI, CD, OCD, stays the same – Alexandre Santos Aug 01 '14 at 07:50
  • I think that the reason for Maven's behaviour is because `test` dependency is still compile time dependency... it is compile dependency for your `src/test/java`... maybe you can set your module to be dependency of surefire plugin. But that is just a wild guess. – Pavel Horal Aug 01 '14 at 07:51
  • @AlexandreSantos not exactly, I will lose the advantage of commit/push/fail/fix cycle, since I will have to move tests to another submodule/repo and CI builds each repo independently, then on each push it won't actually execute any tests on my `main-module`. So i will have to run `mvn clean test` before each commit/push. But I guess there is really no other way around it. – Alexey Kamenskiy Aug 01 '14 at 07:56
  • Just declare a dependency build. Yes, you will have to do something more and it won't be just one neato package, but come on... I give up. – Alexandre Santos Aug 01 '14 at 07:57
  • @PavelHoral as javac can compile java files independently from one another, it could simply compile first `src/main`, use results to compile another submodule, and then use second operation results compile test classes from `src/test` – Alexey Kamenskiy Aug 01 '14 at 07:58
  • @AlexandreSantos I guess it would be resolvable if use Ant instead of Maven, but it's not an easy change. Anyway, thanks for good thoughts, If you put your comments into an answer, I guess I would mark it as accepted. – Alexey Kamenskiy Aug 01 '14 at 08:00
  • Don't worry about it, if you have the answer, that is good enough for me. – Alexandre Santos Aug 01 '14 at 08:01
  • Have a look here: http://stackoverflow.com/a/25322753/548473 Simplest working solution! – Grigory Kislin Nov 26 '16 at 00:31

2 Answers2

2

Instead of moving your tests away, you could move all of your API into it's own module. Then your main module would contain the application and you can freely distribute your application's API to allow others to access it. If they want to develop new functionality they do not necessarily need the sources of your app.

I consider this a much better style, because sub modules with specific functionality can now clearly separate between what is your applications API and what is the code your application needs to startup/shutdown, etc.

This is in my mind the intended way of how maven projects should look like. It also sticks with the single responsibility principle. The main modules responsibility is to startup/shutdown, etc your application. The API modules responsibility is to show how other developers can access your application. And the other submodule provide specific functionality for your application.

SpaceTrucker
  • 13,377
  • 6
  • 60
  • 99
  • So means I just move the interfaces to another submodule and build/deploy it as separate artifact, then use that artifact itself within both `main-module` and `submodule1`? Wouldn't that result in too many artifacts? Managing all can become quite a problem, but I do see point in your solution. This can help me to keep tests with package that should contain them (e.g. with `main-module`) – Alexey Kamenskiy Aug 01 '14 at 08:21
  • @AlexKey yes, that's what I had in mind. This leads to much more artifacts than you might be used to. But I arranged myself with that. And having the compiler, code completion features, etc. to tell me what classes and interfaces I am allowed to access is a great benefit. – SpaceTrucker Aug 01 '14 at 08:25
  • Thanks, I think for now I will go with this solution. However as a side question - as you say you used this approach, any suggestions on how should I deal with CI environment? I use Travis CI for automatic builds and deployment of artifacts (deployment happens automagically once I tag the commit). However currently all modules are in separate repos, which means if I will also need to deal with each repo separately. Such as - first tag API repo so I can have downloadable artifact, then wait for ~2-4 hours until it appears in maven central and only then I can tag the rest. Any advice here? – Alexey Kamenskiy Aug 01 '14 at 08:32
  • As a solution to that side question - I could put all the code in one repo, but this is definitely an overkill... – Alexey Kamenskiy Aug 01 '14 at 08:33
  • @AlexKey I was wondering what you meant when you first mentioned separate repos. It is a bit unusual (but not implicitly incorrect) to have multimodule project separated. If they have the same release cycle, they should be in the same repo. If they don't have the same release cycle you should ask yourself whether it should be multimodule project. – Pavel Horal Aug 01 '14 at 08:51
  • Understand. My idea in splitting into separate repos was that ideally each part can be developed independently and not necessarily use same release cycle. However seems that I should change this approach and move to same release cycle and central repo. Thanks anyway. I will mark this answer as accepted as it provides better (for me) solution among all proposed. – Alexey Kamenskiy Aug 01 '14 at 08:56
1

I know this is more of a comment, but might provide you with (not so pretty) solution:

You can seet your submodule1 as a dependency of maven-surefire-plugin (so that reactor is forced to build it) and then play with its settings... i.e. childDelegation or additionalClasspathElements

Pavel Horal
  • 17,782
  • 3
  • 65
  • 89
  • I will look into that, but sounds like it makes hell of XML configuration (which I do try to avoid in my work and the goal of this OSS project is to help ppl use less XMLs) – Alexey Kamenskiy Aug 01 '14 at 08:24
  • I will probably delete this answer in favor of SpaceTrucker's suggestion to make dedicated API module. – Pavel Horal Aug 01 '14 at 08:25
  • I suggest to keep it, it doesn't harm having it, and someone may find it useful. – Alexey Kamenskiy Aug 01 '14 at 08:25
  • Just realized, that this approach will not work if your tests are linking classes from `submodule1` (i.e. they are **compile time** dependencies for test classes). Surefire is responsible for test execution, not compilation. What my approach can only do is to make sure `submodule1` is on the classpath when running tests. – Pavel Horal Aug 01 '14 at 09:02
  • So you might need to tweak *maven-compiler-plugin* (make the module as a dependency of this plugin as well) and its [`testCompile`](http://maven.apache.org/plugins/maven-compiler-plugin/testCompile-mojo.html) task. It will require a bit of XML config and makes the Maven approach a bit uglier. – Pavel Horal Aug 01 '14 at 09:05