摘要:Java 架构师的核心竞争力,不是会写多少框架,而是能在业务、技术、成本、团队之间做出可落地的架构决策。本文从架构师能力模型出发,系统讲解架构设计原则、DDD 落地、微服务拆分、分布式事务、缓存、消息队列、可观测性,并给出一套可运行的电商订单微服务核心代码。涵盖 Spring Boot 3、Spring Cloud Alibaba、Nacos、Seata、Redis、Redisson、Kafka、Micrometer、Prometheus。
很多程序员以为“架构师 = 高级程序员”,这是最大的误解。
架构师的能力模型包含四层:
层级 | 能力 | 说明 |
|---|---|---|
技术深度 | JVM、并发、框架源码 | 能定位底层问题 |
技术广度 | 中间件、云原生、数据库 | 能做技术选型 |
业务理解 | 领域建模、流程抽象 | 能把业务翻译成架构 |
组织协作 | 沟通、决策、推动 | 能让架构落地 |
一句话:架构师是技术决策者,不是代码产量最高的人。
架构师的核心产出:
架构设计不是堆技术,而是做权衡。核心原则:
阶段一:单体应用
└── 所有功能一个 WAR/JAR
阶段二:模块化单体
└── 按业务分包,边界清晰
阶段三:垂直拆分
└── 按业务拆成多个应用,共享数据库
阶段四:微服务
└── 独立部署、独立数据库、服务治理
阶段五:服务网格 / 云原生
└── Sidecar、Istio、Serverless演进触发条件:
不要为了微服务而微服务。
一个简化电商系统:
用户下单 -> 订单服务 -> 扣减库存 -> 扣减余额 -> 发送通知mall-microservices/
├── pom.xml
├── mall-common/
│ ├── mall-common-core/
│ └── mall-common-web/
├── mall-gateway/
├── mall-order/
│ ├── mall-order-api/
│ └── mall-order-service/
├── mall-inventory/
├── mall-account/
└── mall-notification/// mall-order-service/src/main/java/com/mall/order/domain/model/Order.java
package com.mall.order.domain.model;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
/**
* 订单聚合根。
* 所有对订单的修改必须通过聚合根进行。
*/
public class Order {
private final String orderId;
private final String userId;
private final List<OrderItem> items;
private OrderStatus status;
private BigDecimal totalAmount;
private final LocalDateTime createdAt;
private Order(String orderId, String userId, List<OrderItem> items) {
if (items == null || items.isEmpty()) {
throw new IllegalArgumentException("订单项不能为空");
}
this.orderId = orderId;
this.userId = userId;
this.items = new ArrayList<>(items);
this.status = OrderStatus.CREATED;
this.totalAmount = calculateTotal();
this.createdAt = LocalDateTime.now();
}
public static Order create(String userId, List<OrderItem> items) {
return new Order(UUID.randomUUID().toString(), userId, items);
}
private BigDecimal calculateTotal() {
return items.stream()
.map(OrderItem::subtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
public void pay() {
if (status != OrderStatus.CREATED) {
throw new IllegalStateException("订单状态不允许支付:" + status);
}
this.status = OrderStatus.PAID;
}
public void cancel() {
if (status == OrderStatus.PAID) {
throw new IllegalStateException("已支付订单不能直接取消");
}
this.status = OrderStatus.CANCELLED;
}
public String getOrderId() { return orderId; }
public String getUserId() { return userId; }
public List<OrderItem> getItems() { return Collections.unmodifiableList(items); }
public OrderStatus getStatus() { return status; }
public BigDecimal getTotalAmount() { return totalAmount; }
public LocalDateTime getCreatedAt() { return createdAt; }
}// OrderItem.java
package com.mall.order.domain.model;
import java.math.BigDecimal;
public record OrderItem(
String skuId,
String productName,
BigDecimal price,
int quantity
) {
public OrderItem {
if (quantity <= 0) {
throw new IllegalArgumentException("数量必须大于 0");
}
if (price == null || price.signum() < 0) {
throw new IllegalArgumentException("价格非法");
}
}
public BigDecimal subtotal() {
return price.multiply(BigDecimal.valueOf(quantity));
}
}
// OrderStatus.java
package com.mall.order.domain.model;
public enum OrderStatus {
CREATED,
PAID,
CANCELLED,
COMPLETED
}// OrderRepository.java
package com.mall.order.domain.repository;
import com.mall.order.domain.model.Order;
import java.util.Optional;
public interface OrderRepository {
void save(Order order);
Optional<Order> findById(String orderId);
}// OrderApplicationService.java
package com.mall.order.application;
import com.mall.order.domain.model.Order;
import com.mall.order.domain.model.OrderItem;
import com.mall.order.domain.repository.OrderRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class OrderApplicationService {
private final OrderRepository orderRepository;
private final InventoryClient inventoryClient;
private final AccountClient accountClient;
private final OrderEventPublisher eventPublisher;
public OrderApplicationService(
OrderRepository orderRepository,
InventoryClient inventoryClient,
AccountClient accountClient,
OrderEventPublisher eventPublisher
) {
this.orderRepository = orderRepository;
this.inventoryClient = inventoryClient;
this.accountClient = accountClient;
this.eventPublisher = eventPublisher;
}
@Transactional
public String createOrder(String userId, List<OrderItem> items) {
Order order = Order.create(userId, items);
// 扣减库存(远程调用)
inventoryClient.deduct(order.getItems());
// 扣减余额(远程调用)
accountClient.deduct(userId, order.getTotalAmount());
order.pay();
orderRepository.save(order);
eventPublisher.publishOrderCreated(order);
return order.getOrderId();
}
}// mall-order-service/src/main/java/com/mall/order/OrderApplication.java
package com.mall.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}# application.yml
server:
port: 8081
spring:
application:
name: mall-order
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
datasource:
url: jdbc:mysql://127.0.0.1:3306/mall_order?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
kafka:
bootstrap-servers: 127.0.0.1:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
seata:
enabled: true
application-id: mall-order
tx-service-group: mall_tx_group
service:
vgroup-mapping:
mall_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
management:
endpoints:
web:
exposure:
include: health,info,prometheus,metrics
metrics:
export:
prometheus:
enabled: true// InventoryClient.java
package com.mall.order.infrastructure.client;
import com.mall.order.domain.model.OrderItem;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient(name = "mall-inventory")
public interface InventoryClient {
@PostMapping("/inventory/deduct")
void deduct(@RequestBody List<OrderItem> items);
}// AccountClient.java
package com.mall.order.infrastructure.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.math.BigDecimal;
@FeignClient(name = "mall-account")
public interface AccountClient {
@PostMapping("/account/deduct")
void deduct(@RequestParam("userId") String userId,
@RequestParam("amount") BigDecimal amount);
}Nacos 同时承担注册中心和配置中心。
<!-- pom.xml -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>配置动态刷新:
@RestController
@RefreshScope
public class ConfigController {
@Value("${mall.order.max-items:100}")
private int maxItems;
@GetMapping("/config/max-items")
public int maxItems() {
return maxItems;
}
}// mall-gateway/src/main/java/com/mall/gateway/GatewayApplication.java
package com.mall.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}# application.yml
server:
port: 9000
spring:
application:
name: mall-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: order-route
uri: lb://mall-order
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
- id: inventory-route
uri: lb://mall-inventory
predicates:
- Path=/api/inventory/**
filters:
- StripPrefix=1全局过滤器:JWT 鉴权。
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private final JwtVerifier jwtVerifier;
public AuthGlobalFilter(JwtVerifier jwtVerifier) {
this.jwtVerifier = jwtVerifier;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
if (path.startsWith("/api/auth/")) {
return chain.filter(exchange);
}
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
try {
JwtUser user = jwtVerifier.verify(token.substring(7));
exchange.getAttributes().put("userId", user.userId());
return chain.filter(exchange);
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
@Override
public int getOrder() {
return -100;
}
}@Service
public class OrderApplicationService {
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public String createOrder(String userId, List<OrderItem> items) {
Order order = Order.create(userId, items);
inventoryClient.deduct(order.getItems());
accountClient.deduct(userId, order.getTotalAmount());
order.pay();
orderRepository.save(order);
return order.getOrderId();
}
}@Service
public class InventoryService {
@Transactional
public void deduct(List<OrderItem> items) {
for (OrderItem item : items) {
int affected = inventoryMapper.deductStock(item.skuId(), item.quantity());
if (affected == 0) {
throw new RuntimeException("库存不足:" + item.skuId());
}
}
}
}@Service
public class AccountService {
@Transactional
public void deduct(String userId, BigDecimal amount) {
int affected = accountMapper.deductBalance(userId, amount);
if (affected == 0) {
throw new RuntimeException("余额不足:" + userId);
}
}
}CREATE TABLE undo_log (
branch_id BIGINT NOT NULL,
xid VARCHAR(128) NOT NULL,
context VARCHAR(128) NOT NULL,
rollback_info LONGBLOB NOT NULL,
log_status INT NOT NULL,
log_created DATETIME(6) NOT NULL,
log_modified DATETIME(6) NOT NULL,
UNIQUE KEY ux_undo_log (xid, branch_id)
);Seata 的价值:把分布式事务从“业务补偿”下沉到“中间件自动回滚”。
@Service
public class OrderQueryService {
private final RedissonClient redissonClient;
private final OrderRepository orderRepository;
public OrderQueryService(RedissonClient redissonClient,
OrderRepository orderRepository) {
this.redissonClient = redissonClient;
this.orderRepository = orderRepository;
}
public Order getOrder(String orderId) {
String key = "order:" + orderId;
RBucket<Order> bucket = redissonClient.getBucket(key);
Order cached = bucket.get();
if (cached != null) {
return cached;
}
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException("订单不存在"));
bucket.set(order, Duration.ofMinutes(10));
return order;
}
}@Service
public class InventoryLockService {
private final RedissonClient redissonClient;
public InventoryLockService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public void deductWithLock(String skuId, int quantity, Runnable action) {
RLock lock = redissonClient.getLock("lock:inventory:" + skuId);
boolean locked = false;
try {
locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!locked) {
throw new IllegalStateException("获取库存锁失败");
}
action.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException("锁等待被中断", e);
} finally {
if (locked && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}推荐策略:
@Transactional
public void updateOrder(Order order) {
orderRepository.save(order);
redissonClient.getBucket("order:" + order.getOrderId()).delete();
}@Component
public class OrderEventPublisher {
private final KafkaTemplate<String, Object> kafkaTemplate;
public OrderEventPublisher(KafkaTemplate<String, Object> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void publishOrderCreated(Order order) {
OrderCreatedEvent event = new OrderCreatedEvent(
order.getOrderId(),
order.getUserId(),
order.getTotalAmount(),
order.getCreatedAt()
);
kafkaTemplate.send("order-created", order.getOrderId(), event);
}
}public record OrderCreatedEvent(
String orderId,
String userId,
BigDecimal amount,
LocalDateTime createdAt
) {}@Component
public class NotificationConsumer {
@KafkaListener(topics = "order-created", groupId = "mall-notification")
public void onOrderCreated(OrderCreatedEvent event) {
// 发送短信、邮件、站内信
System.out.printf("通知用户 %s,订单 %s 已创建,金额 %s%n",
event.userId(), event.orderId(), event.amount());
}
}@Component
public class IdempotentConsumer {
private final StringRedisTemplate redisTemplate;
public IdempotentConsumer(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public boolean tryConsume(String eventId) {
Boolean ok = redisTemplate.opsForValue()
.setIfAbsent("event:" + eventId, "1", Duration.ofHours(24));
return Boolean.TRUE.equals(ok);
}
}<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>@Component
public class OrderMetrics {
private final Counter orderCreatedCounter;
private final Timer orderCreateTimer;
public OrderMetrics(MeterRegistry registry) {
this.orderCreatedCounter = Counter.builder("mall.order.created")
.description("创建的订单数")
.register(registry);
this.orderCreateTimer = Timer.builder("mall.order.create.duration")
.description("创建订单耗时")
.register(registry);
}
public void recordCreate(Runnable action) {
orderCreateTimer.record(() -> {
action.run();
orderCreatedCounter.increment();
});
}
}<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>management:
tracing:
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: http://127.0.0.1:9411/api/v2/spans三大可观测支柱:
@RestController
public class OrderController {
@PostMapping("/order/create")
@SentinelResource(value = "createOrder",
blockHandler = "handleBlock",
fallback = "handleFallback")
public String createOrder(@RequestBody CreateOrderRequest request) {
return orderApplicationService.createOrder(
request.userId(), request.items());
}
public String handleBlock(CreateOrderRequest request, BlockException ex) {
return "请求过于频繁,请稍后重试";
}
public String handleFallback(CreateOrderRequest request, Throwable ex) {
return "创建订单失败:" + ex.getMessage();
}
}单体 -> 模块化单体 -> 垂直拆分 -> 微服务 -> 服务网格 -> Serverless每个阶段都要问:
Java 架构师的核心不是写代码,而是做决策:
业务理解 -> 领域建模 -> 技术选型 -> 架构设计 -> 落地治理 -> 持续演进三条主线:
本文代码覆盖了从 DDD 聚合、微服务拆分、Nacos、Gateway、Seata、Redis、Redisson、Kafka 到可观测性的完整链路。你可以在此基础上:
架构师的价值,在于让系统在业务增长中保持可维护、可扩展、可观测、可治理。
<properties>
<java.version>21</java.version>
<spring-boot.version>3.3.0</spring-boot.version>
<spring-cloud.version>2023.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
<seata.version>2.0.0</seata.version>
<redisson.version>3.27.2</redisson.version>
</properties>原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。