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

Python后端Route-Controller-Feature-Repository架构依赖注入最佳实践咨询

寻找Pythonic的依赖注入实现方案(基于Route-Controller-Feature-Repository架构)

我正在对比TypeScript与Python的后端开发结构,采用Route - Controller - Feature - Repository架构(按需使用非基础方法)。该架构在TypeScript中广泛应用:路由调用控制器,控制器完成合法性校验后调用Feature,Feature按需对数据库执行操作。

TypeScript中通过index.ts初始化类并注入依赖,示例如下:

import SomeRepository from '...'

class SomeFeature {
  private someRepository: SomeRepository;

  constructor(someRepository: SomeRepository) {
    this.someRepository = someRepository;
  }
  // ... 其他方法
}
import SomeRepository from '...'
import SomeFeature from '...'

export default new SomeFeature(new SomeRepository());

我在Python项目中找到了四种实现方式,但不确定哪种最符合Python风格,或存在Python特性冲突需规避:

OPTION 1:全局实例初始化

from ... import SomeRepository
from ... import SomeFeature
from ... import SomeController

some_repository = SomeRepository()
some_feature = SomeFeature(some_repository)
some_controller = SomeController(some_feature)

@app.get("/")
async def some_endpoint():
    return some_controller.main_function()

OPTION 2:工厂函数创建实例

from ... import SomeRepository
from ... import SomeFeature

def create_delete_document_feature():
    some_repository = SomeRepository()
    return SomeFeature(some_repository)

OPTION 3:类内部直接初始化依赖

from ... import SomeRepository

class SomeFeature:
    def __init__(self):
        self.some_repository = SomeRepository()

OPTION 4:FastAPI Depends 注入

from fastapi import Depends
from ... import SomeRepository
from ... import SomeFeature
from ... import SomeController

def get_some_repository():
    return SomeRepository()

def get_some_feature(some_repository=Depends(get_some_repository)):
    return SomeFeature(some_repository)

def get_some_controller(some_feature=Depends(get_some_feature)):
    return SomeController(some_feature)

但选项4会为每个请求创建新实例,若Feature涉及模型加载会导致延迟,虽可尝试use_cache=True,但不确定是否为最优解,或应在应用启动时手动初始化实例并复用。

请问哪种方案是最佳选择?


各方案优劣分析与最佳选择

优先排除的选项

  • OPTION 3:直接在SomeFeature的__init__里实例化SomeRepository,完全违背依赖注入的核心逻辑——依赖倒置与解耦。后续若要替换SomeRepository实现(比如测试用Mock),必须修改SomeFeature源码,维护成本极高,绝对不推荐。
  • OPTION 2:工厂函数比OPTION3更灵活,但本质还是硬编码创建依赖。如果后续需要调整依赖的初始化逻辑(比如给SomeRepository传配置参数),所有调用工厂函数的地方都要修改,仅适合小型临时项目,不适合长期维护的架构。

核心对比:OPTION1 vs OPTION4

  • OPTION1:全局实例

    • 优势:应用启动时一次性初始化所有实例,全程复用,彻底避免OPTION4的重复创建问题(比如模型仅加载一次)。结构简单直观,和你熟悉的TS写法逻辑一致,符合Python“显式优于隐式”的设计哲学。
    • 注意点:仅需确保SomeRepository、SomeFeature、SomeController是无状态的——这是该架构的常规设计(Repository只做数据库操作,Feature仅处理业务逻辑,不存储请求上下文),因此不会出现请求间状态污染的问题。
  • OPTION4:FastAPI Depends

    • 优势:原生支持请求级别的依赖隔离,适合需要请求专属资源的场景(比如每个请求用独立的数据库会话)。
    • 劣势:默认每个请求都会创建新实例,若Feature涉及模型加载等重初始化操作,会严重拖慢性能。虽然use_cache=True能让同一个请求内复用实例,但跨请求仍会重复创建;如果手动在依赖函数里实现单例缓存,又会失去Depends的请求隔离优势,反而不如OPTION1直接高效。

最终建议

如果你的架构中,Repository、Feature、Controller均为无状态设计(这是该架构的标准实践),OPTION1是最符合Python风格的选择——它简单、显式,完全契合Python“简单胜于复杂”的原则,同时和你熟悉的TS实现逻辑对齐。

若后续遇到需要请求级隔离的场景(比如数据库会话管理),可以单独针对这类依赖用Depends处理,无需将所有层都改为请求级注入。例如:保留全局的Feature实例,但Feature的方法接收由Depends注入的数据库会话参数。

内容的提问来源于stack exchange,提问作者Alex Abades Grimes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 16:19:55