ZITADEL internal/ 后端架构与 AI Agent 开发规范:分层边界、Source of Truth 与 Nx 验证链路 ZITADEL internal/ 后端架构与 AI Agent 开发规范分层边界、Source of Truth 与 Nx 验证链路【免费下载链接】zitadelZITADEL - Identity infrastructure, simplified for you.项目地址: https://gitcode.com/GitHub_Trending/zi/zitadel本文以 ZITADEL 仓库中面向 AI Agent 的 internal/AGENTS.md 为纲系统解读internal/后端代码的目录职责、三条事实来源Source of Truth原则、命令/查询/仓库三层边界规则并结合apps/api的 Nx 工程配置还原lint、test-unit、test-integration三个验证目标的真实执行链路。读完后你不仅能按该规范安全地修改 ZITADEL 后端还能理解关系表即记录系统system of record 事件写保留这一核心架构模式在源码中的落地方式。1. internal/ 是什么ZITADEL 后端域逻辑的承载层internal/AGENTS.md 开宗明义地给出上下文定义internal/contains core backend domain logic for ZITADEL: commands, queries, repositories, eventstore integration, API service layers, and supporting infrastructure.对照仓库实际目录结构这句话完全对得上。internal/下按职责切分为若干包internal/command/CQRS 中的 Command 侧。核心入口是 internal/command/command.go 中定义的Commands结构体——它持有eventstore *eventstore.Eventstore、权限检查checkPermission domain.PermissionCheck、加密算法、通知发送器等依赖所有业务写入实例、组织、用户、项目、会话等都在这里编排。目录内按实体命名成对出现xxx.go命令实现与xxx_model.go事件模型例如org.goorg_model.go、user_auth.gouser_grant_model.go并配有xxx_test.go单元测试internal/query/CQRS 中的 Query 侧包含各类查询对象user.go、org.go、project_grant.go、introspection.go等以及projection/投影实现负责把事件流折叠reduce成可查询的当前状态internal/eventstore/事件存储内核。从顶层定义可见其构成event.go/event_base.go事件定义、aggregate.go聚合与乐观并发、push.go事件写入、query.go事件读取、read_model.go/write_model.go读写模型抽象、lock.go/unique_constraints.go锁与唯一约束、queue.go队列驱动internal/repository/仓储层约 200 个 Go 文件是查询侧读取关系表与事件的落地实现internal/api/传输适配层按协议划分——grpc/由 proto 生成的 gRPC/connectRPC 存根约 530 个文件、oidc/、saml/、scim/、http/、ui/等支撑性基础设施internal/crypto/、internal/domain/、internal/database/、internal/notification/、internal/i18n/ 等。这种command/query 分离 eventstore 内核 薄 API 适配的组织方式正是下节边界规则的存在依据。2. Source of Truth动手前必须核对的三份事实来源internal/AGENTS.md 的 Source of Truth 一节列出了三条强制性的事实来源逐条拆解如下。2.1 Go 工具链先读根目录 go.mod规范第一条要求Inspection of rootgo.modbefore Go work。仓库实际内容印证了这是一个对工具链敏感的项目// go.mod仓库根目录 module github.com/zitadel/zitadel go 1.25.0 toolchain go1.25.11关键信息有三点模块路径为github.com/zitadel/zitadel所有import github.com/zitadel/zitadel/internal/...都基于此语言版本为 Go 1.25.0且锁定了go1.25.11工具链——本地若使用低于此版本的 Gogo build/go test可能自动拉取或失败依赖里可见connectrpc.com/connect v1.19.2、connectrpc.com/otelconnect说明 API 传输协议基于 connectRPC见 4.3 节。proto/AGENTS.md 中如果生成或后续修复触碰了 Go 代码运行 Go 工具前先检查根go.mod的表述与这条规则相互呼应。2.2 架构模式关系数据是记录系统事件写保留为历史/审计第二条是整个后端架构最核心的一句Relational data is the system of record; keep existing event writes that provide history/audit trails.即关系表projection 生成的当前状态表是权威数据源但事件写入不能删因为它们承载历史与审计能力。在源码中可以直接验证这条原则的落地机制写入路径internal/command通过eventstore.Eventstore.Pushinternal/eventstore/push.go把领域事件写入事件流折叠路径internal/query/projection/中的投影处理器消费这些事件。例如 internal/query/projection/administrator_relational.go 中定义了reduceInstanceAdminAdded、reduceOrganizationAdminChanged、reduceProjectGrantAdminRemoved等一系列reduce方法每个方法把一条管理员工事件翻译成handler.Statement对关系表的 INSERT/UPDATE/DELETE 语句从而维持关系表与事件流一致功能开关internal/feature/feature.go 中存在EnableRelationalTables bool特性位键名含enable_relational_tables说明关系表投影是受 feature flag 控制的渐进式架构迁移——从源码结构看这正是从纯事件溯源走向关系表即记录系统过渡期的证据。由此得到的实操含义修改internal/时若发现既有代码在做事件写入不要因为关系表已经是权威就顺手删掉删除事件写会破坏历史/审计能力违反该架构模式。2.3 API 契约以 API_DESIGN.md 与 proto/AGENTS.md 为准第三条指出 API 面向的 schema 决策应遵循 API_DESIGN.md 与 proto/AGENTS.md。前者确立了几个对后端开发有直接影响的原则API first所有功能必须能通过 API 访问UI 只是 API 的消费者之一Protobuf connectRPC自 V2 API 起以 connectRPC 为主传输协议同时兼容 gRPC 与 HTTP/1.1面向资源设计V2 API 围绕资源Organization、User、Project 等设计每个资源有唯一标识符和属性集合整个生命周期可由 API 管理版本策略服务用主版本号独立版本化主版本内保证向后兼容破坏性变更必须开新主版本新建服务应从 v2 起步v1 保留给旧的 context 式 API弃用规范废弃方法必须设置 OpenAPI 的deprecated true选项并可在 rpc 定义上方以 proto 注释给出替代方法链接与迁移指引。proto/AGENTS.md 则补充了 proto 变更的工程侧要求变更后必须验证下游消费方zitadel/client、zitadel/api、zitadel/docs并给出三个已验证的 Nx 目标pnpm nx run zitadel/proto:generate # 生成 TS Proto 包 pnpm nx run zitadel/api:generate # 生成 API 资产/存根 pnpm nx run zitadel/docs:generate # 生成文档工件也就是说internal/api/grpc/下的大量生成文件不应手工编辑契约变更的正确路径是改proto/下定义后走生成流程。3. 边界规则业务逻辑该放在哪一层Boundary Rules 一节给出了三条边界约束它们直接对应internal/的分层业务行为优先实现在 command/query 层与 repository 包而不是传输处理器里。落地对照传输适配层 internal/api/grpc/约 530 个文件绝大部分为 proto 生成存根与internal/api/http/只负责协议解包、认证上下文提取与错误映射真正的业务编排在 internal/command/如org.go、user.go与 internal/query/ 中完成。若在 handler 里写业务分支就绕过了Commands中注入的权限检查、加密、通知等横切依赖不要用临时的直接持久化绕过既有的 event/repository 流程。这条与 2.2 节的关系数据是记录系统配合理解新增读模型要走internal/query/projection/的投影机制事件 →handler.Statement→ 关系表新增写路径要走internal/command→ eventstore push而不是在某个包内私开一条 SQL 直写API/服务适配器保持薄可复用的域行为放进 internal 域包。从Commands结构体的依赖注入方式checkPermission、newHashedSecret、idGenerator、eventstore等字段见 internal/command/command.go可以推断域行为被刻意与传输层解耦便于单测与复用。4. 验证工作流三个 Nx 目标背后的真实执行链internal/AGENTS.md 的 Validation Workflow 要求用 API 项目目标来验证后端改动pnpm nx run zitadel/api:lint pnpm nx run zitadel/api:test-unit pnpm nx run zitadel/api:test-integration这三个目标定义在 apps/api/project.json 中逐条展开其实际行为比规范本身更有实战价值。4.1 zitadel/api:lint —— golangci-lint 全量检查// apps/api/project.json lint: { description: Lints the Go code with golangci-lint using the configuration in .golangci.yaml, dependsOn: [lint-install, generate-stubs, generate-assets], command: PATH\${PWD}/.artifacts/bin/$(go env GOOS)/$(go env GOARCH):$PATH\ golangci-lint run --timeout 15m --config ./.golangci.yaml --verbose, cache: true }要点它先依赖generate-stubs与generate-assets即先执行 proto/静态资产生成保证生成代码参与检查再用.golangci.yaml配置运行 golangci-lint超时上限 15 分钟且带 Nx 缓存。lint 检查的输入是sourcescmd/**/*.go、internal/**/*.go、proto/**/*.go、pkg/**/*.go、main.go等因此改完internal/代码后必须过这一关。4.2 zitadel/api:test-unit —— 带竞态检测的单元测试// apps/api/project.json test-unit: { description: Runs the unit tests with coverage, dependsOn: [generate], command: go test -race -coverprofileprofile.api.test-unit.cov -coverpkg./internal/...,./backend/... ./... }要点先依赖generateproto 代码生成避免改了 proto 没生成就测造成的假阴性-race开启竞态检测-coverpkg./internal/...,./backend/...说明覆盖率统计明确覆盖internal/全部包——这正是你改的就是被量化的部分的体现产物profile.api.test-unit.cov声明为 Nx 输出命中缓存时可跳过重复执行。4.3 zitadel/api:test-integration —— 端到端集成测试链路集成测试是三者中最重的一个apps/api/project.json 将其拆成一条数据库 缓存 → 构建 → 起服务 → 跑测试的依赖链test-integration-build以-tags integration -race -cover编译出独立测试二进制zitadel.testtest-integration-run-db通过nx run zitadel/devcontainer:compose up ... db-api-integration cache-api-integration拉起集成测试专用数据库与缓存容器continuous 任务长期运行test-integration-run-api以test-integration-api配置启动 API 服务环境变量含GOCOVERDIR覆盖率数据目录与GORACE竞态日志路径配置来源为 apps/api/test-integration-api.yamltest-integration依次执行——wait-on ... ${ZITADEL_API_URL}/debug/ready轮询/debug/ready就绪端点超时 30 分钟go test -race -count 1 -tags integration -timeout 60m -parallel 1 $(go list -tags integration ./... | grep -e integration_test)串行、禁用测试缓存地运行所有integration_test包注释说明原因是测试针对进程外的 API 运行go tool covdata textfmt ...把覆盖率数据转成profile.api.test-integration.cov。这意味着集成测试不是纯 Go 进程内测试而是真实的PostgreSQL 缓存 独立 API 进程组合涉及 connectRPC 端点、事件持久化、投影折叠的改动必须能在这条链路上跑通。测试入口目录为 internal/integration/其中2 *.pem与测试配置佐证了 TLS 与外部服务依赖。4.4 验证顺序建议结合三条 Source of Truth 与边界规则一次典型的internal/改动可按如下顺序验证# 0. 若改了 proto 契约先生成 pnpm nx run zitadel/api:generate # 1. 静态检查 pnpm nx run zitadel/api:lint # 2. 单元测试竞态 覆盖率 pnpm nx run zitadel/api:test-unit # 3. 集成测试需要 Docker自动拉起 db/cache 容器与 API 进程 pnpm nx run zitadel/api:test-integration5. 小结把规范读成工程约束internal/AGENTS.md 篇幅不长但它把 ZITADEL 后端的工程纪律压缩成了四条可执行约束规范条目工程含义仓库佐证先读go.modGo 1.25.0 / toolchain go1.25.11模块路径决定 import 前缀go.mod关系数据是记录系统保留事件写读路径走 projection 折叠的关系表写路径必须继续走 eventstore pushinternal/query/projection/administrator_relational.go、internal/eventstore/push.goAPI 契约遵循 API_DESIGN.md / proto/AGENTS.mdAPI first、面向资源、connectRPC、主版本向后兼容API_DESIGN.md、proto/AGENTS.md业务逻辑放 command/query/repohandler 保持薄传输层只做协议适配internal/command/command.go、internal/api/grpc/用三个 Nx 目标验证lint → 带 race 的单元测试 → 进程外集成测试链apps/api/project.json遵循这套约束你的改动将同时满足架构一致性不破坏事件/投影流与可验证性三个目标全绿两条底线这正是该 Agent 规范想要保障的。【免费下载链接】zitadelZITADEL - Identity infrastructure, simplified for you.项目地址: https://gitcode.com/GitHub_Trending/zi/zitadel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考