从购物车到订单系统:一个真实业务场景,手把手教你用PlantUML画类图并生成Java代码骨架
电商系统设计实战:用PlantUML构建购物车与订单的Java代码骨架
在电商系统的开发中,设计阶段往往决定了整个项目的可维护性和扩展性。我曾参与过一个中型电商平台的重构项目,团队最初直接跳入编码,结果在三个月后陷入了难以维护的泥潭——类之间关系混乱、职责不清,每次添加新功能都像在走钢丝。后来我们引入PlantUML进行设计先行,效率提升了至少40%。本文将分享如何用这个轻量级工具,从购物车到订单系统构建清晰的类关系,并直接生成可运行的Java代码骨架。
1. 环境准备与工具链配置
工欲善其事,必先利其器。PlantUML虽然可以通过简单的文本描述生成UML图,但高效的开发环境能大幅提升工作效率。
1.1 安装PlantUML插件
主流IDE都支持PlantUML插件,这里以IntelliJ IDEA为例:
- 打开IDEA的插件市场(Preferences > Plugins)
- 搜索"PlantUML"并安装
- 重启IDE后,新建
.puml文件即可开始绘图
对于团队协作,建议统一配置PlantUML渲染服务器。可以在本地搭建或使用公司内部服务器:
# 使用Docker快速启动PlantUML服务器
docker run -d -p 8080:8080 plantuml/plantuml-server:jetty
1.2 Lombok的引入
为了保持代码简洁,我们使用Lombok减少样板代码。在Maven项目中添加依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
提示:确保团队成员IDE都安装了Lombok插件,否则代码会显示编译错误
2. 电商核心领域建模
电商系统虽然复杂,但其核心领域模型相对稳定。我们先从最基础的购物车、商品、用户开始建模。
2.1 商品与库存模型
商品是电商系统的核心实体,其类图定义如下:
@startuml
class Product {
+ Long id
+ String name
+ BigDecimal price
+ String description
+ ProductStatus status
}
class Inventory {
+ Long id
+ Product product
+ Integer stock
+ Integer locked
}
enum ProductStatus {
AVAILABLE
OUT_OF_STOCK
DISCONTINUED
}
Product "1" -- "1" Inventory
@enduml
对应的Java骨架代码:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
private Long id;
private String name;
private BigDecimal price;
private String description;
private ProductStatus status;
}
public enum ProductStatus {
AVAILABLE, OUT_OF_STOCK, DISCONTINUED
}
@Data
public class Inventory {
private Long id;
private Product product;
private Integer stock;
private Integer locked;
}
2.2 用户与购物车关系
用户与购物车的关系是典型的组合关系——购物车不能脱离用户独立存在:
@startuml
class User {
+ Long userId
+ String username
+ String email
+ List<Cart> carts
}
class Cart {
+ Long cartId
+ List<CartItem> items
+ BigDecimal totalPrice()
}
class CartItem {
+ Product product
+ Integer quantity
}
User "1" *-- "1..*" Cart
Cart "1" *-- "1..*" CartItem
CartItem --> Product
@enduml
Java实现时,我们使用Lombok的 @Builder 简化对象创建:
@Data
@Builder
public class CartItem {
private Product product;
private Integer quantity;
}
@Data
public class Cart {
private Long cartId;
private List<CartItem> items = new ArrayList<>();
public BigDecimal totalPrice() {
return items.stream()
.map(item -> item.getProduct().getPrice()
.multiply(BigDecimal.valueOf(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
@Data
public class User {
private Long userId;
private String username;
private String email;
private List<Cart> carts = new ArrayList<>();
}
3. 订单系统的复杂关系建模
订单系统涉及更多复杂关系,包括聚合、组合和依赖关系。
3.1 订单核心模型
@startuml
class Order {
+ Long orderId
+ OrderStatus status
+ List<OrderItem> items
+ BigDecimal totalAmount
+ User user
+ Payment payment
+ Shipping shipping
}
class OrderItem {
+ Product product
+ Integer quantity
+ BigDecimal price
}
enum OrderStatus {
CREATED
PAID
SHIPPED
COMPLETED
CANCELLED
}
Order "1" *-- "1..*" OrderItem
Order --> User
Order --> Payment
Order --> Shipping
OrderItem --> Product
@enduml
对应的Java实现需要注意深拷贝问题:
@Data
public class OrderItem {
private Product product;
private Integer quantity;
private BigDecimal price;
public OrderItem(CartItem cartItem) {
this.product = new Product(cartItem.getProduct());
this.quantity = cartItem.getQuantity();
this.price = cartItem.getProduct().getPrice();
}
}
@Data
public class Order {
private Long orderId;
private OrderStatus status;
private List<OrderItem> items = new ArrayList<>();
private BigDecimal totalAmount;
private User user;
private Payment payment;
private Shipping shipping;
public static Order fromCart(Cart cart, User user) {
Order order = new Order();
order.setUser(user);
order.setStatus(OrderStatus.CREATED);
order.setItems(cart.getItems().stream()
.map(OrderItem::new)
.collect(Collectors.toList()));
order.setTotalAmount(cart.totalPrice());
return order;
}
}
3.2 支付与物流的关联
支付和物流通常作为独立服务存在,与订单是聚合关系:
@startuml
class Payment {
+ Long paymentId
+ BigDecimal amount
+ PaymentMethod method
+ PaymentStatus status
}
class Shipping {
+ Long shippingId
+ String address
+ ShippingMethod method
+ ShippingStatus status
}
enum PaymentMethod {
CREDIT_CARD
PAYPAL
BANK_TRANSFER
}
enum ShippingMethod {
STANDARD
EXPRESS
SAME_DAY
}
Order o-- Payment
Order o-- Shipping
@enduml
实现时需要注意事务边界:
public interface PaymentService {
Payment processPayment(Order order, PaymentMethod method);
}
@Service
@RequiredArgsConstructor
public class DefaultPaymentService implements PaymentService {
private final PaymentRepository paymentRepository;
@Transactional
public Payment processPayment(Order order, PaymentMethod method) {
Payment payment = Payment.builder()
.amount(order.getTotalAmount())
.method(method)
.status(PaymentStatus.PENDING)
.build();
return paymentRepository.save(payment);
}
}
4. 高级建模技巧与团队协作
4.1 处理多对多关系
商品分类是典型的多对多关系,我们需要引入关联类:
@startuml
class Product {
+ Long id
+ String name
}
class Category {
+ Long id
+ String name
}
class ProductCategory {
+ Product product
+ Category category
+ Integer displayOrder
}
Product "1" -- "*" ProductCategory
Category "1" -- "*" ProductCategory
@enduml
JPA实现示例:
@Entity
@Data
public class ProductCategory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Product product;
@ManyToOne
private Category category;
private Integer displayOrder;
}
4.2 版本控制与文档化
PlantUML文件应该与代码一起纳入版本控制。建议的目录结构:
src/
main/
java/
resources/
diagrams/
product.puml
order.puml
payment.puml
在团队协作中,可以配置CI流程自动生成UML图并发布到内部文档站点。以下是一个GitLab CI示例:
generate_diagrams:
image: plantuml/plantuml
script:
- plantuml -tsvg src/main/resources/diagrams/*.puml
artifacts:
paths:
- src/main/resources/diagrams/*.svg
注意:确保团队成员遵循统一的命名规范,如使用
camelCase命名关系角色
5. 从设计到实现的完整流程
让我们通过一个完整场景串联所有概念:用户将商品加入购物车并创建订单。
5.1 购物车操作序列
@startuml
actor User
participant "CartController" as CC
participant "CartService" as CS
participant "ProductService" as PS
User -> CC : 添加商品(id=123, quantity=2)
CC -> CS : addItem(userId, productId, quantity)
CS -> PS : getProduct(productId)
PS --> CS : Product
CS --> CC : Cart
CC --> User : 更新后的购物车
@enduml
对应的服务层实现:
@Service
@RequiredArgsConstructor
public class CartServiceImpl implements CartService {
private final CartRepository cartRepository;
private final ProductService productService;
@Transactional
public Cart addItem(Long userId, Long productId, int quantity) {
Product product = productService.getProduct(productId);
Cart cart = cartRepository.findByUserId(userId)
.orElseGet(() -> createNewCart(userId));
cart.getItems().stream()
.filter(item -> item.getProduct().getId().equals(productId))
.findFirst()
.ifPresentOrElse(
item -> item.setQuantity(item.getQuantity() + quantity),
() -> cart.getItems().add(
CartItem.builder()
.product(product)
.quantity(quantity)
.build()
)
);
return cartRepository.save(cart);
}
}
5.2 下单流程的关键设计
下单过程涉及多个聚合根的修改,需要考虑分布式事务:
@startuml
actor User
participant "OrderController" as OC
participant "OrderService" as OS
participant "InventoryService" as IS
participant "PaymentService" as PYS
User -> OC : 创建订单(cartId)
OC -> OS : createOrder(userId, cartId)
OS -> IS : 锁定库存(cartItems)
IS --> OS : 成功/失败
OS -> PYS : 创建支付订单
PYS --> OS : Payment
OS --> OC : Order
OC --> User : 订单详情
@enduml
使用Saga模式处理分布式事务:
public class OrderCreationSaga {
private final InventoryService inventoryService;
private final PaymentService paymentService;
private final OrderRepository orderRepository;
@Transactional
public Order createOrder(Long userId, Long cartId) {
// 1. 获取购物车
Cart cart = cartService.getCart(cartId);
// 2. 锁定库存
try {
inventoryService.lockInventory(cart.getItems());
} catch (InventoryException e) {
throw new OrderException("库存不足");
}
// 3. 创建订单
Order order = Order.fromCart(cart, user);
order = orderRepository.save(order);
// 4. 创建支付
try {
Payment payment = paymentService.createPayment(order);
order.setPayment(payment);
} catch (PaymentException e) {
inventoryService.unlockInventory(cart.getItems());
throw new OrderException("支付创建失败");
}
return orderRepository.save(order);
}
}
在实际项目中,我们发现将PlantUML图作为代码审查的一部分,能显著提高设计质量。团队约定每个Pull Request必须包含相关的UML图更新,这帮助我们早期发现了很多设计缺陷。
更多推荐


所有评论(0)