架构模式对比:分层架构、六边形架构与 DDD 在后端应用中的取舍
架构模式对比分层架构、六边形架构与 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(frozenTrue) class ProblemId: value: str dataclass(frozenTrue) 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 个以上的类。这个差异不是哪个更好的问题而是**为了获得架构上的某些好处你愿意多写多少代码**的问题。四、边界分析与架构权衡刷题系统该用哪种架构对于刷题系统来说分层架构是最合理的选择。原因刷题系统的业务复杂度不高——核心就是 CRUD 加上一些统计分析外部依赖相对固定——MySQL、Redis、AI API没有频繁切换的需求团队规模小——一个人开发不需要按限界上下文分工六边形架构适合的场景外部依赖频繁变化。比如你预计 AI 题解生成服务会从 GPT-4 切换到 Claude再切换到本地模型——用六边形架构切换只影响适配器核心逻辑不动。DDD 适合的场景业务规则复杂到代码逻辑的修改跟不上业务需求的变化。刷题系统远没有到这个复杂程度——它的业务规则用几个 if-else 就能表达清楚不需要 DDD 的战术设计模式聚合、值对象、领域事件。最重要的是避免为了用模式而用模式。简单问题引入复杂架构的后果不是更好维护而是更复杂的代码更难理解的行为更长的修改链路。结论架构模式的选择遵循够用就好的原则。在你的系统处于简单 CRUD 阶段时分层架构就够了。当系统成长到外部依赖需要灵活替换时引入六边形架构。当系统进一步复杂到业务规则密集到代码难以维护时引入 DDD 的战术模式。对刷题系统来说我的推荐是从分层架构开始在 AI 题解生成模块引入六边形架构的端口-适配器思想但不一定完整实现所有组件。DDD 留到我需要和产品经理反复讨论业务规则的时候再考虑。架构是为业务服务的。当你在犹豫要不要引入更复杂的架构时问自己一个问题当前架构下什么需求让你感到痛苦如果回答不了这个问题新架构只是在增加复杂度而不是解决问题。