Why does EK9 limit injection fields per component to four?
← Dependency Injection · Ref: Q960
A component with more than 4 injection fields (marked with '!') triggers E11040. This enforces the Single Responsibility Principle based on Clean Architecture research.
WHY FOUR IS THE LIMIT
Research from Robert C. Martin (Clean Architecture), Mark Seemann (Dependency Injection in .NET), and Shatnawi et al. (IEEE 2010) demonstrates that components with 5+ dependencies are typically doing too much.
HOW TO FIX
Group related dependencies into a facade component:
- Identify clusters of services used together
- Create a facade that wraps the cluster
- Inject the facade instead of individual services
THIS EXAMPLE
The TransactionCoordinator component has 4 injection fields, exactly at the limit. This compiles because 4 is the maximum allowed.
The typicalError mutation adds a fifth injection field, triggering E11040.
See Q862 for injection field limit overview. See Q227 for compile-time DI validation.
Example
defines module qa.di.repeatedinjection defines component <?- Abstract service contracts for transaction processing. -?> LedgerService as abstract recordEntry() as abstract -> description as String default operator ? AuthorizationService as abstract authorize() as abstract -> principal as String <- allowed as Boolean? default operator ? MonitoringService as abstract logActivity() as abstract -> activity as String default operator ? DispatchService as abstract sendResult() as abstract -> recipient as String default operator ? ArchivingService as abstract archiveRecord() as abstract -> recordId as String default operator ? <?- Concrete implementations. -?> InMemoryLedger extends LedgerService override recordEntry() -> description as String Stdout().println("[LEDGER] " + description) default operator ? SimpleAuthorizer extends AuthorizationService override authorize() -> principal as String <- allowed as Boolean: true default operator ? ConsoleMonitor extends MonitoringService override logActivity() -> activity as String Stdout().println("[MONITOR] " + activity) default operator ? LocalDispatcher extends DispatchService override sendResult() -> recipient as String Stdout().println("[DISPATCH] " + recipient) default operator ? FileArchiver extends ArchivingService override archiveRecord() -> recordId as String Stdout().println("[ARCHIVE] " + recordId) default operator ? <?- TransactionCoordinator has exactly 4 injection fields. This is at the limit but valid. Adding a 5th triggers E11040. -?> TransactionCoordinator ledger as LedgerService! authorizer as AuthorizationService! monitor as MonitoringService! dispatcher as DispatchService! processTransaction() -> txnLabel as String permitted <- authorizer.authorize(txnLabel) if permitted ledger.recordEntry(txnLabel) monitor.logActivity(txnLabel) dispatcher.sendResult(txnLabel) default operator ? defines application TransactionApp register InMemoryLedger() as LedgerService register SimpleAuthorizer() as AuthorizationService register ConsoleMonitor() as MonitoringService register LocalDispatcher() as DispatchService defines program RepeatedInjectionDemo() stdout <- Stdout() stdout.println("TransactionCoordinator has 4 injection fields (at limit)") stdout.println("Adding a 5th would trigger E11040")
Common mistakes
E11040 — 5 injection fields exceeds the limit of 4. Extract related services into a facade component and inject the facade instead. See ek9 -h E11040 for details.
Incorrect:
ledger as LedgerService! authorizer as AuthorizationService! monitor as MonitoringService! dispatcher as DispatchService! archiver as ArchivingService!
Correct:
ledger as LedgerService! authorizer as AuthorizationService! monitor as MonitoringService! dispatcher as DispatchService!
Other ways to ask this
- What triggers E11040 EXCESSIVE_INJECTION_FIELDS?
- How many injected dependencies can a component have?
- How do I refactor a component with too many injections?
Coming from another language?
Java Spring: unlimited @Autowired fields. C#: unlimited DI constructor parameters. Python: no DI framework limits. Go Wire: no limits. EK9: compile-time limit of 4 injection fields per component, enforced by E11040.
Keywords: field, component, SRP, quality, limit, excessive, dependency, injection, E11040, facade