实施最佳实践:Spring Boot微服务API

本文详细介绍了技术架构中的各个组件,包括实体、仓库、服务、映射器和控制器的设计原则,以及如何在Java中实现这些组件,强调了DDD和MVC架构模式,并提到了微服务架构的运用和Strangler模式的长线考虑。
摘要由CSDN通过智能技术生成

技术架构
首先,让我们转向架构,这将详细解释。让我们详细看一下每一层。
在这里插入图片描述
我来详细解释一下架构。这些组件通常与遵循领域驱动设计原则的应用程序架构相关联(DDD)和模型-视图-控制器(手动音量调节)或类似的架构模式。让我逐一介绍:

实体
实体代表应用程序中的核心业务对象或概念。它们封装了与业务领域相关的数据。例如,在雇员管理系统中,雇员实体可能具有与雇员相关的姓名、电子邮件和工资等属性。

仓库
存储库负责处理数据访问逻辑。它们提供了对数据存储的抽象,允许应用程序与数据进行交互,而无需担心底层的存储细节。例如,EmployeeRepository将处理像存储、检索、更新和删除数据库中的员工记录这样的操作。

服务
服务包含不适合实体方法的业务逻辑。它们编排实体和存储库之间的交互以满足更高层次的用例。例如,EmployeeService可能具有计算奖金、处理员工调动或处理涉及多个实体的复杂业务规则的方法。

地图绘制者
映射器负责在应用程序的不同层之间转换数据。它们将数据从数据库实体转换为域对象,反之亦然。例如,EmployeeMapper可能将Employee实体转换为可以通过网络发送或由表示层使用的数据传输对象(EmployeeRequest)。

控制器
控制器处理来自用户界面或外部系统的请求。它们解释用户输入,调用必要的服务或业务逻辑,并准备发回的响应。在web应用程序中,控制器接收HTTP请求,提取必要的数据,并将请求委托给适当的服务。然后它格式化服务响应并将其发送回客户端。

前端:您可以选择构建Android和iOS等原生应用程序。桌面浏览器应用程序或移动浏览器应用程序可以使用React或Angular框架构建。

架构实施的最佳实践
实体:有用的提示
在功能名称下将包命名为“实体”
将id设置为Long并将生成类型设置为identity
用复数形式命名类和表,如users
对构造函数和getter/setter代码使用Lombok
每个字符串字段都有长度
将nullable设置为true/false
使用对其他表的引用,如@ManyToOne。请记住,创建的表是自动的,您在实体中写的内容很重要。
使用@OneToMany如果希望在一次调用中保存多个表中的值,请选择双向。
使用@ManyToMany连接表。如果连接表中存在除连接id列之外的字段,则创建一个单独的连接类。
确定is-a关系的正确继承类型。根据每个类中字段的数量在单个表、类表和具体表继承之间进行选择。
例子

package org.project.feature.entities;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class Users {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 100, nullable = false)
private String firstName;
@Column(length = 100, nullable = false)
private String lastName;
@Column(length = 100, nullable = false, unique = true)
private String email;
 }

知识库:有用的提示
在特性名称下将包命名为“存储库”
扩展JpaRepository实体名称和id一样长
尽可能将样式用作查询实体的方法,如findByEmail
对数据库的多个条目使用批处理操作,例如saveAll
尽可能使用可选的返回类型

package org.project.feature.repositories;
 
 import org.project.feature.entities.Users;
 import org.springframework.data.jpa.repository.JpaRepository;
 
 public interface UsersRepository extends JpaRepository<Users, Long> {
 
 }

服务:有用的提示
在特性名称下将包命名为“服务”
为服务中的所有操作创建一个接口,并创建一个实现类
使用@AllArgsConstructor为@Autowired注释
接受请求对象并从模型的包中返回响应对象。
如果需要调用多个存储库,除非您希望启动一个新的事务,否则应该在一个事务中调用。
如果您希望调用多个服务,那么特定的服务必须被命名为聚合服务,并且在一个事务中。
不要从服务返回ResponseEntity这是控制器层的工作。

package org.project.feature.services;
 
 import org.project.feature.models.UsersRequest;
 import org.project.feature.models.UsersResponse;
 
 import java.util.List;
 
 public interface UsersService {
     UsersResponse createUser(UsersRequest usersRequest);
 
     UsersResponse getUserById(Long userId);
 
     List<UsersResponse> getAllUsers();
 
     UsersResponse updateUser(Long id, UsersRequest users);
 
     void deleteUser(Long userId);
 }
package org.project.feature.services.impl;
 
 import lombok.AllArgsConstructor;
 import org.project.feature.entities.Users;
 import org.project.feature.mappers.UsersMapper;
 import org.project.feature.models.UsersRequest;
 import org.project.feature.models.UsersResponse;
 import org.project.feature.repositories.UsersRepository;
 import org.project.feature.services.UsersService;
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 
 @Service
 @AllArgsConstructor
 public class UsersServiceImpl implements UsersService {
 
     private final UsersRepository usersRepository;
 
     private final UsersMapper usersMapper;
     @Override
     public UsersResponse createUser(UsersRequest usersRequest) {
         Users users = usersMapper.convertRequestToEntity(usersRequest);
         Users saved = usersRepository.save(users);
         return usersMapper.convertEntityToResponse(saved);
     }
 
     @Override
     public UsersResponse getUserById(Long userId) {
         Optional<Users> optionalUser = usersRepository.findById(userId);
         return usersMapper.convertEntityToResponse(optionalUser.get());
     }
 
     @Override
     public List<UsersResponse> getAllUsers() {
         List<Users> users = usersRepository.findAll();
         List<UsersResponse> usersResponses = new ArrayList<>();
         for (Users user : users)
             usersResponses.add(usersMapper.convertEntityToResponse(user));
         return usersResponses;
     }
 
     @Override
     public UsersResponse updateUser(Long id, UsersRequest usersRequest) {
         Optional<Users> user = usersRepository.findById(id);
         Users existingUsers = user.orElse(null);
         existingUsers.setFirstName(usersRequest.getFirstName());
         existingUsers.setLastName(usersRequest.getLastName());
         existingUsers.setEmail(usersRequest.getEmail());
         Users updatedUsers = usersRepository.save(existingUsers);
         return usersMapper.convertEntityToResponse(updatedUsers);
     }
 
     @Override
     public void deleteUser(Long userId) {
         usersRepository.deleteById(userId);
     }
 }

地图绘制者:有用的提示
在特性名称下将包命名为“mappers”
创建一个名为制图人使用泛型将实体转换为模型,反之亦然
不要在控制器层中将实体用作返回对象

package org.project.feature.mappers;
 
 import org.project.feature.entities.Users;
 import org.project.feature.models.UsersRequest;
 import org.project.feature.models.UsersResponse;
 import org.springframework.stereotype.Component;
 
 @Component
 public class UsersMapper {
 
     public UsersResponse convertEntityToResponse(Users users) {
         UsersResponse usersResponse = new UsersResponse();
         usersResponse.setId(users.getId());
         usersResponse.setFirstName(users.getFirstName());
         usersResponse.setLastName(users.getLastName());
         usersResponse.setEmail(users.getEmail());
         return usersResponse;
     }
 
     public Users convertRequestToEntity(UsersRequest usersRequest) {
         Users users = new Users();
         users.setFirstName(usersRequest.getFirstName());
         users.setLastName(usersRequest.getLastName());
         users.setEmail(usersRequest.getEmail());
         return users;
     }
 
 }

模型:有用的提示
在特性名称下将包命名为“models”
所有请求和响应对象都将存储在这里
使用@Data模型类的注释
模型应该充当API的前端,服务应该将模型转换为实体以与存储库进行对话。

package org.project.feature.models;
 
 import jakarta.persistence.Column;
 import lombok.Data;
 
 import java.io.Serializable;
 
 @Data
 public class UsersRequest implements Serializable {
     private String firstName;
     private String lastName;
     private String email;
 }
package org.project.feature.models;
 
 import jakarta.persistence.Column;
 import lombok.Data;
 
 import java.io.Serializable;
 
 @Data
 public class UsersResponse implements Serializable {
     private Long id;
     private String firstName;
     private String lastName;
     private String email;
 }

控制器:有用的提示
在特性名称下将包命名为“控制器”
尝试在有限的上下文中为每个资源创建一个API
使用复数形式的资源名称,如/API/users
对于积垢操作:
使用HTTP POST创建以请求为主体的操作
使用HTTP PUT进行更新操作
使用HTTP GET检索所有记录
使用HTTP GET with /{id}进行带有标识符的检索
使用带有/{id}的HTTP DELETE删除记录
对于操作other,CRUD尽量避免使用动词
在控制器层实现错误处理
用@Valid在控制器层实现验证
认识到API思维和RPC思维的区别。这是理解API的关键。

package org.project.feature.controllers;
 
 import lombok.AllArgsConstructor;
 import org.project.feature.models.UsersRequest;
 import org.project.feature.models.UsersResponse;
 import org.project.feature.services.UsersService;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
 @RestController
 @AllArgsConstructor
 @RequestMapping("/api/users")
 public class UsersController {
 
     private final UsersService usersService;
 
     @PostMapping
     public ResponseEntity<UsersResponse> createUser(@RequestBody UsersRequest usersRequest){
         UsersResponse savedUsers = usersService.createUser(usersRequest);
         return new ResponseEntity<>(savedUsers, HttpStatus.CREATED);
     }
 
     @GetMapping("{id}")
     public ResponseEntity<UsersResponse> getUserById(@PathVariable("id") Long userId){
         UsersResponse users = usersService.getUserById(userId);
         return new ResponseEntity<>(users, HttpStatus.OK);
     }
 
     @GetMapping
     public ResponseEntity<List<UsersResponse>> getAllUsers(){
         List<UsersResponse> users = usersService.getAllUsers();
         return new ResponseEntity<>(users, HttpStatus.OK);
     }
 
     @PutMapping("{id}")
     public ResponseEntity<UsersResponse> updateUser(@PathVariable("id") Long userId,
                                             @RequestBody UsersRequest usersRequest){
         UsersResponse updatedUsers = usersService.updateUser(userId, usersRequest);
         return new ResponseEntity<>(updatedUsers, HttpStatus.OK);
     }
 
     @DeleteMapping("{id}")
     public ResponseEntity<String> deleteUser(@PathVariable("id") Long userId){
         usersService.deleteUser(userId);
         return new ResponseEntity<>("User successfully deleted!", HttpStatus.OK);
     }
 }

结论
从长远来看,当代码库变得庞大时,您可能需要使用Strangler模式取出一些服务,并将其作为单独的微服务进行部署。这种编码结构会有所帮助。如果你从一开始就掌握了基本知识,那么以后的旅程将会很顺利。

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值