前语

最近在学习了解领域驱动模型DDD相关的内容,但是由于没有实际的项目支撑,所以大都是停留在一些理论层面。我发现这里面的一些设计思想还是非常有实用价值的,可以直接应用于你现在的项目中,今天我就来谈谈防腐层的妙用。

一个简略的比如

咱们在做项目中是否有过这样的经历,你的项目中需求调用一个外部服务接口,而这个外部服务接口需求在你的项目中的不同当地被屡次运用,比如在公司项目中就出现调用下面获取用户详细信息的外部的接口多达10几次。

SessionUser getUserDetail(String username)SessionUser getUserDetail(String username)

一旦这个外部接口产生改变,那么是不是意味着我就要修正这几十处的当地,简直头大。

那咱们是不是可以对外部接口做一层适配封装,隔离这种或许地、不可控的改变。因而在咱们的manager层中添加了一个UserManager的类,如下所示:

@Component
public class UserManager() {
    @Autowired
    private UserApi remoteUserApi;
    public UserDTO getUserDetail(String username) {
		SessionUser sessionUser = remoteUserApi.getUserDetail(username);
        UserDTO user = convertUser(sessionUser);
        return user;
    }
}@Component
public class UserManager() {
    @Autowired
    private UserApi remoteUserApi;
    public UserDTO getUserDetail(String username) {
		SessionUser sessionUser = remoteUserApi.getUserDetail(username);
        UserDTO user = convertUser(sessionUser);
        return user;
    }
}

咱们让体系中的业务层从本来直接调用remoteUserApi.getUserDetail(String username)改为调用UserManager#getUserDetail(),这样哪怕有一天外部接口的回来内容、方法名产生改变,咱们也只需求修正一下这一个当地,而无需修正上层调用的十几处当地。

别的,咱们还可以再这一层参加更多的功用,比如参数校验,日志打印等等,如下代码所示:

@Component
public class UserManager() {
    @Autowired
    private UserApi remoteUserApi;
    public List<UserDTO> getUserDetail(String username) {
        // 参数校验
        if(StrUtils.isBlank(username)) {
            throw new UserException("用户名不能为空");
        }
        long t1 = System.currentTimeMillis();
		SessionUser sessionUser = remoteUserApi.getUserDetail(username);
        long t2 = System.currentTimeMillis();
        // 打印日志,便利甩锅    
        if(t2 - t1 > 3000L) {
            log.warn("调用外部接口耗时过长,cost:[{}]ms", t2 - t1);
        }
        UserDTO user = convertUser(sessionUser);
        return user;
    }
}@Component
public class UserManager() {
    @Autowired
    private UserApi remoteUserApi;
    public List<UserDTO> getUserDetail(String username) {
        // 参数校验
        if(StrUtils.isBlank(username)) {
            throw new UserException("用户名不能为空");
        }
        long t1 = System.currentTimeMillis();
		SessionUser sessionUser = remoteUserApi.getUserDetail(username);
        long t2 = System.currentTimeMillis();
        // 打印日志,便利甩锅    
        if(t2 - t1 > 3000L) {
            log.warn("调用外部接口耗时过长,cost:[{}]ms", t2 - t1);
        }
        UserDTO user = convertUser(sessionUser);
        return user;
    }
}

咱们可以加上额定的参数验证,打印调用外部接口的耗时,有理由“甩锅”。

防腐层介绍

经过上面一个简略的比如,你是不是对防腐层有了一个初步的知道。通俗的说,咱们以为外部体系、接口

中间件等都是腐烂的,不可控的,咱们需求添加一层去做隔离和防腐,被叫做防腐层。

大多数应用程序依赖于其他体系的某些数据或功用。 例如,旧版应用程序迁移到新式体系时,或许仍需求现有的旧的资源。 新功用必须可以调用旧体系。 逐步迁移尤其如此,随着时间推移,较大型应用程序的不同功用迁移到新式体系中。

这些旧体系通常会出现质量问题,如杂乱的数据架构或过期的 API。 旧体系运用的功用和技能或许与新式体系中的功用和技能有很大差异。 若要与旧体系进行互操作,新应用程序或许需求支撑过期的基础结构、协议、数据模型、API、或其他不会引入新式应用程序的功用。不仅仅是旧体系,不受开发团队控制的任何外部体系(第三方体系)都或许出现类似的问题,因而引入防腐层去做隔离解决。

小设计,大作用——谈谈防腐层的妙用

如上图所示,子体系 A 经过防腐层调用子体系 B。子体系 A 与防腐层之间的通信始终运用子体系 A 的数据模型和体系结构。防腐层向子体系 B 发出的调用符合该B子体系的数据模型或方法。 防腐层包含在两个体系之间转换所必需的所有逻辑。 该层可作为应用程序内的组件或作为独立服务完成。

总结

说了那么多,这是不是和设计形式中的适配器形式很像,实际上防腐层也叫适配层。当然写防腐层也是有价值的。最大的价值就是有「额定的开发成本」。所以如果你的上下游比较少,且比较稳定,其实是可以不必防腐层的。但是在大型团队,付出这些额定的开发成本是有价值的,由于大型团队的上下游关系非常杂乱,他们或许不是在一个团队,也有或许常常进行迭代升级,经过我自己的经历来看,接口改变是常常会产生的。

欢迎重视个人公众号【JAVA旭阳】交流学习!