3. 领域边界概念:领域驱动设计中的限界上下文、在API设计中的映射
领域驱动设计(DDD)中的限界上下文(Bounded Context)是划分领域模型边界的核心模式。每个限界上下文拥有独立的统一语言(Ubiquitous Language)和领域模型,模型内部的术语、规则和关系仅在边界内有效。在API设计中,限界上下文直接映射为服务边界和API聚合边界,决定了API的粒度、数据契约和交互方式。
核心原则:限界上下文是领域逻辑的“自治单元”,API是它的“对外接口”。一个限界上下文对应一个或多个API端点组,但绝不允许跨上下文共享领域模型。
3.1 限界上下文的核心特征
| 特征 | 说明 | API设计映射 |
|---|---|---|
| 统一语言 | 上下文内术语含义唯一,无歧义 | API资源名称、属性、枚举值必须与领域术语一致 |
| 模型自治 | 实体、值对象、聚合根仅在上下文内定义 | 每个API Schema独立定义,不引用其他上下文的模型 |
| 边界清晰 | 上下文之间通过事件、消息或API通信 | API端点按上下文分组,跨上下文调用走独立接口 |
| 独立演化 | 上下文可独立修改、部署、扩展 | API版本号、生命周期、变更频率按上下文独立管理 |
实践提示:在OpenAPI规范中,每个限界上下文建议使用独立的OpenAPI文档(或文档内的独立tag分组),避免将多个上下文的端点混入同一个API规范文件。
3.2 限界上下文到API的映射模式
限界上下文与API的映射并非一对一强制关系,但存在三种典型模式:
- 一对一映射:一个限界上下文对外暴露一个API服务。这是最理想、最推荐的模式,上下文边界与API边界完全对齐。
- 一对多映射:一个限界上下文对外暴露多个API(如内部API、合作伙伴API、公共API)。上下文边界不变,但API的访问策略、数据暴露范围不同。
- 多对一映射(反模式):多个限界上下文合并到一个API中。通常发生在遗留系统或过度聚合的微服务中,会导致模型污染和耦合。
3.3 上下文映射模式在API设计中的应用
DDD定义了多种上下文映射模式(Context Map Patterns),每种模式在API设计中都有对应的实现方式:
| 映射模式 | 关系描述 | API设计实现 |
|---|---|---|
| 合作关系(Partnership) | 两个上下文协同完成一个用例 | API之间通过编排层或事件驱动协调,避免直接耦合 |
| 共享内核(Shared Kernel) | 共享部分领域模型 | API使用共享Schema库,但需严格控制变更影响 |
| 客户-供应商(Customer-Supplier) | 上游API决定契约,下游适配 | 上游API定义OpenAPI规范,下游按契约实现客户端 |
| 防腐层(Anti-Corruption Layer) | 防止外部模型污染内部模型 | API网关或适配器层做模型转换,内部不暴露外部Schema |
| 开放主机服务(Open Host Service) | 对外暴露标准协议 | API遵循OpenAPI标准,提供版本化、自描述的接口 |
| 发布语言(Published Language) | 使用标准数据格式 | API统一使用JSON Schema、Protobuf等标准序列化格式 |
常见误区:不要将“共享内核”误解为“共享数据库”。在API设计中,共享内核应仅限于Schema定义(如DTO),而非共享数据库表或领域实体。跨上下文直接访问数据库会彻底破坏限界上下文的自治性。
3.4 限界上下文在OpenAPI中的表达
在OpenAPI 3.x规范中,可以通过以下方式表达限界上下文边界:
- Tag分组:使用
tags字段将端点按上下文分组,每个tag对应一个限界上下文。 - 独立文档:每个限界上下文使用独立的OpenAPI文档,通过
$ref引用公共组件。 - 路径前缀:API路径以上下文名称开头(如
/orders、/payments),体现边界归属。 - Schema隔离:每个上下文的Schema定义在独立的
components/schemas段中,不混用。
openapi: 3.0.3
info:
title: 订单上下文 API
version: 1.0.0
description: 订单限界上下文对外暴露的API,包含订单创建、查询、状态变更
tags:
- name: 订单
description: 订单上下文统一语言:Order, LineItem, OrderStatus
paths:
/orders:
post:
tags: [订单]
summary: 创建新订单
operationId: createOrder
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderRequest'
responses:
'201':
description: 订单创建成功
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
components:
schemas:
Order:
type: object
properties:
orderId:
type: string
description: 订单唯一标识(订单上下文内唯一)
status:
type: string
enum: [PENDING, CONFIRMED, SHIPPED, DELIVERED]
description: 订单状态,仅在订单上下文内定义
lineItems:
type: array
items:
$ref: '#/components/schemas/LineItem'
LineItem:
type: object
properties:
productId:
type: string
quantity:
type: integer
minimum: 1
CreateOrderRequest:
type: object
properties:
customerId:
type: string
items:
type: array
items:
$ref: '#/components/schemas/LineItem'
最佳实践:在OpenAPI文档的
description字段中明确标注该API所属的限界上下文,并列出该上下文的统一语言术语表。这有助于API消费者理解模型边界,避免误用跨上下文的术语。
3.5 限界上下文与API粒度的关系
限界上下文的大小直接影响API的粒度。过大的上下文(大泥球)会导致API臃肿、职责不清;过小的上下文(微服务碎片)会导致API数量爆炸、调用链复杂。以下是粒度评估参考:
| 上下文粒度 | API特征 | 典型问题 |
|---|---|---|
| 粗粒度 | 一个API包含多个领域概念,端点数量少但每个端点数据量大 | 模型耦合、变更影响范围大、难以独立演化 |
| 适中粒度 | 一个上下文对应一个聚合根或一组紧密相关的实体 | 边界清晰、独立部署、团队自治 |
| 细粒度 | 每个实体甚至每个操作独立成API | API数量过多、跨上下文调用频繁、事务一致性困难 |
3.6 实战案例:电商系统的限界上下文与API映射
以电商系统为例,典型的限界上下文划分及对应的API设计如下:
| 限界上下文 | 核心职责 | API端点示例 | 统一语言示例 |
|---|---|---|---|
| 商品目录 | 管理商品信息、分类、搜索 | /api/catalog/products |
Product, Category, SKU, Price |
| 购物车 | 管理用户购物车、预计算 | /api/cart/items |
Cart, CartItem, Subtotal |
| 订单 | 订单创建、状态流转、履约 | /api/orders |
Order, LineItem, OrderStatus |
| 支付 | 支付处理、退款、对账 | /api/payments |
Payment, Transaction, Refund |
| 库存 | 库存管理、预留、补货 | /api/inventory/stock |
Stock, Warehouse, Reservation |
| 用户 | 用户注册、认证、地址管理 | /api/users |
User, Address, Credential |
关键洞察:在电商系统中,“商品”在商品目录上下文中是Product(包含描述、图片、属性),在订单上下文中是LineItem(包含商品ID、数量、单价),在库存上下文中是StockItem(包含库存量、仓库位置)。同一个现实概念在不同限界上下文中有不同的模型和API表示,这正是限界上下文的价值所在。
通过将限界上下文严格映射到API边界,团队可以独立开发、测试和部署每个上下文对应的API服务,同时保持领域模型的纯净性和一致性。OpenAPI规范为这种映射提供了标准化的描述工具,使得限界上下文之间的契约清晰可见、可验证。