以联系发展的眼光设计系统:从ERP到通用架构模式
引言
通过模块关联图与业务流程分解,深入理解系统设计的联系发展观
1. 系统设计的哲学基础
在马克思哲学中,联系的观点和发展的观点是我们认识世界的基本方法。同样,在系统设计中,我们也要看到:
- 模块间的内在联系 - 没有孤立的组件,只有网络中的节点
- 系统的持续演进 - 软件如同生命体,需要不断适应变化
- 普遍的模式复用 - 输入-处理-输出这一基本模式在各个层面重复出现
2. ERP系统的模块关联网络
让我们通过模块关系图来直观理解ERP系统中各组件的内在联系:
图表说明:
-
中心节点:ERP核心系统作为枢纽,连接所有业务模块
-
颜色编码:不同颜色区分不同类型的业务功能
-
箭头方向:显示数据流动和业务依赖方向
-
关键路径:
- 采购 → 应付账款(资金流出)
- 销售 → 应收账款(资金流入)
- 生产 → 库存 → 销售(实物流动)
这个图清晰地展示了财务管理在ERP中的核心地位 - 所有业务活动最终都会反映到财务数据上。
3. 输入-处理-输出的通用模式
无论系统多么复杂,其基本运作模式都可以简化为三个核心环节:
模式分解:
阶段 | 内容 | ERP示例 | 学习示例 |
---|---|---|---|
输入 | 接收外部刺激 | 客户订单、库存数据 | 阅读材料、听课 |
处理 | 内部转换过程 | 订单处理、成本计算 | 理解思考、归纳总结 |
输出 | 产生结果价值 | 发货单、财务报表 | 应用解题、考试得分 |
这个通用模式解释了为什么不同领域的系统具有相似性 - 因为它们都遵循相同的基本运作逻辑。
4. 业务流程的时序展开
让我们通过订单处理流程,具体看输入-处理-输出模式如何在实际业务中展开:
流程关键点分析:
-
输入验证(步骤1-2)
- 确保数据完整性和合法性
- 早期发现问题,减少后续处理成本
-
业务处理(步骤3-6)
- 多模块协同完成复杂业务逻辑
- 每个模块专注自身职责范围
-
结果交付(步骤7-10)
- 将处理结果转化为客户价值
- 完成业务闭环并触发后续流程
5. 类设计的联系观
在面向对象设计中,类之间的关系网络同样体现了联系的观点:
类关系解读:
- 继承关系:所有实体共享基础属性和行为
- 组合关系:订单由订单项组成,体现整体-部分关系
- 关联关系:订单与客户关联,库存与商品关联
- 依赖关系:业务逻辑处理中的临时协作。
6. 实体类代码
基于前文的类图,通过Java代码实现Order
、Customer
等类的关联关系,体现“组合”“关联”等设计思想。
/*** 客户类(与Order为关联关系:1个客户可有多订单)*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Customer {private String customerId;private String customerName;private BigDecimal creditLimit; // 信用额度// 信用验证方法(封装客户自身业务逻辑)public boolean validateCredit(BigDecimal orderAmount) {// 订单金额不超过信用额度则通过return orderAmount.compareTo(this.creditLimit) <= 0;}
}/*** 商品类(与OrderItem为关联关系:1个商品可在多订单项中)*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {private String productId;private String productName;private BigDecimal price;private Category category; // 关联商品分类
}/*** 库存类(与Product为关联关系:1个商品对应1条库存记录)*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Stock {private Product product; // 关联商品(1对1关系)private Integer quantity; // 库存数量private String location; // 库存位置(如仓库编号、货架位置等)// 检查库存是否可用(判断库存数量是否大于0)public boolean checkAvailability() {return quantity != null && quantity > 0;}// 检查指定数量是否可用public boolean checkAvailability(int requiredQuantity) {return quantity != null && quantity >= requiredQuantity;}// 更新库存(可用于出库或入库)public void updateStock(int changeQuantity) {if (quantity == null) {quantity = 0;}int newQuantity = quantity + changeQuantity;// 确保库存不会为负数(实际业务中可能需要更复杂的校验)this.quantity = Math.max(newQuantity, 0);}
}/*** 订单项类(与Order为组合关系:订单项不能脱离订单存在)*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderItem {private Product product; // 关联商品private Integer quantity;private BigDecimal unitPrice;// 计算订单项小计public BigDecimal calculateSubtotal() {return this.unitPrice.multiply(new BigDecimal(this.quantity));}
}/*** 订单类(与OrderItem为组合关系,与Customer为关联关系)*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {private String orderId;private Customer customer; // 关联客户private List<OrderItem> items; // 组合订单项(订单删除时,订单项也删除)private BigDecimal totalAmount;private LocalDateTime createTime;// 计算订单总金额(依赖订单项的小计)public void calculateTotal() {this.totalAmount = this.items.stream().map(OrderItem::calculateSubtotal).reduce(BigDecimal.ZERO, BigDecimal::add);}public boolean processOrder(List<Stock> stockList) {// 1. 验证客户信用boolean creditPass = customer.validateCredit(this.totalAmount);if (!creditPass) {return false;}// 2. 检查库存是否充足for (OrderItem item : items) {Stock stock = stockList.stream().filter(s -> s.getProduct().getProductId().equals(item.getProduct().getProductId())).findFirst().orElse(null);if (stock == null || !stock.checkAvailability(item.getQuantity())) {return false; // 库存不足}}// 3. 扣减库存for (OrderItem item : items) {stockList.stream().filter(s -> s.getProduct().getProductId().equals(item.getProduct().getProductId())).findFirst().ifPresent(stock -> stock.updateStock(-item.getQuantity()));}return true;}
}
7. 系统演进路径
系统不是一成不变的,而是随着业务需求不断发展:
演进逻辑:
- 初创阶段:单体架构,快速验证业务模式
- 成长阶段:模块化,支持团队并行开发
- 成熟阶段:微服务,实现技术栈自由和独立部署
- 优化阶段:事件驱动,提高系统响应性和松耦合度
当系统从单体架构演进为微服务时,核心变化是模块解耦为独立服务,通过远程调用(如Feign)替代本地依赖。以下是“库存模块”从单体到微服务的代码调整示例。
7.1 单体架构下的库存实现
// 单体架构:库存模块作为本地Service
@Service
public class InventoryService implements InventoryApi {@Autowiredprivate InventoryMapper inventoryMapper; // 本地DAO@Overridepublic boolean checkStock(String productId, Integer quantity) {InventoryDO inventory = inventoryMapper.selectByProductId(productId);return inventory != null && inventory.getStockQuantity() >= quantity;}@Overridepublic boolean deductStock(String productId, Integer quantity) {int rows = inventoryMapper.deductStock(productId, quantity);return rows > 0;}
}
7.2 微服务架构下的库存实现
// 微服务架构:库存模块独立为服务,提供Feign接口
@RestController
@RequestMapping("/api/inventory")
public class InventoryController implements InventoryApi {@Autowiredprivate InventoryService inventoryService;@GetMapping("/check")@Overridepublic boolean checkStock(@RequestParam String productId, @RequestParam Integer quantity) {return inventoryService.checkStock(productId, quantity);}@PostMapping("/deduct")@Overridepublic boolean deductStock(@RequestParam String productId, @RequestParam Integer quantity) {return inventoryService.deductStock(productId, quantity);}
}// 订单服务中通过Feign调用库存服务
@FeignClient(value = "inventory-service", fallback = InventoryFeignFallback.class)
public interface InventoryFeignClient extends InventoryApi {// 继承InventoryApi的方法定义,无需重复编码
}// 熔断降级实现(提高系统容错性)
@Component
public class InventoryFeignFallback implements InventoryFeignClient {@Overridepublic boolean checkStock(String productId, Integer quantity) {log.error("库存服务调用失败,默认返回库存不足");return false;}@Overridepublic boolean deductStock(String productId, Integer quantity) {log.error("库存服务调用失败,默认返回扣减失败");return false;}
}
8. 实践指导原则
8.1 联系性原则:降低模块耦合
- 接口隔离:如
InventoryApi
只暴露必要方法(checkStock
/deductStock
),隐藏库存表的CRUD细节。 - 依赖注入:通过
@Autowired
注入接口(如InventoryApi
),而非具体实现,便于后续替换为微服务Feign客户端。
8.2 发展性原则:预留扩展点
-
策略模式:如支付方式扩展,定义
PaymentStrategy
接口,实现AlipayStrategy
/WechatPayStrategy
,后续新增支付方式无需修改核心代码。// 支付策略接口 public interface PaymentStrategy {boolean pay(OrderVO orderVO); }// 支付宝实现 @Service("alipay") public class AlipayStrategy implements PaymentStrategy {@Overridepublic boolean pay(OrderVO orderVO) {// 调用支付宝SDKreturn true;} }// 订单服务中使用策略 @Service public class OrderPaymentService {@Autowiredprivate Map<String, PaymentStrategy> paymentStrategyMap; // 自动注入所有实现public boolean processPayment(OrderVO orderVO, String payType) {PaymentStrategy strategy = paymentStrategyMap.get(payType);if (strategy == null) {throw new BusinessException("不支持的支付方式:" + payType);}return strategy.pay(orderVO);} }
8.3 模式复用原则:统一代码规范
- DTO/VO分层:输入用
XXXInputDTO
,输出用XXXVO
,避免直接传递数据库实体(DO
)。 - 全局异常处理:通过
@ControllerAdvice
统一捕获业务异常,返回标准化Result
格式。
9. 总结
本文从“联系与发展”的视角拆解系统设计,并通过Java代码落地ERP核心业务,核心收获如下:
- 联系观落地:通过接口定义模块边界(如
InventoryApi
)、类间关系(如Order
与OrderItem
的组合),避免代码孤立。 - 发展观落地:通过策略模式预留扩展点、Feign接口适配微服务演进,应对业务变化。
- 模式复用落地:将IPO抽象为通用接口,降低重复编码,提升代码可维护性。
这种思维不仅适用于ERP系统,也可迁移到电商、物流等其他领域的Java开发中——好的代码不仅能满足当前需求,更能适应未来的演进。