unified-message(统一消息平台),为业务系统提供了标准的消息发送功能
- 支持发送短信、邮件、企业微信等消息,可以扩展支持其它的消息类型
- 可以通过手机号、邮件、企业微信用户名直接发送, 可以直接通过用户名发送短信等
项目地址
gitee: https://gitee.com/wei772/unified-message
github: https://github.com/wei772/unified-message
使用方法
-
运行项目
-
调用发送消息接口
curl --location --request POST "http://localhost:8080/api/message/send" ^
--header "Content-Type: application/json" ^
--data-raw "{ \"channelName\":\"wecom\", \"content\":\"测试\", \"recipients\":[\"LiWei\"]}"
包结构和主要类介绍
- domain包
- 包含Message相关领域类
- repository包
- 访问数据存储相关类
- 使用MongoDb存储数据
- client包
- 访问第三方服务
- 使用原始feign访问第三方web接口
- command包
- 对应用户用例的实现
- 使用标准的Command接口
- controller包
- 对外提供web接口访问
- sender包
- 实现发送Message相关类
整洁架构
来源 Robert C. Martin 的《架构整洁之道》,详情查看第22章 整洁架构
-
业务实体,业务核心包含应用的业务对象
- 包含domain包
-
用例,通常包含特定应用场景的业务逻辑
- 包含command包
-
接口适配器,软件的接口适配器层中通常是一组数据转换器,它们负责将数据从对用例和业务实体而言最方便操作的格式,转化成外部系统(譬如数据库以及Web)最方便操作的格式。
通常这一层的代码也会负责将数据从对业务实体与用例而言最方便操作的格式,转化为对所采用的持久性框架(譬如数据库)最方便的格式。- 包含 client 部分抽象和具体类,访问外部接口的网关
- 包含 repository 的抽象类,比如MessageRepository,是一种数据库网关
- 包含controller包,对外提供Web接口。接收和返回Web请求数据
-
框架与驱动程序,最外层的模型一般是由数据库、Web 框架等组成的。
- 包含client包下的实现类,比如Feign使用BlacktelSendService生成的请求类,对应外部接口
- repository实现相关代码,比如MongoMessageRepository,对应数据库访问。
- 包含Tomcat Web容器和Spring MVC等框架细节
测试驱动开发
本项目使用测试驱动开发的方法开发
复杂类的默认态与运行态
复杂类一般会依赖外部环境,在代码中体系依赖外部接口。这些外部接口可能也会依赖其它复杂对象。
在Java中IoC框架存在就是用来创建这些复杂类,使用起来十分方便
但是这样创建的类确难以进行单元测试
- 单元测试不应该依赖任何复杂框架,太过笨重,也太过复杂
- 严重影响单元测试的速度
为了兼容单元测试和实际运行的两种情况,我将类分成默认态与运行态的方式。这样保持了类的测试性,又保证了类的功能性
- 使用默认构造函数生成默认状态,用于单元测试
- 默认的实现可以依赖外部,也可以基于Mock依赖,主要看外部调用是否方便、稳定、代价是否合理,比如发送短信代价有点高必须Mock
- 还有一个由main函数进行组装的,比如基于Spring IoC,用于运行环境的状态
- 使用@Autowired注解指定构造函数,实现该功能
效果与感受
-
流畅的组织所有开发活动的技术,测试驱动开发这种先写测试再开发的方式很流畅。
- 不要绞尽脑汁再脑中、用文字还有图去思考设计,这种方法浪费时间,想象力也无法有效发挥。
- 也不会一上来就编码,编写难以执行代码,代码基本没有设计,想到哪写到哪。
- 使用单元测试可以持续重构,一直保持设计和编码变得更好的
测试驱动开发本质
下面一些经典数据的一些论述,涉及到测试驱动开发本质
《测试驱动开发 (Kent Beck) 》当中有几段话让我印象深刻,体现了这种方法的核心作用
- 而我从书本上学到的却恰好相反:“编码为今天,设计为明天。”而测试驱动开发看起来已经彻底推翻了这一论点: **“为明天编码,为今天设计。”
** 。 - 测试(Test)--自动、具体、切实的测试。按一个键就可以让测试运行。具有讽刺意味的是测试驱动开发不是一种测试技术(Cunningham
Koan)。 它是一种分析技术、设计技术,更是一种组织所有开发活动的技术。
《敏捷整洁之道:回归本源》关于复式记账的论述
- 会计师们在1000年前发明了一条法则,并将其称为复式记账。每笔交易会写入账本两次:在一组账户中记一笔贷项,然后相应地在另一组账户中记为借项。
这些账户最终汇总到收支平衡表文件中,用总资产减去总负债和权益。差额必须为零。如果不为零,肯定就出错了 - 复式记账与TDD这两种纪律是等效的。它们都具有相同的功用:在极其重要的文档中避免错误,确保每个符号都正确。
尽管编程对社会来说已经必不可少,
但我们还没有用法律强制实施 TDD。可是,既然编写糟糕的软件已经造成了生命财产损失,立法还会远吗?
测试驱动开发步骤
首先编写任务清单,一般包含设计想法、要实现用例、重构任务等等,将TODO的事情要一个简单的文档记录,整个过程比较随意,有价值的想法就记录下来,完成之后将对应的任务划上删除线。
然后是具体编写过程
- 从任务清单中挑选任务,针对任务编写不通过的单元测试,包括无法编译和运行错误的用例
- 使单元测试运行通过
- 重构现有代码,使设计更佳。
每次写代码都重复这3个步骤,直到没有需要完成的任务。
其它测试经验
-
单元测试相关代码也很重要
- Mock代码挺重要,好的Mock代码能够尽可能模拟真实情况,减少gap
- 执行单元测试的代码的重要性也不言而喻,是测试驱动开发的开始。
-
Disabled的单元测试
- 基本都是运行代价比较大或者不稳定的的测试,通常是面向外部依赖。
- 默认不执行测试,可以单独去执行的测试
-
在单元测试注释记录异常
- 在单元测试注释里记录曾经遇过的异常,以及分析还有解决方案,感觉很有趣。
- 能记录一下曾经遇到过什么问题,并且怎么解决了,既有成就感,又可以有经验的积累和传播
-
单元测试不能替代验收测试
- 单元测试通过不保证基本功能正常
- 但是能极大简化验收测试,一般是因为环境、IoC依赖等出现问题
- 可以考虑用spring-test进行简单的验收测试