Skip to content

Dependency Injection ​

Overview ​

Dependency Injection is giving classes the instances that depends on rather than letting these classes creating their dependencies inside.

Dependencies are usually interfaces (contracts) not classes.

Importance ​

  • Decouple components.
  • More testable, you can inject mocks.

Example ​

  • PlanController depends on IPlanService interface.
  • An implementation of the IPlanService interface is injected, given, provided to PlanController.
C#
public class PlanController(IPlanService planService) : ControllerBase, IPlanController
{
	// planService instance is injected to PlanController

	// Without dependency injection:
	// private readonly IPlanService _planService = new PlanService(); // Concrete creation (tightly coupled)
}

// Which instance should be passed? configured in Program.cs:
services.AddSingleton<IPlanService, PlanService>();
Java
@ApplicationScoped
class DefaultPlanController implements PlanController {

	@Inject
	PlanService planService; // PlanService is the refernce type (the interface), planService is the instance (the object) which it's value is of type DefaultPlanService which is an implementation to that interface.
	// PlanService planService = new DefaultPlanService(); // This is dependency concrete creation (tightly coupled)
}
Go
type planHandler struct {
	planService service.PlanService
	logger      logs.Logger
}

func NewPlanHandler(service service.PlanService, logger logs.Logger) PlanHandler {
	return &planHandler{planService: service, logger: logger}
}
// an instance of type PlanService is injected to PlanHandler in main (wiring)
TypeScript
@Controller('plans')
export class DefaultPlansController implements PlansController {
  constructor(@Inject('PlansService') private readonly plansService: PlansService) {}
}

// Definition in factory.ts

@Module({
  imports: [],
  controllers: [
    DefaultPlansController,
  ],
  providers: [
    { provide: 'PlansService', useClass: DefaultPlansService },
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(AppMiddleware).exclude('/health', '/swagger', '/api-docs').forRoutes('*path');
  }
}
Python
def get_plan_service() -> PlanService:
    from infra.factory import App
    return App.plan_service

@cbv(router)
class DefaultPlanRouter(metaclass=ProtocolEnforcer, protocol=PlanRouter):
    def __init__(self, plan_service: PlanService = Depends(get_plan_service)):
        self.plan_service = plan_service

# Definition in factory.py
from feat.plan.plan_service import DefaultPlanService, PlanService

class App():
    plan_repo: PlanRepo = DefaultPlanRepo()
    plan_members_repo: PlanMembersRepo = DefaultPlanMembersRepo()
    user_repo: UserRepo = DefaultUserRepo()
    plan_service: PlanService = DefaultPlanService(plan_repo=plan_repo, plan_members_repo=plan_members_repo, user_repo=user_repo,

See ​