DEV Community

Anh Trần Tuấn
Anh Trần Tuấn

Posted on • Originally published at tuanh.net on

7 Essential Techniques for Implementing Clean Architecture with Spring Boot

1. Understanding Clean Architecture

Image

Clean Architecture, introduced by Robert C. Martin, also known as Uncle Bob, is an architectural pattern designed to ensure that the core business logic of an application remains independent of external frameworks, databases, and user interfaces. This independence is crucial for maintaining the application over time, as it allows you to modify or replace external components without affecting the core logic.

1.1 The Principles of Clean Architecture

  • Independence of Frameworks : The architecture should be independent of any framework, meaning the business logic should not depend on framework-specific features.
  • Testability : Business rules can be tested without external dependencies.
  • Independence of UI : The user interface can be changed without affecting the business rules.
  • Independence of Database : The database should be an implementation detail, not a driver of business logic.
  • Independence of any External Agency : The application should not depend on any external systems or services.

1.2 The Layers of Clean Architecture

Image

Clean Architecture is typically structured into four concentric layers:

  • Entities : These are the core business objects, which contain the business logic and rules.
  • Use Cases : These contain the application-specific business rules and orchestrate the flow of data to and from entities.
  • Interface Adapters : This layer converts data from the format most convenient for the use cases and entities into the format most convenient for frameworks, external APIs, or databases.
  • Frameworks and Drivers : This is the outermost layer, containing frameworks, databases, and other external services.

1.3 Key Benefits of Clean Architecture

  • Maintainability : The separation of concerns ensures that changes in one part of the system do not affect others.
  • Testability : The core logic can be tested in isolation from other parts of the system.
  • Flexibility : The architecture allows for easy replacement or modification of external components.

1.4 Why Use Clean Architecture in Spring Boot?

Spring Boot, with its convention-over-configuration philosophy, is often used for rapid application development. However, without a solid architecture like Clean Architecture, Spring Boot applications can become difficult to maintain as they grow. Clean Architecture provides a structured approach to building Spring Boot applications that are easy to maintain and scale.

2. Implementing Clean Architecture in Spring Boot

Now that we understand the principles behind Clean Architecture, let's explore how to implement it in a Spring Boot application.

2.1 Setting Up the Project Structure

To follow Clean Architecture, you need to set up your Spring Boot project in a way that reflects the architectural layers. Here's how you can structure your project:

src
└── main
    ├── java
    │ ├── com.example.app
    │ │ ├── domain
    │ │ │ ├── entity
    │ │ │ └── usecase
    │ │ ├── application
    │ │ ├── infrastructure
    │ │ │ ├── controller
    │ │ │ ├── repository
    │ │ │ └── config
    │ │ └── adapter
    │ │ ├── controller
    │ │ └── repository
    └── resources
Enter fullscreen mode Exit fullscreen mode

2.2 Building the Core Business Logic

Let's assume we're building a simple e-commerce application. We'll start by defining our core business logic in the entity and usecase packages.

// src/main/java/com/example/app/domain/entity/Product.java

package com.example.app.domain.entity;

public class Product {
    private Long id;
    private String name;
    private Double price;

    // Getters and Setters
}
Enter fullscreen mode Exit fullscreen mode

// src/main/java/com/example/app/domain/usecase/ProductService.java

package com.example.app.domain.usecase;

import com.example.app.domain.entity.Product;
import java.util.List;

public interface ProductService {
    List<Product> getAllProducts();
    Product getProductById(Long id);
    void saveProduct(Product product);
}
Enter fullscreen mode Exit fullscreen mode

2.3 Creating Interface Adapters

In the adapter layer, you will create the implementations of your use cases. This layer adapts the core logic to the external world, such as REST controllers or database repositories.

// src/main/java/com/example/app/adapter/controller/ProductController.java

package com.example.app.adapter.controller;

import com.example.app.domain.entity.Product;
import com.example.app.domain.usecase.ProductService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping("/products")
    public List<Product> getAllProducts() {
        return productService.getAllProducts();
    }

    @GetMapping("/products/{id}")
    public Product getProductById(@PathVariable Long id) {
        return productService.getProductById(id);
    }

    @PostMapping("/products")
    public void saveProduct(@RequestBody Product product) {
        productService.saveProduct(product);
    }
}
Enter fullscreen mode Exit fullscreen mode

2.4 Implementing Repositories

Finally, we need to implement the repositories in the infrastructure layer, which interacts with the database.

// src/main/java/com/example/app/infrastructure/repository/ProductRepository.java

package com.example.app.infrastructure.repository;

import com.example.app.domain.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, Long> {
}
Enter fullscreen mode Exit fullscreen mode

2.5 Wiring It All Together

With the layers in place, we now need to wire them together. This is typically done in the config package of the infrastructure layer.

// src/main/java/com/example/app/infrastructure/config/BeanConfiguration.java

package com.example.app.infrastructure.config;

import com.example.app.domain.usecase.ProductService;
import com.example.app.domain.usecase.impl.ProductServiceImpl;
import com.example.app.infrastructure.repository.ProductRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfiguration {

    @Bean
    public ProductService productService(ProductRepository productRepository) {
        return new ProductServiceImpl(productRepository);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Testing the Clean Architecture in Action

Let's test the application to ensure everything is working as expected.

With everything in place, start your Spring Boot application and test the endpoints using a tool like Postman.

GET /products : Returns a list of all products.

GET /products/{id} : Returns a specific product by ID.

POST /products : Saves a new product to the database.

The demo illustrates how Clean Architecture ensures that our core business logic is completely decoupled from the external world. This makes it easier to maintain and test the application, and it allows for flexibility in swapping out external components.

4. Conclusion

Implementing Clean Architecture in a Spring Boot application is a powerful way to create a maintainable, scalable, and testable codebase. By adhering to the principles of Clean Architecture, you can ensure that your application is well-structured and adaptable to change. If you have any questions or need further clarification, feel free to leave a comment below!

Read posts more at : 7 Essential Techniques for Implementing Clean Architecture with Spring Boot

Top comments (0)