Backend Development 18 min read

Layered Architecture Design for SpringBoot Projects: Nine-Layer Structure and Detailed Implementation

This article explains the concept of layered architecture in Java backend projects, outlines five design dimensions, presents a nine‑layer SpringBoot module structure, and provides concrete code examples for each layer—including util, infrastructure, domain, service, integration, facade, client, controller, and boot—demonstrating how to achieve clean separation of concerns and maintainable code.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Layered Architecture Design for SpringBoot Projects: Nine-Layer Structure and Detailed Implementation

In the computer field, any problem can be solved by adding a virtual layer, which highlights the importance of layered thinking; the same principle applies to Java engineering architecture.

The advantages of layering are that each layer focuses on its own responsibilities, similar to the Single Responsibility Principle, while the drawback is the added communication cost between layers that must be handled by adapters.

Five dimensions for designing layers are introduced: (1) Single – each layer handles one type of work; (2) Noise reduction – only necessary information is passed down; (3) Adaptation – each layer needs an adapter to translate information; (4) Business – business objects can be aggregated, e.g., using a rich model; (5) Data – data objects should remain pure and avoid aggregating business logic.

The article proposes a nine‑layer SpringBoot structure: util, integration, infrastructure, service, domain, facade, controller, client, and boot.

Util layer holds utility code without depending on other modules:

user-demo-service-util
    -/src/main/java
        -date
            -DateUtil.java
        -json
            -JSONUtil.java
        -validate
            -BizValidator.java

Infrastructure layer is responsible for data access; entities reside here and it only depends on the util module:

user-demo-service-infrastructure
    -/src/main/java
        -player
            -entity
                -PlayerEntity.java
            -mapper
                -PlayerEntityMapper.java
        -game
            -entity
                -GameEntity.java
            -mapper
                -GameEntityMapper.java
    -/src/main/resources
        -mybatis
            -sqlmappers
                -gameEntityMapper.xml
                -playerEntityMapper.xml

SQL for creating the player table demonstrates keeping data objects pure:

CREATE TABLE `player` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `player_id` varchar(256) NOT NULL COMMENT '运动员编号',
  `player_name` varchar(256) NOT NULL COMMENT '运动员名称',
  `height` int(11) NOT NULL COMMENT '身高',
  `weight` int(11) NOT NULL COMMENT '体重',
  `game_performance` text COMMENT '最近一场比赛表现',
  `creator` varchar(256) NOT NULL COMMENT '创建人',
  `updator` varchar(256) NOT NULL COMMENT '修改人',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

The corresponding Java entity mirrors the table structure, keeping the `gamePerformance` field as a plain string to avoid business logic in the data layer:

public class PlayerEntity {
    private Long id;
    private String playerId;
    private String playerName;
    private Integer height;
    private Integer weight;
    private String creator;
    private String updator;
    private Date createTime;
    private Date updateTime;
    private String gamePerformance;
}

Mapper interfaces provide CRUD operations:

@Repository
public interface PlayerEntityMapper {
    int insert(PlayerEntity record);
    int updateById(PlayerEntity record);
    PlayerEntity selectById(@Param("playerId") String playerId);
}

Domain layer follows DDD concepts, distinguishing domain objects from data objects and business objects. Example domain classes illustrate rich models and validation logic:

public class PlayerCreateDomain implements BizValidator {
    private String playerName;
    private Integer height;
    private Integer weight;
    private GamePerformanceVO gamePerformance;
    private MaintainCreateVO maintainInfo;
    @Override
    public void validate() {
        if (StringUtils.isEmpty(playerName)) {
            throw new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT);
        }
        // additional validation ...
    }
}

The service layer depends on util, domain, integration, and infrastructure modules and contains business logic such as updating a player:

public class PlayerService {
    @Resource private PlayerEntityMapper playerEntityMapper;
    @Resource private PlayerMessageSender playerMessageSender;
    @Resource private PlayerServiceAdapter playerServiceAdapter;
    public boolean updatePlayer(PlayerUpdateDomain player) {
        AssertUtil.notNull(player, new BizException(ErrorCodeBizEnum.ILLEGAL_ARGUMENT));
        player.validate();
        PlayerEntity entity = playerServiceAdapter.convertUpdate(player);
        playerEntityMapper.updateById(entity);
        playerMessageSender.sendPlayerUpdatemessage(player);
        return true;
    }
}

The integration layer translates external DTOs into internal domain objects, illustrated by a user‑client example:

public class UserClientAdapter {
    public UserInfoDomain convertUserDomain(UserInfoClientDTO userInfo) {
        UserInfoDomain userDomain = new UserInfoDomain();
        UserContactVO contactVO = new UserContactVO();
        contactVO.setMobile(userInfo.getMobile());
        userDomain.setContactInfo(contactVO);
        UserAddressVO addressVO = new UserAddressVO();
        addressVO.setCityCode(userInfo.getCityCode());
        addressVO.setAddressDetail(userInfo.getAddressDetail());
        userDomain.setAddressInfo(addressVO);
        return userDomain;
    }
}

The facade and client layers provide a clean external API while hiding internal complexity. The client contains DTOs, and the facade adapts domain results to these DTOs:

public class PlayerFacadeAdapter {
    public PlayerQueryResultDTO convertQuery(PlayerQueryResultDomain domain) {
        if (domain == null) return null;
        PlayerQueryResultDTO result = new PlayerQueryResultDTO();
        result.setPlayerId(domain.getPlayerId());
        result.setPlayerName(domain.getPlayerName());
        result.setHeight(domain.getHeight());
        result.setWeight(domain.getWeight());
        if (domain.getGamePerformance() != null) {
            result.setGamePerformanceDTO(convertGamePerformance(domain.getGamePerformance()));
        }
        return result;
    }
}

The controller layer exposes HTTP endpoints, delegating to the facade services and handling request headers for authentication:

@RestController
@RequestMapping("/player")
public class PlayerController {
    @Resource private PlayerClientService playerClientService;
    @PostMapping("/add")
    public ResultDTO
add(@RequestHeader("test-login-info") String loginUserId,
                                 @RequestBody PlayerCreateDTO dto) {
        dto.setCreator(loginUserId);
        return playerClientService.addPlayer(dto);
    }
    // update and query endpoints omitted for brevity
}

The boot module is the entry point, scanning mappers and launching the Spring application:

@MapperScan("com.user.demo.service.infrastructure.*.mapper")
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

Finally, the article revisits the five design dimensions—single responsibility, noise reduction, adaptation, business aggregation, and data purity—emphasizing their role in building maintainable, scalable backend systems.

backendJavamicroservicesDDDlayered architectureSpringBoot
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.