架构模式对比:分层架构、六边形架构与 DDD 在后端应用中的取舍
2026/7/29 16:47:42 网站建设 项目流程

架构模式对比:分层架构、六边形架构与 DDD 在后端应用中的取舍

一、深度引言与场景痛点:照着书搭出来的"六边形架构",连我自己都找不到业务逻辑在哪

7 月,我在设计刷题系统时遇到了一个架构选择的问题。最开始用的是经典的分层架构(Controller → Service → Repository),代码结构清晰、上手快。后来读了一些关于"六边形架构"和"领域驱动设计(DDD)"的文章,觉得自己的代码"不够高级",于是开始重构。

重构后的代码确实更"干净"了——端口适配器、领域模型、应用服务层层分离。但随之而来的是:一个简单的"用户提交题解"功能,现在需要跨越 4 个包、6 个类。每次修改一个业务规则,需要在 3 个地方做相应的调整。复杂度不是降低了,而是转移了——从"业务逻辑复杂"变成了"架构逻辑复杂"。

本文通过刷题系统的实际案例,对比分层架构、六边形架构和 DDD 三种架构模式在后端应用中的适用场景和取舍。

二、底层机制与原理深度剖析:三种架构的本质差异

分层架构的本质:按技术职责划分层次。每一层只依赖下一层,上层不应知道下层的实现细节。核心思想是"关注点分离"——表示层处理 HTTP,业务层处理逻辑,数据层处理持久化。

六边形架构的本质:按"内外部"划分边界。核心业务逻辑在"六边形内部",所有外部依赖(数据库、API、消息队列)通过端口(Port)和适配器(Adapter)与核心交互。核心思想是"依赖倒置"——核心不依赖外部实现,外部依赖核心定义的接口。

DDD 的本质:按"业务领域"划分模块边界。系统被拆分为多个"限界上下文",每个上下文中包含聚合、实体、值对象等概念。核心思想是"通过代码反映业务语言"——领域模型和业务专家的用词一致,减少沟通中的翻译成本。

三种架构不是相互排斥的,而是不同粒度和不同关注点的产物。分层架构关注"代码组织",六边形架构关注"依赖方向",DDD 关注"业务建模"。

三、生产级代码实现与最佳实践:同一功能三种架构对比

""" 同一个"提交题解并更新统计"功能在三种架构下的实现对比 通过代码量的直观差异,展示架构选择对开发效率的实际影响 """ # ==================== 方案一:分层架构 ==================== """ 最简单的实现,适合快速开发和简单业务 3 个类、1 个文件、清晰直观 """ class SolutionController: """Controller:接收 HTTP 请求,做参数校验""" def __init__(self, solution_service): self.service = solution_service def submit(self, request_data: dict): user_id = request_data["user_id"] problem_id = request_data["problem_id"] code = request_data["code"] # 调用 Service 处理业务逻辑 return self.service.submit_solution(user_id, problem_id, code) class SolutionService: """Service:核心业务逻辑""" def __init__(self, submission_repo, stats_repo): self.submission_repo = submission_repo self.stats_repo = stats_repo def submit_solution(self, user_id, problem_id, code): # 1. 保存提交记录 submission = self.submission_repo.save(user_id, problem_id, code) # 2. 更新用户统计 self.stats_repo.increment_total_submissions(user_id) return {"submission_id": submission.id, "status": "ok"} # ==================== 方案二:六边形架构 ==================== """ 引入了端口和适配器的概念,核心逻辑不依赖具体实现 代码量是分层架构的 2-3 倍,但外部依赖替换成本低 """ # 端口(Port):定义核心需要的接口,不提供实现 from abc import ABC, abstractmethod class SubmissionRepositoryPort(ABC): """提交记录的存储端口 —— 核心只依赖这个接口""" @abstractmethod def save(self, user_id, problem_id, code): pass class StatsRepositoryPort(ABC): """统计数据的存储端口""" @abstractmethod def increment_submissions(self, user_id): pass # 领域核心:不依赖任何外部框架和数据库 class SubmitSolutionUseCase: """领域用例:纯粹的提交题解业务逻辑""" def __init__( self, submission_repo: SubmissionRepositoryPort, stats_repo: StatsRepositoryPort, ): # 依赖注入的是端口(Port),而非具体实现 self.submission_repo = submission_repo self.stats_repo = stats_repo def execute(self, user_id: int, problem_id: str, code: str) -> dict: """核心业务逻辑 —— 不依赖任何外部框架""" submission = self.submission_repo.save(user_id, problem_id, code) self.stats_repo.increment_submissions(user_id) return {"submission_id": submission.id} # 适配器(Adapter):实现端口的具体方式 class MySQLSubmissionAdapter(SubmissionRepositoryPort): """MySQL 实现的提交记录存储""" def save(self, user_id, problem_id, code): # 具体的 MySQL 操作 pass # ==================== 方案三:DDD 领域驱动设计 ==================== """ DDD 风格:按业务概念组织代码,代码命名直接反映业务语言 代码量最大,适合复杂业务规则密集的场景 """ # 值对象(Value Object):不可变,由属性值确定相等性 @dataclass(frozen=True) class ProblemId: value: str @dataclass(frozen=True) class SourceCode: value: str language: str # 实体(Entity):有唯一标识,可变的业务对象 class Submission: def __init__(self, submission_id: str, user_id: int, problem_id: ProblemId): self.submission_id = submission_id self.user_id = user_id self.problem_id = problem_id self.status = "pending" def mark_as_accepted(self): """领域行为:标记为通过 —— 业务语义清晰""" self.status = "accepted" def mark_as_failed(self): self.status = "failed" # 聚合根(Aggregate Root):管理一组相关实体的生命周期 class UserSolutionStats: """用户刷题统计聚合 —— 用户统计的规则都在这里""" def __init__(self, user_id: int): self.user_id = user_id self._total_submissions = 0 self._accepted_count = 0 def record_submission(self, submission: Submission): """记录一次提交 —— 封装了统计逻辑的变更规则""" self._total_submissions += 1 if submission.status == "accepted": self._accepted_count += 1 @property def acceptance_rate(self) -> float: if self._total_submissions == 0: return 0.0 return self._accepted_count / self._total_submissions # 对比结论 ARCHITECTURE_COMPARISON = { "分层架构": "3 个类。适合:简单 CRUD、快速原型开发。刷题系统的初期版本。", "六边形架构": "6 个类。适合:外部依赖频繁变化、需要高可测试性。AI 服务切换频繁的模块。", "DDD": "8+ 个类。适合:复杂业务规则、与业务专家频繁协作。订单/风控等复杂领域。", }

从代码量对比可以看到:分层架构实现一个功能需要 3 个类,六边形架构需要 6 个类,DDD 需要 8 个以上的类。这个差异不是"哪个更好"的问题,而是**"为了获得架构上的某些好处,你愿意多写多少代码"**的问题。

四、边界分析与架构权衡:刷题系统该用哪种架构

对于刷题系统来说,分层架构是最合理的选择。原因:

  1. 刷题系统的业务复杂度不高——核心就是 CRUD 加上一些统计分析
  2. 外部依赖相对固定——MySQL、Redis、AI API,没有频繁切换的需求
  3. 团队规模小——一个人开发,不需要按"限界上下文"分工

六边形架构适合的场景:外部依赖频繁变化。比如你预计 AI 题解生成服务会从 GPT-4 切换到 Claude,再切换到本地模型——用六边形架构,切换只影响适配器,核心逻辑不动。

DDD 适合的场景:业务规则复杂到"代码逻辑的修改跟不上业务需求的变化"。刷题系统远没有到这个复杂程度——它的"业务规则"用几个 if-else 就能表达清楚,不需要 DDD 的战术设计模式(聚合、值对象、领域事件)。

最重要的是避免"为了用模式而用模式"。"简单问题引入复杂架构"的后果不是"更好维护",而是"更复杂的代码,更难理解的行为,更长的修改链路"。

结论

架构模式的选择遵循"够用就好"的原则。在你的系统处于"简单 CRUD 阶段"时,分层架构就够了。当系统成长到"外部依赖需要灵活替换"时,引入六边形架构。当系统进一步复杂到"业务规则密集到代码难以维护"时,引入 DDD 的战术模式。

对刷题系统来说,我的推荐是:从分层架构开始,在 AI 题解生成模块引入六边形架构的端口-适配器思想(但不一定完整实现所有组件)。DDD 留到"我需要和产品经理反复讨论业务规则"的时候再考虑。

架构是为业务服务的。当你在犹豫要不要引入更复杂的架构时,问自己一个问题:当前架构下,什么需求让你感到痛苦?如果回答不了这个问题,新架构只是在增加复杂度,而不是解决问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询