modular-design-principles

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Modular Design Principles

模块化设计原则

Use this skill when reasoning about structure and boundaries in any codebase. It intentionally avoids framework names, folder conventions, and tooling — map principles to your stack locally.
当你需要思考任何代码库中的结构与边界时,可使用本技能。它刻意避开了框架名称、文件夹约定与工具——你可在本地将这些原则映射到自己的技术栈中。

What to load

需加载的内容

TaskWhere
Principles table + violations + workflows (this file)
SKILL.md
Per-principle definition, agent rules, abstract examples
references/principles.md

任务位置
原则表 + 违规案例 + 工作流(本文件)
SKILL.md
各原则的定义、Agent规则、抽象示例
references/principles.md

Layered mental model

分层心智模型

  • Composition roots (applications, hosts, runners): wire modules together; keep orchestration thin.
  • Modules / bounded contexts: cohesive units of behavior and data ownership; each should be understandable and testable on its own.
  • Shared kernels (use sparingly): only stable, truly cross-cutting concepts; resist turning them into a grab-bag of “everything everyone needs.”
How you physically lay this out (mono repo, multi repo, packages, libraries) is a delivery choice, not the definition of modularity. The principles below still apply.

  • Composition roots(应用、宿主、运行器):负责将模块连接在一起;保持编排逻辑简洁。
  • Modules / bounded contexts:具备内聚性的行为与数据所有权单元;每个单元应可独立理解和测试。
  • Shared kernels(谨慎使用):仅包含稳定、真正跨领域的概念;避免将其变成“所有人所需一切”的大杂烩。
你如何进行物理布局(mono repo、multi repo、包、库)是一种交付选择,而非模块化的定义。以下原则仍然适用。

The ten principles

十大原则

#PrincipleIntent
1Well-defined boundariesA small, stable public surface; everything else is internal. Consumers depend on contracts, not internals.
2ComposabilityModules can be used alone or combined without special knowledge of each other’s internals.
3IndependenceNo hidden shared mutable state across boundaries; each module should be testable in isolation (with fakes or test doubles at the edges).
4Individual scaleResources (compute, storage, rate limits, batch size) can be tuned per module where it matters, without rewriting others.
5Explicit communicationCross-module interaction uses documented contracts (APIs, events, messages, shared types) — not incidental coupling.
6ReplaceabilityDependencies on other modules are expressed through interfaces or protocols so implementations can change.
7Deployment independenceModules do not assume they share a process, host, or release cadence unless that is an explicit architectural decision.
8State isolationEach module owns its persistent state and naming; no silent sharing of the same logical data store or ambiguous global names across boundaries.
9ObservabilityEach module can be diagnosed on its own: logs, metrics, traces, health — attributable to the unit that emitted them.
10Fail independenceFailures are contained (timeouts, bulkheads, circuit breaking, idempotency) so one module’s outage does not blindly cascade.
Principle 8 is often the hardest: ambiguous ownership of data or names is a frequent source of “works until it doesn’t” integration bugs.
For depth (rules for agents + abstract examples per principle), load
references/principles.md
.

序号原则意图
1清晰定义的边界拥有小而稳定的public surface;其余内容均为内部实现。消费者依赖contracts,而非内部细节。
2可组合性模块可单独使用或组合使用,无需了解彼此的内部细节。
3独立性边界间无隐藏的共享可变状态;每个模块应可独立测试(在边界处使用模拟对象或测试替身)。
4独立伸缩性资源(计算、存储、速率限制、批处理大小)可针对单个模块进行调优,无需修改其他模块。
5显式通信跨模块交互使用已文档化的contracts(API、事件、消息、共享类型)——而非偶然耦合。
6可替换性对其他模块的依赖通过接口或协议表达,以便实现可变更。
7部署独立性模块默认不假设共享进程、宿主或发布节奏,除非这是明确的架构决策。
8状态隔离每个模块拥有其持久化状态与命名;边界间无静默共享的同一逻辑数据存储或模糊的全局名称。
9可观测性每个模块可独立诊断:日志、指标、链路追踪、健康状态——均可追溯至产生它们的单元。
10故障独立性故障可被隔离(超时、舱壁、熔断、幂等性),避免一个模块的故障盲目扩散至其他模块。
原则8通常是最难落地的:数据或命名的模糊所有权是“平时正常,出问题就崩溃”这类集成bug的常见根源。
如需深入了解(Agent规则 + 各原则的抽象示例),请加载
references/principles.md

Typical violations (stated abstractly)

典型违规案例(抽象表述)

  1. Colliding concepts — the same name or schema for different things in different modules, or duplicate “global” definitions that diverge over time.
  2. Reach-through persistence — one module reading or writing another module’s tables, buckets, or documents without going through an agreed contract.
  3. Centralized data ownership — a single persistence layer that registers and exposes all stores for all modules, encouraging hidden coupling.
  4. Logic at the edge — business rules in transport adapters (HTTP handlers, UI, CLI) instead of domain/application code.
  5. Edge talking to storage directly — adapters depending on low-level persistence APIs instead of use cases or application services.
  6. Unscoped transactions — writes that span boundaries without clear transaction ownership and failure semantics.
  7. Leaky exports — repositories, internal services, or implementation types exposed as the module’s public API.
  8. Facades that aren’t thin — “public” entry points that embed querying, mapping, or policy instead of delegating to the right layer inside the module.

  1. 概念冲突——不同模块中同一名称或 schema 对应不同事物,或重复的“全局”定义随时间产生分歧。
  2. 穿透式持久化——一个模块绕过约定的contract,直接读写另一个模块的表、存储桶或文档。
  3. 集中式数据所有权——单一持久化层注册并暴露所有模块的所有存储,从而助长隐藏耦合。
  4. 边缘层逻辑——业务规则位于传输适配器(HTTP处理器、UI、CLI)中,而非领域/应用代码里。
  5. 边缘层直接访问存储——适配器依赖底层持久化API,而非用例或应用服务。
  6. 无范围事务——跨边界的写入操作未明确事务所有权与故障语义。
  7. 泄露式导出——将仓库、内部服务或实现类型作为模块的公开API暴露。
  8. 非轻量外观层——“公开”入口点嵌入查询、映射或策略逻辑,而非委托给模块内的正确层级。

Creating a bounded context (workflow)

创建bounded context(工作流)

Use when introducing a new cohesive area of the system (greenfield module or extracted domain).
  1. Scope and language — Name the context; list core nouns/verbs (ubiquitous language). Reject vague names that collide with other contexts.
  2. Responsibilities — What decisions happen only here? What is explicitly out of scope?
  3. State ownership — Which facts are authoritative in this context? Where are they stored conceptually (even if storage tech is undecided)?
  4. Public contract — Operations and/or events other contexts may use. Version or evolve this contract intentionally.
  5. Integrations — For each neighbor: sync call, async message, shared read model, or batch sync? Document consistency (immediate, eventual) and failure behavior.
  6. Invariants and lifecycles — What must always be true inside this boundary? What starts/completes a lifecycle?
  7. Isolation check — Can you test core behavior without spinning up unrelated contexts (fakes at ports)?
  8. Observability — How will you trace a request or job through this context with clear identifiers?
Cross-module interaction (while designing): prefer the minimal contract; define timeouts, retries, idempotency for async; avoid “temporary” direct store access as a shortcut.

适用于引入系统中一个全新的内聚领域(全新模块或提取的领域)。
  1. 范围与语言——为上下文命名;列出核心名词/动词(通用语言)。拒绝与其他上下文冲突的模糊名称。
  2. 职责——哪些决策在此处做出?哪些内容明确不属于本范围?
  3. 状态所有权——哪些事实在本上下文中是权威的?它们在概念上存储于何处(即使存储技术尚未确定)?
  4. 公开契约——其他上下文可使用的操作和/或事件。需有意地对该契约进行版本管理或演进。
  5. 集成方式——针对每个相邻上下文:同步调用、异步消息、共享读模型还是批量同步?记录一致性(即时、最终)与故障行为。
  6. 不变量与生命周期——边界内必须始终保持哪些规则?生命周期的开始与结束条件是什么?
  7. 隔离性检查——是否无需启动无关上下文(在端口处使用模拟对象)即可测试核心行为?
  8. 可观测性——如何通过清晰的标识符追踪请求或作业在上下文中的流转?
跨模块交互(设计时):优先选择最小化契约;为异步操作定义超时重试幂等性;避免将“临时”直接访问存储作为捷径。

When to split or merge

何时拆分或合并

Default: fewer boundaries until real pain appears — “flat is often better” than premature fragmentation. Splitting adds coordination, versioning, and operational cost.
默认原则: 在出现实际痛点前,尽量减少边界——“扁平化通常更优”,避免过早拆分。拆分会增加协调、版本管理与运维成本。

Six-criteria test (favor split when several are true)

六标准测试(多个标准符合时倾向拆分)

#CriterionQuestion
1LanguageDo the sub-areas use different vocabulary or conflicting definitions of the same word?
2Rate of changeDo parts change on different cadences or for unrelated reasons (most edits touch one side)?
3Scale / SLODo parts need different throughput, latency, or availability targets?
4ConsistencyDo they need different transaction boundaries (cannot share one atomic write model cleanly)?
5OwnershipWould different teams or clear ownership lines reduce conflict and review churn?
6Pain signalIs there observable integration pain: ripple effects, fear of change, unclear who owns a bug?
Cohesion / coupling (qualitative). Favor high cohesion inside a module and low, explicit coupling between modules. If the only motivation is “files got big” or “folder aesthetics,” merge or wait.
序号标准问题
1语言差异子领域是否使用不同的词汇,或对同一词汇有冲突的定义?
2变更频率各部分是否按不同节奏变更,或变更原因无关(大多数修改仅涉及一侧)?
3规模/SLO各部分是否需要不同的吞吐量、延迟或可用性目标?
4一致性要求它们是否需要不同的事务边界(无法清晰地共享同一原子写入模型)?
5所有权归属不同团队或明确的所有权划分是否能减少冲突与评审内耗?
6痛点信号是否存在可观测的集成痛点:连锁反应、对变更的恐惧、bug归属不明确?
内聚性/耦合性(定性判断)。倾向于模块内部高内聚,模块间低耦合且耦合显式。如果唯一的动机是“文件变大了”或“文件夹美观性”,请合并或等待

When to merge or not split yet

何时合并或暂不拆分

  • Boundaries are artificial (same language, same lifecycle, constant cross-calls).
  • Splitting would duplicate logic or data without a clear single writer rule.
  • Team is not ready to own contracts, versioning, and ops for extra units.
  • 边界是人为划分的(相同语言、相同生命周期、频繁跨模块调用)。
  • 拆分将重复逻辑或数据,且无明确的单一写入者规则。
  • 团队尚未准备好为额外单元维护契约、版本管理与运维工作。

Decision prompts (short)

决策提示(精简版)

  • Would separation reduce accidental coupling more than it increases coordination cost?
  • Is there a natural ubiquitous language boundary, or only a technical seam?

  • 拆分是否能减少意外耦合,且收益大于增加的协调成本?
  • 是否存在自然的通用语言边界,还是仅存在技术层面的分割?

Sub-units inside a bounded context

Bounded context内的子单元

Sometimes one outer boundary is right, but inside it there are named sub-areas (subdomains, feature areas). Principles still apply within the context.
Ownership
  • Each sub-unit should own its slice of model and persistence concerns where possible — avoid one mega registration layer that wires every store and repository for every sub-unit in one place (encourages reach-through and hidden coupling).
Cross-sub-unit access
  • Prefer internal application APIs or thin internal facades (same context, explicit surface) over peers importing each other’s storage types directly.
  • For async flows, prefer enriched payloads so handlers do not chat across sub-units for data that could travel with the event/command.
Shared kernel inside the context
  • Small, stable shared types or enums can live in a narrow shared area — but resist a growing “utils” dump that becomes the real coupling point.
Anti-pattern: A single “persistence” or “data” sub-module that becomes the only place that knows about all tables/documents for all sub-units, and everyone else reaches through it — same problems as cross-context reach-through, inside the boundary.

有时外部边界是合理的,但内部存在命名子领域(子域、功能区域)。原则在上下文内部同样适用。
所有权
  • 每个子单元应尽可能拥有其模型与持久化相关的切片——避免出现一个巨型注册层,将所有子单元的所有存储与仓库集中在一处(这会助长穿透式访问与隐藏耦合)。
跨子单元访问
  • 优先使用内部应用API轻量内部外观层(同一上下文,显式接口),而非直接导入彼此的存储类型。
  • 对于异步流程,优先使用富化 payload,以便处理程序无需在子单元间“通信”即可获取随事件/命令传输的数据。
上下文内的Shared kernels
  • 小型、稳定的共享类型或枚举可放在狭窄的共享区域——但要避免演变成不断膨胀的“工具集”,成为实际的耦合点。
反模式: 单个“持久化”或“数据”子模块成为唯一知晓所有子单元的所有表/文档的地方,其他所有单元都通过它访问——这与跨上下文的穿透式访问存在相同问题,只是发生在边界内部

Architecture compliance pass

架构合规性检查

Use for reviews or audits without assuming tooling. Treat items as signals, not proof — confirm with domain experts.
适用于不依赖工具的评审审计。将各项视为信号而非证据——需与领域专家确认。

Dependency and API signals

依赖与API信号

  • Inbound vs outbound: Dependencies should align with your chosen architecture (e.g. domain at the center, adapters outside). Inward leaks of infrastructure types into core logic are a smell.
  • Public surface: Can you list exported operations/events/types without including storage or internal services? If not, boundaries are leaky.
  • Neighbor imports: Types or clients from module A used in module B — are they only contract types, or persistence/implementation types?
  • 入站 vs 出站: 依赖应与你选择的架构对齐(例如领域位于中心,适配器在外部)。基础设施类型向内泄露到核心逻辑中是不良信号。
  • 公开接口: 你能否列出导出的操作/事件/类型,且不包含存储或内部服务?如果不能,说明边界存在泄露。
  • 邻接模块导入: 模块A的类型或客户端在模块B中使用——它们是否仅为契约类型,还是持久化/实现类型?

Persistence and data signals

持久化与数据信号

  • Reach-through: References to another context’s physical data (schema, collection, bucket name) outside an agreed contract.
  • Naming collisions: Same logical name for different things, or shared global IDs without a documented mapping rule.
  • Transaction ownership: Writes that span contexts without a clear saga, outbox, or single-owner rule and documented failure cases.
  • 穿透式访问: 绕过约定的contract,引用另一个上下文的物理数据(schema、集合、存储桶名称)。
  • 命名冲突: 同一逻辑名称对应不同事物,或共享全局ID但无文档化的映射规则。
  • 事务所有权: 跨上下文的写入操作未明确sagaoutbox单一所有者规则,也未记录故障场景。

Operational signals

运维信号

  • Blame: Incidents where “we don’t know which module owns this row/behavior” → ownership or observability gap.
  • Cascades: One dependency’s slowdown or failure takes down unrelated user journeys → missing timeouts, bulkheads, or degradation paths.
  • 责任模糊: 事件中出现“我们不知道哪个模块拥有此行/此行为”的情况 → 存在所有权或可观测性缺口。
  • 故障扩散: 某个依赖的变慢或故障导致无关的用户流程中断 → 缺少超时舱壁降级路径。

Severity heuristic (for reporting)

严重程度启发式(用于报告)

TierMeaning
P0Data corruption risk, security boundary violation, or cross-context persistence with no contract
P1Unclear ownership, leaky public API, missing failure semantics at boundaries
P2Observability gaps, composability smells, tech debt that increases future coupling
Maturity note: Scoring is qualitative unless the team defines numeric gates. Use trends: fewer P0/P1 over time, clearer contracts.

等级含义
P0存在数据损坏风险、安全边界违规,或无契约的跨上下文持久化访问
P1所有权不明确、公开API泄露、边界处缺失故障语义
P2可观测性缺口、可组合性不良信号、增加未来耦合的技术债务
成熟度说明: 评分是定性的,除非团队定义了数值阈值。关注趋势:随着时间推移,P0/P1问题减少,契约更清晰。

Quick checklist (before proposing structure)

快速检查清单(提出结构方案前)

  • Public API is minimal; internals are not exported casually.
  • Names and storage ownership are unambiguous per module.
  • No cross-module persistence shortcuts without an explicit contract.
  • Business rules sit behind a clear application/domain layer, not only in adapters.
  • Cross-module calls have explicit failure and timeout behavior.
  • Observability can answer “which module failed and why?” without spelunking.
  • If the context has sub-units: each has clear ownership; no monolithic “registers everything” persistence grab-bag.

  • 公开API保持最小化;内部实现不会随意导出。
  • 每个模块的命名与存储所有权明确无歧义。
  • 无未通过显式契约的跨模块持久化捷径。
  • 业务规则位于清晰的应用/领域层之后,而非仅存在于适配器中。
  • 跨模块调用具有明确的故障与超时行为。
  • 可观测性无需深入排查即可回答“哪个模块故障及原因?”。
  • 若上下文包含子单元:每个子单元所有权清晰;无“注册所有内容”的巨型持久化大杂烩。

Relationship to stack-specific skills

与特定技术栈技能的关系

When a project has concrete conventions (framework modules, DI, repository patterns, folder layout, codegen, CI checks), prefer those documents for how to implement. Use this skill for why boundaries exist and what good modular design optimizes for — so stack-specific advice stays aligned with the same principles.
当项目有具体约定(框架模块、依赖注入、仓库模式、文件夹布局、代码生成、CI检查)时,优先使用这些文档了解如何实现。使用技能理解边界存在的原因,以及良好的模块化设计优化的目标——确保特定技术栈的建议与同一原则保持一致。