Application Project Structure

On this page, you will:

  • Learn about the project structure of an Across application.

  • Learn the difference between the Application Descriptor and the Application Module.

  • Create a simple web controller in the sample application.

File and folder layout

If you created the sample application as described in creating an application you should have the following project layout:

pom.xml
src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── demo
│   │               ├── application (1)
│   │               └── DemoApplication.java (2)
│   └── resources
│       │   └── messages
│       │       └── demo
│       │           └── default.properties (3)
│       ├── application.yml (4)
│       ├── application-dev.yml (4)
│       └── application-prod.yml (4)
└── test
    └── java
        └── com
        │   └── example
        │       └── demo
        │           └── application (5)
        └── it
            └── ITDemoApplication.java (6)
1 The application package represents the Application Module. It is currently empty but is the location where you will store Spring components encapsulating your application specific business logic.
2 This is the Application Descriptor.
3 The default.properties file contains sample message codes used for localisation.
4 The different application-*.yml files are YAML files containing configuration settings for your application. The dev and prod filename segments determine when those settings apply. The section Application configuration explains these files in more detail.
5 Under this package you can place your unit tests.
6 The it package is the location to store your integration tests. The package is outside the application’s package, to avoid scanning of beans that are defined inside the integration tests.

A more complete project structure example can be found in the Cheat Sheet.

Application Module

An Across application is always combination of different Across modules. The Application Descriptor describes which modules are part of the application. One of these modules is the Application Module.

The Application Module contains the components that make up the application-specific functionality. Whereas other Across modules in your application might be shared across multiple applications (shared libraries), the application module is local to a single application.

As noted in the file and folder layout above, the content of the Application Module is represented by all classes in the application package below the package of the Application Descriptor. When the Application Module starts, this package will be scanned for Spring components.

Like any other Across module the Application Module must have a unique name, which is generated based on the name of the Application Descriptor.

Let’s have a look at our sample project:

  • the Application Descriptor is DemoApplication

  • the package of the Application Descriptor is com.example.demo

  • as a result:

    • the components of the Application Module should be in com.example.demo.application

    • the name of the Application Module will be DemoApplicationModule

    • the Application Module resources are expected to use demo as a folder prefix

Looking back at the console output, we see the notice that the Application Module has been created and will be bootstrapped.

---
AcrossContext: DemoApplication (AcrossContext-1)
Bootstrapping 3 modules in the following order:
1 - AcrossWebModule [resources: ]: class com.foreach.across.modules.web.AcrossWebModule
2 - DemoApplicationModule [resources: demo]: class com.foreach.across.core.DynamicAcrossModule$DynamicApplicationModule  (1)
3 - AcrossContextPostProcessorModule [resources: AcrossContextPostProcessorModule]: class com.foreach.across.core.AcrossContextConfigurationModule
---
1 The automatically generated Application Module.

The Application Module is one of the default application modules and is automatically generated based on a Java package. Apart from that it behaves as a regular Across module. Refer to the chapters in Developing modules for more specific information on Across modules.

Why an Application Module?

Across encourages you to bundle all functionality in separate modules to better structure your application and its dependencies. While the Application Module is demarcated by the single application Java package, the components inside it are actually loaded in a separate Across module, with all interaction and visibility benefits or limitations that implies.

Bundling your components in an application module early on also makes it easier to extract them to a shared module at a later stage. The chapter Extracting a module from you application explains how to do this.

If you are familiar with regular Spring Boot applications, it is important to note that an Across application uses a different component scanning approach.

In a Spring Boot application the entire package with the @SpringBootApplication class will be scanned for components.

An Across application does not scan the package holding the @AcrossApplication class but instead creates default modules based on the presence of specific child packages. These child packages might then be scanned for components when the corresponding module bootstraps. These components will be created inside said module, which is in fact a child ApplicationContext of the root ApplicationContext represented by the Across Application Descriptor.

Scanning the entire package of the Application Descriptor should never be done.

Adding a component

Let’s add a component to our sample application. We will add a simple web controller that prints out an application key when called.

Create a class SampleController and place it in the application package.

com.example.demo.application.SampleController.java
@Controller (1)
public class SampleController {
    @GetMapping("/applicationKey") (2)
    @ResponseBody (3)
    public String applicationKey() {
        return "The application key is: DEMO"; (3)
    }
}
1 The @Controller annotation is a Spring Web MVC annotation marking this class as a web controller component. Spring MVC support is activated by Across Web. Please refer to the Spring MVC documentation for details on web controllers and request mappings
2 @GetMapping("/applicationKey") specifies that this method should be executed for any GET HTTP request to the /applicationKey path.
3 @ResponseBody specifies that the return value of the method is not the name of a view template but represents the entire response that should be written to the caller.

Your project layout should now look like:

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           ├── demo
│   │           │   └── application
│   │           │       └── SampleController.java
│   │           ...
│   ...
...

Run or restart the application:

$ mvn spring-boot:run

Pointing your web browser to http://localhost:8080/applicationKey should yield the following output:

The application key controller result.

The Application Module will automatically scan the com.example.demo.application package for Spring components. Any classes (meta-)annotated with @Component (like @Controller) will be created.

Application resources

Resources are non-Java class files that are bundled alongside the classes in the same JAR file. In the file and folder layout above you can see the resource files are present in a physical resources folder. This is a standard convention most (Maven or Gradle based) Java projects use.

Typical resource files include:

  • application configuration files (like application.yml, logback.xml)

  • static web resources like css and javascript files

  • view templates (like html, xml) used for rendering output

  • resource bundles for localisation and translation

Across modules often have their own associated resources. In order to avoid conflicts between modules, Across encourages certain conventions when organizing your resource files. A core element is that every module should have its own unique resources key for grouping its resources. This key is often used as the name of a parent folder of the actual resource file.

Using the conventions correctly avoids conflicts between modules and enhances the developer experience. See also the chapter on developing-modules/creating-a-module/project-structure.adoc#module-resources.

Our sample application contains 2 different types of resources:

  • top-level application resources (configuration files)

  • Application Module resources (message code resource bundle)

Resources in the sample project
src
├── main
│   ├── ...
│   └── resources
│       │   └── messages (1)
│       │       └── demo (1)
│       │           └── default.properties (1)
│       ├── application.yml (2)
│       ├── application-dev.yml (2)
│       └── application-prod.yml (2)
...
1 default.properties respresent a resource bundle with message codes that are associated with the Application Module. The root folder messages is used for all message resource bundle locations. The parent folder demo is the resources key for the Application Module, attaching these resources to that specific module.
2 The application-*.yml files contain configuration settings of the application. These are by convention top-level resources.

Adding a resource file

Let’s change our sample controller to use a HTML template file instead of writing the response from code directly.

Across Web enables support for using Thymeleaf for your view templates, so we’ll be using that.

Change the source code of the sample web controller:

com.example.demo.application.SampleController.java
@Controller
public class SampleController {
    @GetMapping("/applicationKey") (1)
    public String applicationKey(Model model) { (2)
        model.addAttribute("applicationKey", "DEMO");
        return "th/demo/applicationKey"; (1)
    }
}
1 We remove the @ResponseBody which implies that the String return value of our controller method is now the name of the view that should be rendered. In this case the name is th/demo/applicationKey which is the path to our Thymeleaf template.
2 Instead of rendering the entire message, we will pass the application key to the view template by putting it on the Model. The Model (MVC) is the way to pass attributes and data between controller and view. All this is standard Spring MVC functionality.

After changing controller code, add the following template file in the right location:

classpath:views/th/demo/applicationKey.html
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head><title>Application Key</title></head>
<body>
The application key is: <strong th:text="${applicationKey}">APPLICATION KEY</strong> (1)
</body>
</html>
1 Instead of plain text we now return valid HTML. We use the th:text attribute to replace the content of the <strong> element with the value of the applicationKey attribute on the Model.

Your project layout should now look like:

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           ├── demo
│   │           │   └── application
│   │           │       └── SampleController.java
│   │           ...
│   └── resources
│       │   ├── views
│       │   │   └── th
│       │   │       └── demo
│       │   │           └── applicationKey.html
│       │   ...
│       ...
...
Across Web ensures that any view name starting with th/ will look for a Thymeleaf template with .html file extension matching that view name in the root views folder.

Restart the application and when you point your browser to http://localhost:8080/applicationKey you should see the processed markup from the template.

The application key controller result.

Next step

The section on application configuration extends our sample controller to display values from the configuration resources.