当前位置: 首页 > 科技观察

站在一个开发者的角度理解框架的设计思路

时间:2023-03-18 20:16:15 科技观察

必答问题最近有不少读者私信我,电商项目开发为什么选择GoFrame?原因很简单:因为我们公司做电商业务开发用的是GoFrame,而我们同事基本都是把PHP转Go。GoFrame可以说是一个非常适合PHPer转Gopher的开发框架。在加入我们公司之前,我用过Gin和go-micro框架,目前正在学习go-zero。无论是开发语言还是开发框架,都是为我们做的业务服务的。不谈业务,谈语言,谈框架,是没有意义的。将GoFrame作为开源项目使用的另一个原因是:想体验V2版本的新特性,没有历史包袱的情况下如何做自己的项目自己说了算。在我看来,让开发者更好地实现“模块内高内聚,模块间松耦合”才是GoFrameV2设计的精髓所在。使用GoFrame开发商业项目已经有很长时间了。我发现GoFrame的版本更新比较快,社区也很活跃。由于历史原因,我一直使用V1.16版本开发商业项目,虽然我个人有强烈的升级到V2的愿望。但由于项目稳定性和开发成本等原因,商业项目一直没有升级。这可能也是很多小伙伴面临的问题。受鼓舞刚才分享了我的开源项目【GoWEB进阶实践】基于GoFrame的电商前后端API系统得到了大家的关注和支持。GoFrame的作者也点赞转发,更受鼓舞。更重要的是:收到了很多社区小伙伴的建议。最多的建议是建议我使用V2版本,因为它提供了很多新特性,更能满足需求,而且稳定高效。决定升级所以,我决定将我的开源项目从V1.16.x版本升级到最新的V2.2.0版本,踩下升级的坑,享受升级后的快乐。欢迎小伙伴们加入我的开源项目:电商前后端系统API[1],目前V1版本已经完成,包括电商项目常用功能,120多个接口发达。一起参与V2版本的开发过程。到目前为止,已经开发了30多个接口。计划本月内完成开发并开源。欢迎小伙伴们参与共建,也欢迎阅读我的源码,多提宝贵建议:V2版本的GitHub地址比较长,比较硬核,所以决定分两篇分享:重点本文:介绍GoFrameV2相比V1有哪些新特性优势?最大的变化是什么?我会在下一篇文章分享:我从V1到V2的踩坑历程,相信对很多朋友都有帮助。这段经历真的很不容易,希望小伙伴们能够点赞、关注、转发一波。适合掌握Go基础知识后,想使用成熟的框架开发项目的人。推荐阅读我的文章后使用最新的GoFrameV2版本进行实际开发。我目前使用的是V1版本。我愿意但没有太多精力去学习新的V2版本。有特定特征的伙伴,担心升级太高不敢贸然升级的伙伴。想提高学习新知识效率的朋友欢迎复制我的做法。站在开发者的角度,无论你是哪种人,都建议花时间仔细阅读官方文档,尤其是框架介绍[2]。不同于官方文档,本文将结合自己的经验,站在frame用户的角度,帮助大家更快更好的理解GoframeV2版本的设计思路,以及如何基于V2版本更好的开发商业项目。我踩的坑是在版本升级过程中发现的:必须先了解V2的新特性,然后再从V1升级到V2,否则会出现升级到一半无法启动的问题:因为由V2版本dao、model的CLI工具生成,与V1版本不一致:丢弃了gmvc模块,同时引入了新的模块。v2版本支持gfgenservice的方式生成服务层,统一了我们接口逻辑的实现,引入了逻辑层和服务层的概念,结合我自己的升级经验,把你的知识点分享给大家学习GoFrameV2时必须知道:必须知壁会项目的项目结构变了,需要遵循V2标准,因为gf工具生成的代码目录结构变了。更重要的是:V2官方推荐的目录结构也是我们实践“高内聚低耦合”的“更好的项目目录结构。除了像V1一样生成dao层和模型层,gfgendao也会生成目录结构v2版本的do层和entity层实践了业务模型和数据模型解耦的思想(我觉得很好)V2和V1相比,方法或者模块会被丢弃,比如gmvccoupling模块,以后不会进一步支持,同时也为我们提供了更好的实现方式,V2有点像写“微服务”,需要服务注册:controller调用一个或多个服务来实现特定的业务逻辑,但是复杂的业务逻辑并没有在服务中实现,为了解耦,V2版本引入了逻辑目录,用于编写和复用复杂的业务逻辑。r逻辑上的服务,通过服务中的接口,规范逻辑层要实现的方法。最重要的是介绍下我消化了很久的知识点:dao代码生成(很重要)gfgendao业务项目中,官方推荐使用dao/do/entity来操作数据库。这些文件都是由开发工具自动生成的,由开发工具统一维护。与V1版本不同,V2版本引入了do的概念,为什么要这样设计呢?这里我只说结论,文末附上官方链接:dao层用于数据访问,是一层抽象对象,用于与底层数据库进行交互,只包含最基本的CURD方法。模型层是结构模型,是数据结构管理模块,管理数据实体对象,定义输入输出数据结构。2.1模型中的do是一个领域对象,用于转换业务模型和dao数据操作中的实例模型。它由工具维护,用户无法修改。2.2模型中的实体是数据模型,数据模型是模型与数据集合的一对一关系,由工具维护,用户无法修改。后面我会带大家用例子来讲解服务接口的生成(比较重要)gfgenservice服务接口是一个很重要的知识点,我觉得cli工具支持和V1版本最大的区别:为了减少业务项目内部模块的耦合,框架将模块之间的依赖关系抽象成接口,由internal/service包维护。内部/服务可以由开发者自定义维护接口,也可以通过内部/逻辑业务封装的代码,按照一定规则自动生成接口代码文件。文档看10遍不如练一遍。建议大家和我一起练习,欢迎转载:我的思路是这样的:下载官方的sampleproject,学习官方是怎么写的。对自己提出需求,参考官方实现方式,实现自己的业务场景。我将带你实现经典的电子商务场景:添加和查询商品信息。1、下载并运行官方示例GitHub[3]1.1下载并部署项目后,启动:很顺利,启动成功:1.2请求接口,验证DB是否正常连接。1.3查询数据库也很有价值。验证环境无误后,下面带大家参考官方示例实现自己的需求,更好的了解V2版本的新特性和工程实践。2.基于V2编写产品管理。我们按照官方的工程方法来实践一下,看看会不会踩坑:2.1创建goods表如下:2.2第一次通过gfgendao生成dao和model,但是失败了,因为hack没有修改config目录中的.yaml配置文件。注:与V1不同,官方说hack目录用于工具脚本,存放项目开发工具、脚本等内容。例如CLI工具的配置,各种shell/bat脚本等文件。所以我们不像V1那样把cli工具的配置文件写到manifest/config目录下。2.3大功告成,生成成功。提示,如果我们不指定表,则会生成所有表对应的数据。我比较喜欢这样做:因为这样可以避免自己改多个表,但是配置文件中少了某个表会出现意想不到的问题。下面正式开始码字:我会写成大家容易看懂的方式。文末分享一下我的实践经验:各个模块的代码按照什么顺序写比较科学。2.4首先我们实现api层,定义request和response的结构2.5我们在cmd中注册Goods相关的路由2.6我们发现在注册路由的时候controller.Goods是红色的,因为我们还没有写这个方法。我们参考示例代码编写controller层:我们发现在右边的示例工程中,方法内部调用了服务中的方法,但是我们还没有定义服务,怎么办?我们先在示例项目中点击user.go,看看服务中定义了什么:查阅文档得知,我们需要通过编写逻辑层来实现业务逻辑,通过配置自动生成服务代码戈兰插件。如果这是官方推荐的最佳做法:2.7导入官方xml文件(只需要配置一次)xml文件地址[4]强烈建议你这样做。这样配置之后,当我们编写逻辑层代码时,服务可以自动生成一个接口定义文件。当然你也可以不配置,但是需要在每次开发/更新逻辑业务模块后,手动执行gfgenservice命令。太多麻烦了!!!2.8参考右边例子写商品的逻辑代码,处理业务逻辑:2.9经过测试,发现写好逻辑逻辑后,服务层会自动生成对应的商品文件和方法,非常方便。2.10我们继续写添加商品的逻辑和查看商品的逻辑。我们发现在逻辑层写好添加商品的逻辑后,右侧的服务层自动生成了代码。2.11细心的同学可能发现了service层的RegisterGoods方法。为什么要用这个?答案是:我们在服务层生成RegisterXX()方法后,需要在对应的业务模块中添加接口的实现注入。提示:该方法每个业务模块可以添加一次。建议在写完第一个逻辑方法后(或者服务层生成RegisterXX方法后):在逻辑层的init函数中实现服务的注册;然后查看logic.go文件是否添加了相关依赖,如果没有也可以手动添加;成为良好的编码习惯,就会有更少的错误。2.12我们查看了logic目录下的logic.go文件,发现我们这次写的goods相关的import已经自动添加了:这个文件的作用是在程序启动的时候注册接口的具体实现。好了,逻辑和服务到此结束,我们就完成了业务逻辑的编写。内容很多,大家可以翻页再看这部分,消化吸收。2.13回头继续写controller层的代码:我们参考官方的controller/user.go实现自己的controller/goods.go添加商品的方法:2.14至此,我们就完成了新需求,启动服务查看效果:很好,已经看到对应的界面了。编码完毕,我们来测试一下:我们请求接口,添加数据,看到:数据也在数据库中查看:插入成功,流程走通!反思和回顾遵循上述过程,虽然整体工作经历了。就个人而言,我感到很困惑。官方文档的工程实践,结合自己的经验,我花了很长时间消化吸收。再梳理一下V2项目的编写过程。我的建议是这样的:梳理流程设计表结构,使用gfgendao生成对应的dao/do/model目录。代码编写api层:定义“业务模块”的数据结构,提供对外接口输入/输出数据结构的模型层编写:定义“数据模块”的数据结构,提供输入/输出数据内部数据处理结构,编写逻辑层,自动生成服务层代码。(通过配置golandFileWatcher自动生成,或者通过gfgen服务手动执行脚本生成,强烈推荐前者)服务层代码生成RegisterXX()方法后,在对应的逻辑模块中注册服务(每个模块只需要待写一次)编写controller层,接收/解析用户输入的参数,调用service层的服务。注册路由,对外暴露接口。比如这个项目就是写cmd.go文件。添加一行_"project-name/internal/logic"到main.go(只写一次)添加一行_"github.com/gogf/gf/contrib/drivers/mysql/v2"到main.go(如果你用的是mysql,只需要写一次)以上关键过程的步骤只有3~8步,每次开发新的需求都需要。第9、10步只有在创建工程时才需要,以后可以再做一遍。我按照上面的步骤写了查询商品的逻辑。开源项目:https://github.com/wangzhongyang007/goframe-shop-v2带着问题学习写商品管理需求的时候有点迷茫:为什么要定义两次数据结构?在api层定义一次,我在model层又定义了一次,重复的结构我写了两次。重点是什么?我静下心来想了想。这个设计还是值得深思的。我将根据我以前的项目经验分享我的理解。如果大家有什么理解,欢迎在评论区留言。之前遇到的问题我们之前在开发产品中心统一存储的时候,遇到了一些难以维护的问题,因为业务逻辑和数据处理逻辑是耦合在一起的。随着业务越来越复杂,项目的维护成本越来越高,甚至达到难维护的程度。我们是怎么解决的?解决方案是将GoFrame的数据模型和业务模型解耦。底层思想是一样的:我们拆分复杂的逻辑:定义业务模块和数据处理模块。“业务模块”:只处理接收到的参数,不关心如何存储和取值。根据“数据模块”的要求,对前端传入的数据进行处理,将统一的结构传递给“数据模块”。“数据模块”:你不需要关心“业务模块”的具体实现。定义了统一的参数传递标准,要求业务模块根据自身需求统一传入数据。数据模块考虑的重点是如何高效地批量插入数据。如何高效的按需取值,不需要关心业务端需求的变化。升华拆解冗余模块后,我们理清了“数据模块”和“业务模块”的界限。不仅解决了以往项目维护难的问题,还提高了灵活满足客户需求的能力。结合自己的项目经验和这次实践V2版本的经验,我开篇说:让开发者更好的实现“模块内高内聚,模块间松耦合”,我认为这就是GoFrameV2设计的精髓所在。好了,这就是本文的全部内容。这是5,000字的硬核。坚持更新并不容易。欢迎大家点赞、评论、转发。参考资料[1]电商前后台系统API:http://github.com/wangzhongyang007/GoFrame-shop[2]框架介绍:https://GoFrame.org/pages/viewpage.action?pageId=3672399[3]官方示例GitHub:https://github.com/gogf/gf-demo-user[4]xml文件地址:https://GoFrame.org/pages/viewpage.action?pageId=49770772&preview=/49770772/49770777/watchers.xml本文转载自微信公众号《程序员升级打怪之旅》,作者“王中扬围棋”,可通过以下二维码关注。转载本文请联系《程序员升级打怪之旅》公众号。