代码改变世界

Spring.net-AOP 搭建网站通知服务(1)

2011-06-28 16:23  ZQ  阅读(2226)  评论(7编辑  收藏  举报

1、前言:


    本文想分享一下搭建网站通知服务过程中的心得。这包括:AOP 在项目中的应用、面向对象设计与开发、单元测试的应用等。文后将整理出源码供参考。

需求分析,什么是网站通知服务?

  • 在网站内发生某个事件时系统按照某种规则产生通知并发送给相关人。例如:用户注册成功后给用户发送欢迎电子邮件、当您的博客被回复或者您被关注的通知等等。
  • 通知内容及规则需集中管理,可快速调整。
  • 通知需要以各种形式发送给接收者。如:电子邮件、站内消息、短信、IM等等。
  • 接收者可以设定接收方式。例如只接收电子邮件,不接收短信通知。
  • 通知内容需要根据发送的形式而定。根据发送形式的不同会有不同的约束条件,短信方式会对字数有限制而电子邮件可以承载更丰富的内容。

以往我们实现此类需求可能有几种方式:

  • 方式1:将每个通知逻辑硬编码到对应的事件代码(某个方法内)中。
  • 方式2:在业务逻辑层中将通知逻辑打包为服务类,提供API供事件代码调用。
  • 方式3:以数据库为 持久化的系统中,可以构建独立的通知系统,根据规则监控数据库的数据变化以产生通知。

对于几种方式的优缺点比较明显,适用场景也各不相同。方式1不推荐使用,方式3通知逻辑与事件过度分离。

本文将采用方式2+AOP方式构建通知服务,其重点在于营造低耦合的环境以达到敏捷的效果。

我们回到需求上来,看一看需要哪些类来进行协作:

  • 首先我们定义一个通知主题类,它就像一封信件一样,在系统与接收者之间传递。
   /// <summary>
/// 通知主题
/// </summary>
public class NotifySubject
{
public string Title { get; set; }
public string Body { get; set; }
}
  • 根据需求得知有不同的方式发送给接收者,定义通道枚举:
    /// <summary>
/// 发送通道
/// </summary>
public enum NotifyChannel
{
/// <summary>
/// 站内消息
/// </summary>
Messenger,
/// <summary>
/// 电子邮件
/// </summary>
Smtp,
/// <summary>
/// 手机短信
/// </summary>
Sms,
}
  • 为了将通知消息发送给用户,需要定义发送器,由于存在很多种方式发送消息,因此抽象出一个接口,且与通道是1对1关系:
/// <summary>
/// 通知发送器
/// </summary>
public interface INotifySender
{
/// <summary>
/// 通道
/// </summary>
NotifyChannel NotifyChannel { get; }
/// <summary>
/// 发送通知
/// </summary>
/// <param name="subject">通知主题</param>
/// <param name="targetUser">目标用户</param>
void Send(NotifySubject subject, User targetUser);
}
  • 关于接收者可定义自定义接收方式,我们定义了通知设置类,它与通道是1对1关系,另外用户类中增加对其引用,与用户产生1对多关系。此类还可以扩展满足业务需求,例如增加接收时间控制或者对某些事件进行过滤等等。另外需要关注到的是用户的设置需要持久化的。
/// <summary>
/// 通知设置
/// </summary>
[Serializable]
public class NotifySetting
{
/// <summary>
/// 通道
/// </summary>
public NotifyChannel Channel { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnable { get; set; }
}

    public class User
{
......
private IEnumerable<NotifySetting> _notifySettings;
/// <summary>
/// 通知接收设置 
/// </summary>
public virtual IEnumerable<NotifySetting> NotifySettings
{
get { return _notifySettings ?? new NotifySetting[]{}; }
set { _notifySettings = value; }
}
......
}
  • 某个通知产生的逻辑会与业务与通道相关,会有多种多样且易变的需求出现,为此我们抽象了接口,可以看到通知动作接口与业务数据是有对应关系的:
/// <summary>
/// 通知动作
/// </summary>
public interface INotifyAction
{
/// <summary>
/// 业务数据
/// </summary>
dynamic BizData { get; set; }
/// <summary>
/// 获取接收者
/// </summary>
/// <returns></returns>
IEnumerable<User> GetRecievers();
/// <summary>
/// 获取通知主题
/// </summary>
/// <param name="channel">通道</param>
/// <param name="receiver">接收者</param>
/// <returns></returns>
NotifySubject GetNotifyBody(NotifyChannel channel,User receiver);
}

经过的步骤我们得出了一个初步的静态模型:

image

 

下一章节将会更进一步讨论动态模型