AI + DDD + Laravel 开发独立站商城挑战 (第一周)
缘起:因为工作经常写 bug,一直在思考如何减少bug的产生,最后发现设计模式在业务代码中似乎并没有那么万能。
bug 产生的很大一部分原因是未意料到代码更改的连锁反应
之后我逐渐摸索到了架构上,MVC 相对耦合,业务代码分散在各个控制器成为过程式脚本,打破了业务的整体性,增加了更多未意料的可能。
最近学习了点 DDD 方面的知识,发现这种分层解耦能更好的理清各个业务的逻辑,缺点是代码量太多开发太慢。
到这里我猛然惊觉,这不就是为 AI 而生的架构吗,AI 天生适合解决代码量太多样板太多的问题,DDD 分层带来的解耦和关注点分离天生适合 AI 上下文长度有限的情况。 AI 编程的上下文问题并不是 AI 能力不行,而是 MVC 架构本身就是一坨,记忆力再好的人写一样容易出 bug。
思考到这里我异常惊喜,因为我一直想找到一些可以积累代码资产的模式,最终积累成一个赛博城堡,成为互联网房东,现在看到 DDD 的解耦和 AI 的强大,我意识到很快能成为现实(即使在此之前一直空想很少有实际的产出……)
So,先开个帖子,慢慢记录这个实验,自己本身之前也并没有 DDD 开发经验,这是一个尝试,一个过程,结果是未知的
整体架构
核心思想 :DDD + AI 驱动
整体架构:
基于 DCI
- 优点:
- 业务优先,解耦程度足够
- 业务层只是单纯的逻辑抽象,不依赖外部实现(可以让一个 Agent 只关注几个文件)
- 缺点:
- 代码量多,前期开发抽象成本较高
- 对业务领域新手不友好(业务部分可以参考 MVC 商城的业务)
- 简单 CRUD 业务中,DCI 可能过度设计
- 可参考实践相对较少(有利有弊吧,对于 AI 驱动或许可行)
- 优点:
洋葱模型
- 优点:
- 开发成本介于传统 DDD 和 MVC 之间
- 代码比 MVC 更规范
- 缺点:
- 对于简单 CRUD 应用或短期项目不适合
- 可能过度设计(基本和 DCI 缺点相同,都是 DDD 的问题)
- 优点:
CQRS
- 优点:
- 职责分离进一步解耦,一个 Agent 可以关注更少文件
- 查询模型可以完全匹配 UI 需求,更灵活
- 缺点:
- 复杂性增加,维护两个独立模型(尝试 GraphQL 做读查询)
- 数据一致性(这部分可以写完之后立即同步读模型保证强一致性)
- 优点:
目录结构暂时不急于定下来,但大概可以有个参考,毕竟这只是早期产品,后期也一定会有改动,让 AI 生成了一个
src/ ├── Core/ # 核心领域层 (洋葱模型最内层) │ ├── Domain/ # 领域模型 │ │ ├── Product/ # 产品聚合根 │ │ │ ├── Product.php # 领域模型实体 │ │ │ ├── ProductId.php # 值对象 │ │ │ ├── ProductStatus.php # 枚举 │ │ │ └── Events/ # 领域事件 │ │ │ ├── ProductCreated.php │ │ │ └── ProductPriceChanged.php │ │ │ │ │ ├── Order/ # 订单聚合根 │ │ ├── Tenant/ # 租户聚合根(多租户核心) │ │ └── Shared/ # 跨领域对象 │ │ ├── Currency.php │ │ └── Country.php │ │ │ ├── Services/ # 领域服务 │ │ ├── ProductCatalog.php # 产品目录服务 │ │ ├── CrossBorderTax.php # 跨境税务计算 │ │ └── TenantProvisioning.php # 租户配置服务 │ │ │ └── Repositories/ # 仓储接口 │ ├── ProductRepository.php │ ├── OrderRepository.php │ └── TenantRepository.php │ ├── Application/ # 应用层 (CQRS实现) │ ├── Commands/ # 写模型 │ │ ├── Product/ │ │ │ ├── CreateProductCommand.php │ │ │ ├── UpdateProductCommand.php │ │ │ └── Handlers/ │ │ │ ├── CreateProductHandler.php │ │ │ └── UpdateProductHandler.php │ │ │ │ │ └── Tenant/ │ │ ├── OnboardTenantCommand.php │ │ └── Handlers/ │ │ └── OnboardTenantHandler.php │ │ │ ├── Queries/ # 读模型 │ │ ├── Product/ │ │ │ ├── GetProductByIdQuery.php │ │ │ ├── ListProductsQuery.php │ │ │ └── Handlers/ │ │ │ ├── GetProductHandler.php │ │ │ └── ListProductsHandler.php │ │ │ │ │ └── Tenant/ │ │ └── GetTenantConfigQuery.php │ │ │ └── EventHandlers/ # 事件处理器 │ ├── Product/ │ │ └── ProductPriceChangedHandler.php │ └── Tenant/ │ └── TenantActivatedHandler.php │ ├── Infrastructure/ # 基础设施层 │ ├── Persistence/ │ │ ├── ProductRepository.php # 仓储实现 │ │ ├── OrderRepository.php │ │ └── TenantRepository.php │ │ │ ├── Payment/ # 支付抽象实现 │ │ ├── StripeGateway.php │ │ └── PayPalGateway.php │ │ │ └── Shipping/ # 物流抽象实现 │ ├── DHLService.php │ └── FedExService.php │ └── Contexts/ # DCI上下文 ├── Checkout/ # 结账业务流程 │ ├── Roles/ │ │ ├── Buyer.php │ │ ├── Payer.php │ │ └── ShippingRecipient.php │ └── CheckoutContext.php │ └── ProductManagement/ # 商品管理流程 ├── Roles/ │ ├── CatalogEditor.php │ └── PriceModerator.php └── ProductManagementContext.php
技术栈方面不是特别重要,因为基础设施和业务逻辑解耦了,便于任意更换,但最熟悉的就是 laravel 了,先用 laravel
开发流程篇
这部分其实也有不少和架构选型相关的内容,AI 作为领域专家主导项目开发,所以开发流程在一定程度上也会影响到架构
- DDD 原则的开发最开始是
忘记代码
式的,先让 AI 作为领域专家拆解用例- 其次,再由这些用例来抽象概念模型
- 通过概念模型再交给 AI 让他统一语言和专业术语(变量命名纠结症福音!以后开发或许只要稍加几个星期的训练就能上岗,很多细节 AI 都能完成)
- 让 AI 根据以上业务模型厘清不同领域和上下文之间的联系
- 划分 agent 任务,根据角色进行关注点分离
- 建立全局 AI 编辑器规则
- 分建工作空间,对于不同层级和抽象程度的目录建立不同的 windsurf 规则文件,多个 agent 分别负责不同区域
- 对于确实和领域外的代码有耦合的地方,把依赖的外部的接口和自身领域概要总结成一个 llms.txt 放在工作空间的根目录和 windsurf 规则文件同级
- 将测试文件侵入内层,暂时性地方便 AI 生成测试用例完成单元测试
- 当领域层测试通过后,再依次递推到上下文模型、应用服务层、仓储层、基础设施层,其他技术细节在这个过程中逐一解决
第一周工作内容
DDD 开发首忌先上代码,业务主导第一位
- 确定 MVP 功能边界
1. 买家角色用例
这是让 AI 初步拆解的用例,显然太过粗糙不能满足要求,所以还要再拆
graph TD
A[买家] -->|行为| B(浏览商品)
A -->|行为| C(搜索/筛选)
A -->|行为| D(查看商品详情)
A -->|行为| E(添加购物车)
A -->|行为| F(结算支付)
A -->|行为| G(查看订单状态)
A -->|行为| H(联系客服)
B -->|目标| I[找到目标商品]
C -->|目标| I
D -->|目标| J[确认商品信息]
E -->|目标| K[暂存购买意向]
F -->|目标| L[完成交易]
G -->|目标| M[获取物流信息]
H -->|目标| N[解决售后问题]
我让 AI 更加细化用例,最后返回出来的实际上又有点偏离 mvp 的初衷了
最后多轮提示词迭代优化的情况下终于给出了一个还像点样子的东西
graph TD
A[卖家端核心业务] --> B(商品采购中心)
A --> C(订单管理中心)
A --> D(经营分析)
A --> E(营销中心)
A --> F(物流中心)
%% 商品采购中心
B --> B1[供应商管理]
B1 --> B1a("供应商检索")
B1 --> B1b("供应商详情查看")
B1 --> B1c("合作供应商收藏")
B --> B2[商品管理]
B2 --> B2a("商品筛选")
B2 --> B2b("商品详情查看")
B2 --> B2c("代发商品上架")
B2 --> B2d("虚拟库存同步")
B --> B3[采购执行]
B3 --> B3a("一键代发下单")
B3 --> B3b("采购单确认")
B3 --> B3c("采购记录查询")
%% 订单管理中心
C --> C1[订单处理]
C1 --> C1a("新订单提醒")
C1 --> C1b("订单自动同步")
C1 --> C1c("订单状态筛选")
C --> C2[订单操作]
C2 --> C2a("订单详情查看")
C2 --> C2b("订单备注编辑")
C2 --> C2c("订单导出CSV")
C --> C3[售后处理]
C3 --> C3a("退货申请审核")
C3 --> C3b("换货流程发起")
C3 --> C3c("售后记录查询")
%% 经营分析
D --> D1[销售分析]
D1 --> D1a("销售看板")
D1 --> D1b("商品销售排行")
D1 --> D1c("趋势图表导出")
D --> D2[供应商分析]
D2 --> D2a("供货时效分析")
D2 --> D2b("质量评分查看")
D2 --> D2c("供应商对比")
%% 营销中心
E --> E1[促销工具]
E1 --> E1a("折扣创建")
E1 --> E1b("优惠券生成")
E1 --> E1c("活动页面发布")
E --> E2[营销分析]
E2 --> E2a("转化率分析")
E2 --> E2b("促销ROI计算")
E2 --> E2c("客户画像查看")
%% 物流中心
F --> F1[物流跟踪]
F1 --> F1a("物流单号查询")
F1 --> F1b("物流轨迹地图")
F1 --> F1c("延迟预警订阅")
F --> F2[物流设置]
F2 --> F2a("默认物流选择")
F2 --> F2b("运费模板设置")
F2 --> F2c("签收通知配置")
下面是买家端用例
graph TD
A[买家]
%% 商品发现模块
A --> B1(浏览商品)
B1 --> B1a(查看商品列表)
B1a --> B1a1(点击加载更多)
B1a --> B1a2(点击排序按钮)
B1a --> B1a3(点击商品卡片)
A --> B2(搜索商品)
B2 --> B2a(输入关键词)
B2 --> B2b(点击搜索历史项)
B2 --> B2c(点击推荐关键词)
A --> B3(筛选商品)
B3 --> B3a(拖动价格滑块)
B3 --> B3b(选择类目标签)
B3 --> B3c(切换库存开关)
%% 商品详情模块
A --> B4(查看商品详情)
B4 --> B4a(滑动图片画廊)
B4 --> B4b(选择SKU属性)
B4 --> B4c(点击加入购物车)
B4 --> B4d(点击供应商信息)
B4 --> B4e(点击物流政策)
%% 购物车模块
A --> C1(管理购物车)
C1 --> C1a(勾选商品复选框)
C1 --> C1b(点击结算按钮)
C1 --> C1c(点击删除商品)
C1 --> C1d(修改商品数量)
%% 订单创建模块
A --> C2(创建订单)
C2 --> C2a(填写地址表单)
C2 --> C2b(选择物流方式)
C2 --> C2c(输入订单备注)
C2 --> C2d(点击提交订单)
%% 支付模块
A --> D1(完成支付)
D1 --> D1a(选择支付方式)
D1 --> D1b(点击去支付)
D1 --> D1c(点击取消支付)
%% 订单追踪模块
A --> C3(追踪订单)
C3 --> C3a(查看物流轨迹)
C3 --> C3b(点击联系客服)
C3 --> C3c(点击确认收货)
%% 客服模块
A --> E1(联系客服)
E1 --> E1a(发送消息)
E1 --> E1b(上传图片)
E1 --> E1c(查看历史记录)
%% 售后模块
A --> E2(申请售后)
E2 --> E2a(选择售后类型)
E2 --> E2b(填写问题描述)
E2 --> E2c(提交申请)
E2 --> E2d(查看处理进度)
%% 关键业务流
B4c -->|加入购物车| C1
C1b -->|发起结算| C2
C2d -->|生成订单| D1
D1b -->|跳转支付| D1a
E2c -->|创建工单| E1
classDef business fill:#6f9,stroke:#333;
class B1,B2,B3,B4,C1,C2,C3,D1,E1,E2 business
暂时先记录到这里,这只是初步的业务需求算不上正在的用例分析,下周再开始正在的详细的用例分析
包括目录结构,后期可能都会有所改动
先做出个垃圾再抛砖引玉慢慢迭代
推荐文章: