10领域驱动设计

战略设计

大尺度结构

限界上下文上下文映射

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第10章 战略设计 - 大尺度结构 (Strategic Design - Large-Scale Structure)

一、导读

1.1 本章学习目标

通过本章的学习,读者将能够:

理解战略设计在领域驱动设计中的核心地位和作用

掌握限界上下文(Bounded Context)的概念、识别方法和应用技巧

学会分析和设计子系统之间的关系结构

理解核心域、支撑域和通用域的区分标准及其战略意义

掌握大型系统中领域模型的组织和演化策略

能够在实际项目中应用战略设计原则进行系统架构规划

识别并避免战略设计层面的常见错误和反模式

理解战略设计与战术设计的协同关系

1.2 为什么本章如此重要

战略设计是领域驱动设计中处理大规模复杂性的关键方法论。如果说战术设计(实体、值对象、聚合、仓储等)关注的是微观层面的模型构建,那么战略设计关注的则是宏观层面的系统结构和组织方式。

在现代企业级应用中,系统规模往往非常庞大,涉及多个业务领域、多个团队、多种技术栈。试图用单一的领域模型来覆盖整个系统是行不通的,这会导致模型过于复杂、难以理解和维护。战略设计提供了一套方法论,帮助我们将大型系统分解为多个相对独立的子系统,每个子系统有自己的领域模型和边界,同时明确子系统之间的协作关系。

限界上下文是战略设计的核心概念。它定义了领域模型的适用范围,明确了模型中术语的精确含义。同一个术语在不同的限界上下文中可能有完全不同的含义,理解这一点对于正确设计分布式系统至关重要。

战略设计还与微服务架构密切相关。微服务的边界划分本质上就是限界上下文的识别和实现。掌握战略设计原则,能够帮助我们做出更合理的微服务拆分决策,避免服务粒度过细或过粗的问题。

对于架构师和技术负责人而言,战略设计能力是必备的核心技能。它不仅影响系统的技术架构,还影响团队的组织结构、开发流程和运维策略。本章将为读者提供系统的战略设计知识和实践指导。

1.3 核心问题引导

在学习本章之前,请思考以下问题:

什么是"战略设计"?它与"战术设计"的本质区别是什么?

为什么大型系统需要多个领域模型而不是一个统一模型?

如何识别和定义限界上下文的边界?

核心域、支撑域和通用域的划分对资源分配有什么指导意义?

不同限界上下文之间应该如何通信和协作?

如何处理跨多个限界上下文的业务流程?

战略设计如何影响团队的组织结构?

在遗留系统改造中如何应用战略设计原则?


二、核心概念详解

2.1 战略设计的本质与范围

战略设计(Strategic Design)是领域驱动设计中用于处理大规模系统复杂性的方法论体系。它的核心思想是:面对复杂的业务领域,我们应该将其分解为多个相对独立的子领域,每个子领域有自己适合的领域模型,并通过明确定义的接口来实现子领域之间的协作。

战略设计的"战略"一词强调了其决策的全局性和长期性影响。与战术设计关注具体的类设计和对象交互不同,战略设计关注的是系统的整体结构、模块边界、技术选型方向等高层决策。这些决策一旦确定,将对后续的开发工作产生深远影响,因此需要慎重考虑。

战略设计主要解决以下几类问题:

模型边界的界定

任何领域模型都有其适用范围。超出这个范围,模型中的概念可能失去意义或产生歧义。战略设计帮助我们识别这些边界,将系统划分为多个限界上下文,每个上下文有自己的模型和语言。

例如,"客户"这个概念在销售子系统中可能包含潜在客户的信息,在客服子系统中可能关注服务历史,在财务子系统中可能侧重信用记录。这些都是不同的限界上下文,应该有各自的"客户"模型。

子系统的职责划分

大型系统通常由多个子系统组成,每个子系统承担不同的业务职责。战略设计帮助我们明确每个子系统的核心职责,避免职责重叠和模糊。

资源的优先级分配

不是所有的业务功能都同等重要。战略设计通过区分核心域、支撑域和通用域,指导团队将最多的资源投入到最具竞争力的核心业务上。

演进路径的规划

系统不是一成不变的,它会随着业务的发展而演进。战略设计考虑了系统的演进需求,提供了模型整合、拆分、替换的策略。

2.2 限界上下文的核心概念

限界上下文(Bounded Context)是战略设计中最核心的概念,由Eric Evans在《领域驱动设计》一书中首次提出。它定义了一个明确的边界,在这个边界内,领域模型具有统一的语义和一致性规则。

为什么需要限界上下文

在小型系统中,我们可以尝试建立一个统一的领域模型来描述整个业务。但随着系统规模的扩大,这种做法会遇到严重问题:

首先,同一个概念在不同业务场景下可能有不同的含义。例如,"产品"在库存管理中关注数量和位置,在销售中关注价格和促销,在生产中关注工艺和材料。试图用一个"产品"类来满足所有需求,会导致类变得臃肿且充满条件判断。

其次,不同团队对同一概念的理解可能存在差异。如果没有明确的边界,这种差异会导致沟通混乱和实现偏差。

第三,统一的模型会形成紧密耦合,使得系统难以独立部署和扩展。

限界上下文通过明确划分边界来解决这些问题。在每个限界上下文内,模型是内聚一致的;跨越边界时,我们需要进行显式的转换和映射。

限界上下文的特征

一个良好的限界上下文应该具备以下特征:

第一,明确的边界。边界应该是清晰的、可识别的,团队成员都知道某个概念属于哪个上下文。

第二,内聚的模型。上下文内的模型元素(实体、值对象、服务等)应该高度相关,形成一个完整的业务概念体系。

第三,独立的语言。每个上下文有自己的通用语言(Ubiquitous Language),术语的含义在上下文内是一致的。

第四,明确的责任。上下文应该有清晰的业务职责,与其他上下文的职责不重叠。

第五,可控的复杂度。单个限界上下文的规模应该适中,便于团队理解和维护。

限界上下文与微服务的关系

限界上下文是概念层面的边界,微服务是物理层面的部署单元。理想情况下,一个限界上下文对应一个或多个微服务。但两者并非严格的一对一关系:

  • 一个大型的限界上下文可能被拆分为多个微服务以实现更好的可扩展性
  • 多个小型的限界上下文可能合并为一个微服务以降低运维复杂度
  • 在单体应用中,限界上下文可以通过模块(Module)或包(Package)来体现

关键在于保持限界上下文之间的松耦合,无论它们是否部署为独立的服务。

2.3 核心域、支撑域与通用域

在战略设计中,Eric Evans提出了将业务领域划分为三种类型的子域:核心域(Core Domain)、支撑域(Supporting Domain)和通用域(Generic Domain)。这种划分对于资源分配和战略决策具有重要指导意义。

核心域(Core Domain)

核心域是企业区别于竞争对手的关键业务能力,是企业的核心竞争力所在。它是业务成功的关键因素,也是企业投入最多精力的地方。

核心域的特征:

  • 直接创造业务价值
  • 具有独特性和差异化
  • 需要深入的业务专业知识
  • 频繁变化和创新
  • 难以从市场购买现成解决方案

例如,对于电商平台,推荐算法和个性化营销可能是核心域;对于银行,风险控制和信贷审批可能是核心域;对于物流公司,路径优化和调度算法可能是核心域。

对于核心域,企业应该:

  • 投入最优秀的开发人员
  • 采用DDD方法进行精心设计
  • 持续迭代和优化
  • 建立深厚的领域知识积累
  • 考虑知识产权保护

支撑域(Supporting Domain)

支撑域是支持核心域运行的必要业务功能,但它们本身不构成竞争优势。它们是业务运营所必需的,但不是业务的特色所在。

支撑域的特征:

  • 必要的业务功能
  • 相对稳定的业务规则
  • 有一定的复杂性但不需要深度创新
  • 行业内存在相似的实现

例如,电商平台的订单管理系统、物流公司的车辆管理系统、银行的账户管理系统等都属于支撑域。

对于支撑域,企业应该:

  • 保证功能的完整性和可靠性
  • 采用成熟的设计模式和方法
  • 适度投入资源,避免过度设计
  • 可以考虑外包或使用第三方解决方案

通用域(Generic Domain)

通用域是在各个行业中都普遍存在的功能,不涉及特定业务领域的专业知识。这些功能通常有成熟的商业产品或开源解决方案。

通用域的特征:

  • 跨行业通用
  • 有大量现成解决方案
  • 不涉及核心业务逻辑
  • 标准化程度高

例如,用户认证、邮件发送、日志记录、权限管理等都属于通用域。

对于通用域,企业应该:

  • 优先使用现成产品或服务
  • 避免重复造轮子
  • 最小化自定义开发
  • 关注集成和配置而非实现

战略意义

这种划分的战略意义在于指导资源的最优配置。企业的资源(人力、时间、资金)是有限的,不应该平均分配到所有功能上。通过将资源向核心域倾斜,企业可以在关键领域建立竞争优势,同时在非核心领域保持足够的功能性。

在实际项目中,很多团队的问题恰恰相反:他们在核心域上使用简单粗糙的实现,而在通用域上却花费大量精力自行开发。DDD的战略设计提供了一种纠正这种偏差的方法。

2.4 大尺度结构的组织模式

当系统包含多个限界上下文时,我们需要考虑如何组织这些上下文,形成清晰的大尺度结构(Large-Scale Structure)。Eric Evans介绍了几种常见的组织模式:

分层架构(Layered Architecture)

这是最常见的组织方式,将系统划分为表现层、应用层、领域层和基础设施层。在战略层面,可以将不同的限界上下文安排在不同的层次中,或者每个限界上下文内部采用分层结构。

分层架构的优点是职责清晰、易于理解;缺点是可能导致层级过多、性能损耗。

管道-过滤器模式(Pipes and Filters)

在这种模式中,数据通过一系列的处理步骤(过滤器),每个步骤执行特定的转换。限界上下文可以作为管道中的一个阶段,接收上游的输入,进行处理后传递给下游。

这种模式适合数据处理流程明确的场景,如ETL系统、内容处理流水线等。

独立上下文(Independent Contexts)

某些限界上下文之间几乎没有依赖关系,可以完全独立开发和部署。这种情况下,可以采用微服务架构,每个服务是一个独立的限界上下文。

这种模式的优点是解耦彻底、可独立扩展;缺点是需要处理分布式系统的复杂性。

共享内核(Shared Kernel)

当多个限界上下文需要共享某些公共代码或模型时,可以提取出一个共享内核。共享内核是所有相关团队共同维护的代码库,包含通用的领域概念和工具类。

共享内核的优点是减少重复、保持一致性;缺点是需要协调多个团队的变更,可能成为瓶颈。

开放主机服务(Open Host Service)与发布语言(Published Language)

当一个限界上下文需要被多个其他上下文使用时,可以定义一个开放的协议(开放主机服务)和一个标准化的数据格式(发布语言)。这样,使用者不需要了解内部的实现细节,只需要遵循发布的协议即可。

REST API和消息队列的Schema都是发布语言的例子。

遵奉者(Conformist)

当下游系统无法影响上游系统的设计时,下游可能需要完全适配上游的模型。这种情况下,下游成为遵奉者,需要付出额外的努力来处理上游模型的不足。

防腐层(Anti-Corruption Layer)

为了防止外部系统的不良设计污染内部模型,可以在边界处建立一个防腐层。防腐层负责将外部模型转换为内部模型,隔离外部的复杂性。

防腐层通常包括适配器、翻译器和门面等组件。

2.5 上下文边界的演化策略

限界上下文的边界不是一成不变的,它会随着业务的发展和理解的深入而演化。以下是几种常见的演化场景和应对策略:

上下文的合并

当发现两个限界上下文之间的耦合过紧,或者它们的业务职责高度相关时,可以考虑将它们合并为一个上下文。

合并的触发条件:

  • 频繁的跨上下文调用
  • 大量的数据同步需求
  • 业务概念的天然关联
  • 团队结构的调整

合并的注意事项:

  • 评估合并后的复杂度是否在可控范围内
  • 重新定义通用语言
  • 处理数据模型的整合
  • 更新相关的接口和集成点

上下文的拆分

当一个限界上下文变得过大、职责不清或团队难以协同时,可以考虑将其拆分为多个更小的上下文。

拆分的触发条件:

  • 模型中存在明显的子领域
  • 不同部分的变更频率差异大
  • 团队规模扩大导致协调困难
  • 需要独立扩展不同的功能模块

拆分的注意事项:

  • 确保拆分后的上下文有清晰的边界
  • 设计合适的集成机制
  • 处理数据的分布和一致性
  • 评估拆分带来的运维复杂度增加

边界的调整

有时不需要完全的合并或拆分,只需要微调边界,将某些概念从一个上下文移动到另一个上下文。

这种调整通常是渐进的,可以通过以下步骤进行:

在目标上下文中创建新的模型

建立从源上下文到目标上下文的映射

逐步迁移使用该概念的功能

确认迁移完成后,删除源上下文中的旧模型

遗留系统的整合

在引入DDD时,往往需要与现有的遗留系统集成。这时可以将遗留系统视为一个特殊的限界上下文,通过防腐层与其交互。

随着对业务理解的深入,可以逐步将遗留系统中的核心功能重构为新的限界上下文,最终替换掉遗留系统。


三、程序员的视角

3.1 在实际项目中的应用

在实际项目中应用战略设计,需要遵循以下实践步骤:

第一步:识别子域

通过与领域专家的交流和对现有系统的分析,识别出业务中的不同子域。可以使用事件风暴等方法来帮助识别。

对于每个子域,判断它是核心域、支撑域还是通用域。这个判断会影响后续的资源投入和设计策略。

第二步:定义限界上下文

为每个子域定义限界上下文,明确其边界和职责。绘制上下文地图(Context Map),展示各上下文之间的关系。

在定义边界时,考虑以下因素:

  • 业务概念的内聚性
  • 团队的组织结构
  • 技术的独立性
  • 变更的频率和范围

第三步:设计集成机制

确定限界上下文之间的集成方式:

  • 同步调用(REST、gRPC等)
  • 异步消息(消息队列、事件总线等)
  • 共享数据库(谨慎使用)
  • 文件交换

为每个集成点定义清晰的接口契约,包括数据格式、通信协议、错误处理等。

第四步:实施防腐层

对于与外部系统或遗留系统的集成,设计防腐层来保护内部模型。防腐层应该:

  • 转换外部数据格式为内部模型
  • 处理外部系统的异常和不一致
  • 隔离外部系统的变化

第五步:持续验证和调整

定期回顾限界上下文的划分是否合理,边界是否需要调整。收集团队反馈,观察系统演进中的问题,及时进行优化。

3.2 常见陷阱与误区

在战略设计实践中,容易陷入以下陷阱:

陷阱一:过早的微服务化

有些团队在没有清晰理解业务边界的情况下,就急于将系统拆分为微服务。这导致服务边界不合理,后续需要频繁调整,增加了系统复杂度。

避免方法:先通过DDD识别限界上下文,再根据实际需要决定是否拆分为微服务。不要为了微服务而微服务。

陷阱二:忽视上下文映射

有些团队虽然识别了限界上下文,但没有明确定义上下文之间的关系和集成方式。这导致系统间耦合混乱,难以维护和演进。

避免方法:绘制并维护上下文地图,明确每个集成点的责任和契约。

陷阱三:共享数据库的滥用

为了简化集成,多个限界上下文直接共享同一个数据库。这导致上下文之间的隐式耦合,破坏了边界的完整性。

避免方法:每个限界上下文应该有自己的数据存储。如果需要数据共享,通过API或消息进行显式集成。

陷阱四:核心域识别错误

有些团队将通用功能误认为核心域,投入过多资源;或者忽视了真正的核心竞争力,导致差异化不足。

避免方法:与业务领导充分沟通,明确企业的竞争战略和价值主张。定期回顾和调整子域的分类。

陷阱五:边界僵化

有些团队将限界上下文的边界视为固定不变的,即使业务已经发生变化也不调整边界。这导致模型与实际业务脱节。

避免方法:将边界视为可演化的设计决策,定期评估其合理性,必要时进行调整。

3.3 与其他层级的联系

战略设计与软件系统的其他层级密切相关:

与组织架构的联系

康威定律指出:"设计系统的组织,其产生的设计等同于组织间的沟通结构。"限界上下文的划分应该与团队的组织结构相协调。

理想的状况是:每个限界上下文由一个独立的团队负责,团队内部可以自主决策模型的设计和实现。这种"逆向康威定律"的应用可以提高开发效率和系统质量。

与技术架构的联系

战略设计直接影响技术架构的选择:

  • 限界上下文的数量影响微服务的粒度
  • 上下文之间的集成方式影响通信机制的选择
  • 核心域的重要性影响技术选型的优先级

反过来,技术约束也会影响战略设计。例如,某些技术栈可能更适合特定的集成模式,需要在设计时考虑。

与DevOps的联系

限界上下文的划分影响CI/CD流水线的设计:

  • 独立的限界上下文可以有独立的构建和部署流程
  • 上下文之间的依赖关系影响部署顺序
  • 集成的复杂度影响测试策略

四、代码实战

4.1 限界上下文的代码组织示例

以下示例展示如何在代码中体现限界上下文的边界:

src/
├── sales/                    # 销售限界上下文
│   ├── domain/
│   │   ├── model/
│   │   │   ├── Product.java
│   │   │   ├── Customer.java
│   │   │   └── Order.java
│   │   ├── service/
│   │   └── repository/
│   ├── application/
│   └── infrastructure/
│
├── inventory/                # 库存限界上下文
│   ├── domain/
│   │   ├── model/
│   │   │   ├── StockItem.java
│   │   │   ├── Warehouse.java
│   │   │   └── Shipment.java
│   │   ├── service/
│   │   └── repository/
│   ├── application/
│   └── infrastructure/
│
└── shared/                   # 共享内核
    ├── Money.java
    ├── Address.java
    └── DateRange.java

销售上下文中的Product:

java
package com.example.sales.domain.model;

public class Product {
    private ProductId id;
    private String name;
    private Money price;
    private String description;
    private Category category;
    
    public Product(ProductId id, String name, Money price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
    
    public Money calculateDiscountedPrice(Discount discount) {
        return price.multiply(discount.getRate());
    }
}

库存上下文中的StockItem(同名但不同模型):

java
package com.example.inventory.domain.model;

public class StockItem {
    private StockItemId id;
    private ProductReference productReference; // 引用产品信息,不包含价格
    private int quantity;
    private Location location;
    private ReorderLevel reorderLevel;
    
    public StockItem(StockItemId id, ProductReference product, int quantity) {
        this.id = id;
        this.productReference = product;
        this.quantity = quantity;
    }
    
    public boolean isLowStock() {
        return quantity <= reorderLevel.getThreshold();
    }
    
    public void adjustQuantity(int delta) {
        if (quantity + delta < 0) {
            throw new IllegalArgumentException("库存不足");
        }
        this.quantity += delta;
    }
}

上下文之间的映射:

java
package com.example.integration;

// 防腐层:将库存上下文的数据转换为销售上下文需要的格式
public class InventoryToSalesMapper {
    
    public ProductAvailability mapToAvailability(StockItem stockItem) {
        return new ProductAvailability(
            stockItem.getProductReference().getProductId(),
            stockItem.getQuantity(),
            stockItem.isLowStock()
        );
    }
}

4.2 上下文映射模式的实现

开放主机服务与发布语言:

java
// 定义发布语言的DTO
public class ProductDTO {
    private String productId;
    private String name;
    private BigDecimal price;
    private String currency;
    
    // getter/setter
}

// REST Controller作为开放主机服务
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    @Autowired
    private ProductService productService;
    
    @GetMapping("/{id}")
    public ResponseEntity<ProductDTO> getProduct(@PathVariable String id) {
        Product product = productService.findById(new ProductId(id));
        ProductDTO dto = ProductMapper.toDTO(product);
        return ResponseEntity.ok(dto);
    }
    
    @PostMapping
    public ResponseEntity<ProductDTO> createProduct(@RequestBody CreateProductRequest request) {
        Product product = productService.create(request.toDomain());
        ProductDTO dto = ProductMapper.toDTO(product);
        return ResponseEntity.created(URI.create("/api/products/" + product.getId())).body(dto);
    }
}

防腐层实现:

java
// 外部系统的模型
public class ExternalCustomer {
    private String custId;
    private String fullName;
    private String contactInfo;
    private String status;
}

// 内部领域模型
public class Customer {
    private CustomerId id;
    private Name name;
    private Contact contact;
    private CustomerStatus status;
}

// 防腐层:转换外部模型为内部模型
@Component
public class CustomerAntiCorruptionLayer {
    
    public Customer translateFromExternal(ExternalCustomer external) {
        return new Customer(
            new CustomerId(external.getCustId()),
            new Name(parseFullName(external.getFullName())),
            new Contact(external.getContactInfo()),
            translateStatus(external.getStatus())
        );
    }
    
    private CustomerStatus translateStatus(String externalStatus) {
        switch (externalStatus) {
            case "A": return CustomerStatus.ACTIVE;
            case "I": return CustomerStatus.INACTIVE;
            case "S": return CustomerStatus.SUSPENDED;
            default: throw new IllegalArgumentException("未知状态: " + externalStatus);
        }
    }
    
    private Name parseFullName(String fullName) {
        String[] parts = fullName.split(" ", 2);
        return new Name(parts[0], parts.length > 1 ? parts[1] : "");
    }
}

4.3 领域事件的跨上下文传播

java
// 定义领域事件
public class OrderPlacedEvent implements DomainEvent {
    private String orderId;
    private String customerId;
    private List<OrderItemSnapshot> items;
    private LocalDateTime timestamp;
    
    // constructor, getters
}

// 在销售上下文中发布事件
@Service
public class OrderService {
    
    @Autowired
    private DomainEventPublisher eventPublisher;
    
    public Order placeOrder(OrderCommand command) {
        Order order = orderFactory.create(command);
        orderRepository.save(order);
        
        // 发布领域事件
        eventPublisher.publish(new OrderPlacedEvent(
            order.getId().toString(),
            order.getCustomerId().toString(),
            snapshotItems(order.getItems()),
            LocalDateTime.now()
        ));
        
        return order;
    }
}

// 在库存上下文中订阅事件
@Component
public class InventoryEventHandler {
    
    @Autowired
    private InventoryService inventoryService;
    
    @EventListener
    public void handleOrderPlaced(OrderPlacedEvent event) {
        for (OrderItemSnapshot item : event.getItems()) {
            inventoryService.reserveStock(
                new ProductId(item.getProductId()),
                item.getQuantity()
            );
        }
    }
}

// 事件总线实现(简化版)
@Component
public class DomainEventPublisher {
    
    @Autowired
    private ApplicationEventPublisher springPublisher;
    
    public void publish(DomainEvent event) {
        springPublisher.publishEvent(event);
    }
}

五、深入思考

5.1 历史背景

战略设计的思想可以追溯到软件工程的早期。模块化设计、信息隐藏、关注点分离等经典原则都是战略设计的前身。然而,这些原则主要关注技术层面的分解,对业务领域的结构化重视不足。

Eric Evans在2003年的《领域驱动设计》中首次系统地阐述了战略设计的概念。他提出的限界上下文、上下文映射、子域分类等概念,为处理大规模业务复杂性提供了系统化的方法。

随后,Martin Fowler的《企业应用架构模式》补充了更多集成模式的细节。Sam Newman的《微服务架构》则将DDD的战略设计与现代分布式系统架构相结合,推动了限界上下文在微服务拆分中的应用。

近年来,随着领域驱动设计的复兴,战略设计受到了越来越多的关注。特别是在大型企业数字化转型的背景下,如何合理地分解和组织复杂的业务系统成为一个关键挑战。

5.2 前沿方向

当前的战略设计实践正在向以下几个方向发展:

事件驱动的上下文集成

传统的同步API集成正在被异步事件驱动的方式所补充。领域事件成为限界上下文之间松散耦合的集成机制,支持更好的可扩展性和弹性。

基于图的上下文映射

一些团队开始使用图数据库和可视化工具来管理和分析限界上下文之间的关系。这使得复杂的上下文映射更加直观,便于发现潜在的问题。

自动化边界检测

借助代码分析和机器学习技术,一些工具可以自动检测代码库中的潜在边界,辅助人工进行限界上下文的识别和验证。

DDD与领域平台工程

在平台工程中,战略设计被用来定义平台的能力边界和服务目录。平台被视为一组限界上下文的集合,每个上下文提供特定的业务能力。

量子团队与限界上下文

Team Topologies中提出的"量子团队"概念与限界上下文有很好的对应关系。一个量子团队负责一个完整的业务能力,正好对应一个限界上下文。

5.3 延伸阅读

为了深入学习战略设计,推荐以下资源:

Eric Evans, 《领域驱动设计:软件核心复杂性应对之道》- 原始经典,必读

Vaughn Vernon, 《实现领域驱动设计》- 第14章详细讲解战略设计

Sam Newman, 《微服务架构》- 微服务拆分与DDD的结合

Nick Tune, 《Software Architecture: The Hard Parts》- 分布式系统中的战略设计

Team Topologies作者Matthew Skelton的相关文章- 组织与架构的关系

在线资源:

  • Context Mapping官方网站和资源
  • InfoQ关于DDD战略设计的系列文章
  • various DDD community blogs and forums

六、实验笔记

实验一:绘制现有系统的上下文地图

实验目标:

通过对一个现有系统进行分析,练习识别限界上下文并绘制上下文地图。

实验步骤:

选择一个你熟悉的中型或大型系统(可以是工作中的项目或开源项目)

收集系统的文档、代码结构、数据库schema等信息

识别系统中的主要业务功能和子领域

为每个子领域定义潜在的限界上下文

分析上下文之间的关系:

- 上游/下游关系

- 集成方式(API、消息、共享数据库等)

- 数据流向

绘制上下文地图,标注关系类型(如OHS、ACL、Conformist等)

评估当前设计的优缺点,提出改进建议

预期成果:

  • 一份完整的上下文地图
  • 每个限界上下文的简要说明
  • 改进建议文档

反思要点:

  • 识别边界时遇到了哪些困难?
  • 是否存在边界模糊或重叠的情况?
  • 当前的集成方式是否合理?
  • 有哪些可以优化的地方?

实验二:设计新系统的战略架构

实验目标:

为一个假设的新业务系统设计战略架构,练习子域分类和限界上下文定义。

实验步骤:

选择一个业务场景,如:

- 在线医疗预约平台

- 智能仓储管理系统

- 社区团购平台

进行领域探索:

- 列出主要的业务功能

- 识别关键的领域概念

- 理解业务流程

进行子域分类:

- 区分核心域、支撑域、通用域

- 说明分类的理由

定义限界上下文:

- 为每个子域定义边界

- 命名每个上下文

- 描述上下文的主要职责

设计上下文映射:

- 确定上下文之间的关系

- 选择集成模式

- 定义关键的接口契约

制定演进计划:

- 确定开发的优先级

- 规划边界的可能的演化路径

预期成果:

  • 战略设计文档
  • 上下文地图
  • 开发路线图

反思要点:

  • 核心域的识别是否准确?
  • 边界划分是否合理?
  • 集成方案是否可行?
  • 如何应对未来的变化?

七、本章小结

本章深入探讨了领域驱动设计中的战略设计,重点讲解了限界上下文、子域分类、上下文映射等核心概念。

我们首先明确了战略设计的本质:它是处理大规模系统复杂性的方法论,关注系统的整体结构和组织方式,与关注具体模型构建的战术设计形成互补。

限界上下文是战略设计的核心概念,它定义了领域模型的适用边界。在边界内,模型具有一致的语义;跨越边界时,需要进行显式的转换和映射。正确识别和定义限界上下文是成功应用DDD的关键。

子域分类(核心域、支撑域、通用域)为资源分配提供了战略指导。企业应该将最多的资源投入到核心域,在支撑域保持足够的功能性,在通用域尽量使用现成解决方案。

上下文映射模式描述了限界上下文之间的各种关系类型,包括开放主机服务、发布语言、防腐层、遵奉者等。选择合适的映射模式可以降低系统间的耦合,提高可维护性。

我们还讨论了限界上下文边界的演化策略。边界不是一成不变的,它应该随着业务的发展和理解的深入而调整。合并、拆分、微调都是常见的演化方式。

通过代码示例,我们展示了如何在代码中体现限界上下文的边界,如何实现防腐层,以及如何通过领域事件实现跨上下文的集成。

最后,我们通过实验任务让读者有机会将理论知识应用于实践,加深对战略设计的理解。

掌握战略设计需要时间和经验,但它带来的回报是巨大的:清晰的系统结构、合理的资源分配、灵活的可扩展性。希望本章能为读者的DDD实践提供有力的指导。


八、习题

概念理解题: 解释限界上下文与微服务的关系。为什么说它们不是一对一的对应关系?在什么情况下一个限界上下文可能对应多个微服务,或多个限界上下文合并为一个微服务?

案例分析题: 分析以下场景,识别其中的限界上下文:

> "一家连锁餐饮企业拥有线上点餐系统、门店POS系统、中央厨房生产系统、供应链管理系统和会员营销系统。线上点餐接收订单后,会推送到对应门店的POS系统;POS系统将订单详情发送给中央厨房;中央厨房根据订单生成生产计划,并向供应链系统发出原料采购需求;会员系统记录消费积分并推送优惠券。"

请:

- 识别至少5个限界上下文

- 说明每个上下文的主要职责

- 绘制上下文地图

子域分类题: 对于一个在线教育平台,以下功能分别属于哪种子域(核心域、支撑域、通用域)?说明理由:

- 课程内容推荐算法

- 视频播放功能

- 用户登录认证

- 学习进度跟踪

- 支付结算

- 教师排课系统

- 邮件通知

设计题: 为一个B2B电商平台设计战略架构。平台需要支持:供应商入驻、商品管理、采购商注册、询价报价、订单管理、物流配送、发票结算、客户服务。请:

- 划分子域并分类

- 定义限界上下文

- 设计上下文映射关系

- 说明你的设计理由

防腐层设计题: 你的系统需要集成一个外部的CRM系统,该系统的客户数据模型如下:

```json

{

"id": "CUST_12345",

"name": "张三",

"phone": "13800138000",

"email": "zhangsan@example.com",

"level": "VIP",

"tags": ["重要客户", "华东区"]

}

```

你的内部客户模型要求:

- 客户ID使用UUID

- 姓名分为姓和名

- 联系方式是一个独立的值对象

- 客户等级使用枚举(Regular/Silver/Gold/Platinum)

- 标签需要转换为内部的分类体系

请设计防腐层的转换逻辑。

重构题: 一个单体应用中,订单模块和库存模块共享同一个数据库,并且直接互相调用对方的DAO层。现在需要将它们拆分为独立的限界上下文。请设计重构方案,包括:

- 如何解耦数据库依赖

- 如何替换直接调用

- 如何保证数据一致性

- 重构的步骤和风险控制

论述题: 讨论康威定律与限界上下文设计的关系。当团队组织结构与理想的限界上下文划分不一致时,应该如何权衡和处理?

综合应用题: 假设你要将一个大型ERP系统改造为基于DDD的架构。系统目前包含:财务管理、人力资源管理、生产管理、销售管理、采购管理、库存管理等模块。请:

- 分析哪些可能是核心域

- 设计限界上下文的初步划分

- 规划改造的优先级和路径

- 说明如何处理与遗留系统的集成

批判性思维题: 有人认为"限界上下文的划分过于主观,不同人会得出不同的结论"。你同意这个观点吗?请讨论限界上下文识别中的客观依据和主观判断,并提出提高识别准确性的方法。

开放性问题: 随着Serverless和无服务器架构的发展,你认为战略设计和限界上下文的概念会发生什么变化?函数的粒度与限界上下文的边界应该如何协调?

10/12战略设计
0%