架构之分层
引言
“架构分层的本质是职责分离,每一层都应该有明确的定位和边界,就像建筑物的地基、框架和装饰,各司其职,层层递进”
在微服务架构时代,我们成功地将传统的"大泥球"单体应用拆分为多个独立的微服务,实现了分而治之的目标。然而,在微服务内部,一个新的问题正在悄然滋生:“微服务小泥球”。这种现象表现为微服务内部缺乏合理的模块划分,代码耦合严重,调用关系混乱,违背了微服务架构的初衷。
腾讯视频DDD重构案例揭示了这一问题的严重性:在老的微服务架构中,存在分层不明确、下层服务过于理解业务逻辑、存在下层调用上层的问题,导致代码耦合严重,调用关系混乱。本文将深入探讨如何通过明确的架构分层和严格的调用规范,规避"微服务小泥球"的陷阱。
架构分层职责的核心理念
什么是"微服务小泥球"?
"微服务小泥球"是指在微服务内部出现的代码腐化现象,它继承了传统"大泥球"单体的所有问题,只是规模缩小到了单个微服务内部:
传统"大泥球"单体的问题回顾
要理解"微服务小泥球"的危害,我们需要先回顾传统"大泥球"单体的问题:
// 典型的"大泥球"单体代码示例
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private JdbcTemplate jdbcTemplate;
@PostMapping
public ResponseEntity<String> createOrder(@RequestBody Map<String, Object> request) {
// 1. 直接操作数据库,业务逻辑与数据访问耦合
String userSql = "SELECT * FROM users WHERE id = ?";
Map<String, Object> user = jdbcTemplate.queryForMap(userSql, request.get("userId"));
// 2. 业务逻辑散落在控制器中
if (user == null) {
return ResponseEntity.badRequest().body("用户不存在");
}
// 3. 直接操作其他模块的数据表
for (Map<String, Object> item : (List<Map<String, Object>>) request.get("items")) {
String inventorySql = "UPDATE inventory SET quantity = quantity - ? WHERE product_id = ?";
jdbcTemplate.update(inventorySql, item.get("quantity"), item.get("productId"));
}
// 4. 复杂的SQL查询,业务逻辑与数据模型耦合
String orderSql = "INSERT INTO orders (user_id, total_amount, status, create_time) VALUES (?, ?, ?, ?)";
jdbcTemplate.update(orderSql,
request.get("userId"),
calculateTotal(request.get("items")),
"PENDING_PAYMENT",
new Date());
// 5. 直接调用外部服务,缺乏抽象层
String emailResult = sendEmail(user.get("email").toString(), "订单创建成功");
return ResponseEntity.ok("订单创建成功");
}
private BigDecimal calculateTotal(Object items) {
// 复杂的计算逻辑直接写在控制器中
return BigDecimal.ZERO;
}
private String sendEmail(String email, String content) {
// 直接调用邮件服务,缺乏统一抽象
return "success";
}
}
微服务架构的演进与新的挑战
微服务架构成功解决了"大泥球"的问题,但带来了新的挑战:
架构分层职责的设计原则
1. 明确的分层架构
合理的微服务内部分层应该遵循以下原则:
2. 严格的调用规范
各层之间的调用必须遵循严格的规范:
// 正确的分层调用示例
// 1. 接口层 - 只负责接收请求和返回响应
@RestController
@RequestMapping("/api/v1/orders")
@Tag(name = "订单管理", description = "订单相关接口")
public class OrderController {
private final OrderApplicationService orderApplicationService;
@PostMapping
@Operation(summary = "创建订单")
public ApiResponse<OrderDTO> createOrder(@Valid @RequestBody CreateOrderRequest request) {
// 只负责参数校验和调用应用层
OrderDTO order = orderApplicationService.createOrder(request);
return ApiResponse.success(order);
}
}
// 2. 应用层 - 负责服务编排和事务管理
@Service
@Transactional
public class OrderApplicationService {
private final OrderDomainService orderDomainService;
private final UserServiceClient userServiceClient;
private final InventoryServiceClient inventoryServiceClient;
public OrderDTO createOrder(CreateOrderRequest request) {
// 调用用户服务验证用户
UserDTO user = userServiceClient.getUser(request.getUserId());
if (user == null) {
throw new BusinessException("用户不存在");
}
// 调用库存服务检查库存
inventoryServiceClient.checkInventory(request.getItems());
// 调用领域层处理业务逻辑
Order order = orderDomainService.createOrder(
request.getUserId(),
request.getItems(),
request.getShippingAddress(),
request.getPaymentMethod()
);
return OrderConverter.toDTO(order);
}
}
// 3. 领域层 - 负责核心业务逻辑
@DomainService
public class OrderDomainService {
private final OrderRepository orderRepository;
private final DomainEventPublisher eventPublisher;
public Order createOrder(String userId, List<OrderItem> items,
Address shippingAddress, PaymentMethod paymentMethod) {
// 创建订单实体
Order order = Order.builder()
.orderId(generateOrderId())
.userId(userId)
.items(items)
.shippingAddress(shippingAddress)
.paymentMethod(paymentMethod)
.status(OrderStatus.DRAFT)
.build();
// 执行业务逻辑
order.calculateTotalAmount();
order.validateOrder();
order.confirm();
// 保存订单
order = orderRepository.save(order);
// 发布领域事件
eventPublisher.publish(new OrderCreatedEvent(order));
return order;
}
}
// 4. 基础层 - 负责数据持久化
@Repository
public class OrderRepositoryImpl implements OrderRepository {
private final OrderJpaRepository jpaRepository;
private final OrderMapper mapper;
@Override
public Order save(Order order) {
OrderPO orderPO = mapper.toPO(order);
orderPO = jpaRepository.save(orderPO);
return mapper.toDomain(orderPO);
}
@Override
public Optional<Order> findById(OrderId orderId) {
return jpaRepository.findById(orderId.getValue())
.map(mapper::toDomain);
}
}
3. 领域模型的设计
领域层是微服务的核心,应该包含纯粹的业务逻辑:
// 订单聚合根 - 包含核心业务逻辑
@Entity
@Table(name = "orders")
public class Order implements AggregateRoot<OrderId> {
@EmbeddedId
private OrderId orderId;
@Embedded
private UserId userId;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private List<OrderItem> items;
@Embedded
private Money totalAmount;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@Embedded
private ShippingAddress shippingAddress;
@Column(name = "create_time")
private LocalDateTime createTime;
// 核心业务逻辑
public void addItem(Product product, int quantity) {
if (status != OrderStatus.DRAFT) {
throw new DomainException("只有草稿状态的订单才能添加商品");
}
OrderItem item = new OrderItem(
this.orderId,
product.getProductId(),
product.getName(),
product.getPrice(),
quantity
);
items.add(item);
recalculateTotalAmount();
// 发布领域事件
registerEvent(new OrderItemAddedEvent(
this.orderId.getValue(),
product.getProductId().getValue(),
quantity
));
}
public void confirm() {
if (items.isEmpty()) {
throw new DomainException("订单不能为空");
}
if (totalAmount.isZeroOrNegative()) {
throw new DomainException("订单金额必须大于0");
}
this.status = OrderStatus.PENDING_PAYMENT;
this.createTime = LocalDateTime.now();
registerEvent(new OrderConfirmedEvent(
this.orderId.getValue(),
this.userId.getValue(),
this.totalAmount.getAmount()
));
}
public void pay(String paymentId, LocalDateTime paymentTime) {
if (status != OrderStatus.PENDING_PAYMENT) {
throw new DomainException("订单状态不允许支付");
}
this.status = OrderStatus.PAID;
registerEvent(new OrderPaidEvent(
this.orderId.getValue(),
paymentId,
this.totalAmount.getAmount()
));
}
private void recalculateTotalAmount() {
this.totalAmount = items.stream()
.map(OrderItem::getSubtotal)
.reduce(Money.ZERO, Money::add);
}
@Override
public OrderId getId() {
return orderId;
}
}
腾讯视频DDD重构案例分析
重构前的问题
在腾讯视频的旧架构中,存在严重的"微服务小泥球"问题:
// 重构前的代码 - 分层混乱
@RestController
@RequestMapping("/api/video")
public class VideoController {
@Autowired
private VideoRepository videoRepository;
@Autowired
private UserRepository userRepository; // 直接操作用户数据
@Autowired
private RecommendationRepository recommendationRepository; // 直接操作推荐数据
@PostMapping("/{videoId}/play")
public ResponseEntity<String> playVideo(@PathVariable String videoId,
@RequestParam String userId) {
// 1. 直接操作多个数据库表
Video video = videoRepository.findById(videoId);
User user = userRepository.findById(userId);
// 2. 业务逻辑直接写在控制器中
if (user.getVipLevel() < video.getRequiredVipLevel()) {
return ResponseEntity.status(403).body("VIP等级不足");
}
// 3. 直接更新推荐数据
recommendationRepository.updateUserPreference(userId, videoId);
// 4. 复杂的SQL查询
String sql = "UPDATE video_stats SET play_count = play_count + 1, " +
"last_play_time = NOW() WHERE video_id = ?";
videoRepository.update(sql, videoId);
// 5. 直接调用外部服务
String result = callExternalService("http://analytics.service/api/record",
Map.of("userId", userId, "videoId", videoId));
return ResponseEntity.ok("播放成功");
}
}
重构后的架构
通过DDD重构,腾讯视频建立了清晰的分层架构:
// 重构后的代码 - 清晰的分层
// 1. 接口层 - 简洁明了
@RestController
@RequestMapping("/api/v1/videos")
public class VideoController {
private final VideoApplicationService videoApplicationService;
@PostMapping("/{videoId}/play")
public ApiResponse<PlayVideoResponse> playVideo(@PathVariable String videoId,
@RequestParam String userId) {
PlayVideoCommand command = PlayVideoCommand.builder()
.videoId(videoId)
.userId(userId)
.build();
PlayVideoResponse response = videoApplicationService.playVideo(command);
return ApiResponse.success(response);
}
}
// 2. 应用层 - 服务编排
@Service
public class VideoApplicationService {
private final VideoDomainService videoDomainService;
private final UserServiceClient userServiceClient;
private final RecommendationServiceClient recommendationServiceClient;
public PlayVideoResponse playVideo(PlayVideoCommand command) {
// 调用用户服务验证权限
UserDTO user = userServiceClient.getUser(command.getUserId());
// 调用领域层处理核心业务
VideoPlayback playback = videoDomainService.playVideo(
command.getVideoId(),
user,
command.getDeviceInfo()
);
// 异步更新推荐数据
CompletableFuture.runAsync(() -> {
recommendationServiceClient.recordUserBehavior(
command.getUserId(),
command.getVideoId(),
"PLAY"
);
});
return PlayVideoResponse.builder()
.playbackId(playback.getPlaybackId())
.videoUrl(playback.getVideoUrl())
.qualityOptions(playback.getQualityOptions())
.build();
}
}
// 3. 领域层 - 核心业务逻辑
@DomainService
public class VideoDomainService {
private final VideoRepository videoRepository;
private final VideoPlaybackRepository playbackRepository;
private final DomainEventPublisher eventPublisher;
public VideoPlayback playVideo(String videoId, UserDTO user, DeviceInfo deviceInfo) {
// 获取视频信息
Video video = videoRepository.findById(new VideoId(videoId))
.orElseThrow(() -> new VideoNotFoundException("视频不存在"));
// 权限验证(核心业务逻辑)
if (!video.canBePlayedBy(user)) {
throw new InsufficientPermissionException("用户权限不足");
}
// 创建播放记录
VideoPlayback playback = VideoPlayback.create(
generatePlaybackId(),
video,
user,
deviceInfo
);
// 保存播放记录
playback = playbackRepository.save(playback);
// 更新视频统计信息
video.recordPlayback();
videoRepository.save(video);
// 发布领域事件
eventPublisher.publish(new VideoPlayedEvent(
video.getVideoId().getValue(),
user.getUserId(),
playback.getPlaybackId(),
deviceInfo.getDeviceId()
));
return playback;
}
}
// 4. 领域模型 - 包含业务规则
@Entity
@Table(name = "videos")
public class Video implements AggregateRoot<VideoId> {
@EmbeddedId
private VideoId videoId;
@Column(name = "title")
private String title;
@Embedded
private VipLevel requiredVipLevel;
@Embedded
private VideoStatus status;
@Column(name = "play_count")
private long playCount;
// 业务逻辑封装在领域对象中
public boolean canBePlayedBy(UserDTO user) {
// VIP等级检查
if (user.getVipLevel() < this.requiredVipLevel.getValue()) {
return false;
}
// 视频状态检查
if (this.status != VideoStatus.PUBLISHED) {
return false;
}
// 地区限制检查
if (!isAvailableInRegion(user.getRegion())) {
return false;
}
return true;
}
public void recordPlayback() {
this.playCount++;
registerEvent(new VideoPlaybackRecordedEvent(
this.videoId.getValue(),
this.playCount
));
}
private boolean isAvailableInRegion(String region) {
// 地区可用性检查逻辑
return true;
}
}
规避"微服务小泥球"的最佳实践
1. 分层架构设计规范
// 分层架构规范示例
// 1. 接口层规范
@RestController
@RequestMapping("/api/v1")
public class ControllerBase {
// 只处理HTTP相关逻辑
// 不进行业务逻辑处理
// 不进行数据访问
}
// 2. 应用层规范
@Service
@Transactional
public class ApplicationServiceBase {
// 负责服务编排
// 负责事务管理
// 负责权限校验
// 不负责具体业务逻辑
}
// 3. 领域层规范
@DomainService
public class DomainServiceBase {
// 负责核心业务逻辑
// 负责业务规则验证
// 负责领域事件发布
// 不负责技术实现细节
}
// 4. 基础层规范
@Repository
public class RepositoryBase {
// 负责数据持久化
// 负责外部服务调用
// 负责技术实现细节
// 不负责业务逻辑
}
2. 依赖管理规范
// 依赖倒置原则的应用
// 1. 定义领域接口
public interface OrderRepository {
Order save(Order order);
Optional<Order> findById(OrderId orderId);
List<Order> findByUserId(UserId userId);
}
// 2. 基础设施实现接口
@Repository
public class OrderRepositoryImpl implements OrderRepository {
private final OrderJpaRepository jpaRepository;
private final OrderMapper mapper;
@Override
public Order save(Order order) {
OrderPO orderPO = mapper.toPO(order);
orderPO = jpaRepository.save(orderPO);
return mapper.toDomain(orderPO);
}
@Override
public Optional<Order> findById(OrderId orderId) {
return jpaRepository.findById(orderId.getValue())
.map(mapper::toDomain);
}
}
// 3. 领域层依赖抽象
@DomainService
public class OrderDomainService {
private final OrderRepository orderRepository; // 依赖抽象
public OrderDomainService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
}
3. 领域事件驱动
// 使用领域事件实现解耦
// 1. 定义领域事件
public class OrderCreatedEvent implements DomainEvent {
private final String eventId;
private final String orderId;
private final String userId;
private final BigDecimal totalAmount;
private final LocalDateTime occurredOn;
public OrderCreatedEvent(Order order) {
this.eventId = UUID.randomUUID().toString();
this.orderId = order.getOrderId().getValue();
this.userId = order.getUserId().getValue();
this.totalAmount = order.getTotalAmount().getAmount();
this.occurredOn = LocalDateTime.now();
}
}
// 2. 在领域模型中发布事件
@Entity
public class Order implements AggregateRoot<OrderId> {
@Transient
private final List<DomainEvent> domainEvents = new ArrayList<>();
public void confirm() {
// 业务逻辑
this.status = OrderStatus.PENDING_PAYMENT;
// 发布领域事件
registerEvent(new OrderCreatedEvent(this));
}
protected void registerEvent(DomainEvent event) {
this.domainEvents.add(event);
}
@Override
public List<DomainEvent> getDomainEvents() {
return Collections.unmodifiableList(domainEvents);
}
}
// 3. 事件处理器
@Component
public class OrderEventHandler {
@EventListener
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
// 处理订单创建事件
// 发送通知、更新统计、同步数据等
}
}
4. 防腐层设计
// 使用防腐层隔离外部依赖
// 1. 定义防腐层接口
public interface UserServiceClient {
UserDTO getUser(String userId);
boolean validateUserPermission(String userId, String permission);
}
// 2. 实现防腐层
@Component
public class UserServiceClientImpl implements UserServiceClient {
private final RestTemplate restTemplate;
private final String userServiceUrl;
@Override
public UserDTO getUser(String userId) {
try {
ResponseEntity<ApiResponse<UserDTO>> response = restTemplate.exchange(
userServiceUrl + "/api/users/" + userId,
HttpMethod.GET,
null,
new ParameterizedTypeReference<ApiResponse<UserDTO>>() {}
);
if (response.getBody() != null && response.getBody().isSuccess()) {
return response.getBody().getData();
}
throw new ServiceCallException("获取用户信息失败");
} catch (Exception e) {
// 降级处理
log.error("调用用户服务失败: userId={}", userId, e);
return createFallbackUser(userId);
}
}
private UserDTO createFallbackUser(String userId) {
return UserDTO.builder()
.userId(userId)
.userName("默认用户")
.status("UNKNOWN")
.build();
}
}
5. 代码质量保障
// 使用静态代码分析工具
// 1. 定义架构规则
@ArchTest
static final ArchRule controller_should_not_access_repository =
noClasses().that().resideInAPackage("..controller..")
.should().accessClassesThat().resideInAPackage("..repository..");
@ArchTest
static final ArchRule domain_service_should_not_access_infrastructure =
noClasses().that().resideInAPackage("..domain..")
.should().accessClassesThat().resideInAPackage("..infrastructure..");
@ArchTest
static final ArchRule services_should_be_annotated =
classes().that().resideInAPackage("..service..")
.should().beAnnotatedWith(Service.class)
.orShould().beAnnotatedWith(DomainService.class);
// 2. 依赖关系检查
@ArchTest
static final ArchRule layered_architecture_rule =
layeredArchitecture()
.layer("Controller").definedBy("..controller..")
.layer("Application").definedBy("..application..")
.layer("Domain").definedBy("..domain..")
.layer("Infrastructure").definedBy("..infrastructure..")
.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Application").mayOnlyBeAccessedByLayers("Controller")
.whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Domain")
.whereLayer("Infrastructure").mayOnlyBeAccessedByLayers("Application", "Domain");
性能优化与监控
1. 分层性能监控
// 各层性能监控
@Component
public class LayerPerformanceMonitor {
private final MeterRegistry meterRegistry;
public void recordControllerExecution(String method, Runnable task) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
task.run();
} finally {
sample.stop(Timer.builder("layer.controller.execution")
.tag("method", method)
.register(meterRegistry));
}
}
public void recordServiceExecution(String service, String method, Runnable task) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
task.run();
} finally {
sample.stop(Timer.builder("layer.service.execution")
.tag("service", service)
.tag("method", method)
.register(meterRegistry));
}
}
public void recordRepositoryExecution(String repository, String method, Runnable task) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
task.run();
} finally {
sample.stop(Timer.builder("layer.repository.execution")
.tag("repository", repository)
.tag("method", method)
.register(meterRegistry));
}
}
}
2. 分层缓存策略
// 分层缓存配置
@Configuration
@EnableCaching
public class LayeredCacheConfig {
// 控制器层缓存 - 短期缓存
@Bean("controllerCacheManager")
public CacheManager controllerCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS)
.maximumSize(1000));
return cacheManager;
}
// 应用层缓存 - 中期缓存
@Bean("applicationCacheManager")
public CacheManager applicationCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(5000));
return cacheManager;
}
// 领域层缓存 - 长期缓存
@Bean("domainCacheManager")
public CacheManager domainCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.MINUTES)
.maximumSize(10000));
return cacheManager;
}
}
总结
架构分层职责原则是规避"微服务小泥球"的关键法则。通过明确的分层架构和严格的调用规范,我们能够构建出职责清晰、边界明确、易于维护的微服务系统。
核心原则
- 分层明确:接口层、应用层、领域层、基础层各司其职
- 单向依赖:只能从外层调用内层,禁止反向依赖
- 职责单一:每一层只负责特定的职责范围
- 接口抽象:通过接口实现层与层之间的解耦
- 事件驱动:使用领域事件实现跨层通信
关键技术
- DDD领域建模:建立清晰的领域模型和业务边界
- 依赖倒置:依赖抽象而非具体实现
- 防腐层:隔离外部依赖,保护内部架构
- 事件溯源:通过事件实现数据一致性和审计
- 架构测试:通过自动化测试保障架构规范
成功要素
- 深入理解业务:只有深入理解业务才能建立合理的分层
- 严格规范执行:建立代码审查机制,确保分层规范得到执行
- 持续重构优化:根据业务发展持续优化分层架构
- 团队培训:确保团队成员理解并遵循分层原则
- 工具支持:使用架构测试工具自动检查分层规范
通过遵循架构分层职责原则,我们能够有效规避"微服务小泥球"的陷阱,构建出真正具备高内聚、低耦合特性的优秀微服务架构。
架构分层职责原则让我们重新思考微服务内部的组织结构,它提醒我们:微服务不是万能的,只有在内部建立起清晰的架构和规范的调用关系,才能真正发挥微服务架构的优势,避免从一个"大泥球"变成多个"小泥球"。通过遵循这一原则,我们能够构建出既满足当前需求,又具备未来扩展性的优秀架构。
1034

被折叠的 条评论
为什么被折叠?



