大迭代(二):事件聚合异构更新 —— 责任链重构

需求背景

大迭代的第三个方向:重构 ES 异构流程。

随着接入的数据源越来越多,ES 异构代码变成了一个"大杂烩":基础字段的异构逻辑、EAV 属性的异构逻辑、库存系统的异构逻辑、点评系统的异构逻辑……全部混在一起。每次接入新系统,都要在这堆代码里找位置、加逻辑,改完还要担心影响其他部分。

问题现状

当前的异构流程大概是这样的:

任意数据变更
  ↓
进入异构方法
  ↓
if (基础字段变更) { 处理基础字段 }
if (EAV属性变更) { 处理EAV属性 }
if (库存变更)    { 调用库存接口,处理库存字段 }
if (点评变更)    { 调用点评接口,处理点评字段 }
...
  ↓
拼装 ES 文档
  ↓
更新 ES

问题很明显:所有逻辑耦合在一起,新增一个数据源就要改这个方法,改一处可能影响全局,测试成本很高。

核心矛盾

ES 异构的本质是:收集一个实体的所有相关数据,拼装成 ES 文档,写入 ES

这个过程天然是可以拆分的:不同来源的数据,由不同的模块负责收集,最后统一拼装。问题在于当前没有这层抽象,所有逻辑都堆在一起。

方案设计

抽象统一流程

把 ES 异构的整个过程抽象成四个环节:

实体变更事件
  → 聚合变更信息(责任链)
  → 构造 ES 文档
  → 更新 ES(含异常处理)

任何数据变更,无论来自基础字段、EAV 属性还是外部系统,都抽象成一个"变更事件",进入同一套流程处理。

责任链模式

聚合环节采用责任链模式,每个 Handler 负责一类数据的收集:

变更事件
  ↓
责任链
  ├→ BasicFieldHandler    (基础字段)
  ├→ EAVAttributeHandler  (EAV 扩展属性)
  ├→ StockInfoHandler     (库存信息)
  ├→ ReviewInfoHandler    (点评信息)
  └→ [可扩展] 新系统 Handler
  ↓
聚合实体(BookInfoSearchEntity)
  ↓
ES 文档构造器
  ↓
ES 更新器(含重试和幂等)

每个 Handler 只负责自己那部分数据,互不干扰。新增一个数据源,只需要新增一个 Handler,现有代码完全不需要改动——这正是开闭原则的体现。

Handler 接口设计

public interface ESDocumentHandler {
    // 填充 ES 文档的对应部分
    void fillDocument(EntityChangeEvent event, BookESDocument document);
}

以点评信息为例:

@Component
public class ReviewInfoHandler implements ESDocumentHandler {

    @Override
    public void fillDocument(EntityChangeEvent event, BookESDocument document) {
        ReviewInfo review = reviewRpcService.getReviewInfo(bookId);
        document.setStarLevel(review.getStarLevel());
        document.setCommentCount(review.getCommentCount());
        document.setGoodCommentCount(review.getGoodCommentCount());
    }
}

统一的事件处理器

@Service
public class ESDocumentSyncService {

    @Autowired
    private List<ESDocumentHandler> handlers; // Spring 自动注入所有 Handler

    public void syncDocument(EntityChangeEvent event) {
        BookESDocument document = new BookESDocument();
        document.initEmptyDoc(event.getBookId())

        // 责任链:每个 Handler 填充自己负责的部分
        handlers.stream()
            .forEach(h -> h.fillDocument(event, document));

        // 统一更新 ES(含重试和幂等)
        esRepository.upsert(document);
    }
}

成本对比

优化前(接入观看人数系统):修改异构方法,集成新系统数据,变更多,测试麻烦,耗时 2-3 天

优化后:新增 ViewCountHandler,实现方法,单元测试,耗时 1 天

维度优化前优化后
接入新系统周期2-3 天1 天
影响范围异构代码整体独立 Handler
测试成本全面回归单元测试
代码可读性混乱职责清晰

这一步解决了什么

ES 异构流程终于有了清晰的结构。每个数据源对应一个独立的 Handler,职责单一,互不影响。新增数据源只需新增 Handler,不会触碰任何现有代码。

大迭代的前三个方向都完成了。最后一个,也是最核心的问题:标签嵌套规则的配置化。