做了一个 go 语言实现的简化应用开发的框架 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
zhengchengdong
V2EX    分享创造

做了一个 go 语言实现的简化应用开发的框架

  •  
  •   zhengchengdong 2022-10-07 18:11:40 +08:00 1924 次点击
    这是一个创建于 1171 天前的主题,其中的信息可能已经有所发展或是发生改变。

    https://github.com/zhengchengdong/ARP4G

    ARP4G

    ARP4G 是一个 go 语言实现的简化应用开发的框架。

    通过 Aggregate (聚合)、Repository (仓库)、Process (过程) 3 个概念,隔离业务逻辑和技术实现细节,使开发者专注于产品业务本身。

    一个简单的例子

    func (serv *OrderService) CompleteOrder(ctx context.Context, orderId string) *Order { //从仓库取出 order order, _ := serv.orderRepository.Take(ctx, orderId) if order.state == "ongoing" { //改变他的状态 order.state = "compleated" //返回改变后的 order return order } return nil } type OrderService struct { orderRepository OrderRepository } type OrderRepository interface { Take(ctx context.Context, id any) (*Order, bool) } 

    这里我们首先从订单仓库取出了一个订单(聚合),随后改变了他的状态,变成“已完成”,最后返回了这个“已完成”的订单。

    这是一段业务代码,在这过程我们不关心查询和保存这些和数据库打交道的事情,我们也不关心 “并发改变订单状态所带来的问题” 这样的复杂技术细节,代码中没有任何技术细节,只有业务。

    我们需要做的仅仅是调用该业务方法时用 ARP4G 包装一下(无需侵入这段纯粹的业务代码),ARP4G 就会为你照顾一切技术细节。

    订单仓库 “orderRepository” 不需要开发,可以使用[ARP4G 的不同实现](#ARP4G 的不同实现)来实现

    安装

    1. 首先需要 Go 已安装(1.18 及以上版本), 然后可以用以下命令安装 ARP4G 。
    go get -u github.com/zhengchengdong/ARP4G 
    1. 在你的代码中 import:
    import "github.com/zhengchengdong/ARP4G" 

    快速开始

    package main import ( "context" "fmt" "github.com/zhengchengdong/ARP4G/arp" ) func main() { greetingService := &GreetingService{} arp.Go(context.Background(), func(ctx context.Context) { //调用业务方法 greetingService.SayHello(ctx) }) } type GreetingService struct { } func (serv *GreetingService) SayHello(ctx context.Context) { fmt.Println("hello world") } 

    ARP4G 的不同实现

    MongoDB

    ARP4G-mongodb

    Redis

    ARP4G-redis

    使用 ARP 开发业务简介

    假设有一段完成订单的业务,根据订单 id 找到相关订单,把它的状态改为已完成。以下将介绍如何使用Aggregate 、Repository 、Process3 个概念且利用 ARP4G 框架完成这段简单的业务。

    Aggregate (聚合)

    一个聚合就是表示某个实体的对象,该实体可能还包含别的实体,通常,这在业务上意味着被包含的实体是聚合的一部分,比如一辆汽车包含一个引擎。这里说的聚合就是DDD_Aggregate

    显然这里有一个聚合,Order

    type Order struct { Id string State string } 

    Repository (仓库)

    仓库,就是存放聚合对象的仓库。

    你可以把它想象成一个真实世界的仓库。例如有个品牌汽车专卖店,它有一个汽车仓库停满了汽车,当然它不会有引擎仓库,因为当你从仓库开出一辆汽车的时候就已经包含了它的引擎了。

    现在,我们有了订单仓库,OrderRepository

    type OrderRepository interface { Take(ctx context.Context, id any) (order *Order, found bool) } 

    仓库设计成接口是因为当我们在设计业务的时候,不希望扯入任何技术细节。在业务侧,我们需要的是有一个订单仓库,从中可以拿订单,并不关心仓库是怎么建造的。

    从仓库获取我要的聚合

    考虑一个现实世界中的汽车仓库,当一个顾客把一辆汽车买走后,另一个顾客是无法买走同一辆汽车的,同时顾客对自己的新车喷涂了喜欢的图案,当然另一个顾客无法喷涂这辆车,尽管他不喜欢这个图案,因为他没有取得它。我们把从仓库中取出聚合的操作叫做Take

    另一个场景,仓库管理员想要确认某辆车的型号是不是最新款,他来到仓库,找到了这辆车,确认了它的型号,记录在了自己的小本子上。他并不需要把这辆车从仓库开走,同时另一个仓库管理员也可以对同一辆车做确认。我们把这种只需要在仓库中找到聚合的操作叫做Find

    总结起来,当我们需要获取仓库中的聚合,并试图改变它的状态,那么用Take,如果我们只是想在仓库中找到一个聚合并想要看看它的状态,那么用Find

    这里我们要从仓库拿走这个订单,并改变它的状态,所以我们会有 Take

    Take(ctx context.Context, id any) (order *Order, found bool) 

    完成我的业务逻辑

    我们还需要一个 OrderService

    type OrderService struct { orderRepository OrderRepository } 

    OrderService 提供一个业务方法来实现我们的业务逻辑

    func (serv *OrderService) CompleteOrder(ctx context.Context, orderId string) *Order { //从仓库取出 order order, _ := serv.orderRepository.Take(ctx, orderId) if order.state == "ongoing" { //改变他的状态 order.state = "compleated" //返回改变后的 order return order } return nil } 

    看看里面做了些什么,从仓库取出 order ,然后改变它的状态。

    我们认为大多数业务都是这样,我们把从仓库中取出一个或几个聚合,并改变他们的状态这样的一个整体叫做Process(过程)。通常,一个过程就是一个业务服务的方法。

    最后我们需要用 ARP4G 框架来包装一下这个过程,从而照顾所有的技术细节

    func main() { orderService := &OrderService{repoimpl.NewMemRepository(func() *Order { return &Order{} })} arp.Go(context.Background(), func(ctx context.Context) { //调用业务方法 orderService.CompleteOrder(ctx, "12345") }) } 

    可以在下一节了解[ARP4G 做了什么](#ARP4G 做了什么)

    ARP4G 做了什么

    在这之前我愿介绍一些 ARP 的规则:

    1. 不能同时从仓库取得( Take )同一个聚合

    2. 聚合的状态更新是自然的,业务代码不需要显式的 update

      是的,考虑真实世界的仓库,当你买了一辆车,喷涂了新的图案,那么这辆车的外观就被改变了,这是自然的,这不需要你再告诉仓库要 update 才能确定下来。

    3. 一个过程作为一个整体,其中所有涉及到的聚合的状态改变要么全都成功要么一个都不成功

    4. 一个过程结束的时候,不管成功与否,确保从仓库取得( Take )的聚合全部归还仓库

    所以,ARP4G 所做的就是帮你实现了这些规则,使得你可以写出纯粹的业务代码。

    如何持久化到数据库总是关心的,关于持久化聚合到具体的数据库可以看[ARP4G 的不同实现](#ARP4G 的不同实现)。

    4 条回复    2022-10-10 17:47:56 +08:00
    kidlj
        1
    kidlj  
       2022-10-08 10:06:06 +08:00
    很不错,star 了,有机会学习下源码。
    zhengchengdong
        2
    zhengchengdong  
    OP
       2022-10-08 10:36:10 +08:00
    div class="reply_content">@kidlj 欢迎使用它来帮助到你的业务开发,关于源码有任何的问题和想法都请告诉我:)
    morty0
        3
    morty0  
       2022-10-10 16:16:05 +08:00
    玩具
    zhengchengdong
        4
    zhengchengdong  
    OP
       2022-10-10 17:47:56 +08:00
    @morty0 我们希望 ARP 像玩具一样简单:) 通过总结这些年我们上线的几十个业务系统,去除了很多不必要的复杂性,沉淀出了 ARP 设计方法,有任何疑问和想法欢迎讨论
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5170 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 07:27 PVG 15:27 LAX 23:27 JFK 02:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86