原文

你不希望在controller里面出现任何领域知识

开发者经常有这样的疑问“这个代码应该放在哪呢?”应该使用仓储还是query类?....

怎么去实现职责分离和单一职责呢?

MediatR Notifications能帮助我们。

例子

假设你有一个简单的网站,有一个页面提供一个简单的联系我们的表单。

当客户完成了这个表单,会发送一封邮件给网站管理者。

public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
{
var mailMessage = new MailMessage(contactUs.EmailAddress, "you@yoursite.com");
mailMessage.Subject = "Contact from web site";
mailMessage.Body = contactUs.Message; var smtp = new SmtpClient();
smtp.Send(mailMessage); return Ok();
}

现在你老板过来要求你将这个信息往数据库里面也放一份,以方便做一些报表统计。

然后你修改controller,加入了下面的一下代码:

public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
{
var mailMessage = new MailMessage(contactUs.EmailAddress, "you@yoursite.com");
mailMessage.Subject = "Contact from web site";
mailMessage.Body = contactUs.Message; var smtp = new SmtpClient();
smtp.Send(mailMessage); var userMessage = new UserMessage {
Received = System.DateTime.Now,
UserEmailAddress = contactUs.EmailAddress,
UserFullName = contactUs.FullName
};
db.UserMessages.Add(userMessage);
db.SaveChanges(); return Ok();
}

现在单一职责被破坏了,但是你决定先这么处理不管了。

你老板是一个从来不容易满足的人,过了几天他又过来了,他希望将这个表单里面的客户信息存储到CRM系统中。

现在你的controller变成这样了:

public class ContactController : ApiController
{
using BlogSite.Models;
using BlogSite.Models.Crm;
using System.Net.Mail;
using System.Web.Http; namespace BlogSite.Controllers.Api
{
public class ContactController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext(); public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
{
var mailMessage = new MailMessage(contactUs.EmailAddress, "you@yoursite.com");
mailMessage.Subject = "Contact from web site";
mailMessage.Body = contactUs.Message; var smtp = new SmtpClient();
smtp.Send(mailMessage); var userMessage = new UserMessage
{
Received = System.DateTime.Now,
UserEmailAddress = contactUs.EmailAddress,
UserFullName = contactUs.FullName
};
db.UserMessages.Add(userMessage);
db.SaveChanges(); var crm = new CrmInterface();
crm.Connect();
crm.SaveContact(new CrmContact { EmailAddress = contactUs.EmailAddress, FullName = contactUs.FullName }); return Ok();
}
}
}
}

评估/回顾(Taking stock)

这种需求还在不断的增加。每一个新需求都需要修改已有的controller。

这存在一个潜在的风险。对存在的代码的每次修改都有可能破坏现有的功能。

而且这些代码是同步执行的。如果你的CRM系统或者邮件服务器挂了或者变慢了,你的客户在提交表单的页面要等待许久。

分离职责

现在,很明显controller承担了太多的工作。有一种解决方案是将不同的职责放到他们自己的类中去。

using BlogSite.Services.Crm;
using BlogSite.Services.Data;
using BlogSite.Services.Email;
using System.Web.Http; namespace BlogSite.Controllers.Api
{
public class ContactController : ApiController
{
private IEmailService _emailService;
private IUserMessageService _userMessageService;
private ICrm _crm; public ContactController(IEmailService emailService, IUserMessageService userMessageService, ICrm crm)
{
_emailService = emailService;
_userMessageService = userMessageService;
_crm = crm;
} public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
{
_emailService.Send(contactUs.EmailAddress, "you@yoursite.net", "Contact from web site", contactUs.Message);
_userMessageService.RecordUserMessageReceived(contactUs.EmailAddress, contactUs.FullName);
_crm.SaveContact(new CrmContact { EmailAddress = contactUs.EmailAddress, FullName = contactUs.FullName });
return Ok();
}
}
}

好些了,职责分离了,controller看起来整洁些了。

然后你还是在同步的运行(在这个例子中异步执行更好),controller和应用服务产生了耦合。如果要加什么新特性,你仍然需要修改controller。

使用MediatR Notifications

MediatR能给我们什么帮助呢?

比起在action里面写一些业务逻辑,MediatR可以让我们发布一个通知。

通知可以是异步或者同步的。

public class ContactController : ApiController
{
private IMediator _mediator; public ContactController(IMediator mediator)
{
_mediator = mediator;
} public async Task<IHttpActionResult> Post([FromBody]ContactUsForm contactUs)
{
var messageReceivedFromUserNotification = new MessageReceivedFromUserNotification {
EmailAddress = contactUs.EmailAddress,
FullName = contactUs.FullName,
Message = contactUs.Message,
SubmittedAt = DateTime.Now
};
await _mediator.PublishAsync(messageReceivedFromUserNotification); return Ok();
}
}

notification是一个通知,代表了一个通知的详细信息。

public class MessageReceivedFromUserNotification : IAsyncNotification
{
public DateTime SubmittedAt { get; set; }
public string FullName { get; set; }
public string EmailAddress { get; set; }
public string Message { get; set; }
}

接下来你需要为不同的action写不同的handler。

public class SaveUserMessage : IAsyncNotificationHandler<MessageReceivedFromUserNotification>
{
private ApplicationDbContext db = new ApplicationDbContext(); public async Task Handle(MessageReceivedFromUserNotification notification)
{
var userMessage = new UserMessage
{
Received = notification.SubmittedAt,
UserEmailAddress = notification.EmailAddress,
UserFullName = notification.FullName
};
db.UserMessages.Add(userMessage);
await db.SaveChangesAsync();
}
}
public class NotifySalesUserMessageReceived : IAsyncNotificationHandler<MessageReceivedFromUserNotification>
{
public async Task Handle(MessageReceivedFromUserNotification notification)
{
var mailMessage = new MailMessage(notification.EmailAddress, "you@yoursite.com");
mailMessage.Subject = "Contact from web site";
mailMessage.Body = notification.Message; var smtp = new SmtpClient();
await smtp.SendMailAsync(mailMessage);
}
}

[译]使用mediatR的notification来扩展的的应用的更多相关文章

  1. 「译」JUnit 5 系列:扩展模型(Extension Model)

    原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...

  2. [译]深入 NGINX: 为性能和扩展所做之设计

    来自:http://ifeve.com/inside-nginx-how-we-designed-for-performance-scale/ 这篇文章写了nginx的设计,写的很仔细全面, 同时里面 ...

  3. (译+注解)node.js的C++扩展入门

    声明:本文主要翻译自node.js addons官方文档.部分解释为作者自己添加. 编程环境: 1. 操作系统 Mac OS X 10.9.51. node.js v4.4.22. npm v3.9. ...

  4. 中介者模式及在NetCore中的使用MediatR来实现

    在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是"网状结构",它要求每个对象都必须知道它需要交互的对象.例如,每个人必须记住他(她)所有朋友的电话:而且, ...

  5. C++: Virtual Table and Shared Memory

    See at: 补充栏3: C++对象和共享内存 (叙述内容和Link1的内容基本一致) <C++网络编程 卷1:运用ACE和模式消除复杂性> <C++ Network Progra ...

  6. [转]非常好的vsftpd安装于配置

    环境:CentOS 5.0 操作系统一.安装:1.安装Vsftpd服务相关部件:[root@KcentOS5 ~]# yum install vsftpd*Dependencies Resolved= ...

  7. 我见过最好的vsftpd配置教程(转)

    环境:CentOS 5.0 操作系统一.安装:1.安装Vsftpd服务相关部件:[root@KcentOS5 ~]# yum install vsftpd*Dependencies Resolved= ...

  8. vsftp、ftps 搭建

    今天公司某个产品预上线,该产品需要向政府某部门提供一些数据. 该部门提交数据需要使用ftps,苦逼的我只能是测试环境搭建一套,用来测试提交数据. 先自行科普下ftps. 一.搭建vsftp 安装vsf ...

  9. centos6.5下vsftpd服务的安装及配置并通过pam认证实现虚拟用户文件共享

    FTP的全称是File Transfer Protocol(文件传输协议),就是专门用来传输文件的协议.它工作在OSI模型的第七层,即是应用层,使用TCP传输而不是UDP.这样FTP客户端和服务器建立 ...

随机推荐

  1. Python里的赋值 拷贝 深拷贝

    import copy a = [1, 2, 3, 4, ['a', 'b']] #原始对象 b = a #赋值,传对象的引用 c = copy.copy(a) #对象拷贝,浅拷贝 d = copy. ...

  2. [APIO/CTSC 2007]数据备份(贪心+堆)

    你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣. ...

  3. 2017蓝桥杯 省赛C题(承压计算)

    X星球的高科技实验室中整齐地堆放着某批珍贵金属原料. 每块金属原料的外形.尺寸完全一致,但重量不同.金属材料被严格地堆放成金字塔形. 7 5 8 7 8 8 9 2 7 2 8 1 4 9 1 8 1 ...

  4. Python之面向对象编程学习

    不知不觉,学到了python的面向对象编程思想.今天我们来讨论下面向对象编程的思想. 顾名思义,面向对象,就是面向于对象,这里所说的对象不是你现实生活中你的女朋友,你的老婆,你的爱人,在编程的世界里面 ...

  5. IT项目管理分享7个开源项目管理工具

    在一项调查中,有 71% 的组织表示他们在开发过程中会用到敏捷方法. 此外,用敏捷方法管理项目比传统方法管理项目成功率高 28%.在这次工具推荐中,我们从一些比较受欢迎的开源项目管理工具中摘取了支持敏 ...

  6. win10开机自启动

    快捷方式复制到win+ R 编辑shell:startup enter后的文件夹中

  7. java的线程

    public class Test1 extends Thread{ public void run(){ // } } public class Test2 immplement Runnable{ ...

  8. Unity 后处理堆

    Unity安装后处理的过程:windows---PacageManager---Post Processing Post Processing后处理堆需要知道要修改那个相机渲染的内容,先定位到相机,再 ...

  9. c#中委托和事件区别

    委托和事件相同的功能 class Dem5 { public Action deHandler; public event Action eveHa; public Dem5() { deHandle ...

  10. Linux系统诊断必备技能之一:lsof 用法详解!

    lsof(list open files)是一个查看当前系统文件的工具.在linux环境下,任何事物都以文件的形式存在,用户通过文件不仅可以访问常规数据,还可以访问网络连接和硬件:如传输控制协议 (T ...