何为DDD
DDD全称为Domain-Driven Design,它是一种软件设计过程中的一套技术工作集,所以很多人称它为DDD-Lite,它包含了实体,值对象,领域,领域服务,领域事件,限界上下文等等诸多概念,就不在本文中给大家做介绍了,之后我会出一篇关于DDD介绍的文章。
何为CQRS
CQRS全称为Cammand-Query Responsibility Segregation,它是一种将紧缩对象(或组件)设计原则和命令-查询分离应用在架构模式中的结果。
在对象层面,这意味着:
1.如果一个方法修改了对象的状态,该方法便是一个命令(Cammand),它不应该返回数据,在Java中,这样的方法应该声明为void。
2.如果一个方法返回了数据,该方法便是一个查询(Query),此时它不应该通过直接的或者间接的手段修改对象状态,在Java中,这样的方法应该返回的数据类型声明。
上述的指导原则可谓非常的直接明了。下面是CQRS的示意图。
从上图我们可以看出CQRS框架的好处是在于查询与命令的分离,对于从资源库查询跨域多个聚合类型与实例数据时,能够有效的降低组织数据的复杂性。
比如在应用端存在一个这样的查询,在订单列表页面根据客户姓名查询订单并且分页和按照客户ID排序。
这是一个很常规的操作,如果不采用CQRS,这个查询操作需要同时跨越订单域和客户域进行组织数据,这样的查询操作代码显得非常的臃肿。伪代码如下
//1.通过客户姓名过滤客户对象
List<Customer> customers = customerService.findCustomersByCustomerName();
//2.统计客户ids
List<Interger> ids = customers.stream.map(c -> c.id).collect(collectos.toList());
//3.查询订单通过ids
page<Order> orderPage = orderRepository.findOrderByCustomerIds(ids,pageable);
从上述代码可以看出,随着条件的增加,聚合的增加,代码复杂度会越来越高。可读性也越来越差。
如果是采用CQRS的模式,此查询我们可以同时跨越订单域和客户域来组织数据,上述操作我们只要通过一条sql语句就可实现。
如果采用JPA+hibernate持久化数据方案,在查询数据时需要些原生sql或者hql的形式,这种形式代码的可读性,可维护性很差。但是JPA对于DDD的支持是非常良好的,在命令操作中,采用DDD远远比采用mybatis等方案要优雅的多。所以我们既希望在查询时可以像mybatis一样组织查询数据,也希望在命令操作时可以采用JPA的方式来持久化数据,发布领域事件等。
JPA+TkMybatis实现CQRS
maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
....
application.propertis配置
# database Config
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/jpa_batis?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = ***
spring.datasource.password = ***
# Spring Data JPA Config
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
# Mybatis Config
logging.level.com.orange.spring.jpabatis.mapper=debug
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.mapper-locations=classpath:mapping/*Mapper.xml
启动入口函数
@SpringBootApplication
//这里要导入tkmabatis的包,不要导错包了
@MapperScan(value = {"com.orange.spring.jpabatis.mapper"})
public class SpringJpabatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringJpabatisApplication.class, args);
}
}
接下来应用中订单模块可以同时有OrderRepository与OrderMapper两个仓储对象了,我们使用OrderRepository来操作对象持久化,OrderMapper来组织查询数据。
测试
@RunWith(SpringRunner.class)
@Transactional
@Rollback
@SpringBootTest
public class SpringJpabatisApplicationTests {
@Autowired
private CompanyRepository companyRepository;
@Autowired
private CompanyMapper companyMapper;
@Autowired
private EmployeeRepository employeeRepository;
@Test
public void test1() {
//jpa 查询
Optional<CompanyEntity> optionalCompanyEntity = companyRepository.findFirstByCompanyName("云集网络");
Assert.assertTrue(optionalCompanyEntity.isPresent());
//jpa保存
CompanyEntity companyEntity = new CompanyEntity();
companyEntity.setCompanyName("云影网络");
Set<EmployeeEntity> set = new HashSet<>();
EmployeeEntity employeeEntity1 = new EmployeeEntity();
employeeEntity1.setEmployeeNo("23425");
employeeEntity1.setEmployeeName("刘备");
EmployeeEntity employeeEntity2 = new EmployeeEntity();
employeeEntity2.setEmployeeNo("234256");
employeeEntity2.setEmployeeName("刘备");
set.add(employeeEntity1);
set.add(employeeEntity2);
companyEntity.setEmployeeEntitySet(set);
companyRepository.save(companyEntity);
//jpa查询所有
List<CompanyEntity> companyEntityList2 = companyRepository.findAll();
//tkMybatis查询
List<CompanyEntity> list1 = companyMapper.getCompanyList();
Assert.assertTrue(list1.size() > 0);
CompanyDO companyDO = companyMapper.selectByPrimaryKey(1L);
Assert.assertTrue(companyDO != null && companyDO.getCompanyName() != null);
//tKMybatis保存 CQRS模型中不会用TkMybatis做保存操作
CompanyDO company = new CompanyDO();
company.setCompanyName("云荣网络");
companyMapper.insert(company);
}
}
测试结果显示JPA与TkMybatis的所有操作均能正常使用,并且两者在同一个事物中。后续我会出一篇文章讲关于JPA与TkMybatis事物原理。
源码链接:https://github.com/dscxieyong/spring-jpa-mybatis.git