模块¶
模块是组织和组合服务的容器。它们管理其子服务的生命周期,并提供一种层次化地构建应用程序的方式。
定义模块¶
使用 @module() 装饰器定义模块:
from canary_framework import module
from canary_framework.core.module import ModuleBase
@module(services=[Database, UserRepo, AuthApi])
class Auth(ModuleBase):
pass
@module(services=[...])— 仅需要services参数- 名称从类名自动生成(
ClassName+"Module") - 模块自动命名为
AuthModule
模块组合¶
模块可以包含服务和其他模块,创建层次化结构:
from canary_framework import module, service
from canary_framework.core.service import ServiceBase
from canary_framework.core.module import ModuleBase
from canary_framework.core.router import Router
# 核心服务
@service()
class Database(ServiceBase):
pass
@service()
class Cache(ServiceBase):
pass
# Auth 模块
@service()
class AuthService(ServiceBase):
db: Database
@service()
class AuthApi(ServiceBase):
router = Router(prefix="/auth")
auth: AuthService
@module(services=[AuthService, AuthApi])
class Auth(ModuleBase):
pass
# Posts 模块
@service()
class PostsService(ServiceBase):
db: Database
cache: Cache
@service()
class PostsApi(ServiceBase):
router = Router(prefix="/posts")
posts: PostsService
@module(services=[PostsService, PostsApi])
class Posts(ModuleBase):
pass
# 主应用模块
@module(services=[Database, Cache, Auth, Posts])
class App(ModuleBase):
pass
模块子服务访问¶
子服务和子模块通过其类名直接在模块实例上访问:
app = App()
await app.init()
# 通过类名访问子服务(非 snake_case)
app.Database # Database 服务实例
app.Cache # Cache 服务实例
app.Auth # Auth 子模块实例
app.Posts # Posts 子模块实例
模块生命周期¶
模块协调其子服务的生命周期。当调用模块的生命周期方法时,它们按拓扑顺序传播到所有子服务。
app = App()
# 1. 初始化阶段:按依赖顺序初始化所有服务
await app.init()
# 2. 启动阶段:启动所有服务
await app.startup()
# ... 应用运行 ...
# 3. 关闭阶段:按逆序关闭所有服务
await app.shutdown()
模块作为 ASGI 应用¶
模块可以直接用作 ASGI 应用。它自动挂载所有子路由:
@module(services=[...])
class App(ModuleBase):
pass
async def setup():
app = App()
await app.init()
return app
import asyncio
import uvicorn
app = asyncio.run(setup())
uvicorn.run(app, host="0.0.0.0", port=8080, lifespan="on")
模块将: 1. 从其服务中收集所有 Router 2. 根据其 prefix 将它们挂载在路径上 3. 处理 ASGI 请求
模块基类¶
使用 @module() 装饰的类必须显式继承 ModuleBase,该类提供:
init()方法:初始化模块和所有服务startup()方法:启动模块和所有服务shutdown()方法:关闭模块和所有服务asgi_app属性:访问 ASGI 应用
依赖共享¶
模块中的服务共享依赖项。如果多个服务依赖于同一个服务,则只创建并共享一个实例:
@service()
class Database(ServiceBase):
pass
@service()
class ServiceA(ServiceBase):
db: Database
@service()
class ServiceB(ServiceBase):
db: Database
@module(services=[Database, ServiceA, ServiceB])
class App(ModuleBase):
pass
# ServiceA 和 ServiceB 都接收到同一个 Database 实例
完整示例¶
from canary_framework import module, service
from canary_framework.core.service import ServiceBase
from canary_framework.core.module import ModuleBase
from canary_framework.core.router import Router
# 服务
@service()
class Database(ServiceBase):
async def query(self, sql):
pass
@service()
class UserRepo(ServiceBase):
db: Database
@service()
class UserService(ServiceBase):
repo: UserRepo
# 路由
@service()
class Users(ServiceBase):
router = Router(prefix="/api/users")
user: UserService
@router.get("/")
async def list_users(self):
return {"users": []}
# 模块
@module(services=[UserRepo, UserService, Users])
class UsersMod(ModuleBase):
pass
@module(services=[Database, UsersMod])
class App(ModuleBase):
pass
最佳实践¶
- 分层架构:按功能组织模块(如 auth、users、posts)
- 单一职责:每个模块专注于一个功能领域
- 模块组合:通过组合小模块构建大型应用
- 配置隔离:为每个模块提供独立的配置空间
- 测试隔离:每个模块可以独立测试
- 使用有描述性的注解名称:
db、repo、service— 而非d1、d2