E11040: too many injected fields - use a facade component to group dependencies

Category: E11xxx Code quality and IR analysis · Reported in phase: PRE_IR_CHECKS · Classification: EXCESSIVE_INJECTION_FIELDS · All error codes

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

  1. Group related dependencies into a facade component
  2. Split the component into smaller, focused components
  3. Consider if some dependencies should be method parameters instead

See also

  • E11040: excessive injection fields (this error)
  • E11041: promote to facade
  • E11022: class should be component

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: