1. 基础设施层在DDD架构中的定位
基础设施层(Infrastructure Layer)是DDD四层架构中最底层的一环,它像城市的地下管网系统一样默默支撑着整个业务运转。这个类比很形象——我们平时不会注意到地下管网的走向,但一旦停水停电,整个城市就会陷入瘫痪。在技术实现上,基础设施层主要承担三类核心职责:
-
技术细节的封装者:将数据库访问、消息队列、缓存等与技术强相关的实现细节隔离在此层。比如当我们需要将MySQL切换为MongoDB时,只需修改基础设施层的持久化实现,上层业务代码完全不受影响。
-
跨领域能力的提供者:包含邮件发送、文件存储、支付网关等通用能力。某电商项目中,我们曾将支付宝、微信支付的不同SDK调用统一封装为
PaymentGateway接口,业务层只需调用paymentService.charge(order),完全不用关心具体支付渠道。 -
防腐层的物理载体:通过适配器模式(Adapter)实现外部系统与领域模型的转换。对接第三方物流系统时,我们设计了
LogisticsAdapter将对方的货运单格式转化为我们内部的ShippingOrder值对象,有效避免了领域模型被污染。
重要提示:基础设施层应当依赖领域层(向上依赖),但绝对不能让领域层感知基础设施层的存在。这是保持架构清洁的关键红线。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础设施层的典型组件与实现模式
2.1 持久化实现的艺术
JPA和MyBatis是两种主流的持久化方案选择,它们的取舍值得深入探讨:
java复制// JPA领域实体示例
@Entity
class Order {
@Id
private OrderId id;
@Embedded
private Address shippingAddress;
@OneToMany(cascade = ALL)
private List<OrderItem> items;
}
// MyBatis数据映射示例
public interface OrderMapper {
@Select("SELECT * FROM orders WHERE id = #{id}")
@Results({
@Result(property = "id", column = "order_id"),
@Result(property = "items", javaType = List.class,
column = "order_id", many = @Many(select = "findItems"))
})
OrderAggregate findById(OrderId id);
}
JPA的优势在于与领域模型的贴合度,但容易导致贫血模型;MyBatis更灵活,但需要手动处理对象映射。我们的经验是:复杂聚合根用MyBatis实现Repository接口,简单值对象可用JPA注解。
2.2 消息通信的三种范式
- 同步调用:通过Feign或Dubbo暴露领域服务
java复制public class OrderServiceImpl implements OrderService {
@Override
public OrderResult placeOrder(OrderCommand command) {
// 领域逻辑处理
}
}
- 事件驱动:使用Spring Cloud Stream发布领域事件
java复制public class OrderEventPublisher {
private final Source source;
public void publish(OrderPaidEvent event) {
source.output().send(MessageBuilder.withPayload(event).build());
}
}
- CQRS查询:单独的数据投影层
sql复制CREATE MATERIALIZED VIEW order_summary AS
SELECT user_id, COUNT(*) as order_count
FROM orders
GROUP BY user_id;
2.3 缓存策略的层级设计
缓存的设计需要与聚合根的访问模式相匹配:
| 缓存层级 | 适用场景 | 实现示例 | 失效策略 |
|---|---|---|---|
| L1 | 高频访问的单个聚合 | Redis String存储序列化对象 | 主动失效+TTL |
| L2 | 复杂查询结果 | Redis Hash存储字段级数据 | 版本号变更触发批量清除 |
| L3 | 全量数据缓存 | Elasticsearch索引 | 定时全量重建 |
在商品详情页场景中,我们采用三级缓存:L1缓存单品信息(TTL 5分钟),L2缓存规格参数(版本控制),L3用ES处理搜索请求。
3. 基础设施层的代码组织规范
3.1 模块化拆分原则
推荐按技术能力而非业务维度划分模块:
code复制infrastructure
├── persistence
│ ├── jpa
│ ├── mybatis
│ └── redis
├── messaging
│ ├── kafka
│ └── rabbit
└── external
├── payment
└── logistics
每个子模块应当:
- 实现领域层定义的接口(如
OrderRepository) - 提供Spring配置类(如
JpaConfig) - 包含技术异常转换器(将SQLException转为领域层定义的
PersistenceException)
3.2 配置管理的实践
使用Spring Profile管理环境差异:
yaml复制# application-dev.yml
infra:
redis:
cluster-nodes: "redis-dev1:6379,redis-dev2:6379"
datasource:
url: jdbc:mysql://dev-db:3306/order
# application-prod.yml
infra:
redis:
cluster-nodes: ${REDIS_NODES}
datasource:
url: jdbc:mysql://${DB_HOST}/order?useSSL=true
关键经验:
- 密码等敏感信息必须使用Vault或KMS加密
- 连接池参数根据压测结果调整(如HikariCP的maxPoolSize)
- 超时设置要大于下游系统的99线响应时间
4. 基础设施层的性能优化实战
4.1 数据库访问优化
批量处理案例:
java复制public class JpaOrderRepository implements OrderRepository {
@Transactional
public void batchInsert(List<Order> orders) {
EntityManager em = entityManagerFactory.createEntityManager();
for (int i = 0; i < orders.size(); i++) {
em.persist(orders.get(i));
if (i % 50 == 0) {
em.flush();
em.clear();
}
}
}
}
查询优化技巧:
- 为高频查询添加覆盖索引
- 使用
@EntityGraph解决N+1问题 - 对大文本字段单独建表(如商品描述)
4.2 缓存穿透防护方案
采用布隆过滤器+空值缓存的组合拳:
java复制public class RedisOrderCache implements OrderCache {
private final BloomFilter<String> bloomFilter;
public Order getById(String id) {
if (!bloomFilter.mightContain(id)) {
return null;
}
String key = "order:" + id;
Order order = redisTemplate.opsForValue().get(key);
if (order == NULL_OBJECT) {
return null;
}
return order;
}
}
4.3 消息队列的可靠性保障
RabbitMQ的最佳实践配置:
java复制@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMandatory(true);
template.setConfirmCallback((correlationData, ack, cause) -> {
if (!ack) {
log.error("Message lost: {}", cause);
}
});
template.setReturnsCallback(returned -> {
log.warn("Message returned: {}", returned.getReplyText());
});
return template;
}
在订单超时取消场景中,我们结合死信队列和延迟插件实现了可靠的消息延迟投递。
5. 基础设施层的测试策略
5.1 单元测试重点
- 数据转换逻辑:验证DTO与领域对象的互转
java复制@Test
void shouldConvertToDomainEntity() {
OrderDO orderDO = new OrderDO("order123", "PAID");
Order order = OrderConverter.fromDO(orderDO);
assertThat(order.getStatus()).isEqualTo(OrderStatus.PAID);
}
- 缓存行为验证:模拟缓存命中/穿透场景
java复制@Test
void shouldQueryDbWhenCacheMiss() {
when(redisTemplate.get("order:123")).thenReturn(null);
when(orderRepository.findById("123")).thenReturn(testOrder);
Order result = orderCache.getById("123");
verify(orderRepository).findById("123");
}
5.2 集成测试方案
使用Testcontainers进行真实中间件测试:
java复制@Testcontainers
class OrderRepositoryIT {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>();
@Test
void shouldSaveOrder() {
OrderRepository repo = new JpaOrderRepository(createEntityManager());
Order order = new Order(new OrderId("test123"));
repo.save(order);
Order saved = repo.findById(new OrderId("test123"));
assertThat(saved).isNotNull();
}
}
5.3 混沌工程实践
通过Chaos Mesh模拟基础设施故障:
yaml复制apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: redis-latency
spec:
action: delay
mode: one
selector:
labelSelectors:
app: redis-master
delay:
latency: "500ms"
correlation: "100"
duration: "10m"
我们在预发布环境定期执行此类实验,验证系统的容错能力。
