clean-architecture

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Clean Architecture & DDD

Clean Architecture与DDD

Architectural patterns and tactical design techniques for building systems where business logic is isolated, testable, and independent of frameworks, databases, and delivery mechanisms. Rooted in the work of Robert C. Martin, Alistair Cockburn, and Eric Evans.
本文介绍用于构建业务逻辑独立、可测试且与框架、数据库及交付机制无关的系统的架构模式与战术设计技巧,其理论源于Robert C. Martin、Alistair Cockburn和Eric Evans的研究成果。

When to Use

适用场景

  • The domain has meaningful business rules that deserve explicit modeling
  • The system must survive framework upgrades, database migrations, or delivery mechanism changes
  • Multiple entry points (API, CLI, message consumer, scheduled jobs) share the same business logic
  • Long-lived product where maintenance cost outweighs initial development speed
  • Team size or turnover demands clear boundaries and enforceable conventions
  • Testability is a priority — business rules must be verifiable without infrastructure
  • 业务领域具备有意义的业务规则,需要进行显式建模
  • 系统需要在框架升级、数据库迁移或交付机制变更时仍能正常运行
  • 多个入口(API、CLI、消息消费者、定时任务)共享相同的业务逻辑
  • 长期维护的产品,维护成本高于初始开发速度
  • 团队规模较大或人员流动频繁,需要清晰的边界和可执行的规范
  • 可测试性为优先需求——业务规则无需依赖基础设施即可验证

When NOT to Use

不适用场景

Not every system benefits from this level of architectural rigor. Avoid over-engineering when:
  • The application is a simple CRUD wrapper with no meaningful business logic
  • The project is a short-lived prototype or proof of concept
  • The team is very small and the domain is well understood with few rules
  • The overhead of layer separation exceeds the complexity of the problem
  • A simple framework-centric approach (e.g., MVC with active records) adequately serves the need
The key question: Does the domain have enough complexity to justify the investment? If the answer is no, a simpler architecture is the right choice. You can always introduce boundaries later as complexity grows.
并非所有系统都能从这种严格的架构设计中获益。在以下情况避免过度设计:
  • 应用仅为简单的CRUD包装器,无实际业务逻辑
  • 项目为短期原型或概念验证
  • 团队规模极小,且业务领域规则少、已被充分理解
  • 分层带来的开销超过问题本身的复杂度
  • 简单的框架中心化方案(如结合活动记录的MVC)已能满足需求
核心问题:**业务领域的复杂度是否足以证明投入的合理性?**如果答案是否定的,选择更简单的架构即可。随着复杂度提升,后续可再引入边界设计。

Architecture Styles

架构风格

Three well-known styles share the same core principle: dependencies point inward, and the domain sits at the center.
StyleOriginKey MetaphorDistinguishing Idea
Clean ArchitectureRobert C. Martin (2012)Concentric circlesExplicit layer names: Entity, Use Case, Interface Adapter, Framework
Hexagonal (Ports & Adapters)Alistair Cockburn (2005)Hexagon with portsSymmetry between driving (primary) and driven (secondary) adapters
Onion ArchitectureJeffrey Palermo (2008)Onion layersEmphasis on domain model at the core, infrastructure at the outer ring
What they share:
  • Domain logic at the center, free of external dependencies
  • The Dependency Rule: source code dependencies always point inward
  • Infrastructure and delivery concerns live in outer layers
  • Communication crosses boundaries through abstractions (interfaces/ports)
For practical purposes, the differences are naming conventions. The principles below apply to all three.
三种知名的架构风格共享同一核心原则:依赖指向内部,领域层位于中心位置。
风格起源核心隐喻独特理念
Clean ArchitectureRobert C. Martin(2012)同心圆明确的分层命名:实体层、用例层、接口适配层、框架层
六边形架构(Ports & Adapters)Alistair Cockburn(2005)带端口的六边形驱动(主)适配器与被驱动(次)适配器的对称性
洋葱架构Jeffrey Palermo(2008)洋葱分层强调领域模型为核心,基础设施位于最外层
共同特性:
  • 领域逻辑位于中心,无外部依赖
  • 依赖规则:源代码依赖始终指向内部
  • 基础设施与交付相关逻辑位于外层
  • 通过抽象(接口/端口)实现跨边界通信
从实践角度看,三者的差异仅在于命名规范。以下原则适用于所有三种架构。

Ports & Adapters Concept

端口与适配器概念

The Hexagonal Architecture introduces a useful vocabulary that applies across all three styles:
  • Ports are interfaces defined by the application. They describe what the application needs from the outside world (driven/secondary ports) or what the outside world can ask of the application (driving/primary ports).
  • Adapters are implementations that connect a port to a specific technology. A database adapter implements a repository port. An HTTP controller is an adapter for a driving port.
  • Driving (primary) adapters initiate interaction with the application: HTTP controllers, CLI commands, message consumers, scheduled jobs.
  • Driven (secondary) adapters are called by the application: database repositories, email senders, payment gateways, file storage.
This symmetry is powerful: the application does not know whether it is being driven by a REST API or a CLI command, and it does not know whether it is persisting to a relational database or an in-memory store.
六边形架构引入的术语体系适用于所有三种风格:
  • Ports(端口):由应用定义的接口,描述应用对外界的需求(被驱动/次级端口)或外界可向应用发起的请求(驱动/初级端口)。
  • Adapters(适配器):将端口连接到特定技术的实现。数据库适配器实现仓储端口,HTTP控制器是驱动端口的适配器。
  • 驱动(初级)适配器:发起与应用的交互:HTTP控制器、CLI命令、消息消费者、定时任务。
  • 被驱动(次级)适配器:被应用调用:数据库仓储、邮件发送器、支付网关、文件存储。
这种对称性的优势在于:应用无需知晓是被REST API还是CLI命令驱动,也无需知晓数据是持久化到关系型数据库还是内存存储。

The Dependency Rule

依赖规则

Source code dependencies must point inward. Nothing in an inner layer can know anything about an outer layer — no names, types, interfaces, or concepts.
This is the single most important rule. Every other guideline flows from it.
What "inward" means:
  • Inner layers define interfaces (ports); outer layers implement them
  • Data crosses boundaries as simple structures (DTOs, primitives), never as framework objects
  • The domain layer never imports from infrastructure, presentation, or application layers
  • The application layer never imports from infrastructure or presentation layers
Why it matters:
  • The domain can be tested without a database, HTTP server, or message broker
  • Frameworks become replaceable implementation details
  • Business rules are readable without understanding deployment topology
How to enforce it:
  • Use static analysis or architecture testing tools to verify import directions
  • Organize code into modules or packages that reflect layers, making violations visible
  • Code reviews should flag any inner-layer import of outer-layer types
  • The Composition Root (application entry point) is the only place that wires all layers together — it is the exception that knows about everything
源代码依赖必须指向内部。内层的任何内容都不得知晓外层的任何信息——包括名称、类型、接口或概念。
这是最重要的规则,所有其他准则均由此衍生。
“内部”的含义:
  • 内层定义接口(端口),外层实现接口
  • 跨边界传递的数据为简单结构(DTO、基本类型),绝不能是框架对象
  • 领域层从不导入基础设施层、表示层或应用层的代码
  • 应用层从不导入基础设施层或表示层的代码
重要性:
  • 领域层无需数据库、HTTP服务器或消息队列即可测试
  • 框架可被替换为实现细节
  • 无需理解部署拓扑即可读懂业务规则
如何强制执行:
  • 使用静态分析或架构测试工具验证导入方向
  • 按分层组织代码模块或包,使违规行为一目了然
  • 代码评审时标记任何内层导入外层类型的情况
  • 组合根(应用入口点)是唯一连接所有层的地方——它是唯一知晓所有内容的例外

Layer Reference

分层参考

LayerDirectionResponsibilityWhat Belongs HereWhat Does NOT Belong
Domain (innermost)Depends on nothingBusiness rules, invariantsEntities, Value Objects, Aggregates, Domain Services, Domain Events, Repository interfacesFramework imports, database queries, HTTP concepts
ApplicationDepends on DomainUse case orchestrationApplication Services, Command/Query objects, DTOs, Port interfacesBusiness logic, direct infrastructure calls
InfrastructureDepends on Application + DomainTechnical capabilitiesRepository implementations, API clients, message queue adapters, file system accessBusiness rules, use case orchestration
Presentation (outermost)Depends on ApplicationUser/system interactionControllers, CLI commands, API endpoints, View ModelsBusiness logic, direct database access
See Layer Details for in-depth guidance on each layer.
分层依赖方向职责包含内容不包含内容
Domain(领域层)(最内层)无任何依赖业务规则、不变量实体、值对象、聚合、领域服务、领域事件、仓储接口框架导入、数据库查询、HTTP相关概念
Application(应用层)依赖领域层用例编排应用服务、命令/查询对象、DTO、端口接口业务逻辑、直接调用基础设施
Infrastructure(基础设施层)依赖应用层+领域层技术能力实现仓储实现、API客户端、消息队列适配器、文件系统访问业务规则、用例编排
Presentation(表示层)(最外层)依赖应用层用户/系统交互控制器、CLI命令、API端点、视图模型业务逻辑、直接数据库访问
详见分层细节获取各层的深入指导。

DDD Tactical Patterns

DDD战术模式

Tactical patterns give structure to the domain layer. Each pattern has a specific role and placement within the architecture.
PatternPurposeLayerKey Rule
EntityObject with identity and lifecycleDomainIdentity determines equality
Value ObjectImmutable, identity-less conceptDomainAttribute equality, no side effects
AggregateConsistency boundaryDomainOne aggregate per transaction, reference others by ID
RepositoryCollection-like access to aggregatesInterface in Domain, implementation in InfrastructureOne repository per aggregate root
Domain ServiceStateless cross-entity logicDomainOnly when logic doesn't belong to a single entity
Domain EventRecord of something that happenedDomainNamed in past tense, immutable payload
Application ServiceUse case orchestratorApplicationNo business logic — delegates to domain objects
FactoryComplex object creationDomainEncapsulates construction invariants
See DDD Tactical Patterns for detailed guidance on each pattern.
战术模式为领域层提供结构,每种模式在架构中都有特定的角色和位置。
模式用途分层核心规则
Entity(实体)带标识和生命周期的对象领域层标识决定相等性
Value Object(值对象)不可变、无标识的概念领域层属性相等、无副作用
Aggregate(聚合)一致性边界领域层每个事务对应一个聚合,通过ID引用其他聚合
Repository(仓储)类集合的聚合访问方式接口定义在领域层,实现位于基础设施层每个聚合根对应一个仓储
Domain Service(领域服务)无状态的跨实体逻辑领域层仅当逻辑不属于单个实体时使用
Domain Event(领域事件)记录已发生的事件领域层采用过去式命名,负载不可变
Application Service(应用服务)用例编排器应用层无业务逻辑——委托给领域对象
Factory(工厂)复杂对象创建领域层封装构造不变量
详见DDD战术模式获取各模式的详细指导。

DDD Strategic Patterns

DDD战略模式

Strategic patterns address the large-scale structure of a system — how to divide a complex domain into manageable parts and how those parts interact.
战略模式解决系统的大规模结构问题——如何将复杂领域划分为可管理的部分,以及这些部分如何交互。

Bounded Context

Bounded Context(限界上下文)

A Bounded Context is a boundary within which a particular domain model is defined and consistent. Each context has its own Ubiquitous Language — the same word can mean different things in different contexts.
Identifying boundaries:
  • Different teams or departments often indicate different contexts
  • When the same term (e.g., "Account") means different things to different stakeholders, they belong in separate contexts
  • A context should be small enough for one team to own
  • Align contexts with business capabilities, not technical layers
Bounded Context是特定领域模型定义且保持一致的边界。每个上下文都有自己的Ubiquitous Language(通用语言)——同一词汇在不同上下文中可能含义不同。
识别边界:
  • 不同团队或部门通常对应不同的上下文
  • 当同一术语(如“账户”)对不同利益相关者含义不同时,它们属于不同的上下文
  • 上下文应小到足以由单个团队负责
  • 上下文与业务能力对齐,而非技术分层

Ubiquitous Language

Ubiquitous Language(通用语言)

Within each Bounded Context, the team (developers and domain experts) shares a precise vocabulary. Code should use this language directly — class names, method names, and variable names reflect domain concepts exactly as the domain expert would describe them.
Practical tips for maintaining Ubiquitous Language:
  • If a developer cannot explain a class name to a domain expert, the name is wrong
  • Refactor code when the language evolves — renaming is not cosmetic, it is a design activity
  • Avoid technical jargon in domain layer code — use business terms, not implementation terms
  • Document the language in a glossary shared between developers and domain experts
在每个Bounded Context中,团队(开发人员和领域专家)共享精确的词汇表。代码应直接使用这些词汇——类名、方法名和变量名应与领域专家描述的业务概念完全一致。
维护通用语言的实用技巧:
  • 如果开发人员无法向领域专家解释类名,那么这个名称是错误的
  • 当语言演进时重构代码——重命名不是表面工作,而是设计活动
  • 领域层代码避免使用技术术语——使用业务术语而非实现术语
  • 在开发人员和领域专家共享的术语表中记录通用语言

Context Map

Context Map(上下文映射)

A Context Map documents the relationships between Bounded Contexts. It makes integration strategies explicit rather than accidental.
PatternRelationshipWhen to Use
Shared KernelTwo contexts share a subset of the modelClosely collaborating teams willing to coordinate changes
Customer-SupplierUpstream context serves downstream contextDownstream can negotiate with upstream
ConformistDownstream conforms to upstream modelNo negotiating power; upstream won't change
Anti-Corruption LayerTranslation layer protects downstreamIntegrating with legacy or external systems
Open Host ServiceUpstream provides a well-defined protocolMultiple consumers need stable access
Published LanguageShared interchange formatCross-context communication via standard schemas
See Context Mapping for integration patterns and boundary decisions.
Context Map记录Bounded Context之间的关系,使集成策略显式化而非偶然形成。
模式关系适用场景
Shared Kernel(共享内核)两个上下文共享模型的子集密切协作的团队愿意协调变更
Customer-Supplier(客户-供应商)上游上下文为下游上下文提供服务下游可与上游协商
Conformist(追随者)下游上下文遵循上游模型无协商能力;上游不会变更
Anti-Corruption Layer(防腐层)转换层保护下游上下文与遗留系统或外部系统集成
Open Host Service(开放主机服务)上游提供定义良好的协议多个消费者需要稳定访问
Published Language(发布语言)共享的交互格式通过标准 schema 进行跨上下文通信
详见上下文映射获取集成模式和边界决策的指导。

Anti-Corruption Layer

Anti-Corruption Layer(防腐层)

When integrating with external or legacy systems, an Anti-Corruption Layer (ACL) translates between the external model and the internal domain model. The ACL prevents foreign concepts from leaking into the domain.
Structure: External system -> ACL (adapter + translator) -> Domain model
The ACL belongs in the infrastructure layer and implements a port defined by the application or domain layer.
与外部或遗留系统集成时,防腐层(ACL)在外部模型与内部领域模型之间进行转换,防止外来概念渗透到领域层。
结构: 外部系统 -> ACL(适配器+转换器)-> 领域模型
防腐层属于基础设施层,实现由应用层或领域层定义的端口。

CQRS — Command Query Responsibility Segregation

CQRS——命令查询职责分离

CQRS is a natural companion to Clean Architecture and DDD. It separates the write model (commands) from the read model (queries), allowing each to be optimized independently.
How it fits the architecture:
  • Commands flow through the application layer, modify aggregates, and persist through repositories
  • Queries can bypass the domain layer entirely and read from optimized projections or views
  • The write side enforces business rules through the domain model
  • The read side is optimized for the consumer's needs — no need to reconstruct full aggregates for display purposes
When to consider CQRS:
  • Read and write patterns are significantly different (e.g., complex writes but simple reads, or vice versa)
  • Performance requirements differ between reads and writes
  • The read model needs denormalized views that do not map to the domain model
  • Event sourcing is in use (CQRS is nearly always paired with event sourcing)
When to avoid CQRS:
  • Simple CRUD applications where reads and writes are symmetric
  • The added complexity is not justified by the domain's needs
CQRS是Clean Architecture和DDD的天然配套模式,它将写模型(命令)与读模型(查询)分离,允许两者独立优化。
与架构的契合方式:
  • 命令流经应用层,修改聚合,并通过仓储持久化
  • 查询可完全绕过领域层,直接从优化的投影或视图读取数据
  • 写侧通过领域模型强制执行业务规则
  • 读侧针对消费者需求进行优化——无需为显示目的重构完整聚合
考虑使用CQRS的场景:
  • 读和写模式差异显著(如复杂写但简单读,或反之)
  • 读和写的性能要求不同
  • 读模型需要非规范化视图,与领域模型不匹配
  • 使用事件溯源(CQRS几乎总是与事件溯源配对使用)
避免使用CQRS的场景:
  • 简单CRUD应用,读和写对称
  • 增加的复杂度无法通过领域需求证明其合理性

Event Sourcing — Brief Overview

Event Sourcing——简介

Event Sourcing stores the state of an aggregate as a sequence of domain events rather than a current-state snapshot. The current state is derived by replaying events.
Relationship to Clean Architecture:
  • Events are part of the domain layer
  • The event store is an infrastructure concern (a specialized repository)
  • Projections (read models) are built from events in the infrastructure or application layer
  • The domain model does not depend on the event store implementation
When to consider: audit requirements, complex state transitions, temporal queries ("what was the state at time X"), or when domain events are already central to the design.
Event Sourcing将聚合的状态存储为一系列领域事件,而非当前状态快照。当前状态通过重放事件推导得出。
与Clean Architecture的关系:
  • 事件属于领域层
  • 事件存储是基础设施相关的关注点(一种特殊的仓储)
  • 投影(读模型)由基础设施层或应用层从事件构建
  • 领域模型不依赖事件存储的实现
考虑使用的场景: 审计需求、复杂状态转换、时间查询(“X时间点的状态是什么”),或领域事件已成为设计核心时。

Common Misconceptions

常见误解

  • "Clean Architecture means lots of boilerplate" — The layers add some structural code, but the trade-off is explicit boundaries. If the boilerplate is overwhelming, the domain may not be complex enough to justify the approach.
  • "Every application needs all four layers" — Small applications may collapse the application and presentation layers. The critical boundary is between domain and infrastructure.
  • "DTOs everywhere means Clean Architecture" — DTOs are a mechanism for crossing boundaries, not the architecture itself. The architecture is about dependency direction.
  • "The domain model must mirror the database schema" — The opposite is true. The domain model reflects business concepts. The infrastructure layer maps between the domain model and the storage schema.
  • "Hexagonal and Clean Architecture are different things" — They are different descriptions of the same core idea. Pick the vocabulary that resonates with your team.
  • "DDD requires Clean Architecture" — DDD tactical patterns can be used without strict layer separation, though they work best together. Conversely, Clean Architecture does not require DDD patterns.
  • "One bounded context = one microservice" — A bounded context is a logical boundary. It may map to a microservice, a module within a monolith, or any other deployment unit.
  • “Clean Architecture意味着大量样板代码”——分层会增加一些结构性代码,但换来的是明确的边界。如果样板代码过于繁琐,说明领域复杂度可能不足以证明这种方法的合理性。
  • “每个应用都需要四层结构”——小型应用可以合并应用层和表示层。关键边界是领域层与基础设施层之间的边界。
  • “到处使用DTO就是Clean Architecture”——DTO是跨边界的机制,而非架构本身。架构的核心是依赖方向。
  • “领域模型必须与数据库模式镜像”——恰恰相反。领域模型反映业务概念,基础设施层负责领域模型与存储模式之间的映射。
  • “六边形架构和Clean Architecture是不同的东西”——它们是同一核心思想的不同表述。选择团队更容易接受的术语即可。
  • “DDD需要Clean Architecture”——DDD战术模式可以在没有严格分层的情况下使用,尽管两者配合使用效果最佳。反之,Clean Architecture也不需要DDD模式。
  • “一个限界上下文对应一个微服务”——限界上下文是逻辑边界。它可以映射到微服务、单体应用中的模块或任何其他部署单元。

Common Violations

常见违规情况

ViolationSymptomsFix
Domain depends on frameworkDomain classes import HTTP, ORM, or framework annotationsMove infrastructure concerns to outer layers; use plain objects in domain
Business logic in controllerControllers contain conditional logic, validation rules, calculationsExtract logic into domain objects or application services
Anemic domain modelEntities are data bags with only getters/setters; all logic lives in servicesPush behavior into entities and value objects; enforce invariants inside aggregates
Application service contains business rulesApplication layer has complex conditionals, domain calculationsMove rules into domain services or entity methods
Infrastructure leaking inwardDomain layer references database column names, API response shapesIntroduce mapping in infrastructure; keep domain model pure
God aggregateOne aggregate handles too many concerns, grows without limitSplit into smaller aggregates; use domain events for cross-aggregate communication
Shared database between contextsMultiple bounded contexts read/write the same tablesSeparate storage per context; integrate through events or APIs
Circular dependencies between layersInner layer imports from outer layer; compile errors or runtime couplingRe-examine which layer owns the interface; invert the dependency
DTO reuse across boundariesSame data structure used in API response, use case input, and domain logicCreate separate DTOs per boundary; map between them
Missing Anti-Corruption LayerExternal system's model pollutes the domain vocabularyAdd a translation layer in infrastructure
违规行为症状修复方案
领域层依赖框架领域类导入HTTP、ORM或框架注解将基础设施相关代码移到外层;领域层使用普通对象
业务逻辑存在于控制器中控制器包含条件逻辑、验证规则、计算将逻辑提取到领域对象或应用服务中
贫血领域模型实体仅为数据容器,只有getter/setter;所有逻辑都在服务中将行为推送到实体和值对象中;在聚合内部强制执行不变量
应用层包含业务规则应用层有复杂的条件判断、领域计算将规则移到领域服务或实体方法中
基础设施层代码渗透到内层领域层引用数据库列名、API响应结构在基础设施层引入映射;保持领域模型纯净
上帝聚合一个聚合处理过多关注点,无限制膨胀拆分为更小的聚合;使用领域事件进行跨聚合通信
上下文之间共享数据库多个限界上下文读写相同的表为每个上下文分离存储;通过事件或API进行集成
层之间存在循环依赖内层导入外层代码;编译错误或运行时耦合重新审视哪个层拥有接口;反转依赖
跨边界复用DTO同一数据结构用于API响应、用例输入和领域逻辑为每个边界创建独立的DTO;在它们之间进行映射
缺少防腐层外部系统的模型污染领域词汇在基础设施层添加转换层

Testing Strategy

测试策略

The layered architecture enables a clear testing strategy where each layer has a distinct testing approach:
LayerTest TypeDependenciesWhat to Verify
DomainUnit testsNone — pure logicBusiness rules, invariants, value object equality, aggregate consistency
ApplicationUnit tests with mocked portsDomain layer + mocked infrastructure portsUse case orchestration, correct domain method calls, transaction boundaries
InfrastructureIntegration testsReal external systems (database, API, queue)Correct mapping, query behavior, adapter fidelity
PresentationFunctional / acceptance testsFull stack or mocked application layerRequest parsing, response formatting, error handling, status codes
Key principle: The majority of tests should be fast domain unit tests. Infrastructure integration tests are fewer but ensure real systems behave as expected. End-to-end tests cover critical paths but should be minimal.
分层架构支持清晰的测试策略,每个层都有独特的测试方法:
分层测试类型依赖验证内容
领域层单元测试无——纯逻辑业务规则、不变量、值对象相等性、聚合一致性
应用层带模拟端口的单元测试领域层 + 模拟的基础设施端口用例编排、领域方法调用正确性、事务边界
基础设施层集成测试真实外部系统(数据库、API、队列)映射正确性、查询行为、适配器保真度
表示层功能/验收测试全栈或模拟的应用层请求解析、响应格式化、错误处理、状态码
核心原则: 大多数测试应为快速的领域层单元测试。基础设施集成测试数量较少,但可确保真实系统行为符合预期。端到端测试仅覆盖关键路径,应尽量精简。

Quality Checklist

质量检查清单

Use this checklist to evaluate whether the architecture is properly structured:
Dependency Rule
  • Domain layer has zero imports from application, infrastructure, or presentation layers
  • Application layer has zero imports from infrastructure or presentation layers
  • All cross-boundary communication uses interfaces/ports defined by the inner layer
Domain Layer
  • Entities enforce their own invariants — invalid state is unrepresentable
  • Value Objects are immutable
  • Aggregates are the unit of consistency — one aggregate per transaction
  • Repository interfaces are defined in the domain, not in infrastructure
  • Domain events capture meaningful state changes
Application Layer
  • Application services orchestrate but contain no business logic
  • Use cases are explicit — each has a clear input, output, and single responsibility
  • DTOs are used to cross boundaries — domain objects do not leak to outer layers
Infrastructure Layer
  • Repository implementations live here, not in the domain
  • External service integrations use adapters that implement ports
  • Framework-specific code is isolated and replaceable
Presentation Layer
  • Controllers are thin — they delegate to application services immediately
  • No business logic in controllers, views, or CLI commands
  • Input validation (format, type) happens here; business validation happens in the domain
Bounded Contexts
  • Each context has its own ubiquitous language
  • Contexts communicate through well-defined interfaces (events, APIs), not shared databases
  • Anti-Corruption Layers protect against external model pollution
Testing
  • Domain logic is testable without any infrastructure
  • Application services are testable with mocked ports
  • Infrastructure adapters have integration tests against real dependencies
  • No test requires a running framework to verify business rules
使用此清单评估架构是否结构合理:
依赖规则
  • 领域层无任何来自应用层、基础设施层或表示层的导入
  • 应用层无任何来自基础设施层或表示层的导入
  • 所有跨边界通信使用内层定义的接口/端口
领域层
  • 实体强制执行自身的不变量——无法表示无效状态
  • 值对象是不可变的
  • 聚合是一致性单元——每个事务对应一个聚合
  • 仓储接口定义在领域层,而非基础设施层
  • 领域事件捕获有意义的状态变更
应用层
  • 应用服务仅负责编排,不包含业务逻辑
  • 用例明确——每个用例有清晰的输入、输出和单一职责
  • 使用DTO跨边界——领域对象不会渗透到外层
基础设施层
  • 仓储实现位于此处,而非领域层
  • 外部服务集成使用实现端口的适配器
  • 框架特定代码被隔离且可替换
表示层
  • 控制器很薄——立即委托给应用服务
  • 控制器、视图或CLI命令中无业务逻辑
  • 输入验证(格式、类型)在此处进行;业务验证在领域层进行
限界上下文
  • 每个上下文有自己的通用语言
  • 上下文通过定义良好的接口(事件、API)通信,而非共享数据库
  • 防腐层防止外部模型污染
测试
  • 领域逻辑无需任何基础设施即可测试
  • 应用服务可通过模拟端口进行测试
  • 基础设施适配器针对真实依赖有集成测试
  • 验证业务规则无需运行框架

Best Practices

最佳实践

  • Start with the domain — model business rules first, add infrastructure later
  • Name things after domain concepts, not technical patterns (avoid SomethingManager, SomethingHelper)
  • Keep aggregates small — large aggregates are a design smell indicating misplaced boundaries
  • Prefer composition over inheritance across all layers
  • Use domain events to communicate between aggregates rather than direct coupling
  • Make invalid state unrepresentable — push validation into value objects and entity constructors
  • Keep the application layer thin — if it contains business logic, the domain layer is anemic
  • Refactor toward deeper insight — the first domain model is rarely the best one; evolve it as understanding grows
  • Apply architecture testing to enforce dependency rules automatically
  • Introduce Clean Architecture incrementally — start with the most complex subdomain and expand
  • 从领域开始——先建模业务规则,再添加基础设施
  • 以领域概念命名,而非技术模式(避免使用SomethingManager、SomethingHelper这类名称)
  • 保持聚合小型化——大型聚合是边界设计不当的信号
  • 在所有层优先使用组合而非继承
  • 使用领域事件进行聚合间通信,而非直接耦合
  • 使无效状态无法表示——将验证推送到值对象和实体构造函数中
  • 保持应用层轻薄——如果它包含业务逻辑,说明领域层是贫血的
  • 重构以获得更深入的理解——第一个领域模型很少是最佳的;随着理解加深不断演进
  • 应用架构测试自动强制执行依赖规则
  • 逐步引入Clean Architecture——从最复杂的子领域开始,逐步扩展