领域驱动设计(Domain Driven Design)有一个官方的sample工程,名为DDDSample。
架构图

接下来,我们用一个学生例子来看整体的架构
业务分析:
假设,有多个学生,学生有成绩和基本信息组成,我们就做一个最基本的案例,查看和新增。
首先,构建最基本的设施层:
common:共通类存放地址
dao:数据库实现接口
po:映射数据库对象层
repository:服务层
package com.hikktn.infrastructure.common;/*** <p>* 返回消息体* common:共同使用类* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 16:23*/public class ResponseBase {/*** 状态码*/private int code;/*** 消息*/private Object obj;public ResponseBase(int code, Object obj) {this.code = code;this.obj = obj;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public Object getObj() {return obj;}public void setObj(Object obj) {this.obj = obj;}}
package com.hikktn.infrastructure.po;/*** <p>* 学生实体类* PO:数据库表结构到JAVA的映射类* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 16:48*/public class StudentPo {/*** 主键*/private int id;/*** 学生姓名*/private String name;/*** 学生性别*/private String sex;/*** 学生年龄*/private int age;/*** 学生身高*/private double stature;/*** 学生平均成绩*/private double pjcj;/*** 学生语文成绩*/private double ywcj;/*** 学生数学成绩*/private double sxcj;/*** 学生英语成绩*/private double yycj;/*** 兴趣爱好*/private String xqah;/*** 母亲姓名*/private String mqxm;/*** 父亲姓名*/private String fqxm;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getStature() {return stature;}public void setStature(double stature) {this.stature = stature;}public double getPjcj() {return pjcj;}public void setPjcj(double pjcj) {this.pjcj = pjcj;}public double getYwcj() {return ywcj;}public void setYwcj(double ywcj) {this.ywcj = ywcj;}public double getSxcj() {return sxcj;}public void setSxcj(double sxcj) {this.sxcj = sxcj;}public double getYycj() {return yycj;}public void setYycj(double yycj) {this.yycj = yycj;}public String getXqah() {return xqah;}public void setXqah(String xqah) {this.xqah = xqah;}public String getMqxm() {return mqxm;}public void setMqxm(String mqxm) {this.mqxm = mqxm;}public String getFqxm() {return fqxm;}public void setFqxm(String fqxm) {this.fqxm = fqxm;}}
package com.hikktn.infrastructure.dao;import com.hikktn.infrastructure.po.StudentPo;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Select;/*** <p>* 学生数据访问对象* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 17:32*/@Mapperpublic interface StudentDao {/*** 新增学生的信息* @param studentPo 学生的基本信息*/@Insert("insert ")void save(StudentPo studentPo);/*** 根据id查询学生基本信息* @param id 标识符* @return 学生的基本信息*/@Select("select ")StudentPo queryById(int id);}
package com.hikktn.infrastructure.repository;import com.hikktn.infrastructure.dao.StudentDao;import com.hikktn.infrastructure.po.StudentPo;import com.hikktn.domain.repository.IStudentRepository;import org.springframework.beans.factory.annotation.Autowired;/*** <p>* 学生服务层实现类(基础CRUD)* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 17:33*/public class StudentRepositoryImpl implements IStudentRepository {@Autowiredprivate StudentDao studentDao;@Overridepublic void save(StudentPo studentPo) {studentDao.save(studentPo);}@Overridepublic StudentPo queryById(int id) {return studentDao.queryById(id);}}
如果没有复杂的业务,那么到这里就可以进行controller的返回调用:
dto:前端给后端传递的数据
facade:控制层
package com.hikktn.interfaces.dto;/*** <p>* 学生请求参数* DTO:前端给后端传递的数据* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 16:28*/public class StudentDto {/*** 标识符*/private int id;public int getId() {return id;}public void setId(int id) {this.id = id;}}
package com.hikktn.interfaces.facade;import com.hikktn.application.service.IStudentService;import com.hikktn.domain.repository.IStudentRepository;import com.hikktn.infrastructure.common.ResponseBase;import com.hikktn.interfaces.dto.StudentDto;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;/*** <p>* 学生控制层* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 16:02*/@Controllerpublic class StudentController {/*** 学生业务对象*/@Autowiredprivate IStudentRepository studentRepository;/*** 查询一个学生基本信息* @param request 学生id* @return 学生的信息*/@RequestMapping("/queryById")public ResponseBase queryById(@RequestParam("id") StudentDto request) {return new ResponseBase(HttpStatus.OK.value(), studentRepository.queryById(request.getId()));}}
当我们遇到复杂的业务时,比如我有两张表,学生基本信息表,学生成绩表,组成两个不同的对象,但要同时返回给前台,就要用到domain、interfaces层
application下的service:存放复杂接口
domain:
model:对象
aggregates:聚合,后端返回给前端的聚合信息体
vo:后端给前端传递的数据
repository:存放基本接口(CRUD)
service:复杂接口实现层
package com.hikktn.application.service;import com.hikktn.domain.model.aggregates.StudentEverydayModel;/*** <p>* 应用层用户服务* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 16:04*/public interface IStudentService {/*** 根据id查询学生日常* @param id 标识符* @return 学生的日常*/StudentEverydayModel queryStudentEverydayModelById(int id);}
package com.hikktn.domain.model.vo;/*** <p>* 学生基本信息* VO:后端给前端传递的数据* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 16:35*/public class StudentDataVo {/*** 主键*/private int id;/*** 学生姓名*/private String name;/*** 学生性别*/private String sex;/*** 学生年龄*/private int age;/*** 学生身高*/private double stature;/*** 是否成年(根据年龄进行判断,该字段没有在PO对象中)*/private int isCn;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getStature() {return stature;}public void setStature(double stature) {this.stature = stature;}public int getIsCn() {return isCn;}public void setIsCn(int isCn) {this.isCn = isCn;}}
package com.hikktn.domain.model.vo;/*** <p>* 学生成绩的信息* VO:后端给前端传递的数据* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 16:36*/public class StudentScoreVo {/*** 学生平均成绩*/private double pjcj;/*** 学生语文成绩*/private double ywcj;/*** 学生数学成绩*/private double sxcj;/*** 学生英语成绩*/private double yycj;/*** 是否合格(根据平均成绩进行判断,该字段没有在PO对象中)*/private int isHg;public double getPjcj() {return pjcj;}public void setPjcj(double pjcj) {this.pjcj = pjcj;}public double getYwcj() {return ywcj;}public void setYwcj(double ywcj) {this.ywcj = ywcj;}public double getSxcj() {return sxcj;}public void setSxcj(double sxcj) {this.sxcj = sxcj;}public double getYycj() {return yycj;}public void setYycj(double yycj) {this.yycj = yycj;}public int getIsHg() {return isHg;}public void setIsHg(int isHg) {this.isHg = isHg;}}
package com.hikktn.domain.model.aggregates;import com.hikktn.domain.model.vo.StudentDataVo;import com.hikktn.domain.model.vo.StudentScoreVo;/*** <p>* 学生日常* aggregates:聚合,后端返回给前端的聚合信息体* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 16:10*/public class StudentEverydayModel {/*** 学生基本信息*/private StudentDataVo studentDataVo;/*** 学生成绩的信息*/private StudentScoreVo studentScoreVo;public StudentDataVo getStudentDataVo() {return studentDataVo;}public void setStudentDataVo(StudentDataVo studentDataVo) {this.studentDataVo = studentDataVo;}public StudentScoreVo getStudentScoreVo() {return studentScoreVo;}public void setStudentScoreVo(StudentScoreVo studentScoreVo) {this.studentScoreVo = studentScoreVo;}}
package com.hikktn.domain.repository;import com.hikktn.infrastructure.po.StudentPo;import org.springframework.stereotype.Repository;/*** <p>* 学生的信息业务处理* repository:后端传给业务处理的PO数据* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 17:17*/@Repository("studentRepository")public interface IStudentRepository {/*** 新增学生的信息* @param studentPo 学生的基本信息*/void save(StudentPo studentPo);/*** 根据id查询学生基本信息* @param id 标识符* @return 学生的基本信息*/StudentPo queryById(int id);}
package com.hikktn.domain.service;import com.hikktn.application.service.IStudentService;import com.hikktn.domain.model.aggregates.StudentEverydayModel;import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** <p>* 应用层实现类(聚合对象)* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 17:04*/@Service("studentService")public class StudentServiceImpl implements IStudentService {@Resource(name = "iStudentService")private IStudentService studentService;@Overridepublic StudentEverydayModel queryStudentEverydayModelById(int id) {return studentService.queryStudentEverydayModelById(id);}}
最后就是启动类和配置信息
package com.hikktn;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/*** <p>* 主方法启动类* </p>** @author lisonglin* @version 1.0* @since 2022/1/24 15:59*/@SpringBootApplication@MapperScan("com.hikktn.infrastructure.dao")public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}}
server:port: 9000spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=falseusername: rootpassword: 123
