writing-server-code
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRepository Orientation
仓库结构说明
The repo contains:
server- — REST API endpoints
src/Api - — Authentication/identity service
src/Identity - — Business logic, commands, queries, services
src/Core - — Data access, repositories
src/Infrastructure
server- — REST API端点
src/Api - — 身份验证/身份服务
src/Identity - — 业务逻辑、命令、查询、服务
src/Core - — 数据访问、仓储
src/Infrastructure
Architectural Rationale
架构设计原则
Command Query Separation (CQS)
命令查询分离(CQS)
New features should use the CQS pattern — discrete action classes instead of large entity-focused services. See ADR-0008.
Why CQS matters at Bitwarden: The codebase historically grew around entity-focused services (e.g., ) that accumulated hundreds of methods. CQS breaks these into single-responsibility classes (, ), making code easier to test, reason about, and modify without unintended side effects.
CipherServiceCreateCipherCommandGetOrganizationApiKeyQueryCommands = write operations. Change state, may return result. Named after the action: .
RotateOrganizationApiKeyCommandQueries = read operations. Return data, never change state.
When NOT to use CQS: When modifying existing service-based code, follow the patterns already in the file. Don't refactor to CQS unless explicitly asked. If asked to refactor, apply the pattern only to the scope requested.
新功能应采用CQS模式——使用独立的操作类替代大型的实体聚焦服务。详情请见ADR-0008。
Bitwarden采用CQS的原因: 代码库历史上围绕实体聚焦服务(如)发展,这类服务积累了数百个方法。CQS将其拆分为单一职责类(如、),使代码更易于测试、理解和修改,且不会产生意外副作用。
CipherServiceCreateCipherCommandGetOrganizationApiKeyQuery命令 = 写入操作。会更改状态,可能返回结果。命名以动作为准:。
RotateOrganizationApiKeyCommand查询 = 读取操作。返回数据,绝不会更改状态。
无需使用CQS的场景: 修改现有基于服务的代码时,遵循文件中已有的模式。除非明确要求,否则不要重构为CQS模式。若要求重构,仅在指定范围内应用该模式。
Caching
缓存
When caching is needed, follow the conventions in CACHING.md. Use instead of .
IFusionCacheIDistributedCacheDon't implement caching unless requested. If a user describes a performance problem where caching might help, suggest it — but don't implement without confirmation.
未经请求不要实现缓存。 如果用户描述了可能通过缓存解决的性能问题,可以提出建议,但不要未经确认就实现。
GUID Generation
GUID生成
Always use for entity IDs — never . Sequential COMBs prevent SQL Server index fragmentation that random GUIDs cause on clustered indexes, which is critical for Bitwarden's database performance at scale.
CoreHelpers.GenerateComb()Guid.NewGuid()实体ID请始终使用——绝不要使用。顺序COMB可避免随机GUID在聚集索引上导致的SQL Server索引碎片,这对Bitwarden大规模数据库的性能至关重要。
CoreHelpers.GenerateComb()Guid.NewGuid()Critical Rules
核心规则
These are the most frequently violated conventions. Claude cannot fetch the linked docs at runtime, so these are inlined here:
- Use for DI registration (
TryAdd*,TryAddScoped) — prevents duplicate registrations when multiple modules register the same serviceTryAddTransient - File-scoped namespaces — not
namespace Bit.Core.Vault;namespace Bit.Core.Vault { ... } - Nullable reference types are enabled (ADR-0024) — use (null-forgiving) when you know a value isn't null; use
!modifier for properties that must be set during constructionrequired - suffix on all async methods —
Async, notCreateAsync, when the method returnsCreateTask - Controller actions return — not
ActionResult<T>or bareIActionResultT - Testing with xUnit — use (not
[Theory, BitAutoData]),[AutoData]for automatic SUT wiring, andSutProvider<T>from NSubstitute for mockingSubstitute.For<T>()
这些是最常被违反的规范。由于Claude无法在运行时获取链接文档,因此将其内联在此:
- 依赖注入注册使用(
TryAdd*、TryAddScoped)——防止多个模块注册同一服务时出现重复注册TryAddTransient - 文件级命名空间 ——使用而非
namespace Bit.Core.Vault;namespace Bit.Core.Vault { ... } - 启用可空引用类型(ADR-0024)——当确定值不为null时使用(空原谅运算符);对构造时必须设置的属性使用
!修饰符required - 所有异步方法添加后缀 ——方法返回
Async时使用Task而非CreateAsyncCreate - 控制器操作返回——不要返回
ActionResult<T>或裸IActionResultT - 使用xUnit进行测试 ——使用(而非
[Theory, BitAutoData]),使用[AutoData]自动注入被测系统(SUT),使用NSubstitute的SutProvider<T>进行模拟Substitute.For<T>()
Examples
示例
GUID generation
GUID生成
csharp
// CORRECT — sequential COMB prevents index fragmentation
var id = CoreHelpers.GenerateComb();
// WRONG — random GUIDs fragment clustered indexes
var id = Guid.NewGuid();csharp
// CORRECT — sequential COMB prevents index fragmentation
var id = CoreHelpers.GenerateComb();
// WRONG — random GUIDs fragment clustered indexes
var id = Guid.NewGuid();DI registration
依赖注入注册
csharp
// CORRECT — idempotent, won't duplicate
services.TryAddScoped<ICipherService, CipherService>();
// WRONG — silently duplicates registration, last-wins causes subtle bugs
services.AddScoped<ICipherService, CipherService>();csharp
// CORRECT — idempotent, won't duplicate
services.TryAddScoped<ICipherService, CipherService>();
// WRONG — silently duplicates registration, last-wins causes subtle bugs
services.AddScoped<ICipherService, CipherService>();Namespace style
命名空间风格
csharp
// CORRECT — file-scoped
namespace Bit.Core.Vault.Commands;
// WRONG — block-scoped
namespace Bit.Core.Vault.Commands
{
// ...
}csharp
// CORRECT — file-scoped
namespace Bit.Core.Vault.Commands;
// WRONG — block-scoped
namespace Bit.Core.Vault.Commands
{
// ...
}