我在GitChat公众号上读到一篇转译的文章《
代码生成交给机器,我们的工作将变成软件概念的设计师
》,作者是MIT计算机与AI实验室(CSAIL)副主任和计算机科学教授Daniel Jackson,他提出了基于“概念”的软件设计方法。
这篇文章来自Daniel Jackson教授在2024全球软件研发技术大会中的演讲。以我之愚见,Daniel Jackson提出“概念(Concept)”的概念,其定义本身比较抽象,即便他通过iPod、Zoom等应用为例,深入细致地说明如何在应用开发中运用“概念”,但其本意仍有混淆不清之感。当然,也可能是我理解能力不足。
在我反复咀嚼该篇文章之后,我“自大”地发现Daniel Jackson提出的所谓“概念”,与我在自己的著作《解构领域驱动设计》中提出的“业务服务”颇类似。
文中如此定义:“概念是单个软件、一类软件以及各类软件的特征。”这个定义很模糊,但可以读懂它传递的一个重点,即概念是该软件有别于其他软件的某个或某些功能/特性。
接着,Daniel Jackson教授用Zoom举例说:
你可以将 Zoom 理解为由这些概念构建而成,包括会议链接、视频频道(VideoChannel)等。其中一些概念是创新性的,如会议链接;有些是我们非常熟悉的,如在线聊天;还有一些是通用性的概念,几乎在每个应用程序中都能见到,例如用户身份验证,这是最典型的通用性概念之一。这些概念通过
“交互+同步”
的方式融合在一起。
既然概念为软件之特征,则它体现了该软件的主要功能,这些功能又由一些领域概念(此概念非Daniel Jackson教授所倡导之概念,但二者有相似之处)和行为构成:
文中提及的“创新性概念”,可认为是DDD中的核心领域(核心领域是一家企业具备竞争力的高价值领域,创新特性是战略规划的一部分,在经过充分验证之后,理应归入核心领域);而所谓“通用性概念”,应为DDD的通用子领域。
Daniel Jackson教授又提出:“我们可以将应用程序视为概念的集合,并将其简化为最核心的部分。”他认为需要从场景提炼出概念,这一说法更接近于“业务能力”。他举例说:
对于 Zoom 来说,基本的概念可能是会议链接和视频会议。对于像 Calendly 这样的日程安排软件,核心概念可能是自助预约和通知。而对于 iPod 来说,则是歌曲管理、音乐商店以及文件同步的概念。
他提及的这些概念,不就是Zoom、Calendly与IPod最为核心的业务能力吗?正是因为具有了这些业务能力,这些产品才能在市场上傲视群雄,甚至引领一时之风气。
文中还提到:“
概念本质上是一个可以通过 API 来定义的服务
。比方说,列出一系列动作。这些动作实际上是出现在场景中的行为,可以被视为 API 的组成部分。”这就将具有业务含义的内容和技术实现绑定起来了。
概念不像程序那样,可以用较大的包含较小的。相反,每个概念对用户来说都是平等的,软件或系统就是一组串联运行的概念组合。
根据Daniel Jackson教授给出的这些解释和例子,我觉得他提出的“概念”一词仍有含混之处,如果结合领域驱动设计,则可初步得出一些结论:
假如这些结论站得住脚,那么“
概念
”这个概念就和
业务服务
颇为类似,甚至我认为他说的“概念”,还不如用业务服务更加清楚。《解构领域驱动设计》一书对业务服务的定义为:
业务服务是业务的参与者主动向目标系统发起服务请求,完成一次连续而完整的功能交互,并体现服务价值的业务行为。
在识别业务服务时,同样不按照粒度大小划分,而是将参与者与目标系统的
一次完整交互
视为一个独立的业务服务。这种交互往往通过前端或其他客户端向目标系统对外公开的API发起请求,从技术角度看,一个业务服务确实就是一个通过API定义的服务。如果将多个业务服务组合起来,就体现了该系统或系统之下的限界上下文对外提供的各种业务能力。
业务服务承载了用户的业务需求,并成为需求分析阶段的基本业务单元。遵循
统一语言
的原则,可以按照特定规约编写业务服务,使之清晰地传递领域建模需要用到的领域概念、领域行为和领域规则。下图是我规定的业务服务规约及其案例:
好。倘若这一假设成立,则Daniel Jackson教授提出的
用概念驱动编程
,即可称为
业务服务驱动编程
,由于它的粒度和规格具有确定性,即可解决GitHub首席执行官Thomas Dohmke针对AIGC提出的问题:“开发者的关键技能在于,‘
我需要细化到什么程度才能用 AI 自动生成代码?’
”
我的回答是:只需细化到如上图所示的业务服务规约就可以了。注意,这个业务规约完全可以由领域专家完成,接下来,只需将这份输出作为prompt的一部分,交给AI大模型就足够了。
让我们试验一下。我选择最基本的GPT-4 Omni Mini模型,编写的prompt内容为:
你是一名开发人员,在与需求人员充分沟通后,得到了如下格式的需求规格描述,它说明了一个代表用户功能的业务服务:
服务名:提交订单
服务描述:
作为买家,
我想要提交订单,
以便买到我心仪的商品。
触发事件:买家点击“提交订单”按钮
基本流程
1.验证订单有效性;
2.验证订单所购商品是否缺货;
3.插入订单;
4.从购物车中移除所购商品;
5.锁定库存;
6.通知买家订单已提交。
替代流程
1a.如果订单无效,给出提示信息;
2a.如果所购商品缺货,给出提示信息;
3a.若订单提交失败,给出失败原因。
验收标准
1.订单需要包含客户ID、配送地址、联系信息及已购商品的订单项;
2.订单项中商品的购买数量要小于或等于库存量;
3.订单提交成功后,订单状态更改为“已提交”;
4.购物车对应商品被移除;
5. 在完成支付或取消订单前,需要锁定对应数量的商品;
6.买家收到订单已提交的通知。
分析该需求规格描述,提炼出领域概念,然后按照DDD的要求编写该业务服务的领域层代码,用Java语言来表示。
在分析该需求规格描述后,我们可以提炼出一些领域概念。这些概念通常来自业务需求并与领域模型相关联。根据给定的需求,以下是一些可以提取的领域概念:
-
-
Order(订单):表示买家购买的商品及其相关信息。
-
Cart(购物车):存放买家选择但尚未购买的商品。
-
Product(商品):可以购买的物品,包含库存信息。
-
OrderItem(订单项):订单中每个具体商品的详情,包含数量和状态。
-
Inventory(库存):表示商品的可用数量。
下面是用 Java 表达的模型,遵循 DDD(领域驱动设计)原则。
public class Buyer {
private String id;
private String name;
private String contactInfo;
private String deliveryAddress;
}
public class Product {
private String id;
private String name;
private int stockQuantity;
public boolean isAvailable(int quantity) {
return stockQuantity >= quantity;
}
}
public class OrderItem {
private Product product;
private int quantity;
public OrderItem(Product product, int quantity) {
if (!product.isAvailable(quantity)) {
throw new IllegalArgumentException("商品库存不足");
}
this.product = product;
this.quantity = quantity;
}
}
import java.util.List;
public class Order {
private String id;
private Buyer buyer;
private List items;
private String status;
public Order(Buyer buyer, List items) {
this.buyer = buyer;
this.items = items;
this.status = "已提交";
}
}
import java.util.ArrayList;
import java.util.List;
public class Cart {
private List items = new ArrayList<>();
public void addItem(OrderItem item) {
items.add(item);
}
public void removeItem(OrderItem item) {
items.remove(item);
}
public List getItems() {
return items;
}
}
public class OrderService {
public Order submitOrder(Buyer buyer, Cart cart) {
if (cart.getItems().isEmpty()) {
throw new IllegalArgumentException("购物车为空,无法提交订单");
}
List orderItems = new ArrayList<>();
for (OrderItem item : cart.getItems()) {
if (!item.getProduct().isAvailable(item.getQuantity())) {
throw new IllegalArgumentException("商品 " + item.getProduct().getName() + " 缺货");
}
orderItems.add(item);
}
Order order = new Order(buyer, orderItems);
for (OrderItem item : orderItems) {
cart.removeItem(item);
}
return order;
}
}
虽说这样的领域层代码还不够完善,但开发人员完全可以在此基础上做优化和调整,当然,也可以继续通过prompt与AI大模型互动。
如果按照业务服务的要求进行划分(划分业务服务由人来完成),一个业务服务就是一个API定义的服务,如此,还可以在此基础上要求AI大模型帮我们定义一个API。
我在以上回答的基础上继续提问:
如果将“提交订单”业务服务看做是一个后端服务的RESTful API,请生成一个后端服务的Java代码,满足API的定义。要求:在后端服务中,调用前面领域层代码定义的领域服务。
好的!下面是一个将“提交订单”业务服务实现为 RESTful API 的后端服务的 Java 代码示例。这段代码使用 Spring Boot 框架,并通过调用前面定义的领域层服务来执行订单提交功能。
首先,你需要在 pom.xml 中添加 Spring Boot 相关的依赖(只列出核心部分):
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
dependencies>
下面是实现 RESTful API 的控制器代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
@Autowired
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping("/submit")