头图

domain

  • 与传统结构最大的不同,就是领域层
  • 领域层的作用是让我们使用面相对象的思想去编写领域对象

    面相对象

  • 初学Java时,一直在强调Java是面相对象语言
  • 对象有属性和方法(行为)

    public class Dog {
      String name;
      int age;
    
      void eat() {
      }
    
      void run() {
      }
    }

    面相表格

  • 工作一段时间后,感觉较面向表格更为贴切
  • 从数据库获取一行数据,修改某些属性,再保存回去

    • 包含大量胶水代码,无法复用
    @Data
    public class User {
      private Long id;
      private String name;
      private Integer age;
      private String password;
    }
    public class UserService {
      @Autowired
      private UserMapper userMapper;
    
      public void changeInfo(Long id) {
          User user = userMapper.selectById(id);
          user.setName("张三");
          user.setAge(18);
          user.setPassword(getMd5("123456"));
          userMapper.updateById(user);
      }
    
      private String getMd5(String password) {
          return password + "-md5"; // 模拟
      }
    }

    领域对象

  • 领域对象其实是回归面向对象的做法
  • 将逻辑内聚到领域对象中

    @Data
    public class User {
      private Long id;
      private String name;
      private Integer age;
      private String password;
    
      public void changeInfo(String name, Integer age, String password) {
          this.name = name;
          this.age = age;
          this.password = getMd5(password);
      }
    
      private String getMd5(String password) {
          return password + "-md5"; // 模拟
      }
    }
  • 减少胶水代码,更易复用

    public class UserService {
      @Autowired
      private UserGateway gateway;
    
      public void changeInfo(Long id) {
          User user = gateway.get(id);
          user.changeInfo("张三", 18, "12345");
          gateway.save(user);
      }
    }

    领域对象与数据对象

  • 领域对象和数据对象为什么分开?为什么不合在一起,写个充血的数据对象,当做领域模型?
  • 职责单一,各司其职,更利于维护

    代码实例

  • 领域对象中,只需要处理好自己的逻辑

    @Data
    public class User {
      private Long id;
      private String name;
      private List<String> roles; // 权限列表
    
      public User(String name) {
          this.name = name;
          this.roles = new ArrayList<>();
      }
    
      public void addRole(String role) {
          this.roles.add(role);
      }
    }
  • 在数据对象中,考虑如何存储,如"roles"可以用不同方式存储

    • json字符串
    • ","逗号隔开的字符串
    • 一对多的关联表
    // 数据对象
    @Data
    @TableName("user")
    public class UserDO {
      @TableId(type = IdType.AUTO)
      private Long id;
      private String name;
      private String rolesJson; // 转换为json字符串
    }
  • 领域对象和数据对象间使用转换器执行转换

    • 可使用"MapStruce"和"FastJson"实现
    @Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
    public interface UserConvertor {
    
      @Mapping(target = "rolesJson", expression = "java(map(entity.getRoles()))")
      UserDO toDataObject(User entity);
    
      @Mapping(target = "roles", expression = "java(map(dataObject.getRolesJson()))")
      User toEntity(UserDO dataObject);
    
      default String map(List<String> roles) {
          return JSON.toJSONString(roles);
      }
    
      default List<String> map(String rolesJson) {
          return JSON.parseArray(rolesJson, String.class);
      }
    }
  • 当业务拓展时,Role需要增加字段做更多功能,用户表里的roles由Json变更为Role子数据表
  • 这样的设计能够尽可能减少代码修改

    反例

  • 如果省略领域模型,直接将数据模型写成充血模型

    • 好消息:减少代码量,减少类
    • 坏消息:不好维护,屎山代码
    @Data
    @TableName("user")
    public class UserEntity {
      @TableId(type = IdType.AUTO)
      private Long id;
      private String name;
    
      @TableField(exist = false)
      private List<String> roles; // 字段不映射数据表
    
      private String rolesJson;   // 存储字段
    
      public UserEntity(String name) {
          this.name = name;
          this.roles = new ArrayList<>();
          this.rolesJson = "[]";
      }
    
      public void addRole(String role) {
          this.roles.add(role);
          this.rolesJson = JSON.toJSONString(this.roles);
      }
    }

    client

  • client层主要用于微服务架构
  • 非常适合与Dubbo搭配使用

    client接口定义

  • client层的api中编写服务接口

    public interface UserServiceI{
      // 略
    }

    cleint打包发布私有Maven

  • pom.xml
  • 设置Maven私有仓库(Nexus3)配置

      <dependencies>...略</dependencies>
      <build>...略</build>
    
      <distributionManagement>
          <repository>
              <id>maven-releases</id>
              <name>maven-releases</name>
              <url>${这里填写从nexus页面上复制的maven-releases的url}</url>
          </repository>
          <snapshotRepository>
              <id>maven-snapshots</id>
              <name>maven-snapshots</name>
              <url>${这里填写从nexus页面上复制的maven-snapshots的url}</url>
          </snapshotRepository>
      </distributionManagement>
  • maven-settings.xml
  • 私有仓库配置

    <?xml version="1.0" encoding="UTF-8"?>
    
    <settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
      <servers>
          <server>
              <id>maven-releases</id>
              <username>admin</username>
              <password>${密码}</password>
          </server>
          <server>
              <id>maven-snapshots</id>
              <username>admin</username>
              <password>${密码}</password>
          </server>
      </servers>
    </settings>
  • deploy.sh
  • maven打包脚本
  • 使用maven的容器镜像,执行打包脚本

    # 复制maven配置文件
    cp maven-settings.xml /usr/share/maven/conf/settings.xml
    # 执行maven
    mvn deploy -pl cola-springboot-demo-client -am

    app层接口实现

  • app层,编写实现类
  • 注册为Dubbo服务

    @Service
    @DubboService    // 注册为Dubbo服务
    public class UserServiceImpl implements UserServiceI{
      // 略
    }

    其他微服务RPC调用

  • 其他项目从私有Maven仓库中加载client的接口及入参出参

    <dependency>
      <groupId>com.xxc</groupId>
      <artifactId>cola-springboot-demo-client</artifactId>
      <version>1.0.0</version>
    </dependency>
  • 在需要的地方调用,如

    • App层的Executor调用
    • infrastructure层的网关实现类中调用
    @Component
    public class XxxExe{
      @DubboReference
      private UserServiceI userService;
    
      public void execute(){
          // 略
      }
    }

言午日尧耳总
1 声望6 粉丝

不秃顶、不猝死,顺顺利利活到100可以吗?


引用和评论

0 条评论