如何在Hono API中结合Cloudflare Workers上下文绑定遵循整洁架构
问题:Cloudflare Workers绑定与整洁架构的冲突
我正在开发遵循**整洁架构(Clean Architecture)**规范的Hono API应用,核心目标是通过分层隔离实现松耦合,方便测试各模块。但遇到了Cloudflare Workers的绑定(包括数据库连接等)只能从Hono的context变量中获取的问题,导致我不得不在应用层添加接收绑定的函数,既降低了应用层的可测试性,还违反了整洁架构的原则——应用层不应该依赖外部基础设施的细节。
相关代码片段如下:
// application/service/jwtservice.ts export class JWTServiceImpl implements JWTService { private secret: string = ''; bindEnv(env: EnvContextProvider): void { this.secret = env.jwtSecret; } sign(payload: ReturnType<typeof Users.prototype.getSafeData>): string { return jwt.sign(payload, this.secret); } verify(token: string): boolean { return jwt.verify(token, this.secret) ? true : false; } }
// application/authentication/login.ts export class LoginUseCase { constructor( private readonly usersRepository: UsersRepository, private readonly jwtService: JWTService, ) {} async verifyToken(token: string, env: EnvContextProvider): Promise<void> { this.jwtService.bindEnv(env); // this feels wrong const data = this.jwtService.verify(token); if (!data) { throw new AuthenticationError('Invalid Token'); } } }
// interfaces/controller/authcontroller.ts export class AuthController { public router: OpenAPIHono<OpenAPIHonoSettings>; constructor( app: OpenAPIHono<OpenAPIHonoSettings>, private loginUseCase: LoginUseCase, ) { this.router = app; this.setupRoutes(); } private setupRoutes() { return [ this.router.openapi(checkLoginRoute, async (c) => { const token = getCookie(c, 'loginToken3'); if (!token) { return c.json({ success: false }, StatusCodes.UNAUTHORIZED); } try { await this.loginUseCase.verifyToken(token, new EnvContextProviderImpl(c)); // this feels wrong, im supposed to do this in the DI container/app init return c.json({ success: true }, StatusCodes.OK); } catch (error) { if (error instanceof AuthenticationError) { return c.json({ success: false }, StatusCodes.UNAUTHORIZED); } c.var.logger.error('Internal Server Error', { error }); return c.json({ success: false }, StatusCodes.INTERNAL_SERVER_ERROR); } }), ] as const; } }
我尝试过使用上下文提供者,但仍需将上下文注入用例,希望了解正确的解决思路。
解决思路
核心原则是把外部依赖(比如Cloudflare绑定)的注入逻辑从应用层上移到最外层的基础设施层,让应用层完全独立于外部环境细节。
1. 依赖倒置:让服务依赖抽象而非具体环境
不要让JWTService这类应用服务直接处理环境绑定,而是在基础设施层提前把需要的配置(比如jwtSecret)注入到服务实例中,应用层只依赖服务的抽象接口。2. 利用Hono的入口逻辑提前解析并注入依赖
在Cloudflare Workers的入口文件中,从env里提取绑定信息,然后初始化所有服务和用例实例,而不是在Controller或UseCase中动态处理。3. 重构DI逻辑:在应用启动时完成依赖注入
把服务实例的创建和依赖注入逻辑放在最外层,确保应用层的代码完全不需要感知外部环境的存在。
修改后的代码示例
1. 重构JWTService:通过构造函数注入依赖
// application/service/jwtservice.ts export class JWTServiceImpl implements JWTService { constructor(private readonly secret: string) {} sign(payload: ReturnType<typeof Users.prototype.getSafeData>): string { return jwt.sign(payload, this.secret); } verify(token: string): boolean { try { jwt.verify(token, this.secret); return true; } catch { return false; } } }
2. 重构LoginUseCase:移除环境参数
// application/authentication/login.ts export class LoginUseCase { constructor( private readonly usersRepository: UsersRepository, private readonly jwtService: JWTService, ) {} async verifyToken(token: string): Promise<void> { const isValid = this.jwtService.verify(token); if (!isValid) { throw new AuthenticationError('Invalid Token'); } } }
3. 重构Controller:不再传递环境给UseCase
// interfaces/controller/authcontroller.ts export class AuthController { public router: OpenAPIHono<OpenAPIHonoSettings>; constructor( app: OpenAPIHono<OpenAPIHonoSettings>, private loginUseCase: LoginUseCase, ) { this.router = app; this.setupRoutes(); } private setupRoutes() { return [ this.router.openapi(checkLoginRoute, async (c) => { const token = getCookie(c, 'loginToken3'); if (!token) { return c.json({ success: false }, StatusCodes.UNAUTHORIZED); } try { await this.loginUseCase.verifyToken(token); return c.json({ success: true }, StatusCodes.OK); } catch (error) { if (error instanceof AuthenticationError) { return c.json({ success: false }, StatusCodes.UNAUTHORIZED); } c.var.logger.error('Internal Server Error', { error }); return c.json({ success: false }, StatusCodes.INTERNAL_SERVER_ERROR); } }), ] as const; } }
4. 外层入口文件:处理依赖注入与环境绑定
// index.ts (Cloudflare Workers入口) import { OpenAPIHono } from '@hono/zod-openapi'; import { AuthController } from './interfaces/controller/authcontroller'; import { LoginUseCase } from './application/authentication/login'; import { JWTServiceImpl } from './application/service/jwtservice'; import { UsersRepositoryImpl } from './infrastructure/repository/usersrepository'; export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { // 从Cloudflare绑定中提取配置与资源 const jwtSecret = env.jwtSecret; const db = env.DB; // 数据库绑定 // 初始化基础设施层实现 const usersRepository = new UsersRepositoryImpl(db); // 初始化应用层服务与用例 const jwtService = new JWTServiceImpl(jwtSecret); const loginUseCase = new LoginUseCase(usersRepository, jwtService); // 初始化控制器与路由 const app = new OpenAPIHono(); new AuthController(app, loginUseCase); return app.fetch(request, env, ctx); }, };
内容的提问来源于stack exchange,提问作者thisjt
相关产品推荐
相关产品推荐

