看我如何应对业务需求变化,“愚蠢”的应对?

发表于:2014-10-15 11:52

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:跟幸福说晚安    来源:51Testing软件测试网采编

  “愚蠢”的应对
  我个人觉得这一节点内容非常重要,在领域驱动设计的过程中,也是很多人常掉进的“坑”,因为我们长期受“脚本模式”的影响,在业务需求变化后,应用程序需要做出调整,但是你会不自觉的“跑偏”,这就偏离了领域驱动设计的思想,最后使你的应用程序变得“不伦不类”。
  当时为了很快的在应用程序中实现这种功能,我说的是技术上实现,完全没有用领域驱动的思想去考虑,我是怎么思考的呢?先从 UI 上考虑,主要是两个界面:
  消息列表:收件箱、发件箱和未读消息列表。
  消息详情:消息详情页。
  消息列表实现
  之前短消息不管发送,回复,还是转发,都是作为一个新短消息进行发送的,“消息的上下文”作为一个消息的附属体,放在新短息内容中,也就是说,你把之前发送的消息删掉,在新回复的短消息内容中,是仍然看到之前发送内容的,这个在列表的显示就是单独进行显示,但新的需求变化就不能这样进行操作了,这个就有点像两个人聊一个话题,里面都是我们针对这个话题进行讨论的内容,在列表显示的时候,首先,标题显示就是这个话题的标题,就像邮件回复一样,我们可以加上“消息标题(3)”,这个“3”,就表示两个人回复了3次。
  其实用话题这个逻辑是有些不准确的,毕竟我们是短消息项目,我们可以这样想,我给 netfocus 发了一个标题为:“打个招呼”,内容为:“hello netfocus”的消息,然后他给我进行了回复:“hello xishuai”,可能后面还有一些消息回复内容,但都是针对我发的第一条消息回复,也就是说下面都是回复内容,那这个在消息列表显示的时候,标题就显示为“打个招呼(3)”,后面时间为最新回复时间,示意图:
  上面是 netfocus 的收件箱示意图,收件箱列表显示的逻辑就是以发件人和标题为一个标识,比如 Jesse Liu 也给 netfocus 发了一个“打个招呼”的消息,虽然标题一样,但发件人不一样,所以列表显示两条消息。
  那代码怎么实现这个功能呢?贴出代码看看:
  public async Task<IEnumerable<MessageListDTO>> GetInbox(Contact reader, PageQuery pageQuery)
  {
  var query = efContext.Context.Set<Message>()
  .Where(new InboxSpecification(reader).GetExpression()).GroupBy(m => new { m.Sender.ID, m.Title }).Select(m => m.OrderByDescending(order => order.ID).FirstOrDefault());
  int skip = (pageQuery.PageIndex - 1) * pageQuery.PageSize;
  int take = pageQuery.PageSize;
  return await query.SortByDescending(sp => sp.ID).Skip(skip).Take(take)
  .Project().To<MessageListDTO>().ToListAsync();//MessageListDTO 为上一版本遗留问题(Select FileName),暂时没动。
  }
  GetInbox 是 MessageRepository 中的操作,其实原本收件箱的代码不是这样处理的,你会看到,现在的代码其实就是 Linq 的代码拼接,我当时这样处理就是为了可以方便查询,现在看确实像“一坨XX”,代码我就不多说了,上面列表显示功能是可以实现的,除去回复数显示,其实你会看到,这个就是对发件人和标题进行筛选,选取发送时间最新的那一条消息。
  虽然这段 Linq 代码看起来很“简单”,但是如果你跟踪一下生成的 SQL 代码,会发现它是非常的臃肿,没办法,为了实现功能,然后就不得不去优化数据库,主要是对索引的优化,这个当时优化了好久,也没有找到合适的优化方案,最后不得不重新思考这样做是不是不合理?这完全是技术驱动啊,后来,我发现,在领域驱动设计的道路上,我已经完全“跑偏”了。
  消息详情页实现
  业务需求的变化,其实主要是消息详情页的变化,从上面那张消息详情页示意图就可以看出,刚才上面说了,收件箱列表显示是对标题和发件人的筛选,其实详情页就是通过标题和发件人找出回复消息,然后通过发送时间降序排列。具体操作是,在收件箱中点击一条消息,然后通过这条消息和发件人去仓储中找这条消息的回复消息,示例代码:
  public async Task<IEnumerable<Message>> GetMessages(Message message, Contact reader)
  {
  if (message.Recipient.ID == reader.ID)
  {
  return await GetAll(Specification<Message>.Eval(m => m.Title == message.Title
  && ((m.Sender.ID == message.Sender.ID && m.Recipient.ID == message.Recipient.ID && (m.DisplayType == MessageDisplayType.OutboxAndInbox || m.DisplayType == MessageDisplayType.Inbox))
  || (m.Recipient.ID == message.Sender.ID && m.Sender.ID == message.Recipient.ID && (m.DisplayType == MessageDisplayType.OutboxAndInbox || m.DisplayType == MessageDisplayType.Outbox)))),
  sp => sp.ID, SortOrder.Ascending).ToListAsync();
  }
  else
  {
  return await GetAll(Specification<Message>.Eval(m => m.Title == message.Title
  && ((m.Sender.ID == message.Sender.ID && m.Recipient.ID == message.Recipient.ID && (m.DisplayType == MessageDisplayType.OutboxAndInbox || m.DisplayType == MessageDisplayType.Outbox))
  || (m.Recipient.ID == message.Sender.ID && m.Sender.ID == message.Recipient.ID && (m.DisplayType == MessageDisplayType.OutboxAndInbox || m.DisplayType == MessageDisplayType.Inbox)))),
  sp => sp.ID, SortOrder.Ascending).ToListAsync();
  }
  }
  不知道你是否能看懂,反正我现在看这段代码是需要思考一下的,呵呵。消息详情页基本上就是这样实现的,还有一些是在应用层获取“点击消息”,UI 中消息显示判断等一些操作。
32/3<123>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号