During the previous articles, we generated our Spring boot project, downloaded its dependencies from the pom.xml using Maven, set up 2 Spring profiles and added a database connection for our development environment.
Now we would like to get started with our project. To ensure our code will not turn into a mess, we need a folder structure that keeps our source code in order and easy to navigate. Of course, the structure of our project can be changed later, but the earlier we build a solid foundation, the more enjoyable the development of our application will become, once the project growths in size.
Back to the roots
When generating our application using the Spring Initializr web tool during an earlier tutorial, we noticed that the project already comes with a basic structure. As a reminder, the screenshot on the right demonstrates how our project structure should look like at this point.
In the red rectangles, we can see the root folder for our Java source code files called “com.merchantvessel.core”, the application properties of our spring profiles for “dev” and “prod” as well as the pom.xml with the specifications for our maven build, including the project dependencies and the definition of the spring profiles.
The general folder structure as generated by the Spring Initializr is already sufficient for what we would like to achieve. The point where we will need to make the structure more detailed is the Java section, because the number of sources can easily increase to beyond several hundred or even thousands.
Where do I draw the line?
Like with most decisions involving a systems design, there is more than one correct solution. However, they all should have a way to easily locate sources and to allow for the application to grow in size significantly while keeping the project easy to navigate and permitting straightforward refactoring.
What is a project dimension that will remain unchanged, even if the project growths in size? As a rule of thumb, following best practices will in most cases lead us to a viable solution. As described in the previous article about the technology stack, we are following a multi-layered software architecture. There are different ideologies about how many layers there should be, but usually there are three logical divisions that can be found it most applications:
1. Interface Layer
2. Business Layer
3. Persistence Layer
1. Interface Layer
Let us start with the interface layer. In a multi-layered architecture, this is usually called the “presentation layer”. Why are we choosing the name interface layer? You may recall that our presentation layer will be implemented in a separate Angular application. One may therefore argue that our system does not need a presentation layer. If you look at the task of a presentation layer, what it provides is effectively a means of communication between the application and its main user or consumer. As such, our applications “presentation” will be handled via its controllers, which serve as the interface for the communication of our back-end with the Angular front-end. The classes that belong to this Layer bear the “@Controller” annotation.
Are all classes in the Interface layer annotated with “@Controller”? Not quite. We want the interface layer to transfer data using JSON messages. These JSON messages will be generated via a JSON converter based on Java POJO classes. The default converter used by Spring boot is Jackson, which is what we will use here. The POJO classes describing the messages we will transfer via the Controllers will be defined in the interface layer as well.
2. Persistence Layer
The persistence Layer, also called a data access layer, manages the database operations as well as the database design of an application. This is where we would find Java classes with the JPA “@Entity” annotation, which will describe database tables or parts thereof, depending on the join strategies used in classes that involve inheritance. Right above the entities, we can find the JPA repositories, which are annotated with the work “@Repository”. Our JPA repositories are implementations of the Java JpaRepository interface. They let Spring manage most necessary queries for data access and manipulation. To test the functionality of our application, we will also create a set of demo data
3. Business Layer
The business layer is where the “magic” happens. In Spring, classes that are part of the service layer, typically carry the “@Service” annotation. Depending on the software design applied, there may be a service layer besides the business layer. However, in our case these functionalities will comfortably fit into the same layer, so introducing a separate layer will not be necessary as it would raise the question which functionality can be found in the business layer and which in the service layer.
What else will be part of the business layer?
There are entities, that help implementing our business logic while having a limited set of values. This is called reference data. While we could save these values in a database, that would trigger a database access whenever we need these values. Alternatively, Java provides the Enumeration data type, “that enables for a variable to be a set of predefined constants”. Since enumerations are not saved in the database, they will be placed in the business layer.
Turning application layers into packages
Now that we have defined the layers of our application, it is time to apply this definition to create our structure of folders, in Java called “packages”. First off, the class MerchantvesselCoreApplication which contains the Main function and hence entry point to our application will remain in the root folder, so it will always be easily found. Moreover, configurations that affect functionality across the application will be moved to a separate folder on the root level of the application. In our case, that is initially limited to classes concerning the JWT security setup.
Now we can create one folder for each layer and a sub-folder for every functional component of that layer. The end result can be found in the Screenshot.
What we learned
There is no single right way to structure a project. An efficient structure is usually built on a solid foundation of following best practices and will be further optimized along the way, depending on the project requirements. Many developers prefer not to include the application layers in the folder structure of the project to avoid overengineering. Instead, they focus more on a mix of following Spring annotations, like using one folder for @Entities, one for @Controller etc. on one hand and bundling technical functions, for example by placing all security related sources into a sub directory on the other hand. Eventually it comes down to personal preferences and the individual experience of what works well and what does not.