E11040: too many injected fields - use a facade component to group dependencies
What went wrong
A component has too many injected fields (more than 4). This violates the Single Responsibility Principle and indicates the component is coordinating too many dependencies.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 862 (Why does EK9 limit injection fields per component?)
Rationale
Robert C. Martin (Clean Code) advises against more than three arguments, and Mark Seemann (Dependency Injection in .NET) names a constructor with many dependencies "constructor over-injection" - a sign of too many responsibilities. Shatnawi (IEEE TSE 2010) derived risk thresholds for coupling metrics against fault-proneness. The limit of 4 is EK9's choice.
How to fix it
- Group related dependencies into a facade component
- Split the component into smaller, focused components
- Consider if some dependencies should be method parameters instead
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E11040: too many injected fields - use a facade component to group dependencies
#!ek9 defines module error.e11040.bad defines component ServiceA as abstract doWorkA() as abstract <- rtn as Boolean? default operator ? ServiceB as abstract doWorkB() as abstract <- rtn as Boolean? default operator ? ServiceC as abstract doWorkC() as abstract <- rtn as Boolean? default operator ? ServiceD as abstract doWorkD() as abstract <- rtn as Boolean? default operator ? ServiceE as abstract doWorkE() as abstract <- rtn as Boolean? default operator ? ExcessiveInjectionComponent //5 injection fields exceeds limit of 4 svcA as ServiceA! svcB as ServiceB! svcC as ServiceC! svcD as ServiceD! svcE as ServiceE! doAllWork() <- rtn as Boolean: true default operator ?
Fixed: Group related dependencies behind a facade component
#!ek9 defines module error.e11040.good defines component ServiceA as abstract doWorkA() as abstract <- rtn as Boolean? default operator ? ServiceB as abstract doWorkB() as abstract <- rtn as Boolean? default operator ? ServiceC as abstract doWorkC() as abstract <- rtn as Boolean? default operator ? ServiceD as abstract doWorkD() as abstract <- rtn as Boolean? default operator ? ServiceE as abstract doWorkE() as abstract <- rtn as Boolean? default operator ? Backend as abstract doBackendWork() as abstract <- rtn as Boolean? default operator ? FocusedComponent svcA as ServiceA! svcB as ServiceB! backend as Backend! doAllWork() <- rtn as Boolean: svcA.doWorkA() and svcB.doWorkB() and backend.doBackendWork() default operator ?
Questions and answers
These Q&A entries show E11040 and its fix: