在微服务架构中,需要我们对服务进行拆分,各个服务之间需要满足高内聚、低耦合。每个服务之间的改动不收影响。
如何进行拆分?
要了解服务如何拆分,我们要明白项目的启点和终点在哪。
起点: - 当前项目结构状态,是对已有的项目进行改进,还是需要从零开发的新项目。
终点: - 好的结构不是设计出来的,而是进化来的。 一直在进化中…
是否适合上微服务?
在一下业务形态上并不适合微服务结构:
- 系统中包含很多很强事务事务场景的。
- 业务相对稳定,迭代周期长的。
- 访问压力不大,可用性要求不高。
以点餐系统为例,我们根据业务形态进行服务拆分:商品服务和订单服务。
以代码为例更能清楚直观的了解微服务,可以点击右侧使用到的数据库表结构:商品和订单表
商品服务项目搭建
1、新建一个商品工程-product
2、注册配置:
spring:
application:
name: product
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
3、启动类注解添加:@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
启动项目,在注册中心查看是否被注册:
服务成功注册,再引入其依赖,这里使用ORM框架使用的是springData JPA,依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
数据库配置:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password:
url: jdbc:mysql://127.0.0.1/springcloud?characterEncoding=utf-8&useSSL=false
jpa:
show-sql: true
配置完成,启动项目进行测试。
后端开发
1、创建商品与数据库对应的实体类。
- @Data是lombok的注解,省去写get\set方法,@Entity和@Id注解是实体类与数据库表对应注解。
@Data
@Entity
public class ProductInfo {
@Id
private String productId;
private String productName;
private BigDecimal productPrice;
private Integer productStock;
private String productDescription;
private String productIcon;
private Integer productStatus;
private Integer categoryType;
private Date createTime;
private Date updateTime;
}
类目数据库实体类:
- 这里的id是自增的,需要添加注解 @GeneratedValue
@Data
@Entity
public class ProductCategory {
@Id
@GeneratedValue
private Integer categoryId;
private String categoryName;
private Integer categoryType;
private Date createTime;
private Date updateTime;
}
2、在respository包下新建一个ProductInfoRepository接口:查询所有在架商品
public interface ProductInfoRepository extends JpaRepository<ProductInfo,String>{
List<ProductInfo> findByProductStatus(Integer productStatus);
}
查询所有类目:
public interface ProductCategoryRepository extends JpaRepository<ProductCategory,Integer> {
List<ProductCategory> findBycategoryTypeIn(List<Integer> categoryTypeList);
}
3、Service层接口实现:
商品接口及实现
public interface ProductService {
/**
* 查询所有商品在架列表
* @return
*/
List<ProductInfo> findUpAll();
}
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductInfoRepository productInfoRepository;
@Override
public List<ProductInfo> findUpAll() {
return productInfoRepository.findByProductStatus(ProductStatusEnum.UP.getCode());
}
}
类目接口及实现
public interface CategoryService {
public List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
}
实现类
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private ProductCategoryRepository productCategoryRepository;
@Override
public List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList) {
return productCategoryRepository.findBycategoryTypeIn( categoryTypeList);
}
}
4、Controller层
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@Autowired
private CategoryService categoryService;
/**
*
* @return
*/
@RequestMapping("list")
public ResultVO<ProductVO> list () {
// 1、查询所有在架的商品
List<ProductInfo> productInfoList = productService.findUpAll();
// 2、 获取类目type列表
List<Integer> categoryTypeList = productInfoList.stream()
.map(ProductInfo::getCategoryType).collect(Collectors.toList());
// 3、从数据库查询类目
List<ProductCategory> productCategoryList = categoryService.findByCategoryTypeIn(categoryTypeList);
// 4、构造数据
List<ProductVO> productVOList = new ArrayList<>();
for (ProductCategory productCategory : productCategoryList) {
ProductVO productVO = new ProductVO();
productVO.setCategoryName(productCategory.getCategoryName());
productVO.setCategoryType(productCategory.getCategoryType());
List<ProductInfoVO> productInfoVOList = new ArrayList<>();
for (ProductInfo productInfo : productInfoList) {
if (productInfo.getCategoryType().equals(productCategory.getCategoryType())) {
ProductInfoVO productInfoVO = new ProductInfoVO();
BeanUtils.copyProperties(productInfo,productInfoVO);
productInfoVOList.add(productInfoVO);
}
}
productVO.setProductInfoVOList(productInfoVOList);
productVOList.add(productVO);
}
ResultVO resultVO = new ResultVO();
resultVO.setData(productVOList);
resultVO.setCode(0);
resultVO.setMessage("成功");
return resultVO;
}
}
5、在项目架构中视图层对象(VO)和持久层对象(PO)会区分开发,我们这里需要根据需要返回的数据定义商品和类目的实体类。
结果对象实体类:
/**
* HTTP 请求返回的最外层对象
* @param <T>
*/
@Data
public class ResultVO<T> {
/**
* 错误码
*/
private Integer code;
/**
* 提示信息
*/
private String message;
/**
* 具体内容
*/
private T data;
}
类目对象实体类
- 如果json返回的是name,后端使用categoryName更能清楚的表达语义,可以使用 @JsonProperty注解,这样返回的json数据就会转换为name属性。
@Data
public class ProductVO {
@JsonProperty("name")
private String categoryName;
@JsonProperty("type")
private Integer categoryType;
@JsonProperty("foods")
List<ProductInfoVO> productInfoVOList;
}
商品对象实体类
@Data
public class ProductInfoVO {
@JsonProperty("id")
private String productID;
@JsonProperty("name")
private String productName;
@JsonProperty("price")
private BigDecimal productPrice;
@JsonProperty("description")
private String productDescription;
@JsonProperty("icon")
private String productIcon;
}
6、这里再查询在架商品时,我们不是直接在程序中进行0,1硬编码,可以通过创建枚举来表示商品上下架状态。
/**
* 商品上下架装态
*/
@Getter
public enum ProductStatusEnum {
UP(0,"在架"),
DOWN(1,"下架"),
;
private Integer code;
private String message;
ProductStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}