第1章:设计原则与架构风格——分层架构、六边形架构、整洁架构中的设计原则
说实话,很多工程师干了三五年,代码写得挺溜,但一谈到架构就懵了。
我当年也是这样。直到有一次接手一个遗留系统,改一个功能要动七八个模块,改完还总出bug。那时候我才意识到——架构不是花架子,它决定了你的代码能活多久。
这一章,我们来聊聊架构风格和设计原则的关系。说白了,就是看看那些经典的架构模式里,到底藏着哪些设计原则。
1. 分层架构:最朴素的隔离之道
分层架构,大家都不陌生。三层架构、四层架构,本质都是关注点分离。
我个人习惯把分层架构理解为「各司其职」。表现层只管展示,业务层只管逻辑,数据层只管存取。谁也别越界。
核心设计原则:
- 单一职责原则:每一层只做一件事
- 依赖倒置原则:上层依赖抽象,不依赖下层具体实现
- 接口隔离原则:层与层之间通过窄接口通信
我在项目中遇到过一个问题:业务层直接调用了数据库层的具体类。后来数据库换了,业务层代码改得面目全非。嗯,这就是典型的违反依赖倒置。
我的建议:层与层之间只依赖接口。哪怕只有一种实现,也要先定义接口。这不是过度设计,这是给自己留后路。
// 好的做法:依赖抽象
class IUserRepository {
public:
virtual User findById(int id) = 0;
virtual ~IUserRepository() = default;
};
class UserService {
private:
IUserRepository& repo; // 依赖接口
public:
UserService(IUserRepository& r) : repo(r) {}
User getUser(int id) { return repo.findById(id); }
};
2. 六边形架构:把核心逻辑保护起来
六边形架构,也叫端口适配器架构。我第一次看到这个图的时候,觉得挺玄乎。后来用上了,才发现它解决了一个大问题——业务逻辑不被外部污染。
你想想看,传统的分层架构里,业务层直接依赖数据库、消息队列、外部API。一旦这些外部组件变了,业务逻辑也得跟着改。这合理吗?不合理。
六边形架构的核心思想是:业务逻辑在最内层,对外只暴露端口(接口)。外部的一切都是适配器。
核心设计原则:
- 依赖倒置原则:外部依赖内部,内部不依赖外部
- 开闭原则:增加新的外部适配器,不需要改内部逻辑
- 接口隔离原则:每个端口只定义调用方需要的方法
我曾经在一个支付系统里用过六边形架构。核心逻辑处理订单状态机,外部对接微信支付、支付宝、银联。后来新增了一个跨境支付渠道,只需要写一个新的适配器,核心代码一行没动。这种感觉,很爽。
// 端口定义
class IPaymentPort {
public:
virtual bool pay(Order& order) = 0;
virtual bool refund(Order& order) = 0;
};
// 核心业务逻辑
class OrderService {
private:
IPaymentPort& payment;
public:
OrderService(IPaymentPort& p) : payment(p) {}
void processOrder(Order& o) {
// 业务逻辑...
payment.pay(o);
}
};
// 适配器:微信支付
class WechatPayAdapter : public IPaymentPort {
bool pay(Order& o) override { /* 微信支付实现 */ }
bool refund(Order& o) override { /* 微信退款实现 */ }
};
注意:六边形架构不是银弹。如果你的业务逻辑本身就很简单,强行套用反而增加复杂度。我见过有人为了用六边形架构,写了五层抽象,最后自己都绕晕了。
3. 整洁架构:原则的集大成者
整洁架构是Robert C. Martin提出的。说白了,它把六边形架构的思想又往前推了一步——不仅外部要隔离,内部也要分层。
整洁架构把系统分成四层:
| 层级 | 内容 | 依赖方向 |
|---|---|---|
| 企业业务规则 | 实体、核心业务对象 | 最内层,无依赖 |
| 应用业务规则 | 用例、应用服务 | 依赖内层 |
| 接口适配器 | 控制器、网关、展示器 | 依赖内层 |
| 框架与驱动 | 数据库、Web框架、UI | 最外层,依赖内层 |
依赖规则只有一条:外层可以依赖内层,内层绝不依赖外层。
核心设计原则:
- 依赖倒置原则:所有依赖指向抽象
- 开闭原则:新增功能通过扩展实现,不修改已有代码
- 里氏替换原则:内层的接口可以被任意外层实现替换
- 接口隔离原则:每层只暴露必要的接口
我记得有一次重构一个电商系统,原来的代码把SQL查询写在控制器里。按照整洁架构的思路,我们把查询逻辑下沉到用例层,控制器只做参数解析和结果返回。后来换了ORM框架,只改了最外层的适配器,核心逻辑完全不受影响。
// 内层:实体
class Order {
public:
double totalAmount() const;
void addItem(Product p, int qty);
};
// 内层:用例
class CheckoutUseCase {
public:
void execute(Order& order, IPaymentPort& payment) {
double total = order.totalAmount();
payment.charge(total);
}
};
// 外层:控制器
class CheckoutController {
public:
void handleRequest(Request& req) {
Order order = buildOrder(req);
CheckoutUseCase useCase;
useCase.execute(order, paymentAdapter);
}
};
4. 三种架构的对比与选择
这三种架构不是互斥的,而是层层递进的关系。
| 架构风格 | 核心思想 | 适用场景 | 复杂度 |
|---|---|---|---|
| 分层架构 | 关注点分离 | 中小型项目,团队熟悉 | 低 |
| 六边形架构 | 核心与外部隔离 | 需要对接多种外部系统 | 中 |
| 整洁架构 | 严格依赖规则 | 大型复杂系统,长期维护 | 高 |
我个人建议:不要为了用架构而用架构。项目初期,分层架构就够了。随着系统变复杂,再逐步引入六边形或整洁架构。我见过太多人一开始就上整洁架构,结果项目还没上线,架构先把自己累死了。
避坑指南:我曾经在一个小工具项目里强行套用整洁架构,写了十几个接口和适配器。最后发现,核心逻辑只有200行代码,接口定义却写了500行。嗯,这就是过度设计。记住:架构是为业务服务的,不是反过来。
5. 知识体系总览
下面这张图,我把三种架构风格和它们对应的设计原则梳理了一下。你可以把它当作一个快速参考。
看到这张图,你应该能感受到:三种架构风格虽然形式不同,但背后的设计原则是相通的。依赖倒置、接口隔离、关注点分离,这些原则就像建筑的钢筋,不管外面怎么装修,骨架始终不变。
好了,这一章就聊到这里。记住:架构不是目的,是手段。选对架构,你的代码能活十年;选错了,半年就想重写。
公众号:蓝海资料掘金营,微信 deep3321