[译]使用mediatR的notification来扩展的的应用

原文

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

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

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

MediatR Notifications能帮助我们。

例子

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

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

public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
{
    var mailMessage = new MailMessage(contactUs.EmailAddress, "[email protected]"); 
    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, "[email protected]"); 
    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, "[email protected]");
            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, "[email protected]", "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, "[email protected]");
        mailMessage.Subject = "Contact from web site";
        mailMessage.Body = notification.Message;

        var smtp = new SmtpClient();
        await smtp.SendMailAsync(mailMessage);
    }
}

猜你喜欢

转载自www.cnblogs.com/irocker/p/how-to-easily-extend-your-app-using-mediatr-notifications.html