Search code examples
spring-bootmavendependenciesmodularity

How best to split Spring Boot service/repository tier off into a separate Maven project?


I am working on a Spring Boot application with Thymeleaf templates for views, as well as Java classes in three "tiers": Controller, Service, and Repository. I would like to move the Service and Repository tiers into a separate Maven project (I'm calling it "service-tier") that the "View/Controller" application can pull in as a dependency.

The reason for this is that I expect to use the same service/repository/database logic for other, future applications with different audiences. I don't have the green light to go to a (micro)service architecture with separate running programs for the front-end and back-end, but I can separate the code and unite it at build time.

What's the best way to separate out just the Services and Repositories? Here are some specific points to clarify, but there may be other questions I haven't figured out that I need to ask:

  • What starters and dependencies do I need in the service-tier artifact's POM? spring-boot-starter-parent? spring-boot-starter-jdbc? The database driver?

  • Do the @Service and @Repository annotations do anything for me if they're in the service-tier artifact?

  • Do I need a @SpringBootApplication annotated class in the "service-tier" project, for example to run tests? How about the spring-boot-maven-plugin? (The latter seems to prevent the "mvn install" phase.)

  • Where do I put my data source properties such as the JDBC URL? In application.yml in the service-tier, or in the UX tier project that depends on it?

Pointers to working code that is divided up in this way would be welcome!


Solution

  • After much trial and error, this is what I ended up with, and it works:

    The service-tier is a Maven project containing @Service and @Repository annotations.

    • It contains the spring-boot-starter-parent, spring-boot-starter-web, spring-boot-starter-jdbc, spring-boot-starter-security dependencies, as well as the database driver. Some test-scoped dependencies, too: JUnit, spring-boot-starter-test, and spring-security-test. This is almost all the dependencies that the front-end application uses, minus Thymeleaf and some Webjars artifacts.

    • I don't know if the annotations do anything for me or not, but I kept them in.

    • After way too long, I remembered that Maven projects have a test directory for classes and resources that will only be used in testing and not bundled with the packaged JAR or WAR. In that directory structure I put a @SpringBootApplication annotated class, as well as application.yml with database connection info. This is used in running integration tests, and doesn't affect the web applications that depend on the service tier.

    The front-end applications are their own Maven projects. In each of them:

    • They declare service-tier as a dependency.
    • They also declare all the Spring Boot dependencies. Many are redundant but that's not a problem with Maven. Additional dependencies include spring-boot-starter-thymeleaf and a couple of other Thymeleaf dependencies, as well as a few dependencies from org.webjars that include Bootstrap and JQuery.
    • They also have their own @SpringBootApplication annotated classes and application.yml. These are the "real" configurations, not just test configurations.
    • All @Controller classes as well as Thymeleaf templates and static files and messages.properties are in these front-end projects.
    • Spring Security configuration (i.e. WebSecurityConfigurerAdapter instances) are in these projects. (The UserDetailsService is in the service-tier, though.)

    It works pretty smoothly as long as I make sure that the front-end applications always call the latest version of service-tier.

    One interesting complication is that a Spring Boot application will only scan for service and repository classes within its own Java package. Initially I had the service tier in a package hierarchy like "com.mycompany.servicetier" and the front ends in packages like "com.mycompany.appone" and "com.mycompany.apptwo". I had to move the @SpringBootApplication annotated class in each front-end project one level higher, to "com.mycompany", so it would discover the service tier classes "below it" for dependency injection.