daicy
发布于 2025-09-28 / 7 阅读
0
0

DDD中Infrastructure(基础设施层)介绍

DDD中Infrastructure(基础设施层)的核心作用与价值

在DDD(领域驱动设计)的分层架构中,Infrastructure(基础设施层) 是整个系统的“技术支撑底座”,处于分层架构的最底层,为领域层(核心业务)、应用层(业务编排)和接口层(对外交互)提供“非业务相关的技术能力”,其核心价值是隔离技术实现与业务逻辑,确保领域层(系统的业务内核)不被技术框架、存储方案或第三方服务的变更所影响。

一、基础设施层的核心定位:“服务者”与“实现者”

DDD的分层架构遵循“业务内聚、技术外移”的原则——领域层聚焦“what(业务要做什么) ”,而基础设施层聚焦“how(技术上如何实现) ”。具体定位可概括为两点:

  1. 技术能力的提供者:为上层(领域层、应用层)提供通用技术服务,如数据持久化、缓存、日志、消息通信、第三方接口集成等;
  2. 抽象接口的实现者:领域层会定义“业务需要的抽象能力”(如仓储接口OrderRepository、事件发布接口DomainEventPublisher),而基础设施层负责将这些抽象接口落地为具体的技术实现(如基于MySQL的OrderRepositoryImpl、基于Kafka的KafkaDomainEventPublisher)。

二、基础设施层的核心作用(四大核心能力)

基础设施层的作用围绕“支撑上层业务”展开,具体可拆解为四大核心能力,每类能力都直接服务于上层的业务需求,且不侵入业务逻辑本身。

1. 实现领域层的抽象依赖:让业务逻辑“不绑定技术”

领域层为了保持“纯业务属性”,会通过接口定义业务所需的技术能力(如“保存订单”“查询商品库存”),但不关心这些能力的技术实现(是用MySQL还是MongoDB,是查本地库还是调用第三方API)。基础设施层的核心职责之一,就是实现这些抽象接口,让领域层的业务逻辑能“无感知”地使用技术能力。

典型场景:实现领域层的仓储接口(Repository)

仓储(Repository)是领域层定义的“领域对象持久化抽象”,用于描述“如何保存/查询领域对象”(如OrderRepository定义save(Order order)getById(String orderId)方法),但不包含任何数据库操作逻辑。基础设施层则通过具体的技术框架(如MyBatis、JPA、Spring Data)实现这些接口,完成数据与数据库的交互。

示例:订单仓储的抽象与实现

  • 领域层(仅定义抽象接口,无技术依赖):

    // 领域层:订单仓储接口(抽象定义“保存订单”的业务需求)
    public interface OrderRepository {
        // 保存订单领域对象(参数是领域层的Order实体,纯业务对象)
        Order save(Order order);
        // 根据订单ID查询订单领域对象
        Order getById(String orderId);
    }
    
  • 基础设施层(实现抽象接口,绑定具体技术):

    // 基础设施层:基于MySQL的订单仓储实现(技术落地)
    @Repository // 技术框架注解(Spring),与业务无关
    public class OrderRepositoryImpl implements OrderRepository {
        // 依赖数据访问框架(MyBatis的Mapper接口,技术组件)
        private final OrderMapper orderMapper;
        // 依赖值对象转换器(将领域对象的属性转换为数据库表字段)
        private final OrderDataConverter converter;
    
        @Override
        public Order save(Order order) {
            // 1. 领域对象(Order)转换为数据库实体(OrderDO)——技术层面的属性映射
            OrderDO orderDO = converter.toDO(order);
            
            // 2. 调用MyBatis Mapper操作数据库(具体技术实现)
            if (StringUtils.isEmpty(orderDO.getId())) {
                orderMapper.insert(orderDO); // 新增订单
            } else {
                orderMapper.updateById(orderDO); // 更新订单
            }
            
            // 3. 数据库实体转换回领域对象,返回给领域层(业务层无需关心数据库字段)
            return converter.toDomain(orderDO);
        }
    
        @Override
        public Order getById(String orderId) {
            OrderDO orderDO = orderMapper.selectById(orderId);
            return converter.toDomain(orderDO);
        }
    }
    

关键价值:若后续业务需要将订单存储从MySQL迁移到MongoDB,只需在基础设施层新增MongoOrderRepositoryImpl实现OrderRepository接口,领域层和应用层的代码无需任何修改——实现了“业务逻辑与存储方案的解耦”。

2. 提供通用技术服务:封装重复的技术逻辑

上层(领域层、应用层)在执行业务时,会需要一些“与业务无关但通用的技术能力”(如日志记录、缓存、分布式锁、时间工具)。基础设施层会将这些通用技术能力封装为“可复用的服务”,避免技术逻辑在各层重复编写,提升系统的可维护性。

常见通用技术服务及实现

通用技术服务作用说明技术实现示例(Java生态)
日志服务记录系统运行日志(业务日志、错误日志),便于问题排查基于Logback/Log4j封装LoggerService,提供info()/error()方法
缓存服务缓存高频访问数据(如商品信息、用户权限),提升系统性能基于Redis封装CacheService,提供set()/get()/delete()方法
分布式锁服务解决分布式环境下的并发问题(如防止超卖)基于Redis/ZooKeeper封装DistributedLockService,提供lock()/unlock()方法
时间服务统一时间获取(避免直接使用new Date(),便于测试时模拟时间)封装TimeService,提供currentTime()方法,底层调用System.currentTimeMillis()
加密解密服务对敏感数据(如用户密码、支付信息)进行加密存储、解密使用基于AES/RSA封装EncryptionService,提供encrypt()/decrypt()方法

示例:通用缓存服务的封装

// 基础设施层:通用缓存服务(封装Redis操作,提供上层调用)
@Service
public class RedisCacheService implements CacheService {
    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public <T> void set(String key, T value, long expireSeconds) {
        redisTemplate.opsForValue().set(key, value, expireSeconds, TimeUnit.SECONDS);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> clazz) {
        Object value = redisTemplate.opsForValue().get(key);
        return value != null ? (T) value : null;
    }

    @Override
    public void delete(String key) {
        redisTemplate.delete(key);
    }
}

上层调用场景:应用层在查询商品信息时,可调用RedisCacheService先查缓存,缓存未命中再查数据库,避免频繁访问数据库——应用层只需关心“是否用缓存”,无需关心“缓存是Redis还是Memcached”。

3. 集成第三方服务:隔离外部系统的交互逻辑

企业级系统通常需要与外部系统/服务交互(如支付服务、物流服务、短信服务、身份认证服务),这些交互涉及“外部接口协议、签名验证、异常处理”等技术细节,若将这些细节放入应用层或领域层,会导致业务逻辑与外部系统强绑定(如支付接口变更会影响订单业务逻辑)。

基础设施层的职责是封装第三方服务的交互细节,对外提供“符合领域语言的抽象接口”,让上层业务以“业务视角”调用外部服务,而非“技术视角”。

典型场景:集成支付第三方服务(如支付宝、微信支付)

  • 领域层/应用层需求:“发起支付”“查询支付结果”(业务视角);
  • 基础设施层任务:封装支付宝的SDK调用、签名生成、回调处理等技术细节,提供PaymentService接口供上层调用。

示例:支付第三方服务的集成

// 基础设施层:封装支付宝支付的实现(技术细节内聚)
@Service
public class AlipayServiceImpl implements PaymentService {
    // 支付宝SDK配置(技术参数,与业务无关)
    private final AlipayConfig alipayConfig;
    // 支付宝SDK客户端(第三方技术组件)
    private final AlipayClient alipayClient;

    @Override
    public PaymentResult createPayment(PaymentCommand command) {
        try {
            // 1. 构造支付宝请求参数(技术层面的参数组装)
            AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
            request.setBizContent(JSON.toJSONString(Map.of(
                "out_trade_no", command.getOrderId(), // 订单ID(业务参数)
                "total_amount", command.getAmount().toString(), // 支付金额(业务参数)
                "subject", command.getOrderTitle(), // 订单标题(业务参数)
                "product_code", "FAST_INSTANT_TRADE_PAY" // 支付宝固定参数(技术参数)
            )));
            // 2. 调用支付宝SDK发起支付(技术交互)
            AlipayTradeCreateResponse response = alipayClient.execute(request);
            // 3. 转换第三方响应为上层可理解的业务结果(隔离外部响应格式)
            if (response.isSuccess()) {
                return PaymentResult.success(response.getTradeNo(), "支付宝支付链接已生成");
            } else {
                return PaymentResult.fail(response.getMsg(), response.getSubMsg());
            }
        } catch (Exception e) {
            // 4. 捕获第三方异常,转换为业务异常(避免外部异常直接抛给上层)
            throw new ThirdPartyServiceException("支付宝支付调用失败", e);
        }
    }

    @Override
    public PaymentStatus queryPaymentStatus(String orderId) {
        // 类似逻辑:调用支付宝查询接口,转换结果为业务状态(如“支付中”“已支付”“已取消”)
        // ...
    }
}

关键价值:若后续业务需要将支付方式从“支付宝”切换为“微信支付”,只需在基础设施层新增WechatPayServiceImpl实现PaymentService接口,应用层调用PaymentService的代码无需修改——实现了“业务逻辑与第三方服务的解耦”。

4. 实现跨层的技术横切关注点:统一处理通用技术逻辑

除了针对性的技术支撑,基础设施层还负责处理“跨所有层的通用技术需求”(即横切关注点),这些需求不归属任何特定业务,却需要在全系统范围内生效,如:

  • 事务管理:为应用层的业务流程提供分布式事务或本地事务支持(如基于Spring的@Transactional);
  • 数据校验:提供通用的参数校验工具(如基于Hibernate Validator封装的校验服务);
  • 日志切面:通过AOP(面向切面编程)统一记录接口请求日志、方法调用耗时(如基于Spring AOP的LogAspect);
  • 异常转换:将技术异常(如数据库SQL异常、Redis连接异常)转换为上层可理解的业务异常(如DataAccessExceptionBusinessException)。

示例:基于AOP的统一日志切面

// 基础设施层:统一日志切面(横切所有层的方法调用)
@Aspect
@Component
public class LogAspect {
    private final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    // 切入点:拦截应用层所有Service方法
    @Pointcut("execution(* com.example.app.service..*(..))")
    public void appServicePointcut() {}

    // 环绕通知:记录方法调用耗时、参数、返回值
    @Around("appServicePointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        // 记录入参日志
        logger.info("方法调用开始:{},入参:{}", methodName, JSON.toJSONString(args));
        
        try {
            // 执行目标方法(业务逻辑)
            Object result = joinPoint.proceed();
            // 记录返回值和耗时
            logger.info("方法调用结束:{},返回值:{},耗时:{}ms", 
                methodName, JSON.toJSONString(result), System.currentTimeMillis() - startTime);
            return result;
        } catch (Exception e) {
            // 记录异常日志
            logger.error("方法调用异常:{},异常信息:{}", methodName, e.getMessage(), e);
            throw e; // 抛出异常,由上层异常处理器处理
        }
    }
}

价值:通过基础设施层的横切逻辑,避免在每个业务方法中重复编写日志代码,实现“通用技术逻辑的统一管理”,同时不侵入业务代码。

三、基础设施层的设计原则:“内聚技术、对外透明”

为确保基础设施层能有效支撑上层业务,且不成为“技术负债”,设计时需遵循三大核心原则:

1. 不包含任何业务逻辑

基础设施层的核心是“技术实现”,绝对不能包含业务规则、业务判断或业务计算(如订单金额计算、库存校验、订单状态流转)。所有业务逻辑必须归属领域层,基础设施层仅负责“执行技术操作”。

  • 反例:在OrderRepositoryImpl中判断“订单金额是否大于0”(这是领域层Order实体应包含的业务规则);
  • 正例:OrderRepositoryImpl仅负责将Order实体的金额字段“原样”存储到数据库,不关心金额的合法性。

2. 依赖倒置:上层定义接口,下层实现

基础设施层必须遵循依赖倒置原则(DDD的核心原则之一):

  • 上层(领域层、应用层)定义“需要的抽象接口”(如OrderRepositoryPaymentService);
  • 基础设施层“依赖并实现”这些抽象接口,而非上层依赖基础设施层的具体实现。
    这样做的目的是:上层业务逻辑不绑定任何技术实现,技术变更(如换数据库、换第三方服务)时,上层代码无需修改。

3. 技术细节内聚,对外暴露“业务友好”的接口

基础设施层的技术细节(如数据库表结构、第三方SDK参数、缓存Key规则)应完全“封装在层内”,对外暴露的接口必须使用“领域语言”(即业务术语),而非技术术语。

  • 反例:对外暴露saveOrderToMysql(OrderDO orderDO)(包含技术术语“Mysql”和数据对象“OrderDO”);
  • 正例:对外暴露save(Order order)(使用业务术语“Order”,不包含技术细节)。

四、基础设施层与其他层的交互关系

基础设施层与上层的交互严格遵循“单向依赖”原则,即:

  • 基础设施层依赖领域层和应用层(实现领域层的抽象接口,为应用层提供技术服务);
  • 领域层、应用层不依赖基础设施层(仅依赖抽象接口,不依赖具体实现);
  • 接口层(对外交互)可直接依赖基础设施层的通用技术服务(如缓存、日志),但不依赖其业务相关的实现(如OrderRepositoryImpl)。

交互关系图(简化)
领域层(抽象接口) ← 实现 ← 基础设施层(具体技术) → 支撑 → 应用层(调用抽象接口) → 支撑 → 接口层(对外交互)

总结

Infrastructure(基础设施层)是DDD落地的“技术基石”,其核心作用可概括为:

  1. 实现领域层的抽象依赖,让业务逻辑与技术实现解耦;
  2. 提供通用技术服务,封装重复的技术逻辑;
  3. 集成第三方服务,隔离外部系统的交互细节;
  4. 处理跨层横切关注点,统一管理通用技术需求。

没有基础设施层的支撑,领域层的“纯业务逻辑”将无法落地(无法持久化数据、无法调用外部服务);而基础设施层的设计质量,直接决定了系统的“可维护性、可扩展性和技术灵活性”——它是DDD中“让业务逻辑保持纯粹”的关键保障。


评论