Symfony的DependencyInjection容器是按需实例化、延迟加载的调度中心,Bundle是可移植的上下文边界,须独立注册与配置,事件驱动需契约化与松耦合,组件复用依赖contracts与自动装配而非硬编码。

DependencyInjection 容器不是“启动时加载所有服务”的黑箱,而是按需实例化、延迟加载的调度中心。直接在构造函数里写 new SomeService() 会绕过容器,导致依赖无法被替换、无法被测试、无法被装饰——这是模块化设计的第一道裂缝。
Bundle 不是插件,是可移植的上下文边界
一个 Bundle 必须能独立注册、配置、启用/禁用,且不破坏其他 Bundle 的运行。这意味着它不能在 src/ 下硬编码引用另一个 Bundle 的控制器或服务类名;也不能在自己的 config/packages/ 下覆盖全局 doctrine 或 framework 配置项——那属于框架层,不是 Bundle 层该管的事。
- Bundle 内部路由必须用前缀隔离,比如
app_admin_,避免和主应用或其他 Bundle 的admin_冲突 - 模板路径要限定在
templates/bundle_name/下,不混入全局templates/ - 服务定义必须使用
autoconfigure: true和autowire: true,但关键接口绑定仍需显式声明,例如App\Contract\NotifierInterface: '@App\Service\EmailNotifier'
EventDispatcher 不是广播喇叭,是契约驱动的松耦合信道
随便触发一个 new UserRegisteredEvent($user) 没问题,但监听方如果直接调用 $this->mailer->send() 就违背了模块化初衷。事件本身不携带执行逻辑,只携带上下文数据;响应行为应由监听器通过容器注入的服务完成,且监听器本身应可被替换(比如换成 Slack 发送而非邮件)。
- 事件类必须放在
src/Event/,继承Symfony\Contracts\EventDispatcher\Event - 监听器注册必须通过配置或属性,不能在构造函数里手动
addEventListener() - 高频率事件(如请求级日志)要考虑异步处理,否则拖慢主流程——
EventDispatcher默认同步,这点常被忽略
组件复用 ≠ 直接 require,而靠 contracts + autoconfiguration
你可以在非 Symfony 项目里用 symfony/routing,但若直接 new RouteCollection() 并手动 add,就丢掉了它和 symfony/config、symfony/yaml 的协作能力。真正的复用,是让组件自己声明“我需要什么”,然后由容器按 contract 自动装配。
- 单独使用
symfony/http-foundation时,Request和Response可以脱离框架,但别试图给它塞进Kernel生命周期钩子 - 要用
symfony/validator校验 DTO,就定义约束注解,并确保ValidatorInterface已注册——而不是自己 new ConstraintViolationList -
symfony/console命令类必须实现Command,且依赖通过构造注入,否则命令无法参与服务自动注册


















