文章目录
- 引言
- 模型的核心作用与价值
- 四大模型类型
- UML建模工具
- UML类图的核心价值
- 类关系深度剖析
- 企业级建模实践
- 领域模型(推荐) vs 数据模型(不推荐)
- 区别联系
- 错把领域模型当数据模型
- 错误方案 vs 正确方案对比
- 正确方案的实现
- 1. 数据库设计(MySQL 8.0+)
- 2. 数据对象(DO)定义
- 3. 领域模型定义
- 4. 数据转换层实现
- 5. 业务服务层使用
- 6. DTO转换示例
- 架构分层示意图
- 扩展性
- 性能优化
- 关键设计原则
- 错把数据模型当领域模型
- JSON字段与垂直表扩展案例详解
- JSON字段扩展案例:价格规则配置
- 场景说明
- 数据模型设计
- 领域模型设计
- 转换层实现
- 垂直表扩展案例:商品扩展属性
- 场景说明
- 数据模型设计
- 领域模型设计
- 转换层实现
- 关键对比与选型建议
- 反模式警示
- 阿里巴巴中台启示
- 两种模型各司其职
- 结语:模型思维的三重境界
引言
在软件工程中,有两个高阶工作,一个是架构,另一个是建模。如果把写代码比喻成“搬砖”,那么架构和建模就是“设计图纸”了。相比于编码,建模的确是对设计经验和抽象能力要求更高的一种技能。
模型的核心作用与价值
-
简化与抽象:模型通过剔除无关细节,聚焦问题核心
-
跨领域通用性:无论是物理实体还是抽象概念,模型均能通过不同形式(数学公式、图形、思维框架)表达系统特性。
-
动态演进:模型需随认知迭代更新,如同代码重构,需用发展的眼光持续优化。
四大模型类型
-
物理模型:
- 定义:实物缩小/放大版,如风洞测试中的飞机模型。
- 应用场景:硬件系统原型验证、建筑可视化设计。
- 案例:汽车碰撞测试中,物理模型用于模拟真实撞击效果。
-
数学模型:
- 定义:数学语言描述的系统行为,如线性回归公式。
- 应用场景:预测分析(如销售预测)、算法设计(如推荐系统)。
- 案例:电商平台通过
\( Y = aX + b \)
预测用户购买行为,优化库存管理。
-
概念模型:
- 定义:领域实体抽象,独立于技术实现,如UML类图。
- 应用场景:领域驱动设计(DDD)、系统架构规划。
- 案例:在线教育平台中,用“课程-学生-教师”概念模型定义核心业务关系。
-
思维模型:
- 定义:问题解决框架,如奥卡姆剃刀、金字塔原理。
- 应用场景:需求分析、技术决策、团队协作。
- 案例:用“分治思维”拆分微服务架构,降低系统复杂度。
UML建模工具
UML类图的核心价值
-
统一语义桥梁
- 突破语言屏障:Java开发者可通过类图快速理解C#系统设计
- 跨角色协作:产品经理通过类图理解业务实体关系,DBA依据类图设计数据库表结构
- 典型案例:Apache Kafka官方文档使用类图展示Broker-Producer-Consumer交互架构
-
设计模式可视化
- 观察者模式类图清晰展现主题-观察者解耦机制
类关系深度剖析
-
关联关系实战辨析
关系类型 代码特征 生命周期 典型场景 普通关联 成员变量持有引用 独立 User-Order(用户拥有订单) 聚合 构造注入(setter/参数传入) 独立 Car-Engine(汽车包含引擎) 组合 直接实例化(无setter) 同步 Window-Frame(窗口包含框架) -
依赖关系三种实现方式
// 方式1:参数依赖 class Teacher {void teach(Projector proj) {proj.display();} }// 方式2:局部变量 class ReportGenerator {void generate() {PDFExporter exporter = new PDFExporter();exporter.export();} }// 方式3:静态调用 class PaymentService {void process() {Logger.log("Payment processed");} }
-
泛化与接口的黄金法则
- Liskov替换原则:子类必须完全实现父类约定
// 错误示范 class Bird {void fly() {} } class Penguin extends Bird {} // 企鹅不会飞,违反LSP// 正确方案 interface Flyable {void fly(); } class Sparrow implements Flyable {} class Penguin extends Bird {}
- Liskov替换原则:子类必须完全实现父类约定
企业级建模实践
- 电商领域建模案例
- 关键设计点:
- 订单聚合根管理OrderItem
- 支付与订单的限界上下文划分
- 微服务建模陷阱
-
过度解耦反模式:
- 问题:服务间直接依赖基础设施导致耦合
- 优化方案:引入适配器层隔离技术细节
-
类图大师的三重境界
- 精确建模:严格遵循UML规范(如IBM Rational统一过程)
- 意图建模:突出设计重点(如DDD聚合根加粗显示)
- 价值建模:通过类图驱动架构演进(如识别领域事件改进CQRS)
领域模型(推荐) vs 数据模型(不推荐)
区别联系
领域模型关注的是领域知识,是业务领域的核心实体,体现了问题域中的关键概念,以及概念之间的联系。领域模型建模的关键在于模型能否显性化、清晰地表达业务语义,其次才是扩展性。
数据模型关注的是数据存储,所有的业务都离不开数据,以及对数据的CRUD。数据模型建模的决策因素主要是扩展性、性能等非功能属性,无须过多考虑业务语义的表征能力。
根据Robert在《架构整洁之道》一书中的观点:领域模型是核心,数据模型是技术细节。现实情况是,二者都很重要。领域模型和数据模型之所以容易被混淆,是因为两者都强调实体(Entity)和强调关系(Relationship)
者的确有一些共同点,有时领域模型和数据模型会长得很像,甚至会趋同,这很正常。但更多的时候,二者是有区别的。正确的做法应该是有意识地把这两个模型区别开来,分别进行设计,因为它们建模的目标有所不同。
数据模型负责数据存储,其要义是扩展性、灵活性、性能;而领域模型负责业务逻辑的实现,其要义是业务语义显性化的表达,以及充分利用面向对象的特性增强代码的业务表征能力。
维度 | 领域模型 | 数据模型 |
---|---|---|
核心目标 | 显性化业务语义,封装领域逻辑 | 高效存储数据,支持扩展性和性能优化 |
设计原则 | 高内聚、低耦合,反映业务概念本质 | 遵循数据库范式(或合理反范式),兼顾查询效率 |
技术无关性 | 与技术实现解耦,面向业务语言设计 | 依赖存储技术(如关系型/NoSQL数据库) |
变化频率 | 随业务需求演进频繁调整 | 相对稳定,避免频繁表结构变更 |
典型工具 | UML类图、事件风暴 | ER图、DDL脚本、JSON Schema |
示例对比:
- 订单场景
- 领域模型:
Order
聚合根包含OrderItem
、Payment
等子实体,封装验价、履约校验逻辑 - 数据模型:
orders
表使用分库分表策略,order_items
表通过JSON存储商品快照
- 领域模型:
然而在实际情况中,大多数业务系统设计并没有很好地区分二者的关系,我们经常会犯两个错误,一个是错把领域模型当数据模型,另一个是错把数据模型当领域模型
错把领域模型当数据模型
一个报价优化的项目,其中涉及报价规则的问题。这个业务逻辑大概是,对于不同的商品(通过类目、品牌、供应商类型等维度区分),给出不同的价格区间,然后判断商家的报价是应该被自动审核通过(autoApprove
),还是应该被自动拦截(autoBlock
)。
对于这个规则,领域模型很简单,就是提供价格管控需要的配置数据
如果按照这个领域模型去设计存储模型,那么需要两张表,分别是price_rule和price_range,一张用来存放价格规则,另一张用来存放价格区间 。
不合理的存储模型
如果这样设计数据模型,我们就犯了把领域模型当数据模型的错误。这里更合适的做法是只用一张表,把price_range作为一个字段,在price_rule中用一个字段存储, 对于多个价格区间信息,只用一个json字段存储即可
合理的存储模型
这样做的好处显而易见。
- 首先,维护一张数据库表肯定比维护两张的成本要低。
- 其次,其数据的扩展性更好。比如,假设有新需求,需要增加一个建议价格(suggest price)区间,如果是两张表,那么需要在price_range中加两个新字段;而如果只用json存储,那么数据模型可以保持不变。
在业务代码中,我们需要把json的数据对象转换成有业务语义的领域对象,这样既可以享受数据模型扩展性带来的便捷性,又不损失领域模型对业务语义显性化带来的代码可读性
错误方案 vs 正确方案对比
维度 | 错误方案(领域模型直转数据模型) | 正确方案(领域模型与数据模型解耦) |
---|---|---|
存储结构 | 2张表(price_rule + price_range) | 1张表(price_rule含JSON字段) |
扩展性 | 新增字段需改表结构 | 新增属性只需改JSON结构 |
查询复杂度 | 需要JOIN操作 | 单表查询 |
代码维护性 | 需同时维护两个DAO | 只需维护一个DAO |
领域对象纯度 | 领域对象被数据库外键污染 | 领域对象保持业务语义完整性 |
正确方案的实现
1. 数据库设计(MySQL 8.0+)
-- 价格规则表
CREATE TABLE price_rule (id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键',category_code VARCHAR(50) NOT NULL COMMENT '类目编码',brand_code VARCHAR(50) NOT NULL COMMENT '品牌编码',supplier_type VARCHAR(20) NOT NULL COMMENT '供应商类型',price_ranges JSON NOT NULL COMMENT '价格区间配置',created_time DATETIME DEFAULT CURRENT_TIMESTAMP,updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,INDEX idx_category_brand (category_code, brand_code)
) COMMENT='价格规则表';-- 示例数据
INSERT INTO price_rule (category_code, brand_code, supplier_type, price_ranges)
VALUES ('ELECTRONICS', 'APPLE', 'OFFICIAL','[{"min": 5000.00,"max": 8000.00,"type": "AUTO_APPROVE","currency": "CNY"},{"min": 8000.01,"max": 10000.00,"type": "MANUAL_REVIEW","currency": "CNY"}]'
);
2. 数据对象(DO)定义
// 持久化对象
public class PriceRuleDO {private Long id;private String categoryCode;private String brandCode;private String supplierType;private String priceRanges; // JSON字符串// 其他字段及getter/setter
}
3. 领域模型定义
// 值对象:价格区间
public class PriceRange {private BigDecimal min;private BigDecimal max;private PriceType type; // 枚举:AUTO_APPROVE/MANUAL_REVIEWprivate Currency currency; // 枚举:CNY/USD// 业务方法:校验价格是否在区间内public boolean contains(BigDecimal price) {return price.compareTo(min) >= 0 && price.compareTo(max) <= 0;}
}// 聚合根:价格规则
public class PriceRule {private Long id;private String categoryCode;private String brandCode;private String supplierType;private List<PriceRange> priceRanges;// 核心业务方法:校验报价public PriceCheckResult checkPrice(BigDecimal price) {return priceRanges.stream().filter(range -> range.contains(price)).findFirst().map(range -> new PriceCheckResult(range.getType())).orElse(PriceCheckResult.BLOCK);}
}
4. 数据转换层实现
// Repository实现
@Repository
public class PriceRuleRepositoryImpl implements PriceRuleRepository {@Autowiredprivate JdbcTemplate jdbcTemplate;private final ObjectMapper objectMapper = new ObjectMapper();@Overridepublic PriceRule findByConditions(String category, String brand) {String sql = "SELECT * FROM price_rule WHERE category_code = ? AND brand_code = ?";PriceRuleDO priceRuleDO = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(PriceRuleDO.class), category, brand);return convertToEntity(priceRuleDO);}private PriceRule convertToEntity(PriceRuleDO priceRuleDO) {try {List<PriceRange> ranges = objectMapper.readValue(priceRuleDO.getPriceRanges(),new TypeReference<List<PriceRange>>(){});return new PriceRule(priceRuleDO.getId(),priceRuleDO.getCategoryCode(),priceRuleDO.getBrandCode(),priceRuleDO.getSupplierType(),ranges);} catch (JsonProcessingException e) {throw new DataConversionException("价格规则数据转换失败", e);}}// 反向转换略...
}
5. 业务服务层使用
@Service
public class PriceCheckService {@Autowiredprivate PriceRuleRepository priceRuleRepository;public PriceCheckResult validatePrice(String category, String brand, BigDecimal price) {PriceRule rule = priceRuleRepository.findByConditions(category, brand);return rule.checkPrice(price);}
}
6. DTO转换示例
// 返回给前端的DTO
public class PriceRuleDTO {private String category;private String brand;private List<PriceRangeDTO> ranges;public static PriceRuleDTO fromEntity(PriceRule rule) {PriceRuleDTO dto = new PriceRuleDTO();dto.setCategory(rule.getCategoryCode());dto.setBrand(rule.getBrandCode());dto.setRanges(rule.getPriceRanges().stream().map(range -> new PriceRangeDTO(range.getMin(),range.getMax(),range.getType().name())).collect(Collectors.toList()));return dto;}
}
架构分层示意图
扩展性
新增建议价格区间需求时:
- 领域模型变更
public class PriceRange {// 增加新字段private BigDecimal suggestPrice;// 新增业务方法public BigDecimal calculatePriceDeviation(BigDecimal offerPrice) {return offerPrice.subtract(suggestPrice).abs();}
}
- JSON结构变更
{"min": 5000.00,"max": 8000.00,"type": "AUTO_APPROVE","currency": "CNY","suggest_price": 7500.00
}
- 无需修改数据库结构
性能优化
- JSON索引优化(MySQL 8.0+)
-- 创建虚拟列并建立索引
ALTER TABLE price_rule
ADD COLUMN min_price DECIMAL(15,2) GENERATED ALWAYS AS (JSON_EXTRACT(price_ranges, '$[0].min')) STORED,
ADD INDEX idx_min_price (min_price);
- 缓存策略
@Cacheable(value = "priceRules", key = "#category + '_' + #brand")
public PriceRule findByConditions(String category, String brand) {// 查询逻辑
}
- 异步预热
@Scheduled(fixedRate = 30 * 60 * 1000) // 每30分钟刷新
public void refreshPriceRules() {// 批量加载热数据到缓存
}
关键设计原则
-
单一职责原则
- 领域对象:专注业务逻辑
- 数据对象:专注存储结构
- DTO:专注数据传输
-
开闭原则
- 领域模型修改不影响存储层
- 存储结构变化不影响业务逻辑
-
显式语义原则
- 通过
PriceRange.contains()
等业务方法明确表达规则 - 避免在service层出现复杂的价格判断逻辑
- 通过
-
技术适配原则
- 利用JSON字段处理动态结构
- 通过虚拟列解决查询性能问题
通过这种设计,我们实现了:
- 存储层:1张表+JSON字段 => 无限扩展能力
- 领域层:纯净的PriceRule聚合根 => 显性化业务规则
- 架构层:清晰的层级边界 => 提升可维护性
当需要增加新的价格维度(如区域定价)时,只需要:
- 扩展PriceRange值对象
- 调整JSON结构
- 更新转换层逻辑
而无需进行耗时的数据库迁移操作,真正实现了领域模型与技术实现的解耦。
错把数据模型当领域模型
数据模型最好是可扩展的,毕竟改动数据库是一个大工程,不管是加字段、减字段,还是加表、删表,都涉及不少的工作量.
说到数据模型的扩展设计经典之作,非阿里巴巴的业务中台莫属。得益于良好的扩展性设计,仅仅其核心的商品、订单、支付、物流4张表,就支撑了阿里巴巴的几十个业务、成千上万个业务场景。
以商品中台为例,它只用了一张auction_extend
垂直表,就解决了所有业务商品数据存储扩展性的需求。从理论上来说,这种数据模型可以满足无限的业务扩展需求。
JSON字段也好,垂直表也好,虽然都可以很好地解决数据存储扩展的问题,但我们最好不要把这些扩展当成领域对象来处理,否则代码根本就不是在面向对象编程,而是在面向扩展字段(Features)编程,这就犯了把数据模型当领域模型的错误。更好的做法应该是把数据对象(Data Object)转换成领域对象来处理。
JSON字段与垂直表扩展案例详解
JSON字段扩展案例:价格规则配置
场景说明
电商系统需要为不同类目商品配置价格管控规则,每个规则包含多个价格区间(如最低价、最高价、建议价),且区间数量可能动态变化。
数据模型设计
CREATE TABLE price_rule (id BIGINT PRIMARY KEY COMMENT '主键',category_id VARCHAR(20) NOT NULL COMMENT '类目ID',brand_id VARCHAR(20) NOT NULL COMMENT '品牌ID',price_ranges JSON NOT NULL COMMENT '价格区间配置(JSON数组)'
);
示例数据
{"price_ranges": [{"min": 100.00,"max": 500.00,"type": "AUTO_APPROVE"},{"min": 500.00,"max": 1000.00,"type": "MANUAL_REVIEW"}]
}
领域模型设计
// 领域对象:价格规则聚合根
public class PriceRule {private Long id;private String categoryId;private String brandId;private List<PriceRange> priceRanges; // 显式业务对象// 业务方法:校验价格是否合法public PriceCheckResult checkPrice(BigDecimal price) {return priceRanges.stream().filter(range -> range.contains(price)).findFirst().map(range -> new PriceCheckResult(range.getType())).orElse(PriceCheckResult.BLOCK);}
}// 值对象:价格区间
public class PriceRange {private BigDecimal min;private BigDecimal max;private PriceType type; // 枚举:AUTO_APPROVE/MANUAL_REVIEW
}
转换层实现
// Repository层转换逻辑
public class PriceRuleRepository {public PriceRule findById(Long id) {PriceRuleDO priceRuleDO = jdbcTemplate.queryForObject(...);return convertToEntity(priceRuleDO);}private PriceRule convertToEntity(PriceRuleDO priceRuleDO) {List<PriceRange> ranges = objectMapper.readValue(priceRuleDO.getPriceRanges(), new TypeReference<List<PriceRange>>(){});return new PriceRule(priceRuleDO.getId(),priceRuleDO.getCategoryId(),priceRuleDO.getBrandId(),ranges);}
}
优势
- 扩展性:新增价格类型时无需修改表结构
- 业务语义清晰:
PriceRange
对象封装校验逻辑 - 技术解耦:JSON解析完全隐藏在Repository层
垂直表扩展案例:商品扩展属性
场景说明
商品系统需要支持不同类目商品的扩展属性(如图书类商品需要作者、出版社,电子类商品需要型号、保修期)。
数据模型设计
-- 商品主表
CREATE TABLE product (id BIGINT PRIMARY KEY COMMENT '主键',sku VARCHAR(50) NOT NULL COMMENT '商品编码',category VARCHAR(20) NOT NULL COMMENT '类目'
);-- 商品扩展表(垂直表)
CREATE TABLE product_extend (product_id BIGINT COMMENT '商品ID',extend_key VARCHAR(50) COMMENT '扩展键',extend_value VARCHAR(500) COMMENT '扩展值',PRIMARY KEY (product_id, extend_key)
);
示例数据
product_id | extend_key | extend_value |
---|---|---|
1001 | author | J.K. Rowling |
1001 | publisher | Bloomsbury |
2002 | model | iPhone 15 |
2002 | warranty | 2 years |
领域模型设计
// 领域对象:商品聚合根
public class Product {private Long id;private String sku;private String category;private ProductDetail detail; // 显式业务对象
}// 值对象:商品详情(根据类目动态扩展)
public class ProductDetail {// 图书类属性private String author;private String publisher;// 电子类属性private String model;private String warranty;// 通用方法:校验类目与属性匹配public void validateCategory(String category) {if ("BOOK".equals(category) && author == null) {throw new BusinessException("图书必须包含作者信息");}}
}
转换层实现
// 防腐层转换逻辑
public class ProductAdapter {public Product convert(ProductDO productDO, List<ProductExtendDO> extendDOs) {ProductDetail detail = new ProductDetail();extendDOs.forEach(extend -> {switch (extend.getKey()) {case "author": detail.setAuthor(extend.getValue()); break;case "publisher": detail.setPublisher(extend.getValue()); break;case "model": detail.setModel(extend.getValue()); break;case "warranty": detail.setWarranty(extend.getValue()); break;}});return new Product(productDO.getId(),productDO.getSku(),productDO.getCategory(),detail);}
}
优势
- 无限扩展:新增属性只需插入新记录
- 查询优化:可通过索引快速定位特定扩展属性
- 领域隔离:
ProductDetail
对象强制业务校验逻辑
关键对比与选型建议
维度 | JSON字段 | 垂直表 |
---|---|---|
存储效率 | 高(单字段存储) | 低(多行存储) |
查询性能 | 差(无法走索引) | 优(可对key/value建索引) |
扩展成本 | 零成本(无需DDL变更) | 低(仅插入新记录) |
适用场景 | 配置类数据(如规则、参数) | 需要检索的扩展属性(如商品特征) |
领域适配 | 需反序列化为领域对象 | 需键值对到领域属性的映射 |
反模式警示
错误示例:在业务层直接操作扩展字段
// 错误!领域层直接处理技术细节
public class ProductService {public void updateProduct(Long productId, Map<String, String> features) {productExtendDao.batchUpdate(features); // 直接操作扩展表}
}
正确实践:通过防腐层隔离技术细节
public class ProductService {public void updateProduct(Product product) {product.validate(); // 业务校验List<ProductExtendDO> extendDOs = convertToExtendDOs(product);productExtendDao.batchUpdate(extendDOs); }private List<ProductExtendDO> convertToExtendDOs(Product product) {// 将领域对象转换为数据对象}
}
阿里巴巴中台启示
auction_extend
表设计精髓
CREATE TABLE auction_extend (auction_id BIGINT COMMENT '商品ID',extend_key VARCHAR(64) COMMENT '扩展键',extend_value VARCHAR(4096) COMMENT '扩展值',PRIMARY KEY (auction_id, extend_key)
) COMMENT='商品扩展表';
配套领域模型设计
// 商品领域服务
public class AuctionService {// 根据业务场景动态组装领域对象public AuctionDetail getAuctionDetail(Long auctionId) {AuctionDO auctionDO = auctionDao.findById(auctionId);List<AuctionExtendDO> extendDOs = auctionExtendDao.findByAuctionId(auctionId);return AuctionDetailAssembler.assemble(auctionDO, extendDOs);}
}
经验总结
- 物理存储与逻辑模型分离:扩展表只关心数据存储,不参与业务逻辑
- 双向转换机制:
- 写入时:将领域对象拆解为
auction
主表+auction_extend
扩展表 - 读取时:通过
Assembler
将多表数据组合为完整领域对象
- 写入时:将领域对象拆解为
- 缓存优化:对高频访问的扩展属性建立二级缓存(如作者、型号等)
通过这两个案例可以看出:优秀的数据模型扩展方案必须与领域模型解耦。JSON字段和垂直表解决的是存储层的扩展问题,而领域模型需要保持对业务语义的精确表达。二者的协作需要通过明确的转换层来实现,这正是DDD中"防腐层"思想的精髓所在。
两种模型各司其职
应该是把领域模型和数据模型区别开来,让它们各司其职,从而使应用系统架构更合理。
领域模型是面向领域对象的,要尽量具体,尽量语义明确,显性化地表达业务语义是其首要任务,扩展性是其次;数据模型是面向数据存储的,要尽量可扩展。
在具体落地时,我们可以采用COLA的架构思想,使用gateway作为数据对象(Data Object, DO)和领域对象(Entity)之间的转义网关,如图所示。其中,gateway除了起到转义的作用,还起到了防腐解耦的作用,解除了业务代码对底层数据(DO、DTO等)的直接依赖,从而提升系统的可维护性。
领域模型和数据模型具体落地
此外,教科书上告诉我们,在做关系数据库设计时要满足3NF(第三范式),然而在实际工作中,我们经常会因为性能、扩展性的原因故意打破这个原则。比如,通过数据冗余提升访问性能,通过元数据、垂直表、扩展字段提升表的扩展性。
不同的业务场景对数据扩展的诉求也不一样,像price_rule
这种简单的配置数据扩展,json
就能胜任。更复杂的,用auction_extend
这种垂直表
也是不错的选择。
看到这里,可能会问:这样做,数据是可扩展了,可数据查询怎么解决呢?总不能用join表或者like吧。实际上,
- 对一些配置类的数据或者数据量不大的数据,我们完全可以用like。
- 然而,对于海量数据,当然不能用like,不过这个问题很容易通过读写分离、构建搜索(Search)的办法解决,如图所示。
使用搜索解决读写性能问题
领域模型和数据模型有明显区别,领域模型关心的是业务概念,其要义是显性化地表达业务语义;数据模型关心的是数据存储,其核心是数据访问的性能、数据的扩展性等非功能属性。
结语:模型思维的三重境界
- 见山是山:严格遵循现有模型(如教科书中的设计模式);
- 见山不是山:根据上下文裁剪模型(如微服务中的CQRS变形);
- 见山仍是山:超越模型形式,直击问题本质(如用简单事件溯源替代复杂ESB)。
掌握模型思维,实则是培养“以简驭繁”的能力——正如埃隆·马斯克推崇的第一性原理,穿透表象,直抵核心矛盾。在软件工程的复杂迷局中,优秀的模型如同指南针,在混沌中开辟清晰路径。