跳至主要內容

COLA各模块理解

言午日尧耳总大约 4 分钟JavaSpringBoot视频COLA

COLA各模块理解

  • COLA架构使用过程中对各模块的一些理解

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(){
        // 略
    }
}
上次编辑于:
贡献者: 许晓聪