接上一篇 Newbe.Claptrap 框架入门,第一步 —— 创建项目,实现简易购物车 ,我们继续要了解一下如何使用 Newbe.Claptrap 框架开发业务。通过本篇阅读,您便可以开始尝试使用 Claptrap 实现业务了。
本篇,我通过实现 “清空购物车” 的需求来了解一下如何在已有的项目样例中增加一个业务实现。
主要包含有以下这些步骤:
这是一个从下向上的过程,实际的编码过程中开发也可以自上而下进行实现。
EventCode 是 Claptrap 系统每个事件的唯一编码。其在事件的识别,序列化等方面起到了重要的作用。
打开 HelloClaptrap.Models
项目中的 ClaptrapCodes
类。
添加 “清空购物车事件” 的 EventCode。
| namespace HelloClaptrap.Models { public static class ClaptrapCodes { public const string CartGrain = "cart_claptrap_newbe"; private const string CartEventSuffix = "_e_" + CartGrain; public const string AddItemToCart = "addItem" + CartEventSuffix; public const string RemoveItemFromCart = "removeItem" + CartEventSuffix; + public const string RemoveAllItemsFromCart = "remoeAllItems" + CartEventSuffix; } } |
|:----|
Event 是事件溯源的关键。用于改变 Claptrap 中的 State。并且 Event 会被持久化在持久层。
在 HelloClaptrap.Models
项目的 Cart/Events
文件夹下创建 RemoveAllItemsFromCartEvent
类。
添加如下代码:
| + using Newbe.Claptrap; + + namespace HelloClaptrap.Models.Cart.Events + { + public class RemoveAllItemsFromCartEvent : IEventData + { + } + } |
|:----|
由于在这个简单的业务场景中,清空购物车不需要特定的参数。因此,只要创建空类型即可。
IEventData
接口是框架中表示事件的空接口,用于在泛型推断时使用。
EventHandler
用于将事件更新到 Claptrap 的 State
上。例如此次的业务场景,那么 EventHandler 就负责将 State 购物车中的内容清空即可。
在 HelloClaptrap.Actors
项目的 Cart/Events
文件夹下创建 RemoveAllItemsFromCartEventHandler
类。
添加如下代码:
| + using System.Threading.Tasks; + using HelloClaptrap.Models.Cart; + using HelloClaptrap.Models.Cart.Events; + using Newbe.Claptrap; + + namespace HelloClaptrap.Actors.Cart.Events + { + public class RemoveAllItemsFromCartEventHandler + : NormalEventHandler<CartState, RemoveAllItemsFromCartEvent> + { + public override ValueTask HandleEvent(CartState stateData, + RemoveAllItemsFromCartEvent eventData, + IEventContext eventContext) + { + stateData.Items = null; + return new ValueTask(); + } + } + } |
|:----|
这里有一些常见的问题:
stateData.Items = null;
而不用 stateData.Items.Clear();
stateData 是保存在内存中的对象,Clear 不会缩小字典已占用的自身内存。当然,一般一个购物车也不会有数十万商品。但其实关键是在于,更新 State 时,需要注意的是 Claptrap 是一种常驻于内存中的对象,数量增加时会加剧内存的消耗。因此,尽可能在 State 中保持更少的数据。EventHandler 实现完成之后,不要忘记对其进行单元测试。这里就不罗列了。
实现并测试完 EventHandler 之后,便可以将 EventHandler 进行注册,以便与 EventCode 以及 Claptrap 进行关联。
打开 HelloClaptrap.Actors
项目的 CartGrain
类。
使用 Attribute 进行标记。
| using Newbe.Claptrap; using Newbe.Claptrap.Orleans; namespace HelloClaptrap.Actors.Cart { ClaptrapEventHandler(typeof(AddItemToCartEventHandler), ClaptrapCodes.AddItemToCart) ClaptrapEventHandler(typeof(RemoveItemFromCartEventHandler), ClaptrapCodes.RemoveItemFromCart) + ClaptrapEventHandler(typeof(RemoveAllItemsFromCartEventHandler), ClaptrapCodes.RemoveAllItemsFromCart) public class CartGrain : ClaptrapBoxGrain<CartState>, ICartGrain { public CartGrain( IClaptrapGrainCommonService claptrapGrainCommonService) : base(claptrapGrainCommonService) { } .... |
|:----|
ClaptrapEventHandlerAttribute
是框架定义的一个 Attribute,可以标记在 Grain 的实现类上,以实现 EventHandler 、 EventCode 和 ClaptrapGrain 三者之间的关联。
关联之后,如果在此 Grain 中产生的对应 EventCode 的事件将会由指定的 EventHandler 进行处理。
修改 Grain 接口的定义,才能够提供外部与 Claptrap 的互操作性。
打开 HelloClaptrap.IActors
项目的 ICartGrain
接口。
添加接口以及 Attribute。
| using System.Collections.Generic; using System.Threading.Tasks; using HelloClaptrap.Models; using HelloClaptrap.Models.Cart; using HelloClaptrap.Models.Cart.Events; using Newbe.Claptrap; using Newbe.Claptrap.Orleans; namespace HelloClaptrap.IActor { ClaptrapState(typeof(CartState), ClaptrapCodes.CartGrain) ClaptrapEvent(typeof(AddItemToCartEvent), ClaptrapCodes.AddItemToCart) ClaptrapEvent(typeof(RemoveItemFromCartEvent), ClaptrapCodes.RemoveItemFromCart) + ClaptrapEvent(typeof(RemoveAllItemsFromCartEvent), ClaptrapCodes.RemoveAllItemsFromCart) public interface ICartGrain : IClaptrapGrain { Task<Dictionary<string, int>> AddItemAsync(string skuId, int count); Task<Dictionary<string, int>> RemoveItemAsync(string skuId, int count); Task<Dictionary<string, int>> GetItemsAsync(); + Task RemoveAllItemsAsync(); } } |
|:----|
其中增加了两部分内容:
ClaptrapEvent
,使得事件与 Grain 进行关联。注意,这里与前一步的 ClaptrapEventHandler
是不同的。此处标记的是 Event,上一步标记的是 EventHandler。接下来按照上一步的接口修改,来修改相应的实现类。
打开 HelloClaptrap.Actors
项目中的 Cart
文件夹下的 CartGrain
类。
添加对应的实现。
| using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using HelloClaptrap.Actors.Cart.Events; using HelloClaptrap.IActor; using HelloClaptrap.Models; using HelloClaptrap.Models.Cart; using HelloClaptrap.Models.Cart.Events; using Newbe.Claptrap; using Newbe.Claptrap.Orleans; namespace HelloClaptrap.Actors.Cart { ClaptrapEventHandler(typeof(AddItemToCartEventHandler), ClaptrapCodes.AddItemToCart) ClaptrapEventHandler(typeof(RemoveItemFromCartEventHandler), ClaptrapCodes.RemoveItemFromCart) ClaptrapEventHandler(typeof(RemoveAllItemsFromCartEventHandler), ClaptrapCodes.RemoveAllItemsFromCart) public class CartGrain : ClaptrapBoxGrain<CartState>, ICartGrain { public CartGrain( IClaptrapGrainCommonService claptrapGrainCommonService) : base(claptrapGrainCommonService) { } + public Task RemoveAllItemsAsync() + { + if (StateData.Items?.Any() != true) + { + return Task.CompletedTask; + } + + var removeAllItemsFromCartEvent = new RemoveAllItemsFromCartEvent(); + var evt = this.CreateEvent(removeAllItemsFromCartEvent); + return Claptrap.HandleEventAsync(evt); + } } } |
|:----|
增加了对接口方法的对应实现。需要注意的有以下几点:
if (StateData.Items?.Any() != true)
这行判断。因为这可以明显的减小存储的开销。
事件在当执行 Claptrap.HandleEventAsync(evt)
便会持久化。而就此处的场景而言,如果购物车中原本就没有内容,清空或者持久化这个事件只是增加开销,而没有实际的意义。
因此,在此之前增加判断可以减小存储的无用消耗。Claptrap.HandleEventAsync(evt)
之前进行判断尤为重要。
因此,一定要实现单元测试来确保 Event 的产生和 EventHandler 的处理逻辑已经被覆盖。前面的所有步骤完成之后,就已经完成了 Claptrap 的所有部分。但由于 Claptrap 无法直接提供与外部程序的互操作性。因此,还需要在在 Controller 层增加一个 API 以便外部进行 “清空购物车” 的操作。
打开 HelloClaptrap.Web
项目的 Controllers
文件夹下的 CartController
类。
| using System.Threading.Tasks; using HelloClaptrap.IActor; using Microsoft.AspNetCore.Mvc; using Orleans; namespace HelloClaptrap.Web.Controllers { [Route("api/controller")] public class CartController : Controller { private readonly IGrainFactory _grainFactory; public CartController( IGrainFactory grainFactory) { _grainFactory = grainFactory; } + HttpPost("{id}/clean") + public async Task<IActionResult> RemoveAllItemAsync(int id) + { + var cartGrain = _grainFactory.GetGrain<ICartGrain>(id.ToString()); + await cartGrain.RemoveAllItemsAsync(); + return Json("clean success"); + } } } |
|:----|
至此,我们就完成了 “清空购物车” 这个简单需求的所有内容。
您可以从以下地址来获取本文章对应的源代码:
最近作者正在构建以反应式
、Actor模式
和事件溯源
为理论基础的一套服务端开发框架。希望为开发者提供能够便于开发出 “分布式”、“可水平扩展”、“可测试性高” 的应用系统 ——Newbe.Claptrap
本篇文章是该框架的一篇技术选文,属于技术构成的一部分。如果读者对该内容感兴趣,欢迎转发、评论、收藏文章以及项目。您的支持是促进项目成功的关键。
联系方式:
您还可以查阅本系列的其他选文:
GitHub 项目地址:https://github.com/newbe36524/Newbe.Claptrap
Gitee 项目地址:https://gitee.com/yks/Newbe.Claptrap
您当前查看的是先行发布于 www.newbe.pro 上的博客文章,实际开发文档随版本而迭代。若要查看最新的开发文档,需要移步 http://claptrap.newbe.pro。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。