前言
源碼地址:
https://github.com/SkylerSkr/Skr3D
在前面的文章里秽誊,我們會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題躯保。
通過(guò)MediatR發(fā)送處理的請(qǐng)求蹄衷,返回值都是void,這等于說(shuō)變成了發(fā)后即忘的狀態(tài)。在正常開(kāi)發(fā)中毡证,這是不被允許的,至少系統(tǒng)出現(xiàn)異常蔫仙,要返回結(jié)果出去料睛。
而通知的問(wèn)題,可以歸入領(lǐng)域事件Event摇邦。
領(lǐng)域事件
以我們的例子恤煞,假設(shè)領(lǐng)導(dǎo)加了一個(gè)需求:創(chuàng)建訂單后,需要給用戶發(fā)送通知短信施籍。
可能你會(huì)直接在處理完訂單后居扒,直接加上一段SendMessage的代碼,但是問(wèn)題來(lái)了法梯,發(fā)短信跟處理訂單有關(guān)系嘛苔货?
發(fā)送短信是創(chuàng)建訂單必須的功能嘛,顯然不是立哑。那么如果以后頻繁加入類似短信夜惭,郵件或者其他與當(dāng)前業(yè)務(wù)無(wú)關(guān)的代碼,那么項(xiàng)目遲早面目全非铛绰。所以我們加入了領(lǐng)域事件诈茧。
領(lǐng)域事件:對(duì)于業(yè)務(wù)來(lái)說(shuō),不是必定的捂掰,可以變化的業(yè)務(wù)敢会,是領(lǐng)域事件。
事件抽象類这嚣,和實(shí)現(xiàn)
/// <summary>
/// 事件模型 抽象基類鸥昏,繼承 INotification
/// 也就是說(shuō),擁有中介者模式中的 發(fā)布/訂閱模式
/// 同時(shí)繼承了Messgae 也就是繼承了 請(qǐng)求/響應(yīng)模式
/// </summary>
public abstract class Event : INotification
{
// 時(shí)間戳
public DateTime Timestamp { get; private set; }
// 每一個(gè)事件都是有狀態(tài)的
protected Event()
{
Timestamp = DateTime.Now;
}
}
public class RegisterOrderEvent : Event
{
}
/// <summary>
/// 領(lǐng)域通知模型姐帚,用來(lái)獲取當(dāng)前總線中出現(xiàn)的通知信息
/// 繼承自領(lǐng)域事件和 INotification(也就意味著可以擁有中介的發(fā)布/訂閱模式)
/// </summary>
public class DomainNotification : Event
{
// 標(biāo)識(shí)
public Guid DomainNotificationId { get; private set; }
// 鍵(可以根據(jù)這個(gè)key吏垮,獲取當(dāng)前key下的全部通知信息)
// 這個(gè)我們?cè)谑录春褪录厮莸臅r(shí)候會(huì)用到,伏筆
public string Key { get; private set; }
// 值(與key對(duì)應(yīng))
public string Value { get; private set; }
// 版本信息
public int Version { get; private set; }
public DomainNotification(string key, string value)
{
DomainNotificationId = Guid.NewGuid();
Version = 1;
Key = key;
Value = value;
}
}
我們用同樣的方式寫事件的處理程序罐旗,實(shí)現(xiàn)INotificationHandler<RegisterOrderEvent>和INotificationHandler<DomainNotification>
public class OrderEventHandler : INotificationHandler<RegisterOrderEvent>
{
public Task Handle(RegisterOrderEvent notification, CancellationToken cancellationToken)
{
// 恭喜您膳汪,注冊(cè)成功削祈,歡迎加入我們拄轻。
return Task.CompletedTask;
}
}
/// <summary>
/// 領(lǐng)域通知處理程序,把所有的通知信息放到事件總線中
/// 繼承 INotificationHandler<T>
/// </summary>
public class DomainNotificationHandler : INotificationHandler<DomainNotification>
{
// 通知信息列表
private List<DomainNotification> _notifications;
// 每次訪問(wèn)該處理程序的時(shí)候臣缀,實(shí)例化一個(gè)空集合
public DomainNotificationHandler()
{
_notifications = new List<DomainNotification>();
}
// 處理方法鼓蜒,把全部的通知信息痹换,添加到內(nèi)存里
public Task Handle(DomainNotification message, CancellationToken cancellationToken)
{
_notifications.Add(message);
return Task.CompletedTask;
}
// 獲取當(dāng)前生命周期內(nèi)的全部通知信息
public virtual List<DomainNotification> GetNotifications()
{
return _notifications;
}
// 判斷在當(dāng)前總線對(duì)象周期中征字,是否存在通知信息
public virtual bool HasNotifications()
{
return GetNotifications().Any();
}
// 手動(dòng)回收(清空通知)
public void Dispose()
{
_notifications = new List<DomainNotification>();
}
}
依賴注入:
public class NativeInjectorBootStrapper
{
public static void RegisterServices(IServiceCollection services)
{
// ASP.NET HttpContext dependency
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// ASP.NET Authorization Polices
//services.AddSingleton<IAuthorizationHandler, ClaimsRequirementHandler>();
// 注入 應(yīng)用層Application
services.AddScoped<IOrderAppService, OrderAppService>();
//命令總線Domain Bus(Mediator)
services.AddScoped<IMediatorHandler, InMemoryBus>();
// 領(lǐng)域?qū)?- 領(lǐng)域命令
// 將命令模型和命令處理程序匹配
services.AddScoped<IRequestHandler<RegisterOrderCommand, Unit>, OrderCommandHandler>();
// 領(lǐng)域事件
services.AddScoped<INotificationHandler<RegisterOrderEvent>, OrderEventHandler>();
// 領(lǐng)域通知
services.AddScoped<INotificationHandler<DomainNotification>, DomainNotificationHandler>();
// 領(lǐng)域?qū)?- Memory
services.AddSingleton<IMemoryCache>(factory =>
{
var cache = new MemoryCache(new MemoryCacheOptions());
return cache;
});
// 注入 基礎(chǔ)設(shè)施層 - 數(shù)據(jù)層
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<OrderContext>();
}
}
領(lǐng)域?qū)犹幚硗暾{(diào)用事件:
/// <summary>
/// Order命令處理程序
/// 用來(lái)處理該Order下的所有命令
/// 注意必須要繼承接口IRequestHandler<,>,這樣才能實(shí)現(xiàn)各個(gè)命令的Handle方法
/// </summary>
public class OrderCommandHandler : CommandHandler,
IRequestHandler<RegisterOrderCommand, Unit>
{
// 注入倉(cāng)儲(chǔ)接口
private readonly IOrderRepository _OrderRepository;
// 注入總線
private readonly IMediatorHandler Bus;
/// <summary>
/// 構(gòu)造函數(shù)注入
/// </summary>
/// <param name="OrderRepository"></param>
/// <param name="uow"></param>
/// <param name="bus"></param>
/// <param name="cache"></param>
public OrderCommandHandler(IOrderRepository OrderRepository,
IMediatorHandler bus
) : base( bus)
{
_OrderRepository = OrderRepository;
Bus = bus;
}
// RegisterOrderCommand命令的處理程序
// 整個(gè)命令處理程序的核心都在這里
// 不僅包括命令驗(yàn)證的收集晴音,持久化柔纵,還有領(lǐng)域事件和通知的添加
public Task<Unit> Handle(RegisterOrderCommand message, CancellationToken cancellationToken)
{
// 實(shí)例化領(lǐng)域模型,這里才真正的用到了領(lǐng)域模型
// 注意這里是通過(guò)構(gòu)造函數(shù)方法實(shí)現(xiàn)
var Order = new Order(Guid.NewGuid(), message.Name, message.Address, message.OrderItem);
//返回錯(cuò)誤
if (Order.Name.Equals("Err"))
{
Bus.RaiseEvent(new DomainNotification("", "訂單名為Err"));
return Task.FromResult(new Unit());
}
// 持久化
_OrderRepository.Add(Order);
if (_OrderRepository.SaveChanges() > 0)
{
Bus.RaiseEvent(new RegisterOrderEvent());
}
Bus.RaiseEvent(new DomainNotification("", "Register成功") );
return Task.FromResult(new Unit());
}
// 手動(dòng)回收
public void Dispose()
{
_OrderRepository.Dispose();
}
}
在UI層獲取消息:
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
private readonly IOrderAppService _OrderAppService;
private readonly DomainNotificationHandler _notification;
public OrderController(IOrderAppService OrderAppService, INotificationHandler<DomainNotification> notification)
{
_OrderAppService = OrderAppService;
_notification = (DomainNotificationHandler) notification;
}
// POST: Order/Create
// 方法
[HttpPost("Create")]
//[ValidateAntiForgeryToken]
public object Create([FromBody]OrderViewModel OrderViewModel)
{
// 視圖模型驗(yàn)證
if (!ModelState.IsValid)
return false;
// 這里為了測(cè)試锤躁,手動(dòng)賦值items
OrderViewModel.Items = new List<OrderItemViewModel>() {
new OrderItemViewModel (){
Name="詳情"+DateTime.Now
}
};
// 執(zhí)行添加方法
_OrderAppService.Register(OrderViewModel);
if (_notification.HasNotifications())
{
return _notification.GetNotifications();
}
return true;
}
}
總結(jié):
到這里為止搁料,這個(gè)系列就算完成了。此系列重點(diǎn)講解DDD和微服務(wù)的思想系羞,其他部分都以最簡(jiǎn)單的方式實(shí)現(xiàn)郭计。請(qǐng)大佬們不要噴我!我已經(jīng)很努力的寫了椒振!嗚嗚嗚嗚嗚昭伸!