写点什么

原创 | 使用 JPA 实现 DDD 持久化 - 只要 O,忘记 R & Maven 配置

发布于: 2020 年 11 月 25 日
原创 | 使用JPA实现DDD持久化-只要O,忘记R & Maven配置



第七节 只要 O,忘记 R

面向对象和面向关系是两个不同的范式。要同时面向两种不同的范式并且在两者之间频繁切换,对开发者来说是一种既筋疲力尽又容易出错的体验。通过JPA这样的ORM规范,我们期待达到的效果是:对于一般开发者来说:

只要 O,不要 R

就是说:对于大多数开发者,在大多数时候,只需要掌握和处理对象模型,不需要处理数据库和进行SQL访问。

我们仍然可以保留“仓储(Repository)”的概念,但是我们不再是根据列值来查询数据行,而是根据属性值来查询领域对象。


第一节 Maven 配置

遵循良好的工程实践,为了实现业务代码和技术代码的分离,我将范例项目按多模块Maven项目的方式来构建:

  • tmall-domain模块:纯粹的业务层代码,与技术实现(Hibernate、Spring、数据库等)无关。

  • tmall-persistence-jpa模块:使用原生JPA API实现tmall-domain模块中定义的仓储接口。

  • tmall-persistence-spring-data-jpa模块:使用Spring Data JPA实现tmall-domain模块中定义的仓储接口。

本项目的代码可以在github网站https://github.com/dayatang/jpa-sample-tmall下载。

一、外层伞项目

最外层的“伞”项目包含以上三个子模块,并定义通用的Maven配置(主要是统一配置依赖项及其版本),供三个子模块继承。各个子模块通过继承这个伞项目的Maven配置,一方面简化了自身的配置项,另一方面也为子模块定义了依赖项的统一版本号。

伞项目的pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>yang.yu.tmall</groupId> <artifactId>jpa-sample-tmall</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <name>jpa-sample-tmall</name> <url>http://tmall.yyang.io</url> <modules> <module>tmall-domain</module> <module>tmall-persistence-jpa</module> <module>tmall-persistence-spring-data-jpa</module> </modules>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.4.23.Final</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>2.4.1</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.200</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.5.15</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.17.2</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.2.0</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.2.0</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.9.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.1.1</version> </plugin> </plugins> </pluginManagement> </build></project>
复制代码

pom.xml中,通过dependencyManagement节点,统一指定了各个子项目共同依赖项的版本号;通过build节点下的pluginManagement节点,统一指定了构建中需要用到的Maven插件的版本号。继承伞项目的子模块使用到这些依赖项和插件的时候,不再需要指定它们的版本号。

二、tmall-domain 模块

本模块实现纯粹的业务逻辑,是软件中的领域层(Domain Layer)。主要包含需要持久化的实体和值对象,以及各个实体所对应的仓储接口(值对象的生命周期从属于实体,因此不需要自己的仓储接口)。

仓储(Repository接口旧称数据访问对象DAO)接口,用于从数据库中存取和查询实体。根据Evans领域驱动设计(DDD思想,仓储和实体、值对象、领域服务一样,属于领域对象的一种,它的存在意义主要是:通过隔离掉领域层对数据库和数据访问技术的概念依赖,实现了依赖倒置:领域层不依赖于数据访问层(例如后面两个模块),相反,数据访问层依赖于领域层,因为数据访问层需要实现领域层中的仓储接口。

因为仓储和实体的都是领域对象,两者地位等同,甚至可能相互引用(实体可能使用仓储来完成自己的某些功能),因此我将仓储接口和实体、值对象放到同一个包中,而不是将所有的仓储接口放到一个专门的包中,否则可能导致两个包之间的双向依赖或循环依赖。这是“基于功能而不是基于类型划分包”原则的应用。

基于“仓储可视为实体的集合”这个理念,我们将用实体类名(例如Order)的复数形式作为该实体对应的仓储接口的名字(例如Orders)。

tmall-domain模块的pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<parent> <groupId>yang.yu.tmall</groupId> <artifactId>jpa-sample-tmall</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>tmall-domain</artifactId>
<dependencies> <dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> </dependency> </dependencies></project>
复制代码

该模块仅仅依赖于JPA规范,不包含对任何技术框架(例如Hibernate等)和具体数据库(例如H2MySQL等)的依赖。

三、tmall-persistence-jpa 模块

本模块使用原生JPA API实现tmall-domain领域层模块中定义的仓储接口。在系统中属于数据访问层(Data Access Layer)或更加泛化意义上的基础设施层(Infrastructure Layer)。广义上的基础设施层不但包含数据库访问代码,还包含对缓存、消息中间件、第三方系统等等的访问代码。

tmall-persistence-jpa模块的pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<parent> <groupId>yang.yu.tmall</groupId> <artifactId>jpa-sample-tmall</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>tmall-persistence-jpa</artifactId>
<dependencies> <dependency> <groupId>yang.yu.tmall</groupId> <artifactId>tmall-domain</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies></project>
复制代码

这个模块使用了HibernateH2数据库的JDBC驱动程序作为运行时依赖,但是代码是纯粹基于JPA规范的,没有使用任何Hibernate或数据库特定的类或接口。这意味着:如果我们更改了所用的数据库例如MySQL或者JPA实现框架例如Apache OpenJPA,我们只需要修改pom.xmlpersistence.xml等配置文件,而不需要对产品代码做丝毫的改动。

四、tmall-persistence-spring-data-jpa 模块

本模块使用Spring Data JPA框架实现tmall-domain领域层模块中定义的仓储接口,大大简化了实现。在系统中属于数据访问层/基础设施层

tmall-persistence-spring-data-jpa模块的pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<parent> <groupId>yang.yu.tmall</groupId> <artifactId>jpa-sample-tmall</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>tmall-persistence-spring-data-jpa</artifactId>
<dependencies> <dependency> <groupId>yang.yu.tmall</groupId> <artifactId>tmall-domain</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</version> </dependency> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.1</version> </dependency> </dependencies></project>
复制代码

本模块需要对Spring Data JPA框架的编译时依赖,对数据库驱动程序和JPA实现框架Hibernate的运行时依赖。由于项目中采用C3P0实现数据库连接池,所以还需要对C3P0的编译时依赖(如果采用XML形式配置Spring,则只需要运行时依赖)。javax.inject是依赖注入规范,Spring遵循这个规范。


详细内容请戳这里↓↓↓

原创 | 使用JPA实现DDD持久化-只要O,忘记R & Maven配置


这一节就讲到这里,下一节我们讲"数据库连接配置:persistence.xml"


如果觉得有收获,点个【赞】鼓励一下呗!



发布于: 2020 年 11 月 25 日阅读数: 36
用户头像

高级架构师,技术顾问,交流公号:编程道与术 2020.04.28 加入

杨宇于2020年创立编程道与术,致力于研究领域分析与建模、测试驱动开发、架构设计、自动化构建和持续集成、敏捷开发方法论、微服务、云计算等顶尖技术领域。 了解更多公众号:编程道与术

评论

发布
暂无评论
原创 | 使用JPA实现DDD持久化-只要O,忘记R & Maven配置