Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions docs/skills/coze-backend-go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
## Coze Backend Go + DDD - Skill

This is the Coze Studio backend engineering conventions

### What this skill covers

- **IDL-first API design** (Thrift → Hertz codegen)
- **DDD layering boundaries** (Handler / Application / Domain / Repository / DAL)
- **DB DDL → `gorm/gen` reverse generation**
- **Domain-scoped error codes (errno)** and layered error-handling rules
- **crossdomain** interface-based calls
- **Middleware and session** conventions
- **Transactions**, **mocks/tests**, and **ID generation**

### Files

- Entry: `SKILL.md`
- SOP: `checklist.md`
- Notes: `notes.md`
- Code pointers: `citations.md`
- Detailed topics: `skill-01-*.md` … `skill-13-*.md`

### How to use

This is a **tool-agnostic** prompt+documentation bundle. You can install it in either:

- **Project-level**: put this directory anywhere inside the target repo, then point your AI coding tool to load it as repository knowledge/instructions.
- **Global-level**: store it in your personal prompt library, and attach/reference it when working on `coze-studio/backend`.

### Common tool setups (optional)

Assume you vendor these skills under `docs/skills/` in your repository (for example: `docs/skills/coze-backend-go/`).

#### Cursor

Project root:

```bash
mkdir -p .cursor && ln -snf ../docs/skills .cursor/skills
```

Windows: copy to `.cursor/skills/` instead of symlink.

#### Claude Code

Project root:

```bash
mkdir -p .claude && ln -snf ../docs/skills .claude/skills
```

Windows: copy to `.claude/skills/` instead of symlink.

#### Codex

Copy the skill directory into your global skills home, then restart your tool:

```bash
cp -r docs/skills/coze-backend-go "$CODEX_HOME/skills/"
```

If you have multiple skills, copy multiple subdirectories.

Suggested trigger keywords (when asking an AI coding tool for help):
"backend", "DDD", "IDL", "Thrift", "errno", "gorm/gen", "DAL", "crossdomain", "middleware", "session", "transaction", etc.

### Alignment with official standards

This skill is derived from:

- The official Coze Studio [Development Standards](https://github.com/coze-dev/coze-studio/wiki/7.-Development-Standards)
- The actual structure and code of this repository

Where they differ, this skill follows **the real repository** (for example, using `mockgen` for Go mocks rather than introducing other mock libraries).

84 changes: 84 additions & 0 deletions docs/skills/coze-backend-go/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
name: coze-backend-go
description: "Documentation of Coze Studio backend Go + DDD conventions. Use when editing `backend/`, implementing APIs (IDL-first), working with Domain/Application/Repository/DAL boundaries, generating DAL via gorm/gen, handling errno and layered errors, crossdomain calls, sessions/middleware, transactions, mocks/tests, and ID generation."
---

# Coze Studio Backend Go + DDD

This skill documents the Coze Studio backend conventions as **actionable AI-coding guidelines**: **Go practices**, **DDD layering**, **IDL-driven development**, DB reverse generation via `gorm/gen`, and Coze-specific patterns such as **domain-scoped error codes**, **space/session isolation**, and **workflow-domain conventions**.

> This skill is aligned with `coze-studio` backend **v0.5.1**. If the repository evolves (structure/codegen commands), treat the repo as the source of truth and refresh this skill accordingly.

## Emphasis

- **Go**: interface vs implementation separation, Functional Options, error-as-value, `internal` packages, `mockgen`, context propagation, and chunked batch queries.
- **DDD**: business invariants live in the Domain layer; API/Application layers orchestrate and map models only.
- **Coze conventions**: IDL-first, unified `{code,msg,data}` responses, errno ranges per domain, crossdomain interfaces, and workflow-specific error handling.
- **Multi-space (tenant-like)**: most resources are scoped by `SpaceID` and must enforce access control (user ↔ space membership) at the Application boundary; never query/update cross-space data without explicit checks.

---

## Skill 0: Architecture and layering overview

```
idl/ ← (1) IDL definitions (Thrift)
backend/
api/
handler/coze/ ← (2) HTTP handlers (generated stubs + manual bodies)
router/coze/ ← (3) Route registration (generated)
model/ ← (4) API request/response structs (generated)
middleware/ ← (5) Middleware (session/auth/log/etc.)
application/{domain}/ ← (6) Application services (orchestration only)
domain/{domain}/
entity/ ← (7) Domain entities
service/ ← (8) Domain service interfaces + implementations
repository/ ← (9) Repository interfaces + implementations
internal/dal/
model/ ← (10) gorm/gen models (*.gen.go)
query/ ← (11) gorm/gen query APIs (*.gen.go)
dto/ ← (12) Domain DTOs
crossdomain/ ← (13) Cross-domain per-domain packages (ACL)
{domain}/
contract.go ← cross-domain interface definitions (anti-corruption layer)
impl/ ← concrete implementations
model/ ← cross-domain models (stable, avoid domain entities)
*mock/ ← mocks (generated)
infra/ ← (14) Infrastructure capabilities (interface + impl/)
{capability}/
*.go ← capability interfaces/types (acts as "contract")
entity/ ← (optional) infra-level entities/VOs
impl/ ← implementations (mysql/redis/nsq/...)
types/
ddl/ ← (15) DB reverse-generation scripts (gen_orm_query.go)
errno/ ← (16) Global/domain error codes
consts/ ← (17) Global constants
```

Boot entry: `backend/main.go` → `application.Init(ctx)` → `router.GeneratedRegister(s)`

Cross-domain calls must go through the `crossdomain/{domain}/contract.go` interfaces instead of directly depending on other domains’ `service` packages.

---

## Documents index

| Topic | File |
|------|------|
| End-to-end “add a module” SOP | [checklist.md](checklist.md) |
| Notes and caveats | [notes.md](notes.md) |
| Code pointers (paths + line ranges) | [citations.md](citations.md) |
| Skill 1: IDL-first API design | [skill-01-idl.md](skill-01-idl.md) |
| Skill 2: DB DDL → gorm/gen DAL generation | [skill-02-dal.md](skill-02-dal.md) |
| Skill 3: Domain module structure (DDD) | [skill-03-domain.md](skill-03-domain.md) |
| Skill 4: Application layer conventions | [skill-04-application.md](skill-04-application.md) |
| Skill 5: Transaction patterns | [skill-05-transaction.md](skill-05-transaction.md) |
| Skill 6: Errno + layered validation/error handling | [skill-06-errno.md](skill-06-errno.md) |
| Skill 7: Handler conventions | [skill-07-handler.md](skill-07-handler.md) |
| Skill 8: Middleware & session conventions | [skill-08-middleware.md](skill-08-middleware.md) |
| Skill 9: crossdomain conventions | [skill-09-crossdomain.md](skill-09-crossdomain.md) |
| Skill 10: DTO conventions | [skill-10-dto.md](skill-10-dto.md) |
| Skill 11: Mocks & tests | [skill-11-mock.md](skill-11-mock.md) |
| Skill 12: ID generation rules | [skill-12-id.md](skill-12-id.md) |
| Skill 13: Mermaid flowcharts & architecture diagrams | [skill-13-flowchart-generator.md](skill-13-flowchart-generator.md) |
| Skill 14: Eino conventions | [skill-14-eino.md](skill-14-eino.md) |

53 changes: 53 additions & 0 deletions docs/skills/coze-backend-go/checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# End-to-end Development Checklist (AI Coding SOP)

```
Full steps to add a new feature/module:

Step 1: Database DDL
- Create/upgrade tables in the test database (DDL/DDL_UPGRADE)
- Add mapping in types/ddl/gen_orm_query.go: path2Table2Columns2Model
- Run: go run types/ddl/gen_orm_query.go (requires MYSQL_DSN env var)
Comment on lines +8 to +9
- Generates:
domain/{X}/internal/dal/model/*.gen.go
domain/{X}/internal/dal/query/*.gen.go

Step 2: IDL definition
- Define Service/Request/Response in idl/{module}/*.thrift
- Run Hertz codegen (repo uses `hz`, see `backend/.hz`):
- `hz update -idl idl/api.thrift -enable_extends`
- Note: `-enable_extends` is critical for the master entry `idl/api.thrift` (otherwise route generation may be empty). See coze-studio issue #372.
- Generates:
api/router/coze/api.go (routes)
api/model/{gen_path}/*.go (API structs)
api/handler/coze/{service}_service.go (handler stubs)
Comment on lines +20 to +22

Step 3: Domain layer implementation
- entity/ domain entities (embed crossdomain model + domain methods)
- dto/ service input/output DTOs (domain-scoped)
- service/
- service.go Service interface (with //go:generate mockgen)
- service_impl.go impl struct + NewService (dependency injection)
- {feature}.go split by responsibility (multiple files)
- repository/
- {X}_repository.go Repository interface
- {X}_impl.go implementation (holds DAL/DAO)
- option.go Functional Options (field projection)
- internal/dal/
- {table}.go DAO implementation (CRUD + WithTX variants)

Step 4: Register errno
- Add error-code constants in types/errno/{domain}.go (allocate within range)
- Register in init(): code.Register(...)

Step 5: Application layer
- application/{X}/init.go InitService + dependency injection
- application/{X}/*.go orchestrate, call Domain services

Step 6: Implement handler bodies
- api/handler/coze/{service}_service.go fill handler function bodies

Step 7: Register crossdomain (if needed)
- crossdomain/{X}/ define interface contracts
- application/application.go SetDefaultSVC(...)
Comment on lines +43 to +51
```

87 changes: 87 additions & 0 deletions docs/skills/coze-backend-go/citations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Citations (code pointer index)

This repository’s code changes over time. To avoid copying stale code into the docs, this file provides **file paths + line ranges** you can open on-demand.

All paths are relative to the repo root; `backend/` is the backend directory.

---

## Skill 0 / Boot and layering

| What | Path and lines |
|------|---------------|
| main entry, `application.Init`, `startHttpServer`, middleware ordering | `backend/main.go` (L43-104) |
| Route registration (generated) | `backend/api/router/coze/api.go` (L17-80) |
| `GeneratedRegister` | `backend/api/router/register.go` (L34-39) |

---

## Skill 1: IDL

| What | Path and lines |
|------|---------------|
| Base / `BaseResp`, `EmptyResp` (code+msg+data) | `idl/base.thrift` (L1-42) |
| Service methods and `api.post` / `api.gen_path` annotations | `idl/plugin/plugin_develop.thrift` (L6-50) |

---

## Skill 2: DAL generation

| What | Path and lines |
|------|---------------|
| `path2Table2Columns2Model` config | `backend/types/ddl/gen_orm_query.go` (L44-225) |
| generator main, DSN, FieldModify | `backend/types/ddl/gen_orm_query.go` (L231-326) |
| PO models (`*.gen.go`) | `backend/domain/plugin/internal/dal/model/plugin_draft.gen.go` (L1-33) |
| Query APIs (`*.gen.go`) | `backend/domain/plugin/internal/dal/query/plugin_draft.gen.go` (L1-46) |

---

## Skill 3: DDD (4 layers)

| What | Path and lines |
|------|---------------|
| Entity embeds crossdomain model | `backend/domain/plugin/entity/plugin.go` (L24-54) |
| Service interface, `//go:generate mockgen` | `backend/domain/plugin/service/service.go` (L27-92) |
| `service_impl`, `NewService` DI | `backend/domain/plugin/service/service_impl.go` (L30-65) |
| Repository interface | `backend/domain/plugin/repository/plugin_repository.go` (L27-53) |
| Functional Options (`option.go`) | `backend/domain/plugin/repository/option.go` (L23-86) |
| DAO Create, `ToDO`, `genPluginID` | `backend/domain/plugin/internal/dal/plugin_draft.go` (L39-138) |
| Keyset-pagination list | `backend/domain/plugin/internal/dal/plugin_draft.go` (L158-190) |
| MGet, `slices.Chunks` | `backend/domain/plugin/internal/dal/plugin_draft.go` (L192-212) |
| `CreateWithTX` | `backend/domain/plugin/internal/dal/plugin_draft.go` (L287-315) |
| Repository impl (holds DAO) | `backend/domain/plugin/repository/plugin_impl.go` (L45-72) |
| Transaction template (Begin/defer Rollback/Commit) | `backend/domain/plugin/repository/plugin_impl.go` (L133-170) |
| Multi-table transaction: PublishPlugin | `backend/domain/plugin/repository/plugin_impl.go` (L283-351) |
| DTO definitions | `backend/domain/plugin/dto/plugin.go` (L27-96) |
| Repository mocks | `backend/domain/plugin/repository/mock/` |

---

## Skill 4: Application

| What | Path and lines |
|------|---------------|
| Application struct, DomainSVC | `backend/application/plugin/plugin.go` (L49-59) |
| InitService and DI | `backend/application/plugin/init.go` (L38-89) |
| `basicServices` → `primaryServices` → `complexServices`, `SetDefaultSVC` | `backend/application/application.go` (L84-171) |

---

## Skill 6: Errors and handlers

| What | Path and lines |
|------|---------------|
| errno constants and range | `backend/types/errno/plugin.go` (L25-54) |
| `init`, `code.Register`, `WithAffectStability` | `backend/types/errno/plugin.go` (L56-103) |
| `StatusError` interface | `backend/pkg/errorx/error.go` (L26-36) |
| `errorx.New`, `WrapByCode`, `Wrapf` | `backend/pkg/errorx/error.go` (L54-78) |
| HTTP error response mapping | `backend/api/internal/httputil/error_resp.go` (L35-54) |
| `invalidParamRequestResponse`, `internalServerErrorResponse` | `backend/api/handler/coze/base.go` (L27-33) |
| Handler example: BindAndValidate, validation, calling Application | `backend/api/handler/coze/plugin_develop_service.go` (L34-77) |
| `GetUIDFromCtx`, `MustGetUIDFromCtx` | `backend/application/base/ctxutil/session.go` (L36-52) |
| Session auth middleware | `backend/api/middleware/session.go` (L41-75) |

---

Open the files above at the referenced line ranges. If line numbers drift due to edits, search nearby for the mentioned symbols.

22 changes: 22 additions & 0 deletions docs/skills/coze-backend-go/notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Notes

1. **Generated code vs handwritten code**: files under `api/router/coze/api.go`, `api/model/`, `dal/model/*.gen.go`, and `dal/query/*.gen.go` are **generated** and must **not** be edited manually. Handler function bodies, middleware bodies, the `application/` layer, and the `domain/` layer are handwritten.

2. **Framework choices**: HTTP uses [Hertz](https://github.com/cloudwego/hertz) (CloudWeGo), ORM uses gorm + `gorm/gen`, and IDL uses Apache Thrift.

3. **The Application layer is not a mandatory “classic DDD” layer**, but in this codebase it acts as the DDD Application Service: API model mapping, cross-domain orchestration, and event publishing.

4. **`internal/` packages** enforce Go access boundaries: `domain/{X}/internal/dal/` can only be used by code within the same domain module. The Application layer must not call DAL directly; it must go through repository interfaces.

5. **Batch MGet**: for bulk reads, use `slices.Chunks(ids, 20)` to chunk requests and avoid overly large `IN (...)` clauses.

6. **Go error handling**: business errors should be returned as `error` (via `errorx` + `errno`). Avoid `panic` on business paths; reserve `panic` for unrecoverable programming errors. The Application layer may use `defer` + `recover` as a final safety net and convert panics to standard error codes.

7. **Errors must carry a business code**: the Application and Domain layers must not return bare `errors.New(...)` or `fmt.Errorf(...)` for expected business errors. Use `errorx.New(errno.ErrXxx, ...)` for business errors; otherwise the Handler layer will treat it as a system error and respond with HTTP 500, which is both non-actionable and hard to distinguish from real incidents. When propagating errors, use `errorx.Wrapf` / `errorx.WrapByCode` (or `github.com/pkg/errors` Wrap/Wrapf) to preserve call context; before returning to HTTP, ensure it is converted to a status error with a proper errno code.

8. **File header comments**: new files do not need to add legacy copyright headers (this is an internal project). Keep existing headers in old files as-is.

9. **When to use `pkg/`**: small generic utilities (e.g., time/json/file/lang helpers) without business semantics and without direct external system access. Do not put DB/Redis/HTTP SDK integrations into `pkg/`; those belong to `infra/` or domain-specific adapters.

10. **Avoid reinventing wheels**: before adding “utility” code, check whether `pkg/` already provides a similar helper.

63 changes: 63 additions & 0 deletions docs/skills/coze-backend-go/skill-01-idl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Skill 1: Design APIs starting from IDL (IDL-first)

## Rules

1. **All APIs start from `.thrift` files under `idl/`.**
2. Each service method’s annotations define the HTTP route, category, and generated code path.
3. `base.thrift` defines the shared `Base` (request) and `BaseResp` (response) structures.
4. Responses follow a unified `{code, msg, data}` shape and include `base.BaseResp` as the tail field.
5. **One method parameter and one return value only**, both must be custom `struct` types.
6. Request/response naming follows `{Method}Request` / `{Method}Response`.
7. Each `Request` struct must include a `Base` field (`base.Base`, field id `255`, `optional`).
8. Each `Response` struct must include a `BaseResp` field (`base.BaseResp`, field id `255`, `optional`).
9. New fields should be declared as `optional`; avoid `required` for forward compatibility.

## Codegen (Hertz `hz`)

`coze-studio` backend v0.5.1 uses `hz` (Hertz toolkit) to generate routes, handler stubs, and `api/model` structs from Thrift IDL. See `backend/.hz` for the configured output directories (handler/model/router).

Common command (run from repo root):

```bash
# Update the master IDL (aggregation/extends) generated routes + handler/model
hz update -idl idl/api.thrift -enable_extends
```

Notes:
- `idl/api.thrift` is usually the master IDL aggregating multiple services (incl. `extends`); in practice `-enable_extends` is required, otherwise route generation may be empty.
- `hz update` only needs the IDL file that defines the `service`; dependent IDLs will be generated automatically.

**IDL template** (see `plugin_develop.thrift` as reference):

```thrift
service XxxService {
XxxResponse XxxMethod(1: XxxRequest request)
(api.post='/api/xxx/yyy', api.category="xxx", api.gen_path="xxx")
}
struct XxxRequest {
1: optional i64 id (api.js_conv = "str"),
255: optional base.Base Base
}
struct XxxResponse {
1: i64 code,
2: string msg,
3: XxxData data,
255: optional base.BaseResp BaseResp
}
```

## Conventions

- Service names and method names use **camelCase**.
- Typically, one service per `.thrift` file (except for explicit aggregation via `extends`).
- APIs follow a **RESTful** style for paths; when adding new endpoints, use existing modules as a style reference instead of inventing new patterns.

## Generated code artifacts

- `backend/api/router/coze/api.go` — route registration (**generated**; do not edit)
- `backend/api/router/coze/middleware.go` — middleware stubs (can be manually filled)
- `backend/api/model/{gen_path}/` — request/response structs (**generated**)
- `backend/api/handler/coze/` — handler stubs (implement handler bodies manually)

See [citations.md](citations.md) for code pointers.

Loading