何爲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