isolating-product-facade-contracts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Isolating a product with facade and contracts

使用外观(Facade)与契约实现产品隔离

Use this skill to migrate an existing product to the isolated architecture used by Visual review. Keep migrations incremental, with narrow PRs that avoid broad breakage.
Prerequisite: the product must already live under
products/<name>/
. This skill does not cover moving code out of
posthog/
,
ee/
, or other shared directories — do that first.
使用此方法将现有产品迁移至Visual review所采用的隔离架构。 保持迁移的增量性,通过范围狭窄的PR避免大范围的代码损坏。
前置条件:产品必须已位于
products/<name>/
目录下。此方法不包含将代码从
posthog/
ee/
或其他共享目录中移出的操作——请先完成该步骤。

Core docs to load first

首先需要阅读的核心文档

Read these before changing code:
  1. products/architecture.md
  2. products/README.md
  3. docs/internal/monorepo-layout.md
  4. posthog/models/team/README.md (team extension model rule)
  5. docs/published/handbook/engineering/type-system.md (serializer/OpenAPI type flow)
  6. docs/published/handbook/engineering/ai/implementing-mcp-tools.md (schema quality and team isolation expectations)
Use Visual review as the concrete reference implementation:
  • products/visual_review/backend/facade/contracts.py
  • products/visual_review/backend/facade/api.py
  • products/visual_review/backend/presentation/views.py
  • products/visual_review/backend/presentation/serializers.py
  • products/visual_review/backend/logic.py
  • products/visual_review/backend/tests/test_api.py
  • products/visual_review/backend/tests/test_presentation.py
For detailed sequencing, load references/phased-migration-plan.md.
在修改代码前,请阅读以下文档:
  1. products/architecture.md
  2. products/README.md
  3. docs/internal/monorepo-layout.md
  4. posthog/models/team/README.md(团队扩展模型规则)
  5. docs/published/handbook/engineering/type-system.md(序列化器/OpenAPI类型流程)
  6. docs/published/handbook/engineering/ai/implementing-mcp-tools.md(Schema质量与团队隔离要求)
请以Visual review作为具体的参考实现:
  • products/visual_review/backend/facade/contracts.py
  • products/visual_review/backend/facade/api.py
  • products/visual_review/backend/presentation/views.py
  • products/visual_review/backend/presentation/serializers.py
  • products/visual_review/backend/logic.py
  • products/visual_review/backend/tests/test_api.py
  • products/visual_review/backend/tests/test_presentation.py
如需了解详细的迁移顺序,请查看references/phased-migration-plan.md

Guardrails

约束规则

  • Keep facades thin; put business rules in
    logic.py
    .
  • Never return ORM models across product boundaries.
  • Keep contracts pure (no Django/DRF imports).
  • Filter by
    team_id
    in querysets.
  • Do not add product-specific fields to
    Team
    ; use a Team Extension model.
  • Add request/response schema annotations on viewset endpoints (
    @validated_request
    or
    @extend_schema
    ).
  • Regenerate OpenAPI/types (
    hogli build:openapi
    ) when serializer/view changes affect API schema.
  • 保持外观层简洁;将业务规则放在
    logic.py
    中。
  • 切勿跨产品边界返回ORM模型。
  • 保持契约层纯净(不导入Django/DRF相关内容)。
  • 在查询集中按
    team_id
    进行过滤。
  • 不要向
    Team
    中添加产品特定字段;请使用团队扩展模型。
  • 在视图集端点上添加请求/响应Schema注解(
    @validated_request
    @extend_schema
    )。
  • 当序列化器/视图的更改影响API Schema时,重新生成OpenAPI/类型定义(
    hogli build:openapi
    )。

Required migration workflow

必填迁移流程

  1. Build an import map for the target product.
    • Find cross-product imports into target internals (
      models
      ,
      logic
      ,
      presentation
      , non-facade modules).
    • Classify each usage by capability (read/list, detail/read, create/update/delete, async/task, webhook/event).
  2. Define the minimal contract surface.
    • Start from currently consumed fields only.
    • Create frozen dataclasses in
      backend/facade/contracts.py
      .
  3. Introduce a thin facade in
    backend/facade/api.py
    .
    • Map ORM instances to contracts with explicit mapper functions.
    • Keep method names capability-oriented and stable.
  4. Migrate callers in small batches.
    • Replace one caller cluster at a time (single endpoint, single task, or single service area).
    • Keep compatibility shims only when needed; remove promptly.
  5. Move presentation to consume the facade.
    • Serializers convert JSON <-> contracts.
    • Views call facade methods only.
  6. Enforce boundaries and verify.
    • Update
      tach.toml
      interfaces/dependencies when applicable.
    • Run focused tests for changed files, then product-level backend tests.
  1. 为目标产品构建导入映射。
    • 找出跨产品导入目标产品内部模块的情况(
      models
      logic
      presentation
      、非外观层模块)。
    • 按功能类型对每种使用场景进行分类(读取/列表、详情/读取、创建/更新/删除、异步/任务、Webhook/事件)。
  2. 定义最小化的契约接口。
    • 仅从当前被使用的字段开始。
    • backend/facade/contracts.py
      中创建冻结的数据类。
  3. backend/facade/api.py
    中引入简洁的外观层。
    • 使用显式的映射函数将ORM实例转换为契约对象。
    • 保持方法名以功能为导向且稳定。
  4. 分批迁移调用方。
    • 一次替换一个调用方集群(单个端点、单个任务或单个服务区域)。
    • 仅在必要时保留兼容性垫片,并及时移除。
  5. 修改表示层以调用外观层。
    • 序列化器负责JSON与契约对象之间的转换。
    • 视图仅调用外观层方法。
  6. 强制执行边界并验证。
    • 适当时更新
      tach.toml
      中的接口/依赖配置。
    • 先对修改的文件运行针对性测试,再运行产品级后端测试。

PR slicing strategy

PR拆分策略

Default to several PRs instead of one big migration:
  • PR 1: Add contracts + facade methods without changing external callers.
  • PR 2-N: Migrate caller clusters one-by-one to the facade.
  • Final PR: Remove deprecated internal import paths, tighten tach boundaries, and clean dead adapters.
If a product has many endpoints, migrate in this order:
  1. Read-only list/detail APIs (lowest risk)
  2. Internal service-to-service call sites
  3. Write paths (create/update/delete)
  4. Background tasks / async entrypoints
  5. Remaining edge endpoints and cleanup
默认拆分为多个PR,而非单次大迁移:
  • PR 1:添加契约与外观层方法,不修改外部调用方。
  • PR 2至N:逐个迁移调用方集群至外观层。
  • 最终PR:移除已废弃的内部导入路径,收紧tach边界,清理无用的适配器。
如果产品包含多个端点,请按以下顺序迁移:
  1. 只读列表/详情API(风险最低)
  2. 内部服务间调用站点
  3. 写入路径(创建/更新/删除)
  4. 后台任务/异步入口点
  5. 剩余边缘端点与清理工作

Done criteria

完成标准

Treat migration as complete only when:
  • Cross-product imports use
    backend/facade
    only.
  • Facade returns/accepts contracts, not ORM.
  • Presentation layer no longer encodes business logic.
  • Tests cover facade and presentation boundaries.
  • Turbo/tach config reflects intended product isolation.
仅当满足以下所有条件时,才视为迁移完成:
  • 跨产品导入仅使用
    backend/facade
  • 外观层返回/接收契约对象,而非ORM模型。
  • 表示层不再包含业务逻辑。
  • 测试覆盖外观层与表示层的边界。
  • Turbo/tach配置反映了预期的产品隔离状态。