Akka开发手册

背景

  1. 保证多人协作也只写出一种代码, 而非多种代码; 例如: 保证10人只写出一套代码, 避免10人写出100种代码.
  2. 有利于与异构系统进行交互
  3. 有利于工作交接
  4. 系统做大做强的基础

命名规范

交互协议

  • 交互协议根据业务类型封装在object中, 例如: DeviceProtocol
  • 与外围系统交互封装在包protocol/api
  • 内部系统交互封装在包protocol/service

变量命名

时刻与时间的区别: 时刻指某一个点(比如: 2010-01-01 10:00:00), 时间指两个时刻间的差值(比如: 2分钟, 5小时)

  • XXXAt: 表示与时刻相关
    • createdAt: 创建时刻
    • updatedAt: 更新时刻
    • triggerAt: 触发时刻
    • startedAt: 开始时刻, 用于搜索中开始时刻
    • endedAt: 结束时刻, 用于搜索中结束时刻

外围系统交互

  • XXXReq: 接收外围系统的请求参数并构造成该对象
  • XXXResponse: 将业务层提供的数据封装成该对象, 并序列化成json返回给外围调用方

内部Actor间交互

  • 消息通道: 目前均采用点对点消息通道
  • 消息: 三类消息
    • 命令消息: ExecuteXXX, 例如: ExecuteBindDevice
    • 事件消息: XXXExecuted, 例如: BindDeviceExecuted
    • 文档消息: XXXMsg, 例如: BindDeviceMsg

部分文档消息内部包装类: XXXWrapper, 例如: BindDeviceWrapper

同构系统交互

不允许使用ask模式, 根据需要选择使用tell, forward, pipeTo

异构系统交互

发起调用时可以使用ask模式

HTTP接口命名

  • url: 使用纯REST风格
  • 函数名: queryXXX, modifyXXX, removeXXX, addXXX

领域模型

  • XXX, 例如: Device

聚合Actor

  • XXXActor, 例如: BindDeviceActor

Dao层Actor

  • SelectXXX, 例如: SelectDeviceById
  • UpdateXXX, 例如: UpdateDeviceById
  • InsertXXX, 例如: InsertDevice
  • DeleteXXX, 例如: DeleteDeviceById

getquill 用法

仅针对单表进行映射(ORM), 多表关联产生的数据使用元组进行接收; 减少domain的无限膨胀.

getquill schema 命名

业务表使用复数, 例如: activites; 关联表使用单数, 例如: userActivity;

分布式服务命名规范

格式

域名.module.模块名.*

服务枚举

  • 基础协议的项目名和包名
    • 项目名: GDP-BASE-PROTOCOL
    • 包名: com.gyenno.module.base.*
  • 通用服务
    • 服务项目名: GDP-COMMON
    • 协议项目名: GDP-COMMON-PROTOCOL
    • 包名: com.gyenno.module.common.*
  • 用户服务
    • 服务项目名: GDP-USER
    • 协议项目名: GDP-USER-PROTOCOL
    • 包名: com.gyenno.module.user.*
  • 消息服务
    • 服务项目名: GDP-MESSAGE
    • 协议项目名: GDP-MESSAGE-PROTOCOL
    • 包名: com.gyenno.module.message.*
  • 文件服务
    • 服务项目名: GDP-FILE
    • 协议项目名: GDP-FILE-PROTOCOL
    • 包名: com.gyenno.module.file.*
  • 医动力服务
    • 服务项目名: GDP-MEDICAL
    • 协议项目名: GDP-MEDICAL-PROTOCOL
    • 包名: com.gyenno.module.medical.*

数据库规范

  • 库命名: 服务名_database
  • 表命名
    • 非关联表: 使用名词复数, 例如: users, roles
    • 关联表: 使用名词单数, 例如: user_activity, user_role

处理业务发生异常后的响应(接口v3.1进行整体迁移)

根据目前项目的需求, 使用单容错结构, 减少调用方解析成本; 以后有足以平衡调用成本的需求, 再平滑升级到多容错结构;

异常响应体中, code分类如下:

  • conflict: 冲突, 所要新增的资源已存在
  • invalid: 无效, 参数超出文档约定范围, 例如: 1-男, 2-女, 输入3将收到此code
  • missing: 必选参数不存在
  • notFound: 资源不存在

单容错(数据结构)

检测到输入或业务调用异常, 直接返回, 不再对其它resourcefield进行检测;

1
2
3
4
5
6
7
8
{
"message": "该手机号已被注册",
"error": {
"resource": "user",
"field": "mobile",
"code": "conflict"
}

}

多容错(数据结构)

检测到输入或业务调用异常, 继续对其它resourcefield进行检测, 封装组合后再返回;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"message": "注册失败",
"errors": [
{
"resource": "user",
"field": "mobile",
"code": "conflict"
},

{
"resource": "user",
"field": "sex",
"code": "invalid"
},

{
"resource": "user",
"field": "birthedAt",
"code": "missing"
}

]
}

为什么要设计error ?

如果只使用message, 只能人类看懂, 机器很难进行分类解析.

场景: 绑定设备, 假设提供的userIdchId在库中均不存在, 如果只返回message的话, 将返回用户不存在设备不存在; 人能看懂, 但代码无法进行分类; 假如增加error的话, 调用方就可根据resourcefield进行自定义提示, 而不依赖于后端提供的message;

如下:

1
2
3
4
5
6
7
8
{
"message": "用户不存在",
"error": {
"resource": "user",
"field": "id",
"code": "notFound"
}

}

1
2
3
4
5
6
7
8
{
"message": "设备不存在",
"error": {
"resource": "device",
"field": "chId",
"code": "notFound"
}

}

参考文章

转载

本文出自<<arccode>>, 欢迎转载, 转载请注明出处.