You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于readOnlyAndLog选项复用服务,实现CRUD执行与仅日志的逻辑分离?

资源同步服务的复用方案与逻辑分离实践

核心思路

要复用同一服务适配「实际执行CRUD」和「只读日志模拟」两种场景,核心是把「业务决策逻辑」和「具体执行逻辑」完全拆分:

  • 业务决策逻辑:负责对比JSON资源与数据库数据,计算出需要创建、保留、删除的资源集合,这部分逻辑完全通用,不需要改动。
  • 执行逻辑:分两种实现——一种对接数据库执行真实CRUD,另一种仅输出日志模拟操作,通过切换执行器来适配不同场景。

具体实现步骤

1. 定义执行器接口

先抽象出执行操作的统一接口,包含业务需要的所有操作类型:

public interface SubjectSyncExecutor {
    // 创建资源
    void create(Subject subject);
    // 删除资源
    void delete(Subject subject);
    // 记录已存在的资源
    void logExisting(Subject subject);
}

2. 实现两种执行器

  • 数据库执行器(真实CRUD场景):
public class DbSubjectSyncExecutor implements SubjectSyncExecutor {
    private final SubjectRepository subjectRepo;

    // 通过构造注入数据库仓库
    public DbSubjectSyncExecutor(SubjectRepository subjectRepo) {
        this.subjectRepo = subjectRepo;
    }

    @Override
    public void create(Subject subject) {
        subjectRepo.save(subject);
        // 可选:同时记录操作日志
        System.out.println("已创建资源:" + subject.getName());
    }

    @Override
    public void delete(Subject subject) {
        subjectRepo.delete(subject);
        System.out.println("已删除资源:" + subject.getName());
    }

    @Override
    public void logExisting(Subject subject) {
        System.out.println(subject.getName() + " 已存在于数据库");
    }
}
  • 日志模拟执行器(只读日志场景):
public class LogOnlySubjectSyncExecutor implements SubjectSyncExecutor {
    @Override
    public void create(Subject subject) {
        System.out.println(subject.getName() + " will be created");
    }

    @Override
    public void delete(Subject subject) {
        System.out.println(subject.getName() + " will be deleted");
    }

    @Override
    public void logExisting(Subject subject) {
        System.out.println(subject.getName() + " is already defined in the DB");
    }
}

3. 编写核心同步服务(复用决策逻辑)

核心服务只负责业务决策,具体执行交给注入的执行器:

public class SubjectSyncService {
    private final SubjectJsonLoader jsonLoader;
    private final SubjectRepository subjectRepo;
    private final SubjectSyncExecutor executor;

    // 构造注入所有依赖,包括执行器
    public SubjectSyncService(SubjectJsonLoader jsonLoader, SubjectRepository subjectRepo, SubjectSyncExecutor executor) {
        this.jsonLoader = jsonLoader;
        this.subjectRepo = subjectRepo;
        this.executor = executor;
    }

    public void sync() {
        // 1. 加载JSON中的资源并转成内部模型
        List<Subject> jsonSubjects = jsonLoader.loadFromRootJson();
        Set<String> jsonSubjectNames = jsonSubjects.stream()
                .map(Subject::getName)
                .collect(Collectors.toSet());

        // 2. 查询数据库中已有的资源
        List<Subject> dbSubjects = subjectRepo.findAll();
        Set<String> dbSubjectNames = dbSubjects.stream()
                .map(Subject::getName)
                .collect(Collectors.toSet());

        // 3. 计算需要创建的资源(JSON有,数据库无)
        List<Subject> toCreate = jsonSubjects.stream()
                .filter(subject -> !dbSubjectNames.contains(subject.getName()))
                .collect(Collectors.toList());

        // 4. 计算需要删除的资源(数据库有,JSON无)
        List<Subject> toDelete = dbSubjects.stream()
                .filter(subject -> !jsonSubjectNames.contains(subject.getName()))
                .collect(Collectors.toList());

        // 5. 计算已存在的资源(两边都有)
        List<Subject> existing = jsonSubjects.stream()
                .filter(subject -> dbSubjectNames.contains(subject.getName()))
                .collect(Collectors.toList());

        // 6. 交给执行器处理(核心:无论哪种场景,决策逻辑完全复用)
        toCreate.forEach(executor::create);
        toDelete.forEach(executor::delete);
        existing.forEach(executor::logExisting);
    }
}

4. 场景切换

通过注入不同的执行器来切换场景:

// 实际同步场景:注入数据库执行器
SubjectSyncService realSyncService = new SubjectSyncService(
        new SubjectJsonLoader(),
        new SubjectDbRepository(),
        new DbSubjectSyncExecutor(new SubjectDbRepository())
);
realSyncService.sync();

// 只读日志场景:注入日志模拟执行器
SubjectSyncService logOnlySyncService = new SubjectSyncService(
        new SubjectJsonLoader(),
        new SubjectDbRepository(),
        new LogOnlySubjectSyncExecutor()
);
logOnlySyncService.sync();

此类服务逻辑分离的最佳实践

  1. 接口抽象与依赖注入:通过抽象执行器接口,让核心服务不依赖具体实现,而是通过构造注入执行器,实现解耦,方便灵活切换场景。
  2. 单一职责原则:每个模块只负责一件事:
    • SubjectJsonLoader:仅负责加载JSON并转成内部模型;
    • SubjectSyncService:仅负责业务决策(计算操作集合);
    • 执行器:仅负责具体的操作(CRUD或日志)。
  3. 配置驱动切换:可以通过配置文件(如application.yml中的sync.mode=read-only)配合框架自动配置,实现无需修改代码即可切换场景。
  4. 可测试性优化:因为执行器是接口,单元测试时可以用Mock执行器,无需操作真实数据库,就能单独测试核心决策逻辑的正确性。
  5. 避免逻辑冗余:所有业务决策逻辑只写一次,两种场景共享,避免重复代码导致的维护成本。

内容的提问来源于stack exchange,提问作者webdudewithnoname

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 18:31:02