Bird
Raised Fist0
LLDsystem_design~20 mins

Dependency injection framework in LLD - Practice Problems & Coding Challenges

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Challenge - 5 Problems
🎖️
Dependency Injection Mastery
Get all challenges correct to earn this badge!
Test your skills under time pressure!
component_behavior
intermediate
2:00remaining
How does constructor injection behave in this example?

Consider a class Service that depends on Repository. The dependency is injected via the constructor. What will be the output when calling service.getData()?

LLD
class Repository {
  getData() {
    return 'data from repo';
  }
}

class Service {
  constructor(repo) {
    this.repo = repo;
  }
  getData() {
    return this.repo.getData();
  }
}

const repo = new Repository();
const service = new Service(repo);
console.log(service.getData());
ATypeError: this.repo.getData is not a function
B"undefined"
C"data from repo"
D"null"
Attempts:
2 left
💡 Hint

Think about how the Service class uses the injected Repository instance.

🧠 Conceptual
intermediate
2:00remaining
Which option correctly registers a singleton in a dependency injection container?

Given a simple dependency injection container, which option correctly registers a singleton instance of Logger so that all requests get the same instance?

LLD
class Logger {}

class Container {
  constructor() {
    this.services = new Map();
  }
  registerSingleton(name, factory) {
    const instance = factory();
    this.services.set(name, () => instance);
  }
  resolve(name) {
    return this.services.get(name)();
  }
}

const container = new Container();
Acontainer.registerSingleton('logger', new Logger());
Bcontainer.registerSingleton('logger', Logger);
Ccontainer.registerSingleton('logger', () => Logger);
Dcontainer.registerSingleton('logger', () => new Logger());
Attempts:
2 left
💡 Hint

The registerSingleton method expects a factory function that returns an instance.

🔍 Analysis
advanced
2:00remaining
Why does this circular dependency cause a runtime error?

Two classes A and B depend on each other via constructor injection. What error will occur when trying to instantiate A through the container?

LLD
class A {
  constructor(b) {
    this.b = b;
  }
}

class B {
  constructor(a) {
    this.a = a;
  }
}

class Container {
  constructor() {
    this.services = new Map();
  }
  register(name, factory) {
    this.services.set(name, factory);
  }
  resolve(name) {
    const factory = this.services.get(name);
    return factory(this.resolve(name === 'A' ? 'B' : 'A'));
  }
}

const container = new Container();
container.register('A', (b) => new A(b));
container.register('B', (a) => new B(a));
container.resolve('A');
ARangeError: Maximum call stack size exceeded
BReferenceError: b is not defined
CTypeError: factory is not a function
DNo error, returns instance of A
Attempts:
2 left
💡 Hint

Think about what happens when resolve calls itself recursively without a stop condition.

state_output
advanced
2:00remaining
What is the state of the container after registering and resolving a transient service?

Given a container that registers transient services (new instance every resolve), what will be the output of comparing two resolved instances?

LLD
class Service {}

class Container {
  constructor() {
    this.factories = new Map();
  }
  registerTransient(name, factory) {
    this.factories.set(name, factory);
  }
  resolve(name) {
    return this.factories.get(name)();
  }
}

const container = new Container();
container.registerTransient('service', () => new Service());
const s1 = container.resolve('service');
const s2 = container.resolve('service');
console.log(s1 === s2);
Aundefined
Bfalse
Ctrue
DTypeError
Attempts:
2 left
💡 Hint

Transient services create a new instance each time they are resolved.

🧠 Conceptual
expert
2:00remaining
Which option best describes the benefit of using dependency injection frameworks?

Why do developers use dependency injection frameworks in large applications?

AThey allow components to be loosely coupled by managing dependencies externally, improving testability and flexibility.
BThey replace the need for any manual object creation or configuration.
CThey enforce strict type checking at runtime to prevent errors.
DThey automatically optimize application performance by caching all objects globally.
Attempts:
2 left
💡 Hint

Think about how dependencies are managed and how that affects code structure and testing.

Practice

(1/5)
1. What is the main purpose of a dependency injection framework?
easy
A. To store data permanently on disk
B. To automatically provide parts (dependencies) to your code
C. To make your code run faster by compiling it
D. To write all code manually without any helpers

Solution

  1. Step 1: Understand what dependency injection means

    Dependency injection means giving the parts your code needs automatically instead of creating them inside the code.
  2. Step 2: Identify the role of the framework

    A dependency injection framework helps by managing and providing these parts for you, making your code easier to change and test.
  3. Final Answer:

    To automatically provide parts (dependencies) to your code -> Option B
  4. Quick Check:

    Dependency injection = automatic parts supply [OK]
Hint: Think: Who gives parts to your code? The injector does! [OK]
Common Mistakes:
  • Confusing dependency injection with data storage
  • Thinking it speeds up code execution directly
  • Believing it replaces manual coding completely
2. Which of the following is the correct way to register a service in a dependency injection framework?
easy
A. injector.register(ServiceClass)
B. ServiceClass.inject()
C. register.injector(ServiceClass)
D. ServiceClass.register()

Solution

  1. Step 1: Recall the registration syntax

    In most dependency injection frameworks, you register a service by calling a method on the injector object and passing the service class.
  2. Step 2: Match the correct syntax

    The correct syntax is injector.register(ServiceClass), which tells the injector to manage that service.
  3. Final Answer:

    injector.register(ServiceClass) -> Option A
  4. Quick Check:

    Register service = injector.register() [OK]
Hint: Register services by calling register on the injector [OK]
Common Mistakes:
  • Calling register on the service class instead of injector
  • Mixing method order or names
  • Using non-existent methods like inject() on service
3. Given the code below, what will serviceA.getName() output?
class ServiceA {
  getName() { return 'Service A'; }
}

injector.register(ServiceA);
const serviceA = injector.get(ServiceA);
console.log(serviceA.getName());
medium
A. null
B. undefined
C. Error: Service not found
D. Service A

Solution

  1. Step 1: Understand registration and retrieval

    The code registers ServiceA with the injector, then asks the injector to give an instance of ServiceA.
  2. Step 2: Check the method call on the instance

    The instance has a method getName() that returns the string 'Service A'. So calling serviceA.getName() returns 'Service A'.
  3. Final Answer:

    Service A -> Option D
  4. Quick Check:

    Registered service returns its name [OK]
Hint: Registered services return their methods normally [OK]
Common Mistakes:
  • Assuming injector.get returns undefined or null
  • Forgetting to register before getting
  • Expecting an error without registration
4. Identify the error in the following code snippet using a dependency injection framework:
class ServiceB {}

const serviceB = injector.get(ServiceB);
injector.register(ServiceB);
medium
A. ServiceB is registered after trying to get it
B. ServiceB class is missing a constructor
C. injector.get should be injector.fetch
D. injector.register should be called twice

Solution

  1. Step 1: Check the order of registration and retrieval

    The code tries to get ServiceB from the injector before registering it, which causes an error because the injector doesn't know about ServiceB yet.
  2. Step 2: Confirm correct usage order

    Services must be registered before they can be retrieved from the injector.
  3. Final Answer:

    ServiceB is registered after trying to get it -> Option A
  4. Quick Check:

    Register before get = correct order [OK]
Hint: Always register before getting a service [OK]
Common Mistakes:
  • Trying to get service before registration
  • Confusing method names like get vs fetch
  • Thinking constructor is required for registration
5. You want to inject a Logger service into a UserService using a dependency injection framework. Which approach correctly applies dependency injection to make UserService easier to test?
class Logger {
  log(msg) { console.log(msg); }
}

class UserService {
  constructor(logger) {
    this.logger = logger;
  }
  createUser(name) {
    this.logger.log(`User ${name} created`);
  }
}

injector.register(Logger);
injector.register(UserService);

How should you get UserService with Logger injected?
hard
A. const userService = injector.get(Logger);
B. const userService = new UserService(new Logger());
C. const userService = injector.get(UserService);
D. const userService = UserService();

Solution

  1. Step 1: Understand constructor injection

    UserService expects a Logger instance in its constructor. The injector knows how to create Logger and UserService because both are registered.
  2. Step 2: Use injector to get UserService with dependencies

    Calling injector.get(UserService) lets the injector create UserService and automatically provide Logger to it.
  3. Final Answer:

    const userService = injector.get(UserService); -> Option C
  4. Quick Check:

    Injector provides dependencies via constructor [OK]
Hint: Get the service from injector to auto-inject dependencies [OK]
Common Mistakes:
  • Manually creating dependencies instead of using injector
  • Getting Logger instead of UserService
  • Calling UserService without new keyword