Newbe.Claptrap 框架入门,第二步 —— 简单业务,清空购物车

接上一篇 Newbe.Claptrap 框架入门,第一步 —— 建立项目,实现浅易购物车 ,我们继续要领会一下若何使用 Newbe.Claptrap 框架开发营业。通过本篇阅读,您便可以最先实验使用 Claptrap 实现营业了。

开篇摘要

本篇,我通过实现 “清空购物车” 的需求来领会一下若何在已有的项目样例中增添一个营业实现。

主要包罗有以下这些步骤:

  1. 界说 EventCode
  2. 界说 Event
  3. 实现 EventHandler
  4. 注册 EventHandler
  5. 修改 Grain 接口
  6. 实现 Grain
  7. 修改 Controller

这是一个从下向上的历程,现实的编码历程中开发也可以自上而下举行实现。

界说 Event Code

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

Event 是事宜溯源的要害。用于改变 Claptrap 中的 State。而且 Event 会被持久化在持久层。

在 HelloClaptrap.Models 项目的 Cart/Events 文件夹下建立 RemoveAllItemsFromCartEvent 类。

添加如下代码:

 

+ using Newbe.Claptrap;
+
+ namespace HelloClaptrap.Models.Cart.Events
+ {
+     public class RemoveAllItemsFromCartEvent : IEventData
+     {
+     }
+ }

 

由于在这个简朴的营业场景中,清空购物车不需要特定的参数。因此,只要建立空类型即可。

IEventData 接口是框架中示意事宜的空接口,用于在泛型推断时使用。

实现 EventHandler

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();
+         }
+     }
+ }

 

这里有一些常见的问题:

  1. NormalEventHandler 是什么?

    NormalEventHandler 是框架界说的一个简朴基类,用于利便实现 Handler。
    其中第一个泛型参数是 Claptrap 对应的 State 类型。连系前篇文档中,我们的购物车 State 类型就是 CartState。
    第二个泛型参数是该 Handler 需要处置的 Event 类型。

  2. 为什么用 stateData.Items = null; 而不用 stateData.Items.Clear();

    stateData 是保留在内存中的工具,Clear 不会缩小字典已占用的自身内存。固然,一样平常一个购物车也不会有数十万商品。但实在要害是在于,更新 State 时,需要注重的是 Claptrap 是一种常驻于内存中的工具,数目增添时会加剧内存的消耗。因此,尽可能在 State 中保持更少的数据。

  3. ValueTask 是什么?

    可以通过这篇《Understanding the Whys, Whats, and Whens of ValueTask》举行领会。

EventHandler 实现完成之后,不要遗忘对其举行单元测试。这里就不罗列了。

注册 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)
          {
          }

          ....

 

「从零单排canal 05」 server模块源码解析

ClaptrapEventHandlerAttribute 是框架界说的一个 Attribute,可以符号在 Grain 的实现类上,以实现 EventHandler 、 EventCode 和 ClaptrapGrain 三者之间的关联。

关联之后,若是在此 Grain 中发生的对应 EventCode 的事宜将会由指定的 EventHandler 举行处置。

修改 Grain 接口

修改 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();
      }
  }

 

其中增添了两部门内容:

  1. 符号了 ClaptrapEvent,使得事宜与 Grain 举行关联。注重,这里与前一步的 ClaptrapEventHandler 是差别的。此处符号的是 Event,上一步符号的是 EventHandler。
  2. 增添了 RemoveAllItemsAsync 方式,示意 “清空购物车” 的营业行为。需要注重的是 Grain 的方式界说有一定限制。详细可以参见《Developing a Grain》

实现 Grain

接下来根据上一步的接口修改,来修改响应的实现类。

打开 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);
+         }
      }
  }

 

增添了对接口方式的对应实现。需要注重的有以下几点:

  1. 一定要增添 if (StateData.Items?.Any() != true) 这行判断。由于这可以显著的减小存储的开销。

    事宜在当执行 Claptrap.HandleEventAsync(evt) 便会持久化。而就此处的场景而言,若是购物车中原本就没有内容,清空或者持久化这个事宜只是增添开销,而没有现实的意义。
    因此,在此之前增添判断可以减小存储的无用消耗。

  2. 一定要判断 State 以及传入参数是否知足事宜执行的条件。

    这与上一点所形貌的内容偏重差别。上一点偏重解释 “不要发生没有意义的事宜”,这一点解释 “绝不发生 EventHandler 无法消费的事宜”。
    在事宜溯源模式中,营业的完成是以事宜的持久化完成作为营业确定完成的依据。也就是说事宜只要入库了,就可以以为这个事宜已经完成了。
    而在 EventHandler 中,只能接受从持久化层读出的事宜。此时,根据事宜的不能变性,已经无法再修改事宜,因此一定要确保事宜是可以被 EventHandler 消费的。以是,在 Claptrap.HandleEventAsync(evt) 之前举行判断尤为主要。
    因此,一定要实现单元测试来确保 Event 的发生和 EventHandler 的处置逻辑已经被笼罩。

  3. 此处需要使用到一些 TAP 库中的一些方式,可以参见基于义务的异步模式

修改 Controller

前面的所有步骤完成之后,就已经完成了 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

本篇文章是该框架的一篇手艺选文,属于手艺组成的一部门。若是读者对该内容感兴趣,迎接转发、谈论、珍藏文章以及项目。您的支持是促进项目乐成的要害。

联系方式:

您还可以查阅本系列的其他选文:

  1. Newbe.Claptrap – 一套以 “事宜溯源” 和 “Actor 模式” 作为基本理论的服务端开发框架
  2. 十万同时在线用户,需要若干内存?——Newbe.Claptrap 框架水平扩展实验
  3. 谈反应式编程在服务端中的应用,数据库操作优化,从 20 秒到 0.5 秒
  4. 谈反应式编程在服务端中的应用,数据库操作优化,提速 Upsert
  5. docker-mcr 助您全速下载 dotnet 镜像
  6. Newbe.Claptrap 项目周报 1 – 还没轮影,先用轮跑
  7. Newbe.Claptrap 框架入门,第一步 —— 建立项目,实现浅易购物车
  8. Newbe.Claptrap 框架入门,第二步 —— 简朴营业,清空购物车
  9. Newbe.Claptrap 框架中为什么用 Claptrap 和 Minion 两个词?

GitHub 项目地址:https://github.com/newbe36524/Newbe.Claptrap

Gitee 项目地址:https://gitee.com/yks/Newbe.Claptrap

您当前查看的是先行公布于 www.newbe.pro 上的博客文章,现实开发文档随版本而迭代。若要查看最新的开发文档,需要移步 http://claptrap.newbe.pro

Newbe.Claptrap 框架入门,第二步 —— 简单业务,清空购物车

 

原创文章,作者:28x0新闻网,如若转载,请注明出处:https://www.28x0.com/archives/24197.html