Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API support for multi-modules project creation #663

Open
dvsingh9 opened this issue May 1, 2018 · 15 comments · May be fixed by #1288
Open

Add API support for multi-modules project creation #663

dvsingh9 opened this issue May 1, 2018 · 15 comments · May be fixed by #1288

Comments

@dvsingh9
Copy link

dvsingh9 commented May 1, 2018

Spring.io provided initialiser and thats really time saver and helpful, but if anyone wish to create multi-module spring application its still little bit of work. Multi-module project from spring initialiser will add up next level of ease.

@snicoll
Copy link
Contributor

snicoll commented May 2, 2018

I am not disagreeing with that but I am not sure how we can make that happen while keeping the straightforward use of the UI we have now. Please share a more complete analysis with a concrete use case.

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label May 2, 2018
@wilkinsona
Copy link
Contributor

wilkinsona commented May 2, 2018

I am not sure how we can make that happen while keeping the straightforward use of the UI we have now

That's my concern with trying to support multi-module projects too. Providing all of the information about the modules and their dependencies would add quite a lot of complexity. There's also the question of what should go in each module. Presumably only one would contain a @SpringBootApplication, but which one? What would the others contain?

I'd really like to be proven wrong, but my current feeling is that there's unlikely to be a common way to initialise a multi-module project and that providing all of the customisation that people would need would add too much complexity.

@snicoll
Copy link
Contributor

snicoll commented May 2, 2018

Brainstorming but something where a multi-modules setup with a single project is added (the requested project) could be a good middle-ground. We're still limited to one project, with a well defined set of dependencies but the project initialises the proper structure to easily add new modules in the future.

The step of adding a new module in that project can be quite minimal as using the parent gives dependency management and all nice defaults. If we go in that direction we should probably be parent-less by default, as we want to make sure the spring-boot-maven-plugin does not kick in for any other modules by default.

@snicoll

This comment has been minimized.

@JacksonZhangHuaQuan
Copy link

I am also very concerned about the creation of multi-module skeleton, I hope to have an example for reference.

@sagacity
Copy link

sagacity commented Jul 5, 2019

To give a bit of background why we would like this feature as well: My company's services typically have a multi-module structure. This does not necessarily mean initialzr needs to create multiple modules from the get-go, but it would be nice if it could provide a single module that contains the application class and the relevant dependencies, with perhaps some dependency/plugin management in the parent.

This setup would make it a bit simpler for our developers to add modules since they don't need to migrate the initialzr-generated non-multimodule project to a multimodule project (which they'll inevitably have to do at some point).

We are currently using some custom contributors for this, which during generation move some files around in a ProjectContributor, use a subclassed MavenBuildWriter to write the modules section, etc. It would be nice if there was 'native' support for this in Initialzr itself, since we are now heavily dependent on the ordering of the contributors, which feels a bit flaky.

Btw, I'm not necessarily saying this should be an integral feature of Initialzr, but perhaps a few extension points could be introduced to make it easier for people to customize Initialzr to be able to define a multimodule project?

@snicoll
Copy link
Contributor

snicoll commented Jul 9, 2019

Thanks for sharing your thoughts, that's very interesting. I've experienced those issues myself so it's nice to have a summary from someone else.

Having the notion of modules in our core abstraction could be interesting indeed. I am convinced we could implement this feature with the current API but it would probably require inelegant code to tie things together. It looks like anyway that's what you might have in your current proposal. Would you mind sharing the code somewhere (doesn't have to a clean PR, just enough so that I can figure out the amount of code required).

since we are now heavily dependent on the ordering of the contributors, which feels a bit flaky.

That was my concern and a concern in general for those having a lot of customizations. Feel free to join us on Gitter if you want to discuss this any further. If you have concrete ideas or problem to report in this area, I'd appreciate if you take the time to create a separate issue.

@snicoll snicoll added type: enhancement and removed status: waiting-for-feedback We need additional information before we can continue labels Jul 9, 2019
@snicoll snicoll changed the title Add multi-module project creation Add API support for multi-modules project creation Jul 9, 2019
@sagacity
Copy link

@snicoll Great, we will try to package up our modifications and host a zip somewhere. It'll take a bit of time to organize, but that should definitely be possible.

@sdoxsee
Copy link

sdoxsee commented May 28, 2020

@RoyJacobs great comment above. I'm curious if you still might be able to share some of the modifications you made to support a multi-module project structure?

@sagacity
Copy link

Hi, unfortunately due to the usual time constraints we weren't able to deliver on our promise :(

However, I can perhaps briefly list some of the things we've done:

  • To add support for submodules we create our own ModulesMavenBuild class which derives from Initializr's MavenBuild. It simply adds a list of strings with the module names.
  • To actually use this list of modules we also crated a custom class based on MavenBuildWriter which writes these modules. Unfortunately MavenBuildWriter has a bunch of private methods so we needed to copy/paste some of the implementation. See below for part of the implementation.
  • We have a MavenParentBuildContributor which is both a BuildWriter and ProjectContributor which wraps the two custom classes mentioned in the previous bullet points. It essentially takes Initializr's default project and moves it to a submodule, and then uses our custom classes to write the parent module.

Hopefully this helps you get underway a little.

The relevant part of our hacky ModulesMavenBuildWriter code:

    fun writeTo(writer: IndentingWriter, build: AxleMavenBuild) {
        ... write the pom to a StringWriter 'sw' ...

        val pomLines = sw.toString().lines()
        pomLines.forEach { line ->
            writer.println(line)

            if (line.contains("</properties>")) {
                // Write the modules just after we write the properties
                writeModules(writer, build)
            }
        }
    }

    private fun writeModules(writer: IndentingWriter, build: AxleMavenBuild) {
        writer.println()

        if (build.modules.isNotEmpty()) {
            writeElement(writer, "modules") {
                build.modules.forEach { writeSingleElement(writer, "module", it) }
            }
        }
    }

   ...

The relevant code in our MavenParentBuildContributor:

    private val mavenParentBuildWriter = ModulesMavenBuildWriter()
    private var parentMavenBuild = ModulesMavenBuild()

    override fun contribute(projectRoot: Path) {
        val childPomPath = projectRoot.resolve("pom.xml")
        Files.deleteIfExists(childPomPath)
        val childPom = Files.createFile(childPomPath)
        configureParentPom()
        writeBuild(Files.newBufferedWriter(childPom))
    }

    private fun configureParentPom() {
        parentMavenBuild.settings()
            .version(mavenBuild.settings.version)
            .artifact(mavenBuild.settings.artifact)
            .group(mavenBuild.settings.group)
            .packaging("pom")
            .parent(
                mavenBuild.settings.parent.groupId,
                mavenBuild.settings.parent.artifactId,
                mavenBuild.settings.parent.version
            )
    }

    override fun writeBuild(out: Writer) {
        indentingWriterFactory.createIndentingWriter("maven", out).use {
            mavenParentBuildWriter.writeTo(it, parentMavenBuild)
        }
    }

@sdoxsee
Copy link

sdoxsee commented May 29, 2020

@RoyJacobs Thanks so much!

@dvsingh9
Copy link
Author

@snicoll thank you so much

@lnavarette
Copy link

I started down the path of implementing this for the gradle build system, using this approach

a multi-modules setup with a single project is added (the requested project)

And assumes the consumer is hosting their own instance of the initializr and can inject this behavior via a ProjectDescriptionCustomizer.

So the change is mainly just adding a module root to the BuildSystem and updating some ProjectContributors to write their files under the application's module rather than in the project root.

If that seems like a reasonable approach and there'd be interest in supporting this functionality, I can wrap this up and submit a PR.
https://github.com/spring-io/initializr/compare/main...lnavarette:multiproject-build?diff=unified

@wilkinsona
Copy link
Contributor

@lnavarette Thanks for sharing your prototype. Multi-module support is definitely of interest. Unfortunately, the team has lots of other things on its plate at the moment and we're a bit lacking in bandwidth to spend much time on this. To avoid you spending any more time on this at the moment, would you like to submit what you've got in its current form as a draft PR? That'll allow us to review and discuss it more easily as soon as time permits.

@lnavarette lnavarette linked a pull request Dec 8, 2021 that will close this issue
sayeedap pushed a commit to sayeedap/initializr that referenced this issue Sep 16, 2022
…onstructor-sideeffect

Fixes spring-io#663. Remove 'Configuration' constructor sideeffect
@christianhujer
Copy link

It would be a good enhancement to get this.
Here's what I currently do for new projects, because I basically always want them to be multi-module (and if it's just for a library module for potentially reusable code):

  1. Create a project using Spring Initializr. Shelve it.
  2. Create a project using Gradle. Shelve it.
  3. Create a project manually from scratch, combining the results of 1. and 2.
  4. Add the "I always want this" stuff, like latest version of JUnit, Cucumber, Selenium WebDriver, WebdriverManager, KtLint, Detekt, SonarLint.

Would be create if I could skip step 2 and 3 (and 4, but that's out of scope for this issue).

Why do I want my projects to be multi-module?
I usually have at least 3 (4) modules:

  • The application module
  • A library module for potentially reusable production code
  • A library module for potentially reusable test code
  • (The build-logic module)

I could imagine that Spring would automatically create a 3 module structure:

  • The app module
  • A library module that uses Spring
  • A library module that doesn't use Spring
  • (The build-logic module)
    This would be in line with what gradle init does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants