在了解工具之前,我们先了解一些基础的核心概念,这有助于理解工具的设计和使用。
先限定好范围,我们以下讨论的是对外提供接口服务的Web API应用,这是当前使用最广泛的一种方式,也是本工具适用的范围。
作为开发者,我们可以先梳理一下,从0到1开发一个系统的步骤,使用不同语言不同框架,不同习惯的人可能有所差异,这里我以使用ASP.NET Core
和EF Core
为例。
这里的核心就是定义实体模型
,DRY工具也是围绕它进行设计的。
实体模型是用来表示业务模型
的,可以说,不同的业务系统之间,使用什么语言框架和技术架构实现并不重要,它们不能区分出不同的业务。真正决定业务的不同就是抽象出来的业务模型
,我们一般称为实体模型
或领域模型
,就是说通过模型定义的内容,你大概就能理解你做的是什么业务。
当开发者拿到一个需求时,首先我要理解需求中涉及到的实体
,然后思考要用什么数据结构和表示它,用什么样的存储介质来存储和查询它。
举个简单的例子:
现在我要开发一个非常简单的博客系统,让我能够编写,修改和删除博客。
根据这个需求,我们可以抽象出来两个实体:
而它们之间的关系是:一个用户可以管理多个博客。
得益于EF Core
的Code First
,我们可以不必关心数据库结构,而是直接使用代码来表示业务模型,由此我们将使用类
定义两个实体模型
,如:
public class User { public required string UserName { get; set; } } /// <summary> /// 博客 /// </summary> public class Blog { /// <summary> /// 标题 /// </summary> public required string Title { get; set; } public string? Content { get; set; } /// <summary> /// 创建时间 /// </summary> public DateTimeOffset CreatedTime { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? UpdatedTime { get; set; } }
以上是两个非常简单的类,在定义时,你可以看到:
required
:表示该属性是必须的?
:表示该属性是可空的=DateTimeOffset.UtcNow;
:表示该属性的默认值是当前时间得益于C#优秀的表现力,定义以上内容比手动创建数据库表要更容易。
如果只是如此定义,它只是普通的模型类
,并没有体现实体
的含义。用户和博客这两个实体之间有是关系的,让我们来加一些实体
的内含:
[Index(nameof(UserName), IsUnique = true)] public class User { [Key] public Guid Id { get; set; } [MaxLength(20)] public required string UserName { get; set; } /// <summary> /// 拥有的博客 /// </summary> public List<Blog> Blogs { get; set; } = []; } /// <summary> /// 博客 /// </summary> [Index(nameof(Title), IsUnique = true)] public class Blog { [Key] public Guid Id { get; set; } /// <summary> /// 标题 /// </summary> [MaxLength(100)] public required string Title { get; set; } [MaxLength(10_000)] public string? Content { get; set; } /// <summary> /// 创建时间 /// </summary> public DateTimeOffset CreatedTime { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? UpdatedTime { get; set; } /// <summary> /// 所属用户 /// </summary> public required User User { get; set; } }
我们添加了以下元素:
Id
:表示实体的唯一标识,用于区分不同的实体,其中[Key]
表示主键。[Index]
:表示索引,最终影响存储时的数据结构,其中的IsUnique = true
表示该属性的值是唯一的。[MaxLength]
:表示该属性的最大长度,用于限制输入的内容。List<Blog> Blogs
:表示用户拥有的博客,用一个集合来表示。required User User
:表示博客所属的用户,required表示博客必须拥有一个用户,不能单独存在。通过以上添加的代码,我们对实体
添加了更多的定义,包括它们之间的一对多关系,以及一些约束条件,这样我们就可以更好的理解这两个实体的含义。
当然,我们可以进一步完善实体的定义,以表示出用户可以添加
,修改
和删除
博客的操作,在领域驱动设计
中,通常会将这些操作定义在实体中,这样仅仅通过实体表达的信息,
我们就能理解业务的含义。
本工具的代码生成就是根据实体模型
来生成代码。
得益于C#
强大且优雅的表达力,我们可以将我们的业务意图通过实体模型
定义。然后借助微软官方的Roslyn
解析和理解语义,从而理解最原始的业务意图,然后根据这些意图来生成业务代码。
DTO作为数据传输对象,起到了桥梁的作用,DTO也属性定义的范畴,通过DTO的内容定义,我们能了解到更多的业务意图。为了满足常规的CURD
操作,
工具会生成以下DTO:
Dto | 作用 |
---|---|
ItemDto | 列表元素 |
DetailDto | 某实体的详情 |
FilterDto | 请求筛选条件模型 |
AddDto | 添加时的模型 |
UpdateDto | 更新时的模型 |
Note
关于DTO生成的策略和规则,可查看DTO生成。
Manager用来实现业务逻辑,通过实体
可以生成一个Manager
,生成的Manager
默认包含以下方法:
控制器用来接收请求,进行数据验证,通过调用Manager
的方法处理逻辑,然后将处理后的结果返回。生成的控制器包含以下接口方法:
这也就是说,对于任意一个实体
,我们可以通过工具一键生成完整的CURD
功能,在此基础上,你可以根据实际业务需求去调整和扩展其他方法。
基于实体模型
的代码生成是本工具的核心,其本质是尝试理解业务的含义,然后在此基础上生成代码。
后续,工具将探索将大语言模型引入到工具中,从而通过实体模型
理解更多的业务意图,从而生成更多更有效的代码。