其实业务下单系统网站源码分享的问题并不复杂,但是又很多的朋友都不太了解业务订单接单下单流程,因此呢,今天小编就来为大家分享业务下单系统网站源码分享的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!
接上一篇Newbe.Claptrap框架入门,第三步——定义Claptrap,管理商品库存,我们继续要了解一下如何使用Newbe.Claptrap框架开发业务。通过本篇阅读,您便可以开始学会在Claptrap框架中使用Minion进行异步的业务处理。
Newbe.Claptrap是一个用于轻松应对并发问题的分布式开发框架。如果您是首次阅读本系列文章。建议可以先从本文末尾的入门文章开始了解。
开篇摘要
本篇,我通过实现“商品下单”的需求来了解一下如何在已有的项目样例中使用Minion来完成异步的业务处理。
首先,先了解一下本篇需要涉及的业务用例:
用户可以进行下单操作,下单时将使用当前购物车中的所有SKU形成一个订单。下单后将会扣除相关SKU的库存。如果某一SKU库存不足,则下单失败。下单操作仅到扣减库存成功为止,后续步骤不需要本样例讨论范围。因此,本样例在成功下单之后会在数据库中生成一条订单记录,表示订单创建结束。
本篇虽然重点在于Minion的使用,不过由于需要使用到一个新的OrderGrain对象,因此还是需要使用到前一篇“定义Claptrap”的相关知识。
Minion是一种特殊的Claptrap,它与其MasterClaptrap之间的关系如下图所示:
其主体开发流程和Claptrap类似,只是有所删减。对比如下:
步骤ClaptrapMinion定义ClaptrapTypeCode??定义State??定义Grain接口??实现Grain??注册Grain??定义EventCode?定义Event?实现EventHandler??注册EventHandler??实现IInitialStateDataFactory??
这个删减的原因是由于Minion是Claptrap的事件消费者,所以事件相关的定义不需要处理。但是其他的部分仍然是必须的。
本篇开始,我们将不再罗列相关代码所在的具体文件位置,希望读者能够自行在项目中进行查找,以便熟练的掌握。
实现OrderGrain
基于前一篇“定义Claptrap”相关的知识,我们此处实现一个OrderGrain用来表示订单下单操作。为节约篇幅,我们只罗列其中关键的部分。
OrderState
订单状态的定义如下:
usingSystem.Collections.Generic;\nusingNewbe.Claptrap;\n\nnamespaceHelloClaptrap.Models.Order\n{\npublicclassOrderState:IStateData\n{\npublicboolOrderCreated{get;set;}\npublicstringUserId{get;set;}\npublicDictionary<string,int>Skus{get;set;}\n}\n}
OrderCreated表示订单是否已经创建,避免重复创建订单UserId下单用户IdSkus订单包含的SkuId和订单量
OrderCreatedEvent
订单创建事件的定义如下:
usingSystem.Collections.Generic;\nusingNewbe.Claptrap;\n\nnamespaceHelloClaptrap.Models.Order.Events\n{\npublicclassOrderCreatedEvent:IEventData\n{\npublicstringUserId{get;set;}\npublicDictionary<string,int>Skus{get;set;}\n}\n}
OrderGrain
usingSystem.Threading.Tasks;\nusingHelloClaptrap.Actors.Order.Events;\nusingHelloClaptrap.IActor;\nusingHelloClaptrap.Models;\nusingHelloClaptrap.Models.Order;\nusingHelloClaptrap.Models.Order.Events;\nusingNewbe.Claptrap;\nusingNewbe.Claptrap.Orleans;\nusingOrleans;\n\nnamespaceHelloClaptrap.Actors.Order\n{\n[ClaptrapEventHandler(typeof(OrderCreatedEventHandler),ClaptrapCodes.OrderCreated)]\npublicclassOrderGrain:ClaptrapBoxGrain<OrderState>,IOrderGrain\n{\nprivatereadonlyIGrainFactory_grainFactory;\n\npublicOrderGrain(IClaptrapGrainCommonServiceclaptrapGrainCommonService,\nIGrainFactorygrainFactory)\n:base(claptrapGrainCommonService)\n{\n_grainFactory=grainFactory;\n}\n\npublicasyncTaskCreateOrderAsync(CreateOrderInputinput)\n{\nvarorderId=Claptrap.State.Identity.Id;\n//throwexceptioniforderalreadycreated\nif(StateData.OrderCreated)\n{\nthrownewBizException($&34;);\n}\n\n//getitemsfromcart\nvarcartGrain=_grainFactory.GetGrain<ICartGrain>(input.CartId);\nvaritems=awaitcartGrain.GetItemsAsync();\n\n//updateinventoryforeachsku\nforeach(var(skuId,count)initems)\n{\nvarskuGrain=_grainFactory.GetGrain<ISkuGrain>(skuId);\nawaitskuGrain.UpdateInventoryAsync(-count);\n}\n\n//removeallitemsfromcart\nawaitcartGrain.RemoveAllItemsAsync();\n\n//createaorder\nvarevt=this.CreateEvent(newOrderCreatedEvent\n{\nUserId=input.UserId,\nSkus=items\n});\nawaitClaptrap.HandleEventAsync(evt);\n}\n}\n}
OrderGrain实现订单的创建核心逻辑,其中的CreateOrderAsync方法完成购物车数据获取,库存扣减相关的动作。OrderCreatedEvent执行成功后将会更新State中相关的字段,此处就不再列出了。
通过Minion向数据库保存订单数据
从系列开头到此,我们从未提及数据库相关的操作。因为当您在使用Claptrap框架时,绝大多数的操作都已经被“事件的写入”和“状态的更新”代替了,故而完全不需要亲自编写数据库操作。
不过,由于Claptrap通常是对应单体对象(一个订单,一个SKU,一个购物车)而设计的,因而无法获取全体(所有订单,所有SKU,所有购物车)的数据情况。此时,就需要将状态数据持久化到另外的持久化结构中(数据库,文件,缓存等)以便完成全体情况的查询或其他操作。
在Claptrap框架中引入了Minion的概念来解决上述的需求。
接下来,我们就在样例中引入一个OrderDbGrain(一个Minion)来异步完成OrderGrain的订单入库操作。
定义ClaptrapTypeCode
namespaceHelloClaptrap.Models\n{\npublicstaticclassClaptrapCodes\n{\n34;cart_claptrap_newbe&34;_e_&34;addItem&34;removeItem&34;remoeAllItems&endregion\n\n34;sku_claptrap_newbe&34;_e_&34;inventoryUpdate&endregion\n\n34;order_claptrap_newbe&34;_e_&34;orderCreated&34;db_order_claptrap_newbe&endregion\n}\n}
Minion是一种特殊的Claptrap,换言之,它也是一种Claptrap。而ClaptrapTypeCode对于Claptrap来说是必需的,因而需要增加此定义。
定义State
由于本样例只需要向数据库写入一条订单记录就可以了,并不需要在State中任何数据,因此该步骤在本样例中其实并不需要。
定义Grain接口
+usingHelloClaptrap.Models;\n+usingNewbe.Claptrap;\n+usingNewbe.Claptrap.Orleans;\n+\n+namespaceHelloClaptrap.IActor\n+{\n+[ClaptrapMinion(ClaptrapCodes.OrderGrain)]\n+[ClaptrapState(typeof(NoneStateData),ClaptrapCodes.OrderDbGrain)]\n+publicinterfaceIOrderDbGrain:IClaptrapMinionGrain\n+{\n+}\n+}
ClaptrapMinion用来标记该Grain是一个Minion,其中的Code指向其对应的MasterClaptrap。ClaptrapState用来标记Claptrap的State数据类型。前一步,我们阐明该Minion并不需要StateData,因此使用NoneStateData这一框架内置类型来代替。IClaptrapMinionGrain是区别于IClaptrapGrain的Minion接口。如果一个Grain是Minion,则需要继承该接口。ClaptrapCodes.OrderGrain和ClaptrapCodes.OrderDbGrain是两个不同的字符串,希望读者不是星际宗师。
星际宗师:因为星际争霸比赛节奏快,信息量大,选手很容易忽视或误判部分信息,因此经常发生“选手看不到发生在眼皮底下的关键事件”的搞笑失误。玩家们由此调侃星际玩家都是瞎子(曾经真的有一场盲人和职业选手的对决),段位越高,瞎得越严重,职业星际选手清一色的盲人。
实现Grain
+usingSystem.Collections.Generic;\n+usingSystem.Threading.Tasks;\n+usingHelloClaptrap.Actors.DbGrains.Order.Events;\n+usingHelloClaptrap.IActor;\n+usingHelloClaptrap.Models;\n+usingNewbe.Claptrap;\n+usingNewbe.Claptrap.Orleans;\n+\n+namespaceHelloClaptrap.Actors.DbGrains.Order\n+{\n+[ClaptrapEventHandler(typeof(OrderCreatedEventHandler),ClaptrapCodes.OrderCreated)]\n+publicclassOrderDbGrain:ClaptrapBoxGrain<NoneStateData>,IOrderDbGrain\n+{\n+publicOrderDbGrain(IClaptrapGrainCommonServiceclaptrapGrainCommonService)\n+:base(claptrapGrainCommonService)\n+{\n+}\n+\n+publicasyncTaskMasterEventReceivedAsync(IEnumerable<IEvent>events)\n+{\n+foreach(var@eventinevents)\n+{\n+awaitClaptrap.HandleEventAsync(@event);\n+}\n+}\n+\n+publicTaskWakeAsync()\n+{\n+returnTask.CompletedTask;\n+}\n+}\n+}
MasterEventReceivedAsync是定义自IClaptrapMinionGrain的方法,表示实时接收来自MasterClaptrap的事件通知。此处暂不展开说明,按照上文模板实现即可。WakeAsync是定义自IClaptrapMinionGrain的方法,表示MasterClaptrap主动唤醒Minion的操作。此处暂不展开说明,按照上文模板实现即可。当读者查看源码时,会发现该类被单独定义在一个程序集当中。这只是一种分类办法,可以理解为将Minion和MasterClaptrap分别放置在两个不同的项目中进行分类。实际上放在一起也没有问题。
注册Grain
此处,由于我们将OrderDbGrain定义在单独的程序集,因此,需要额外的注册这个程序集。如下所示:
usingSystem;\nusingAutofac;\nusingHelloClaptrap.Actors.Cart;\nusingHelloClaptrap.Actors.DbGrains.Order;\nusingHelloClaptrap.IActor;\nusingHelloClaptrap.Repository;\nusingMicrosoft.AspNetCore.Hosting;\nusingMicrosoft.Extensions.Hosting;\nusingMicrosoft.Extensions.Logging;\nusingNewbe.Claptrap;\nusingNewbe.Claptrap.Bootstrapper;\nusingNLog.Web;\nusingOrleans;\n\nnamespaceHelloClaptrap.BackendServer\n{\npublicclassProgram\n{\npublicstaticvoidMain(string[]args)\n{\nvarlogger=NLogBuilder.ConfigureNLog(&34;).GetCurrentClassLogger();\ntry\n{\nlogger.Debug(&34;);\nCreateHostBuilder(args).Build().Run();\n}\ncatch(Exceptionexception)\n{\n//NLog:catchsetuperrors\nlogger.Error(exception,&34;);\nthrow;\n}\nfinally\n{\n//Ensuretoflushandstopinternaltimers/threadsbeforeapplication-exit(AvoidsegmentationfaultonLinux)\nNLog.LogManager.Shutdown();\n}\n}\n\npublicstaticIHostBuilderCreateHostBuilder(string[]args)=>\nHost.CreateDefaultBuilder(args)\n.ConfigureWebHostDefaults(webBuilder=>{webBuilder.UseStartup<Startup>();})\n.UseClaptrap(\nbuilder=>\n{\nbuilder\n.ScanClaptrapDesigns(new[]\n{\ntypeof(ICartGrain).Assembly,\ntypeof(CartGrain).Assembly,\n+typeof(OrderDbGrain).Assembly\n})\n.ConfigureClaptrapDesign(x=>\nx.ClaptrapOptions.EventCenterOptions.EventCenterType=EventCenterType.OrleansClient);\n},\nbuilder=>{builder.RegisterModule<RepositoryModule>();})\n.UseOrleansClaptrap()\n.UseOrleans(builder=>builder.UseDashboard(options=>options.Port=9000))\n.ConfigureLogging(logging=>\n{\nlogging.ClearProviders();\nlogging.SetMinimumLevel(LogLevel.Trace);\n})\n.UseNLog();\n}\n}
实现EventHandler
+usingSystem.Threading.Tasks;\n+usingHelloClaptrap.Models.Order.Events;\n+usingHelloClaptrap.Repository;\n+usingNewbe.Claptrap;\n+usingNewtonsoft.Json;\n+\n+namespaceHelloClaptrap.Actors.DbGrains.Order.Events\n+{\n+publicclassOrderCreatedEventHandler\n+:NormalEventHandler<NoneStateData,OrderCreatedEvent>\n+{\n+privatereadonlyIOrderRepository_orderRepository;\n+\n+publicOrderCreatedEventHandler(\n+IOrderRepositoryorderRepository)\n+{\n+_orderRepository=orderRepository;\n+}\n+\n+publicoverrideasyncValueTaskHandleEvent(NoneStateDatastateData,\n+OrderCreatedEventeventData,\n+IEventContexteventContext)\n+{\n+varorderId=eventContext.State.Identity.Id;\n+await_orderRepository.SaveAsync(eventData.UserId,orderId,JsonConvert.SerializeObject(eventData.Skus));\n+}\n+}\n+}
IOrderRepository是直接操作存储层的接口,用于订单的增删改查。此处调用该接口实现订单数据库的入库操作。
注册EventHandler
实际上为了节约篇幅,我们已经在“实现Grain”章节的代码中进行注册。
实现IInitialStateDataFactory
由于StateData没有特殊定义,因此也不需要实现IInitialStateDataFactory。
修改Controller
样例中,我们增加了OrderController用来下单和查询订单。读者可以在源码进行查看。
读者可以使用以下步骤进行实际的效果测试:
POST/api/cart/123{“skuId”:”yueluo-666”,”count”:30}向123号购物车加入30单位的yueluo-666号浓缩精华。POST/api/order{“userId”:”999”,”cartId”:”123”}以999userId的身份,从123号购物车进行下单。GET/api/order下单成功后可以,通过该API查看到下单完成的订单。GET/api/sku/yueluo-666可以通过SKUAPI查看下单后的库存余量。
小结
至此,我们就完成了“商品下单”这个需求的基础内容。通过该样例可以初步了解多个Claptrap可以如何合作,以及如何使用Minion完成异步任务。
不过,还有一些问题,我们将在后续展开讨论。
您可以从以下地址来获取本文章对应的源代码:
GithubGitee
最后但是最重要!
最近作者正在构建以反应式、Actor模式和事件溯源为理论基础的一套服务端开发框架。希望为开发者提供能够便于开发出“分布式”、“可水平扩展”、“可测试性高”的应用系统——Newbe.Claptrap
本篇文章是该框架的一篇技术选文,属于技术构成的一部分。如果读者对该内容感兴趣,欢迎转发、评论、收藏文章以及项目。您的支持是促进项目成功的关键。
联系方式:
GithubIssueGiteeIssue公开邮箱newbe-claptrap@googlegroups.com(发送到该邮箱的内容将被公开)GitterQQ群610394020
您还可以查阅本系列的其他选文:
理论入门篇
Newbe.Claptrap-一套以“事件溯源”和“Actor模式”作为基本理论的服务端开发框架
术语介绍篇
Actor模式事件溯源(EventSourcing)ClaptrapMinion事件(Event)状态(State)状态快照(StateSnapshot)Claptrap设计图(ClaptrapDesign)Claptrap工厂(ClaptrapFactory)ClaptrapIdentityClaptrapBoxClaptrap生命周期(ClaptrapLifetimeScope)序列化(Serialization)
实现入门篇
Newbe.Claptrap框架入门,第一步——创建项目,实现简易购物车Newbe.Claptrap框架入门,第二步——简单业务,清空购物车Newbe.Claptrap框架入门,第三步——定义Claptrap,管理商品库存
样例实践篇
构建一个简易的火车票售票系统,Newbe.Claptrap框架用例,第一步——业务分析在线体验火车票售票系统
其他番外篇
谈反应式编程在服务端中的应用,数据库操作优化,从20秒到0.5秒谈反应式编程在服务端中的应用,数据库操作优化,提速Upsert十万同时在线用户,需要多少内存?——Newbe.Claptrap框架水平扩展实验docker-mcr助您全速下载dotnet镜像十多位全球技术专家,为你献上近十个小时的.Net微服务介绍年轻的樵夫哟,你掉的是这个免费8核4G公网服务器,还是这个随时可用的Docker实验平台?
GitHub项目地址:https://github.com/newbe36524/Newbe.Claptrap
Gitee项目地址:https://gitee.com/yks/Newbe.Claptrap
您当前查看的是先行发布于www.newbe.pro上的博客文章,实际开发文档随版本而迭代。若要查看最新的开发文档,需要移步claptrap.newbe.pro。
本文作者:newbe36524本文链接:https://www.newbe.pro/Newbe.Claptrap/Get-Started-4/版权声明:本博客所有文章除特别声明外,均采用BY-NC-SA许可协议。转载请注明出处!
关于业务下单系统网站源码分享的内容到此结束,希望对大家有所帮助。
