Why does EK9 suggest promoting an injected type to a field when it appears in three or more methods (E11041)?
← Code Quality · Ref: Q1340
When the SAME injected type is requested via the method-local '!' suffix inside 3 or more methods of a single component, E11041 (INJECTION_PROMOTE_TO_FACADE) advises promoting that dependency to a component field. Repeating the local injection in every method is a smell: the dependency is clearly central to the component's purpose, so it belongs as a single declared field rather than being re-resolved on every call.
Fix: declare the dependency once as a field with '!' at component scope, then have each method use that field. If a component accumulates many such central dependencies, group the related collaborators behind one facade component and inject the facade instead.
See Q960 for the per-component injection-field limit (E11040). See Q311 for quality checks.
Example
defines module qa.quality.injection.promote defines component //Abstract contract for the central dependency. AuditService as abstract record() as abstract -> message as String default operator ? ConsoleAudit extends AuditService override record() -> message as String Stdout().println("[AUDIT] " + message) default operator ? //THE FIX: the AuditService is central to this component, so it is //declared ONCE as a field with '!' rather than re-injected locally //inside every method. Each method simply uses the field. OrderProcessor audit as AuditService! submit() -> label as String audit.record(`submit ${label}`) cancel() -> label as String audit.record(`cancel ${label}`) refund() -> label as String audit.record(`refund ${label}`) default operator ? defines application OrderApp register ConsoleAudit() as AuditService defines program //CORRECT: the central dependency is a single field, used by all methods. PromoteInjectionDemo() with application of OrderApp stdout <- Stdout() processor <- OrderProcessor() processor.submit("order-1") stdout.println("Central dependency promoted to a single field")
Common mistakes
E11041 — The same AuditService type is method-local injected ('!') inside three separate methods, signalling that it is central to the component. Promote it to a single component field declared once with '!', then have every method use that field. If many central dependencies pile up, group them behind one facade component. See ek9 -h E11041 for details.
Incorrect:
submit()
-> label as String
audit as AuditService!
audit.record(label)
cancel()
-> label as String
audit as AuditService!
audit.record(label)
refund()
-> label as String
audit as AuditService!
audit.record(label)
Correct:
audit as AuditService! submit() -> label as String audit.record(`submit ${label}`)
Other ways to ask this
- What triggers E11041 INJECTION_PROMOTE_TO_FACADE?
- Why does EK9 flag the same '!' injected type used in 3+ methods of one component?
- How do I fix a component that re-injects the same service in every method?
Coming from another language?
Java Spring: @Autowired on a field is conventional, but nothing stops repeated method-parameter injection or ObjectProvider lookups in every method; detected only by SonarQube/ArchUnit if configured. C#/.NET: Seemann recommends constructor injection of central dependencies but it is advisory. Go Wire: no per-method guidance. EK9: the compiler observes the repetition of a single injected type across 3+ methods and steers you to promote it to a field (E11041).
Keywords: injection, field, promote, quality, E11041, dependency, method, component, facade