使用DDD、BDD和TDD开发基于微服务应用的过程

以下是一个结合 领域驱动设计(DDD)行为驱动开发(BDD)测试驱动开发(TDD) 的完整微服务开发流程,以电商系统(订单服务、库存服务)为例,使用 Node.js 和 JavaScript 实现。


一、阶段划分与核心活动

1. 战略设计阶段(DDD 核心)

  • 目标:划分限界上下文(Bounded Context),明确微服务边界
  • 活动
    • 事件风暴(Event Storming):识别领域事件、命令、聚合根
    • 上下文映射:定义订单服务与库存服务的交互方式(如同步调用、异步事件)
  • 输出
    • 限界上下文清单:Order Context, Inventory Context
    • 上下文映射图:订单服务通过 OrderPlaced 事件驱动库存扣减

工具与环境

  • 协作工具:Miro(在线白板) + 领域事件卡片模板
  • 代码辅助
    1
    2
    3
    4
    5
    6
    7
    8
    // 领域事件定义示例(TypeScript)
    interface OrderPlacedEvent {
    eventType: "ORDER_PLACED";
    payload: {
    orderId: string;
    items: Array<{ productId: string; quantity: number }>;
    };
    }

2. 战术设计阶段(DDD 实现)

  • 目标:实现领域模型(聚合根、实体、值对象、领域服务)
  • 活动
    • 聚合根设计
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // src/order/domain/OrderAggregate.js
      class Order {
      constructor(orderId, userId, items) {
      this._id = orderId;
      this.status = "CREATED";
      this.items = items; // 值对象集合
      }

      place() {
      if (this.items.length === 0) throw new Error("Empty order");
      this.status = "PLACED";
      DomainEventPublisher.publish(new OrderPlacedEvent(this)); // 发布领域事件
      }
      }
    • 仓储接口定义
      1
      2
      3
      4
      5
      // src/order/repositories/OrderRepository.js
      class OrderRepository {
      async save(order) { /* 抽象方法 */ }
      async findById(orderId) { /* 抽象方法 */ }
      }

工具与验证

  • 类图工具:PlantUML 或 Visual Paradigm
  • TDD 验证模型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // __tests__/domain/Order.test.js
    test("订单创建后状态应为CREATED", () => {
    const order = new Order("order-1", "user-1", [item1, item2]);
    expect(order.status).toBe("CREATED");
    });

    test("提交空订单应抛出异常", () => {
    expect(() => new Order("order-2", "user-1", [])).toThrow();
    });

3. 开发实现阶段(TDD+BDD 驱动)

步骤 1 - BDD 定义用户故事

1
2
3
4
5
6
# features/order_placement.feature
Feature: 订单提交
场景: 用户提交有效订单
当 用户请求创建包含2件商品A的订单
那么 订单状态变为PLACED
并且 触发库存扣减事件

步骤 2 - TDD 实现内部逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// __tests__/application/PlaceOrderService.test.js
const { PlaceOrderService } = require('../../src/application/PlaceOrderService');
const MockOrderRepository = require('../mocks/MockOrderRepository');

test("提交订单应保存到仓库并发布事件", async () => {
const mockRepo = new MockOrderRepository();
const service = new PlaceOrderService(mockRepo);

const orderId = await service.execute({ userId: "user-1", items: [...] });

expect(mockRepo.saveCalled).toBe(true);
expect(DomainEventPublisher.events).toContainEqual(
expect.objectContaining({ eventType: "ORDER_PLACED" })
);
});

步骤 3 - 实现基础设施层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/infrastructure/MongoOrderRepository.js
class MongoOrderRepository extends OrderRepository {
constructor(db) {
super();
this.collection = db.collection('orders');
}

async save(order) {
await this.collection.updateOne(
{ _id: order.id },
{ $set: order.toJSON() },
{ upsert: true }
);
}
}

4. 集成与测试阶段

跨服务测试(BDD+契约测试)

1
2
3
4
5
# features/inventory_adjustment.feature
Feature: 库存扣减
场景: 接收订单已创建事件
当 订单服务发布ORDER_PLACED事件
那么 库存服务应减少对应商品库存

工具链配置

  • 本地开发环境
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # docker-compose.dev.yml
    services:
    order-service:
    build: ./order
    ports: ["3000:3000"]
    inventory-service:
    build: ./inventory
    ports: ["3001:3000"]
    rabbitmq:
    image: rabbitmq:3-management
  • 契约测试工具(Pact)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 订单服务消费者测试
    const pact = new Pact({
    consumer: "OrderService",
    provider: "InventoryService",
    port: 1234
    });

    pact.addInteraction({
    state: "商品A库存充足",
    uponReceiving: "库存扣减请求",
    willRespondWith: { status: 200 }
    });

五、持续交付阶段

CI/CD 流水线(GitHub Actions 示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
name: DDD+BDD+TDD Pipeline
on: [push]

jobs:
build-and-test:
runs-on: ubuntu-latest
services:
mongodb:
image: mongo:5.0
ports: ["27017:27017"]
rabbitmq:
image: rabbitmq:3-management
ports: ["5672:5672"]

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18.x

- name: Install Dependencies
run: npm ci

- name: Run Unit Tests (TDD)
run: npm test:unit

- name: Run BDD Tests
run: npm test:bdd
env:
ORDER_SERVICE_URL: http://localhost:3000
INVENTORY_SERVICE_URL: http://localhost:3001

- name: Pact Verification
run: npm run test:pact

六、监控与维护

关键工具

  • 日志聚合:ELK Stack(Elasticsearch + Logstash + Kibana)
  • 链路追踪:Jaeger 或 Zipkin
  • 健康检查
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // src/interfaces/rest/healthRouter.js
    router.get('/health', (req, res) => {
    res.json({
    status: 'UP',
    checks: [
    { name: "MongoDB", status: checkMongo() ? "UP" : "DOWN" },
    { name: "RabbitMQ", status: checkRabbitMQ() ? "UP" : "DOWN" }
    ]
    });
    });

七、核心工具链总结

领域 工具/技术栈
DDD建模 Event Storming + PlantUML
BDD测试 Cucumber.js + Gherkin + Allure Report
TDD测试 Jest + Sinon + Testcontainers
基础设施 Docker + Kubernetes + RabbitMQ
监控 Prometheus + Grafana + Jaeger
契约测试 Pact

八、典型代码结构(订单服务)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/src
/domain # 领域模型
Order.js
OrderItem.js
/application # 应用服务
PlaceOrderService.js
/infrastructure
/repositories
MongoOrderRepository.js
/messaging
RabbitMQEventPublisher.js
/interfaces
/rest
OrderController.js
/graphql
OrderResolver.js
/test
/unit # TDD测试
Order.test.js
/bdd # BDD步骤定义
orderSteps.js

通过这种分层架构,开发者可以:

  1. 使用 DDD 确保业务逻辑在代码中清晰体现
  2. 通过 TDD 保证每个领域对象的正确性
  3. 通过 BDD 验证跨服务的端到端流程
  4. 利用 CI/CD 实现自动化质量门禁

例如在电商场景中:

  • DDD 明确 OrderInventory 的限界上下文边界
  • TDD 确保 Order.place() 方法正确发布领域事件
  • BDD 验证用户下单后库存同步扣减的业务流程
  • Pact 保障订单服务与库存服务的接口契约一致性