看我如何应对业务需求变化,“愚蠢”的应对?
作者:网络转载 发布时间:[ 2014/10/15 14:58:23 ] 推荐标签:软件测试管理
消息发送、回复、销毁等实现
其实除了上面列表和详情页的变化,消息发送、回复和销毁实现也需要做出调整,因为消息领域模型没有任何变动,发送消息还是按照之前的发送逻辑,所以发送消息是没有变化的,回复消息也没有大的变化,只不过回复的时候需要获取一下消息标题,因为除了第一条发送消息需要填写标题,之后的消息回复是不需要填写标题的,需要添加的只不过是消息内容。消息销毁的改动相对来说大一点,因为之前都是独立的消息发送,所以可以对每个独立的消息进行销毁操作,但是从上面消息详情页示意图中可以看到,独立的消息是不能销毁的,只能销毁这个完整的消息,也是详情页下面的删除按钮,示例代码:
public async Task<OperationResponse> DeleteMessage(int messageId, string readerLoginName)
{
IContactRepository contactRepository = new ContactRepository();
IMessageRepository messageRepository = new MessageRepository();
Message message = await messageRepository.GetByKey(messageId);
if (message == null)
{
return OperationResponse.Error("抱歉!获取失败!错误:消息不存在");
}
Contact reader = await contactRepository.GetContactByLoginName(readerLoginName);
if (reader == null)
{
return OperationResponse.Error("抱歉!删除失败!错误:操作人不存在");
}
if (!message.CanRead(reader))
{
throw new Exception("抱歉!获取失败!错误:没有权限删除");
}
message.DisposeMessage(reader);
var messages = await messageRepository.GetMessages(message, reader);
foreach (Message item in messages)
{
item.DisposeMessage(reader);
messageRepository.Update(item);
}
await messageRepository.Context.Commit();
return OperationResponse.Success("删除成功");
}
这个是应用层中消息销毁操作,可以看到应用层的这个操作代码很凌乱,这是为了实现而实现的代价,除了消息销毁,还有一个操作是消息状态设置,也是消息“未读”和“已读”设置,这个代码实现在应用层 ReadMessage 操作中,代码更加凌乱,我不贴出来了,和消息销毁操作比较类似,消息状态设置只不过设置一些状态而已。
回到原点的一些思考
为什么我会详细描述我当时实现的思路?其实是想让你和我产生一些共鸣,上面的一些实现操作,完全是为了实现而实现,不同的应用场景下的业务需求变化是不同的,但思考的方式一般都是想通的,也是说如果你能正确应对这个业务需求变化,那换一个应用场景,你照样可以应对,如果你不能正确应对,那领域驱动设计是“空头白话”,为什么?因为领域驱动设计是更好的应对业务需求变化的。
其实上面的需求变化,我们已经变相的实现了,只不过没有发布出来,像一个多月之前的发布公告中所说,“Does your code look like this?”,如果按照这种方式实现了,那以后的短消息代码,是那一坨面条,惨不忍睹。
回到原点的一些思考,其实是回到领域模型去看待这次的业务需求变化,关于这部分内容,我还没有准确的做法,这边我说一下自己的理解:
业务需求变化,领域模型变化了吗?
首先,在之前的实现中,消息列表显示这部分内容,应该是应用层中体现的,所以在领域模型中可以暂时不考虑,这个在仓储中应该着重思考下。那领域模型变化了什么?先说发送消息,这个变化了吗?我觉得没有,还是点对点的发送一个消息,这个之前是用 SendSiteMessageService 领域服务实现的,逻辑也没有太大的变化,那回复消息呢?其实我觉得这是大的一个变化,如果你看之前的回复代码,我是没有在领域模型中实现回复消息操作的,为什么?因为我当时认为,回复消息其实也是发送消息,所以在应用层中回复消息操作,其实是调用的 SendSiteMessageService 领域服务,这个现在看来,是不应该这样实现的。
我们先梳理一下回复消息这个操作的处理流程,这个其实上面有过分析,除了第一条消息是发送以外,之后的消息都是回复操作,这要有一个标识,用来说明这条消息是回复的那一条发送消息,那这个怎么来设计呢?回复消息设计成实体好?还是值对象好?我个人觉得,应该设计成实体,原因大家想想知道了,虽然它依附于发送消息存在,但是它也是的,比如一个人给另外两个人回复同样内容的消息,那这两个回复消息应该都是独立存在的,那这个依附关系怎么处理呢?我们可以在消息实体中添加一个标识,用来表示它回复的是那条消息。
上面这个确定之后,那我们如何实现回复消息操作呢?我们可以用一个领域服务实现,比如 ReplySiteMessageService,用来处理回复消息的一些操作,这个和 SendSiteMessageService 领域服务可能会有些不同,比如一个人 1 天只能发送 200 条消息,但是这个逻辑我们不能放在回复消息领域服务中,回复只是针对一个人的回复,所以这个可以不做限制,发送是针对任何人的,为了避免广告推广,这个我们必须要做一个发送限制,当然具体实现,要看需求的要求了。
除了回复消息这个变化,说多一点,消息状态(未读和已读)和消息销毁,这个可能也会有细微的变化,比如消息状态,在消息列表中打开一个消息,其实是把这条消息的回复内容都设置成已读了,我们之前的设计是针对独立的消息状态,也是说每个消息都有一个消息状态,按照这种方式,其实我们可以把这个状态放在发送消息实体中,如果有人回复了,那这个消息状态是设置为未读,回复消息没有任何状态,如果这样设计的话,有点像值对象的感觉,可以从消息实体中独立出来一个回复消息值对象,当然这只是我的一种思路。消息销毁和这个消息状态比较类似,这边不多说了,除了这两个变化,其实还有一些细节需要考虑,这个只能在实现中进行暴露出来了。
对象读取的额外思考
这个其实是我看了仓储那惨不忍睹的实现代码,所引起的一些思考,你可以读一下,这样的一篇博文:你正在以错误的方式使用ORM。
仓储在领域驱动设计的作用,可以看作是实体的存储仓库,我们获取实体对象要经过仓储,仓储的实现可以是任何方式,但传输对象必须是聚合根对象,这个在理论中没有什么问题,但是在实际项目中,我们从仓储中获取对象,一般有两种用途:
用于领域模型中的一些验证操作。
用于应用层中的 DTO 对象转化。
第一种没有什么问题,但是第二种,这个不可避免的造成性能问题,也是上面文中 Jimmy(AutoMapper 作者)所说的 Select N 问题,这个我之前也遇到过,后的解决方式,我是按照他在 AutoMapper 映射的一些扩展,也是上面代码中的 Project().To(),但这样不可避免的违背了领域驱动设计中仓储的一些思想。
关于这个内容,我不想说太多,重点是上面领域模型的思考,仓储的问题,我是一定要做一些改变的,因为它现在的实现,让强迫症的我感觉到非常不爽,不管是 CQRS、ES、还是六边形架构,总归先尝试实现再说,有问题不可怕,可怕的是不懂得改正。
写在后
在领域驱动设计的道路上,有很多你意想不到的情况发生,稍微不注意,你会偏离的大方向,很遗憾,我没有针对这次的业务需求变化,做出一些具体的实现,但我觉得意识到问题很重要,这篇博文分享希望能与你产生一些共鸣。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南