模型思维 - 领域模型的应用与解析

news/2025/2/24 20:30:54

文章目录

  • 引言
  • 模型的核心作用与价值
  • 四大模型类型
  • UML建模工具
    • UML类图的核心价值
    • 类关系深度剖析
    • 企业级建模实践
  • 领域模型(推荐) vs 数据模型(不推荐)
    • 区别联系
    • 错把领域模型数据模型
      • 错误方案 vs 正确方案对比
      • 正确方案的实现
        • 1. 数据库设计(MySQL 8.0+)
        • 2. 数据对象(DO)定义
        • 3. 领域模型定义
        • 4. 数据转换层实现
        • 5. 业务服务层使用
        • 6. DTO转换示例
      • 架构分层示意图
      • 扩展性
      • 性能优化
      • 关键设计原则
    • 错把数据模型领域模型
      • JSON字段与垂直表扩展案例详解
        • JSON字段扩展案例:价格规则配置
        • 垂直表扩展案例:商品扩展属性
        • 关键对比与选型建议
        • 反模式警示
        • 阿里巴巴中台启示
    • 两种模型各司其职
  • 结语:模型思维的三重境界

在这里插入图片描述


引言

在软件工程中,有两个高阶工作,一个是架构,另一个是建模。如果把写代码比喻成“搬砖”,那么架构和建模就是“设计图纸”了。相比于编码,建模的确是对设计经验和抽象能力要求更高的一种技能。


模型的核心作用与价值

  • 简化与抽象:模型通过剔除无关细节,聚焦问题核心

  • 跨领域通用性:无论是物理实体还是抽象概念,模型均能通过不同形式(数学公式、图形、思维框架)表达系统特性。

  • 动态演进:模型需随认知迭代更新,如同代码重构,需用发展的眼光持续优化。


四大模型类型

  • 物理模型

    • 定义:实物缩小/放大版,如风洞测试中的飞机模型。
    • 应用场景:硬件系统原型验证、建筑可视化设计。
    • 案例:汽车碰撞测试中,物理模型用于模拟真实撞击效果。
  • 数学模型

    • 定义:数学语言描述的系统行为,如线性回归公式。
    • 应用场景:预测分析(如销售预测)、算法设计(如推荐系统)。
    • 案例:电商平台通过 \( Y = aX + b \) 预测用户购买行为,优化库存管理。
  • 概念模型

    • 定义:领域实体抽象,独立于技术实现,如UML类图。
    • 应用场景:领域驱动设计(DDD)、系统架构规划。
    • 案例:在线教育平台中,用“课程-学生-教师”概念模型定义核心业务关系。
  • 思维模型

    • 定义:问题解决框架,如奥卡姆剃刀、金字塔原理。
    • 应用场景:需求分析、技术决策、团队协作。
    • 案例:用“分治思维”拆分微服务架构,降低系统复杂度。

UML建模工具

UML类图的核心价值

  1. 统一语义桥梁

    • 突破语言屏障:Java开发者可通过类图快速理解C#系统设计
    • 跨角色协作:产品经理通过类图理解业务实体关系,DBA依据类图设计数据库表结构
    • 典型案例:Apache Kafka官方文档使用类图展示Broker-Producer-Consumer交互架构
  2. 设计模式可视化
    在这里插入图片描述

    • 观察者模式类图清晰展现主题-观察者解耦机制

类关系深度剖析

  1. 关联关系实战辨析

    关系类型代码特征生命周期典型场景
    普通关联成员变量持有引用独立User-Order(用户拥有订单)
    聚合构造注入(setter/参数传入)独立Car-Engine(汽车包含引擎)
    组合直接实例化(无setter)同步Window-Frame(窗口包含框架)
  2. 依赖关系三种实现方式

    // 方式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");
        }
    }
    
  3. 泛化与接口的黄金法则

    • Liskov替换原则:子类必须完全实现父类约定
      // 错误示范
      class Bird {
          void fly() {}
      }
      class Penguin extends Bird {} // 企鹅不会飞,违反LSP
      
      // 正确方案
      interface Flyable {
          void fly();
      }
      class Sparrow implements Flyable {}
      class Penguin extends Bird {}  
      

企业级建模实践

  1. 电商领域建模案例

在这里插入图片描述

  • 关键设计点:
    • 订单聚合根管理OrderItem
    • 支付与订单的限界上下文划分
  1. 微服务建模陷阱
    • 过度解耦反模式
      在这里插入图片描述

      • 问题:服务间直接依赖基础设施导致耦合
      • 优化方案:引入适配器层隔离技术细节

类图大师的三重境界

  1. 精确建模:严格遵循UML规范(如IBM Rational统一过程)
  2. 意图建模:突出设计重点(如DDD聚合根加粗显示)
  3. 价值建模:通过类图驱动架构演进(如识别领域事件改进CQRS)

领域模型(推荐) vs 数据模型(不推荐)

区别联系

领域模型关注的是领域知识,是业务领域的核心实体,体现了问题域中的关键概念,以及概念之间的联系。领域模型建模的关键在于模型能否显性化、清晰地表达业务语义,其次才是扩展性。

数据模型关注的是数据存储,所有的业务都离不开数据,以及对数据的CRUD。数据模型建模的决策因素主要是扩展性、性能等非功能属性,无须过多考虑业务语义的表征能力。

根据Robert在《架构整洁之道》一书中的观点:领域模型是核心,数据模型是技术细节。现实情况是,二者都很重要。领域模型数据模型之所以容易被混淆,是因为两者都强调实体(Entity)和强调关系(Relationship)

者的确有一些共同点,有时领域模型数据模型会长得很像,甚至会趋同,这很正常。但更多的时候,二者是有区别的。正确的做法应该是有意识地把这两个模型区别开来,分别进行设计,因为它们建模的目标有所不同。

数据模型负责数据存储,其要义是扩展性、灵活性、性能;而领域模型负责业务逻辑的实现,其要义是业务语义显性化的表达,以及充分利用面向对象的特性增强代码的业务表征能力。

在这里插入图片描述

维度领域模型数据模型
核心目标显性化业务语义,封装领域逻辑高效存储数据,支持扩展性和性能优化
设计原则高内聚、低耦合,反映业务概念本质遵循数据库范式(或合理反范式),兼顾查询效率
技术无关性与技术实现解耦,面向业务语言设计依赖存储技术(如关系型/NoSQL数据库)
变化频率随业务需求演进频繁调整相对稳定,避免频繁表结构变更
典型工具UML类图、事件风暴ER图、DDL脚本、JSON Schema

示例对比

  • 订单场景
    • 领域模型Order聚合根包含OrderItemPayment等子实体,封装验价、履约校验逻辑
    • 数据模型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_REVIEW
    private 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 {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    @Override
    public 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 {
    
    @Autowired
    private 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;
    }
}

架构分层示意图

在这里插入图片描述


扩展性

新增建议价格区间需求时:

  1. 领域模型变更
public class PriceRange {
    // 增加新字段
    private BigDecimal suggestPrice;
    
    // 新增业务方法
    public BigDecimal calculatePriceDeviation(BigDecimal offerPrice) {
        return offerPrice.subtract(suggestPrice).abs();
    }
}
  1. JSON结构变更
{
    "min": 5000.00,
    "max": 8000.00,
    "type": "AUTO_APPROVE",
    "currency": "CNY",
    "suggest_price": 7500.00
}
  1. 无需修改数据库结构

性能优化

  1. 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);
  1. 缓存策略
@Cacheable(value = "priceRules", key = "#category + '_' + #brand")
public PriceRule findByConditions(String category, String brand) {
    // 查询逻辑
}
  1. 异步预热
@Scheduled(fixedRate = 30 * 60 * 1000) // 每30分钟刷新
public void refreshPriceRules() {
    // 批量加载热数据到缓存
}

关键设计原则

  1. 单一职责原则

    • 领域对象:专注业务逻辑
    • 数据对象:专注存储结构
    • DTO:专注数据传输
  2. 开闭原则

    • 领域模型修改不影响存储层
    • 存储结构变化不影响业务逻辑
  3. 显式语义原则

    • 通过PriceRange.contains()等业务方法明确表达规则
    • 避免在service层出现复杂的价格判断逻辑
  4. 技术适配原则

    • 利用JSON字段处理动态结构
    • 通过虚拟列解决查询性能问题

通过这种设计,我们实现了:

  • 存储层:1张表+JSON字段 => 无限扩展能力
  • 领域层:纯净的PriceRule聚合根 => 显性化业务规则
  • 架构层:清晰的层级边界 => 提升可维护性

当需要增加新的价格维度(如区域定价)时,只需要:

  1. 扩展PriceRange值对象
  2. 调整JSON结构
  3. 更新转换层逻辑

而无需进行耗时的数据库迁移操作,真正实现了领域模型与技术实现的解耦。


错把数据模型领域模型

数据模型最好是可扩展的,毕竟改动数据库是一个大工程,不管是加字段、减字段,还是加表、删表,都涉及不少的工作量.

说到数据模型的扩展设计经典之作,非阿里巴巴的业务中台莫属。得益于良好的扩展性设计,仅仅其核心的商品、订单、支付、物流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_idextend_keyextend_value
1001authorJ.K. Rowling
1001publisherBloomsbury
2002modeliPhone 15
2002warranty2 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)的办法解决,如图所示。

使用搜索解决读写性能问题

在这里插入图片描述

领域模型数据模型有明显区别,领域模型关心的是业务概念,其要义是显性化地表达业务语义;数据模型关心的是数据存储,其核心是数据访问的性能、数据的扩展性等非功能属性。


结语:模型思维的三重境界

  1. 见山是山:严格遵循现有模型(如教科书中的设计模式);
  2. 见山不是山:根据上下文裁剪模型(如微服务中的CQRS变形);
  3. 见山仍是山:超越模型形式,直击问题本质(如用简单事件溯源替代复杂ESB)。

掌握模型思维,实则是培养“以简驭繁”的能力——正如埃隆·马斯克推崇的第一性原理,穿透表象,直抵核心矛盾。在软件工程的复杂迷局中,优秀的模型如同指南针,在混沌中开辟清晰路径。

在这里插入图片描述


http://www.niftyadmin.cn/n/5864794.html

相关文章

苍穹外卖中的模块总结

本文总结苍穹外卖项目中可复用的通用设计 sky-common constant存放常量类&#xff0c;包括消息常量&#xff0c;状态常量 context是上下文对象&#xff0c;封装了threadlocal package com.sky.context;public class BaseContext {public static ThreadLocal<Long> thre…

毕业项目推荐:基于yolov8/yolov5/yolo11的番茄成熟度检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…

k8s部署针对外部服务器的prometheus服务

在Kubernetes(K8s)集群中部署Prometheus以监控外部服务器&#xff0c;涉及到几个关键步骤&#xff1a;配置Prometheus以抓取远程目标、设置服务发现机制、以及确保网络可达性。下面是一个详细指南&#xff0c;指导您如何在Kubernetes中部署并配置Prometheus&#xff0c;以便有效…

Python的子线程与主线程之间的通信并通知主线程更新UI

新建PLC类 PLC.py import json import time from threading import Threadfrom HslCommunication import SiemensS7Net, SiemensPLCS from PySide6.QtCore import QThread, Signal, QObjectfrom tdm.MsgType import MSG_TYPE_LOG, MSG_TYPE_MSGBOX# 自定义信号类&#xff0c;用…

ElasticSearch查询指南:从青铜到王者的骚操作

ElasticSearch查询指南&#xff1a;从青铜到王者的骚操作 本文来源于笔者的CSDN原创&#xff0c;由于掘金>已经去掉了转载功能&#xff0c;所以只好重新上传&#xff0c;以下图片依然保持最初发布的水印&#xff08;如CSDN水印&#xff09;。&#xff08;以后属于本人原创均…

vue-fastapi-admin 部署心得

vue-fastapi-admin 部署心得 这两天需要搭建一个后台管理系统&#xff0c;找来找去 vue-fastapi-admin 这个开源后台管理框架刚好和我的技术栈所契合。于是就浅浅的研究了一下。 主要是记录如何基于原项目提供的Dockerfile进行调整&#xff0c;那项目文件放在容器外部&#xf…

基于Spring Boot的公司资产网站设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

从零实现机器人自主避障

1. 编译工具安装 sudo apt update sudo apt install python3-catkin-pkg python3-rosdep python3-rosinstall-generator python3-wstool python3-rosinstall build-essential sudo rosdep init rosdep update2. 构建节点 mkdir -p ~/ros2_ws/src cd ~/ros2_ws ros2 pkg creat…