encore-go-service

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Encore Go Service Structure

Encore Go 服务结构

Instructions

操作说明

In Encore Go, each package with an API endpoint is automatically a service. No special configuration needed.
在Encore Go中,每个包含API端点的包都会自动成为一个服务,无需额外配置。

Creating a Service

创建服务

Simply create a package with at least one
//encore:api
endpoint:
go
// user/user.go
package user

import "context"

type User struct {
    ID    string `json:"id"`
    Email string `json:"email"`
    Name  string `json:"name"`
}

//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (*User, error) {
    // This makes "user" a service
}
只需创建一个包含至少一个
//encore:api
端点的包即可:
go
// user/user.go
package user

import "context"

type User struct {
    ID    string `json:"id"`
    Email string `json:"email"`
    Name  string `json:"name"`
}

//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (*User, error) {
    // 这会将"user"标记为一个服务
}

Minimal Service Structure

最小服务结构

user/
├── user.go          # API endpoints
├── db.go            # Database (if needed)
└── migrations/      # SQL migrations
    └── 1_create_users.up.sql
user/
├── user.go          # API端点
├── db.go            # 数据库(如有需要)
└── migrations/      # SQL迁移脚本
    └── 1_create_users.up.sql

Application Patterns

应用架构模式

Single Service (Recommended Start)

单服务(推荐入门方式)

Best for new projects - start simple, split later if needed:
my-app/
├── encore.app
├── go.mod
├── api.go           # All endpoints
├── db.go            # Database
└── migrations/
    └── 1_initial.up.sql
最适合新项目——从简单结构开始,必要时再拆分:
my-app/
├── encore.app
├── go.mod
├── api.go           # 所有端点
├── db.go            # 数据库
└── migrations/
    └── 1_initial.up.sql

Multi-Service

多服务架构

For distributed systems with clear domain boundaries:
my-app/
├── encore.app
├── go.mod
├── user/
│   ├── user.go
│   ├── db.go
│   └── migrations/
├── order/
│   ├── order.go
│   ├── db.go
│   └── migrations/
└── notification/
    └── notification.go
适用于具有清晰领域边界的分布式系统:
my-app/
├── encore.app
├── go.mod
├── user/
│   ├── user.go
│   ├── db.go
│   └── migrations/
├── order/
│   ├── order.go
│   ├── db.go
│   └── migrations/
└── notification/
    └── notification.go

Large Application (System-based)

大型应用(基于系统分组)

Group related services into systems:
my-app/
├── encore.app
├── go.mod
├── commerce/
│   ├── order/
│   │   └── order.go
│   ├── cart/
│   │   └── cart.go
│   └── payment/
│       └── payment.go
├── identity/
│   ├── user/
│   │   └── user.go
│   └── auth/
│       └── auth.go
└── comms/
    ├── email/
    │   └── email.go
    └── push/
        └── push.go
将相关服务按系统分组:
my-app/
├── encore.app
├── go.mod
├── commerce/
│   ├── order/
│   │   └── order.go
│   ├── cart/
│   │   └── cart.go
│   └── payment/
│       └── payment.go
├── identity/
│   ├── user/
│   │   └── user.go
│   └── auth/
│       └── auth.go
└── comms/
    ├── email/
    │   └── email.go
    └── push/
        └── push.go

Service-to-Service Calls

服务间调用

Just import and call the function directly - Encore handles the RPC:
go
package order

import (
    "context"
    "myapp/user"  // Import the user service
)

//encore:api auth method=GET path=/orders/:id
func GetOrderWithUser(ctx context.Context, params *GetOrderParams) (*OrderWithUser, error) {
    order, err := getOrder(ctx, params.ID)
    if err != nil {
        return nil, err
    }
    
    // This becomes an RPC call - Encore handles it
    orderUser, err := user.GetUser(ctx, &user.GetUserParams{ID: order.UserID})
    if err != nil {
        return nil, err
    }
    
    return &OrderWithUser{Order: order, User: orderUser}, nil
}
只需直接导入并调用函数即可——Encore会自动处理RPC通信:
go
package order

import (
    "context"
    "myapp/user"  // 导入user服务
)

//encore:api auth method=GET path=/orders/:id
func GetOrderWithUser(ctx context.Context, params *GetOrderParams) (*OrderWithUser, error) {
    order, err := getOrder(ctx, params.ID)
    if err != nil {
        return nil, err
    }
    
    // 这会自动转为RPC调用——由Encore处理
    orderUser, err := user.GetUser(ctx, &user.GetUserParams{ID: order.UserID})
    if err != nil {
        return nil, err
    }
    
    return &OrderWithUser{Order: order, User: orderUser}, nil
}

When to Split Services

何时拆分服务

Split when you have:
SignalAction
Different scaling needsSplit (e.g., auth vs analytics)
Different deployment cyclesSplit
Clear domain boundariesSplit
Shared database tablesKeep together
Tightly coupled logicKeep together
Just organizing codeUse sub-packages, not services
出现以下信号时考虑拆分:
信号操作
不同的扩容需求拆分(例如,认证服务 vs 分析服务)
不同的部署周期拆分
清晰的领域边界拆分
共享数据库表保持合并
逻辑高度耦合保持合并
仅为代码组织使用子包,而非拆分服务

Internal Helpers (Non-Service Packages)

内部工具包(非服务包)

Create packages without
//encore:api
endpoints for shared code:
my-app/
├── user/
│   └── user.go       # Service (has API)
├── order/
│   └── order.go      # Service (has API)
└── internal/
    ├── util/
    │   └── util.go   # Not a service (no API)
    └── validation/
        └── validate.go
创建不含
//encore:api
端点的包用于共享代码:
my-app/
├── user/
│   └── user.go       # 服务(包含API)
├── order/
│   └── order.go      # 服务(包含API)
└── internal/
    ├── util/
    │   └── util.go   # 非服务(无API)
    └── validation/
        └── validate.go

Guidelines

指导原则

  • A package becomes a service when it has
    //encore:api
    endpoints
  • Services cannot be nested within other services
  • Start with one service, split when there's a clear reason
  • Cross-service calls look like regular function calls
  • Each service can have its own database
  • Package names should be lowercase, descriptive
  • Don't create services just for code organization - use sub-packages instead
  • 包含
    //encore:api
    端点的包会成为服务
  • 服务不能嵌套在其他服务内部
  • 从单个服务开始,有明确理由时再拆分
  • 跨服务调用看起来与常规函数调用无异
  • 每个服务可以拥有独立的数据库
  • 包名应使用小写且具有描述性
  • 不要仅为了代码组织而创建服务——使用子包替代