You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:35:08