Spring Boot Custom Components
Tags: programming, architecture, java, gradle
Published: 2020-12-10 07:47:43

Spring Boot philosophy is to develop applications by composing **independent**, **modular** and **highly configurable** **components**. Spring Boot provides smart and simple mechanisms and conventions for building such components, that could be easily applied in custom projects.

In this post, we will explore those ideas, and show how to create **domain-oriented custom components** the Spring Boot way.

## What Is Spring Boot

[Spring Boot](https://spring.io/projects/spring-boot) takes an **opinionated view** of the [Spring platform](https://spring.io) and third-party libraries. This means, all necessary dependencies are included and pre-configured, and there is none or very little additional configuration most Spring Boot applications need to add.

Dependencies are consistent with each other and tested in integration.

For web applications, embedded servers (Tomcat, Jetty, Undertow) are included. Even the traditional deployment into an application container is possible, Spring Boot is self-contained as default, everything is included in the final JAR and no additional infrastructure is needed (except JDK, obviously).

In addition, Spring Boot comes with a lot of [production ready features](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html), such as loggers, metrics, auditing, tracing, and monitoring.

A simple application skeleton can be easily prototyped by [Spring Initializr](https://start.spring.io).

## Build Systems

Spring Boot offers [Gradle](https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle) and [Maven](https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle) plugins for packaging and running Spring Boot applications. It is also possible to create and publish Docker images via the plugins.

### Gradle
Spring Boot comes with `org.springframework.boot` and `io.spring.dependency-management` plugins:

```groovy
plugins {
  id 'org.springframework.boot' version '2.5.0'
  id 'io.spring.dependency-management' version '1.0.11.RELEASE'
}
```

Alternatively, only the dependency management could be used in isolation:

```groovy
plugins {
  id 'io.spring.dependency-management' version '1.0.11.RELEASE'
}
 
dependencyManagement {
  dependencies {
    dependency 'org.springframework:spring-core:5.3.0'
  }
}
```

### Maven

Typically, your Maven POM file inherits from the `spring-boot-starter-parent` project:

```xml
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.5.0</version>
</parent>
```

The parent POM brings the Spring Boot plugin, dependency management and other sensible defaults. All default settings can be overwritten in your POM.

Alternatively, only dependency management can be used without the parent POM by using an import scoped dependency:

```xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.5.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
```

## Features Overview

Spring Boot introduces several features new to the Spring ecosystem.

### Spring Boot Application

> The `SpringApplication` class provides a convenient way to bootstrap a Spring application that is started from a `main()` method.

When the annotation `@SpringBootApplication` is included on the main class, the application is bootstrapped, auto-configuration enabled and Spring components scan is set to the current package as root.

It is recommended to locate the application class in a root package above other classes. So the package defines the search base for the components scan.

```
org.example.myshop
+- MyShopApplication
+- catalog
+- delivery
+- order
...
```

### Starters

> Starters are a set of convenient dependency descriptors that you can include in your application.

Starters bring all necessary dependencies and configurations into the application:

```groovy
dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
}
```

All starters have the core starter `spring-boot-starter` a direct dependency, which includes auto-configuration support, logging and YAML.

Starters are part of Spring Boot philosophy, a convenient way to include components into your application.

### Auto-Configuration

> Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies that you have added.

When auto-configuration is enabled, components found on the classpath are automatically loaded and configured.

Most standard Spring auto-configurations are highly [configurable via properties](https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html).

### Application Properties

Spring Boot automatically finds and loads `application[-profile].(properties|yaml|yml)` from the classpath and other default [locations](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-files).

Properties from the loaded files are added into Spring environment.

Application properties form a configuration structure of a Spring Boot application with default values, which are meant to be overwritten in runtime from the environment or other [external sources](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config).

Configuration properties are grouped into trees by the context, typically prefixed by the component name and feature:



```yaml
# application.yml
 
spring:
  datasource:
    url: jdbc:postgresql://db:5432/test
    username: admin
    password: secret
```

## Custom Components

Features of Spring Boot can serve as a conventional template for custom components. To do things right, a deeper knowledge of Spring Boot concepts and mechanisms is needed.

In the following section, we take a look at techniques for creating a modular application with independent domain-driven components. The components can be assembled into a single monolithic application or separate applications as microservices afterwards.

For each component a Spring Boot starter with auto-configuration will be created.

Assembling of components into an application is achieved simply by adding the component onto the classpath. Practically, by putting the component starter into the application dependencies.

Spring Boot **components are logically defined by auto-configurations**, starters are an additional mechanism to bring components into the application conveniently.

### Code Structure

We will use Java packages for structuring code by the domain and Maven modules or Gradle sub-projects for technical cuts.

Every component is a **self-contained business capability service**, exposing multiple artifacts for API and implementation, formed by physical modules.

A typical component source code structure looks like:

```
delivery/	-- comp. root
+- domain/	-- domain API
+- events/	-- events API
+- jdbc/	-- JDBC impl
+- rest/	-- Restful API
+- spring-boot-starter/ -- starter
+- pom.xml
+- build.gradle
\- settings.gradle
```

Alternatively, auto-configurations can live in a separate module, which the starter includes as its dependency.

In a single-application scenario a separate module for a Spring Boot application is created on the root level:

```
myshop/
+- application/
+- catalog/
+- delivery/
+- order/
...
```

In a microservices scenario, each component has its own application module:

```
myshop/
+- catalog/
|  +- application/
|  +- domain/
|  +- jdbc/
|  +- rest/
|  \- spring-boot-starter/
+- delivery/
|  +- application/
|  +- domain/
|  +- events/
|  +- jdbc/
|  +- rest/
|  \- spring-boot-starter/
+- order/
|  +- application/
|  +- domain/
|  +- events/
|  +- jdbc/
|  +- rest/
|  \- spring-boot-starter/
...
```

For both scenarios, the separation of concerns is the structure driver. Every module has its own unique role in the final application.

Starters bring Spring Boot auto-configuration, applications bring Spring Boot application main classes, but all other modules has no Spring Boot concerns or dependencies.

For example, even if the REST module is built upon Spring Web, only the corresponding Spring framework dependency should be included:

```groovy
// rest/build.gradle
 
implementation 'org.springframework:spring-web'
```

The Spring Boot Web starter will be included into the starter module only:

```groovy
// spring-boot-starter/build.gradle
 
implementation 'org.springframework.boot:spring-boot-starter-web'
```

### Java Packages

As we build **domain-oriented** components, packages should be domain-driven as well.

All the modules of a component should share a root package. The modules could be further structured by the domain feature or a technical aspect. Same features should be included in an identical package.

This strategy enables information hiding of implementation classes using Java package accessibility. Consider an example, where `○` and `●` mean *public* and *package-protected*, respectively:

```
delivery/
+- domain/
|  \- src/main/java/
|     \- org.example.myshop.delivery
|        \- ○DeliveryService.java
+- events/
|  \- src/main/java/
|     \- org.example.myshop.delivery
|        \- ○DeliveryDispatched.java
+- jdbc/
|  \- src/main/java/
|     \- org.example.myshop.delivery.jdbc
|        \- ●DeliveryServiceJdbc.java
+- rest/
|  \- src/main/java/
|     \- org.example.myshop.delivery.rest
|        \- ●DeliveryRestController.java
\- spring-boot-starter/
   \- src/main/java/
      \- org.example.myshop.delivery
         \- jdbc
            \- ●DeliveryJdbcConfig.java
         \- rest
            \- ●DeliveryRestConfig.java
```

Classes `DeliveryServiceJdbc` and `DeliveryRestController` are in the same package as `DeliveryJdbcConfig` and `DeliveryRestConfig`, respectively. This makes them accessible to the configuration classes, which is the only one place they have to be accessible from, and hidden for the rest of the world.

This kind of protection based on the basic language features is a great asset to the overall modularity, preventing the temptation to access implementation details of a foreign component, and violate its sovereignty so.

### Custom Spring Boot Starter

A typical Spring Boot starter contains auto-configuration code and declared dependencies, and it’s extensible via configuration properties in a dedicated namespace (prefix).

By convention, the name of a starter starts with the component name followed by `-spring-boot-starter` suffix:

```
org.example.myshop:delivery-spring-boot-starter
```

#### Auto-Configurations

The entry point to a Spring Boot starter is the `META-INF/spring.factories` file. Spring Boot checks for the presence of the file within your published JAR. The file should list component auto-configuration classes under the `EnableAutoConfiguration` key:

```properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.example.myshop.delivery.DeliveryConfiguration
```

Auto-configurations must be loaded that way only. Make sure that they are defined in a specific package space and that they are never the target of component scanning.

Under the hood, auto-configuration is implemented with standard `@Configuration` classes. Multiple configuration classes could be composed via the `@Import` annotation:

```java
@Configuration
@Import(JdbcDeliveryConfiguration.class)
class DeliveryConfiguration {
  ...
}
```

The auto-configuration creates and registers all necessary Spring beans for the particular component.

A component starter is the only place configurations should exist. Other modules serve different purposes.

#### Dependencies

A Spring Boot starter contains all dependencies required by the component as whole.

If, for example, a component has a module with Spring Web restful controllers, the starter should contain the corresponding Spring Boot Starter for web:

```groovy
implementation 'org.springframework.boot:spring-boot-starter-web'
```

The minimal dependency every Spring Boot starter must include is Spring Boot core starter:

```groovy
implementation 'org.springframework.boot:spring-boot-starter'
```

The component starters are then added as dependencies into the application module:

```groovy
implementation 'org.example.myshop:catalog-spring-boot-starter'
implementation 'org.example.myshop:delivery-spring-boot-starter'
implementation 'org.example.myshop:order-spring-boot-starter'
...
```

### Configuration Properties

> Spring Boot provides an alternative method of working with properties that lets strongly typed beans govern and validate the configuration of your application.

```java
@ConfigurationProperties(
    prefix = "myshop.delivery")
@Setter
@Getter
class DeliveryProperties {
 
  private String cargoName;
  private String dateFormat;
}
```

Configuration properties are meant to be a convenient way for initializing auto-configuration:

```java
@Configuration
@EnableConfigurationProperties(
DeliveryProperties.class)
class DeliveryConfiguration {
 
  @Bean
  DeliveryService deliveryService(
      DeliveryProperties properties
  ) {
    return new DeliveryServiceImpl(
      properties.getCargoName(),
      properties.getDateFormat()
    );
  }
}
```

The Spring Boot application defines the configuration structure with default values:

```yaml
# application.yml
 
myshop:
  delivery:
    cargo-name: PPL
    date-format: yyyy-mm-dd
  order:
    prefix-id: OrderID
```

Defaults can be overwritten in runtime, for example via environment variables:

```properties
MYSHOP_DELIVERY_CARGO_NAME=DHL
```

## Conclusion

Spring Boot is a great tool for developing **modular monolithic** and **microservices** applications.

Auto-configurations provide a convenient mechanism for creating independent components in isolation.

Spring Boot starters contain configurations and dependencies for components and define configuration structure via configuration properties with dedicated namespaces.

A Spring Boot application assemblies the components and provides cross-cutting concerns in addition.

## Example

An example code of a rich modular Spring Boot application can be found on [my GitHub](https://github.com/ttulka/ddd-example-ecommerce-microservices).

## Links

- [Spring Boot Reference Documentation](https://docs.spring.io/spring-boot/docs/current/reference/html)
- [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle)
- [Spring Boot Maven Plugin Documentation](https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle)
- [Getting Started With Spring Boot](https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html)
- [Using Spring Boot](https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html)
- [Spring Boot How-to Guides](https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html)
- [Creating Your Own Auto-configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-developing-auto-configuration)
- [Spring Initializr](https://start.spring.io)

---

Originally published on [my blog](https://blog.ttulka.com/spring-boot-custom-components).