惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Simon Willison's Weblog
Simon Willison's Weblog
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
Last Week in AI
Last Week in AI
月光博客
月光博客
有赞技术团队
有赞技术团队
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
Martin Fowler
Martin Fowler
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Check Point Blog
U
Unit 42
The Register - Security
The Register - Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
D
DataBreaches.Net
WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
H
Hacker News: Front Page
Recent Announcements
Recent Announcements
C
CXSECURITY Database RSS Feed - CXSecurity.com
Latest news
Latest news
小众软件
小众软件
P
Palo Alto Networks Blog
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest
S
Secure Thoughts
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
O
OpenAI News
S
Securelist
云风的 BLOG
云风的 BLOG
H
Help Net Security
T
Troy Hunt's Blog

博客园 - Swizard

逐行解剖:扒开 Lovable Agent 源码,看顶级 AI 是如何“思考”与“动刀”的 还在无脑堆砌提示词?三分钟看懂 Vercel v0 价值千万的 System Prompt 底层逻辑 OpenClaw+OpenViking + NVIDIA API 配置教程 拒绝“挤牙膏”!用苏格拉底提示法,让 AI 从“复读机”进化为“咨询顾问” 别再写丑陋的 Shell 脚本了:用 Gum 给你的终端穿上“高定西装” 告别 sudo 滥用!用 gosu 优雅解决 Docker 容器权限的“千古难题” 拒绝视觉噪音:用“数据墨水比”法则重塑你的专业图表 告别 Git Stash:用 Git Worktree 实现多任务并行,效率起飞! 手机里的 Linux 实验室:Termux 让你随时随地开启“黑客”模式 拒绝“隐形”!用 JSON-LD 让你的网页在 Google 搜索结果中“自带光环” ⚔️ Google SGE vs. Perplexity:一场内容争夺战,两套生存法则 SEO 已死?拥抱 AEO/GEO:如何让 AI 主动“推荐”你的网站 Python 重构神器:告别 ast 模块的痛苦,三行代码搞定复杂批量修改 别再手写递归找文件了!用 Python rglob 一行代码搞定海量搜索 告别笨重的 For 循环:用 xargs 打造 Linux 命令行流水线神器 告别“断网即崩溃”:像存档游戏一样管理你的终端会话(Tmux 终极入门) 沉默的观察者:Multi-Agent 架构如何实现“零指令”主动服务? 杀死那个提示词框:揭秘“意图识别 + 动态组装”的幕后逻辑 UI 设计如何落地“Boss Mode”?告别聊天框的四个交互法则 用 100 倍 Token 换取 10% 体验:关于 AI Agent 的“暴力美学”
还在被框架绑架?一文看懂“六边形架构”,让你的核心业务稳如泰山!
Swizard · 2026-06-14 · via 博客园 - Swizard

引言:那些年我们写过的“面条代码”

痛点场景: 你一定经历过这样的噩梦:系统最初用 MySQL 存储数据,后来为了性能要迁移到 MongoDB。结果你发现,业务代码里密密麻麻全是对 MySQL 驱动的直接调用。或者,老板突发奇想,要求把原本的 Web 页面功能,原封不动地搬到一个新的命令行工具(CLI)里,你却发现业务逻辑和 HTTP 的 Request / Response 对象死死绑定在一起。

“牵一发而动全身”,修改一行代码,整个系统崩溃。这是因为我们的核心业务逻辑被外部框架、数据库和 UI 强行“绑架”了。

解决方案: 这时候,六边形架构(Hexagonal Architecture) 闪亮登场。它的核心理念极其简单:将核心业务逻辑与外部依赖彻底隔离。它能让你的业务代码变得像插座一样通用,无论外接的是哪种数据库或哪个前端框架,核心系统都能从容应对。

概念拆解:餐厅后厨的生存哲学 (The "What" & "Why")

生活化类比:米其林餐厅的运作

别被“六边形”这个高大上的名字吓到,它其实也被称为端口与适配器模式(Ports and Adapters)。我们用“去餐厅点餐”来理解它:

想象一家顶级的米其林餐厅,它的核心竞争力是“后厨大厨的烹饪手艺”(核心业务逻辑)。

  1. 输入端(Driving):大厨根本不在乎客人是通过服务员点餐、通过美团外卖下单,还是打电话预定。大厨只认一样东西:标准化的点餐单(输入端口 Input Port)。服务员和外卖App在这里就是输入适配器(Input Adapter),负责把各种乱七八糟的请求翻译成大厨能看懂的点餐单。

  2. 输出端(Driven):大厨做菜需要土豆。他不会自己跑去菜市场买,他只会对采购员下达指令:“给我拿两个土豆”(输出端口 Output Port)。至于采购员是从门口超市买的,还是从远洋货轮上空运的(输出适配器 Output Adapter,如 MySQL、Redis、第三方 API),大厨毫不关心。

工作流图解:洋葱般的结构

如果画个图,六边形架构就像一个洋葱,分为内、中、外三层:

  • 最内层(Domain):纯粹的业务实体和规则(大厨的手艺)。这里没有任何外部框架的代码。

  • 中间层(Ports):接口定义层(点餐单和采购单)。它规定了外部如何与核心交互,以及核心如何向外部要数据。

  • 最外层(Adapters):具体的实现(服务员、外卖App、采购员)。比如 Spring Boot 控制器、REST API、MyBatis Mapper。

核心原则:依赖只能从外向内! 外层可以调用内层的接口,但内层绝对不能知道外层的任何细节。

动手实战:用 TypeScript 烤一个“六边形”MVP (The "How")

让我们用 TypeScript 写一个极简的“创建用户”功能,感受一下六边形架构的魅力。

1. 核心领域与端口(Domain & Ports)

首先,我们定义核心逻辑和它需要的“契约”。这部分代码绝对不能引入任何外部库(比如 expressmongoose)。

// 1. 领域模型 (Domain Model) - 纯纯的业务对象
export class User {
  constructor(public readonly id: string, public name: string, public email: string) {}
}

// 2. 输出端口 (Output Port) - 核心业务向外要数据的“采购单”
export interface UserRepositoryPort {
  save(user: User): Promise<void>;
  findByEmail(email: string): Promise<User | null>;
}

// 3. 输入端口 (Input Port) - 外部调用核心业务的“点餐单”
export interface CreateUserUseCase {
  execute(name: string, email: string): Promise<User>;
}

2. 应用服务(Application Service)

接下来,实现核心业务逻辑。它实现了输入端口,并调用输出端口。

// 4. 核心业务逻辑实现
export class UserService implements CreateUserUseCase {
  // 依赖注入:我不在乎你传给我的是 MySQL 还是 MongoDB,只要实现了接口就行!
  constructor(private readonly userRepository: UserRepositoryPort) {}

  async execute(name: string, email: string): Promise<User> {
    const existingUser = await this.userRepository.findByEmail(email);
    if (existingUser) {
      throw new Error("邮箱已被注册!"); // 纯粹的业务异常
    }
    
    const newUser = new User(Date.now().toString(), name, email);
    await this.userRepository.save(newUser); // 调用输出端口
    
    return newUser;
  }
}

3. 适配器层(Adapters)

最后,我们编写外围代码,连接真实的世界。

// 5. 输出适配器 (Output Adapter) - 真正连接数据库的地方
// 假设这里用的是内存数据库,随时可以换成 MongoUserRepository
export class InMemoryUserRepository implements UserRepositoryPort {
  private users: User[] = [];

  async save(user: User): Promise<void> {
    this.users.push(user);
    console.log(`[Database] 用户 ${user.name} 已保存到内存数据库。`);
  }

  async findByEmail(email: string): Promise<User | null> {
    return this.users.find(u => u.email === email) || null;
  }
}

// 6. 组装运行 (类似框架的 Controller / CLI 入口)
async function main() {
  // 装配阶段:将适配器插入端口
  const repository = new InMemoryUserRepository();
  const userService = new UserService(repository);

  // 模拟一个 HTTP 请求进来
  console.log("--> 收到前端请求:创建用户张三");
  try {
    const user = await userService.execute("张三", "zhangsan@example.com");
    console.log("<-- 响应前端:创建成功", user);
  } catch (error) {
    console.error("<-- 响应前端:创建失败", error.message);
  }
}

main();

代码解析: 为什么这么写?你会发现 UserService 里没有任何数据库的影子。如果明天老板说要把用户数据存到 Redis,你只需要新建一个 RedisUserRepository 实现 UserRepositoryPort 接口,然后在组装阶段替换掉 InMemoryUserRepository 即可。核心业务代码一行都不用改!

进阶深潜:新手防坑指南 (Deep Dive)

常见陷阱

⚠️ 在领域实体里写 ORM 注解 很多新手会在 User 类上加上 @Entity@Table(name="users")。千万别这么干!这就等于让大厨在菜谱上写满了采购员的进货路线。领域对象必须是纯洁的,数据库持久化对象(Entity/Model)应该在 Adapter 层单独定义,并在 Adapter 里进行转换映射(Mapper)。

最佳实践

  1. 测试驱动开发(TDD)的绝佳搭档:由于核心逻辑(Domain & Application)只依赖接口(Ports),你可以非常轻松地 Mock 掉数据库,实现秒级的单元测试。

  2. 切忌过度设计:六边形架构会增加类的数量(多了一堆接口和转换代码)。如果你的项目只是一个简单的、生命周期很短的 CRUD 脚本,传统的 MVC 三层架构可能更高效。好钢要用在刀刃(复杂核心业务)上。

总结与延伸 (Conclusion)

核心总结:六边形架构通过“端口与适配器”的机制,建立了一堵防火墙,让你的核心业务逻辑摆脱了底层技术选型的束缚,实现了真正的可插拔、可测试、可维护。