Command Palette

Search for a command to run...

PHASE 3Intermediate ~7 min· topic 5 of 7

Topic 3.5

Dependency Injection

In one line

The binding that holds LLD designs together: interface → implementation → constructor injection → Spring bean. It is the mechanism that makes strategy, port/adapter, and testability real.

0/7 · 0%

Think of it like this

A torch that takes batteries. The torch doesn't make its own battery; you put one in. You can use a normal battery, a rechargeable one, or a test battery, and the torch doesn't care.

Key ideas

  1. 01

    Constructor injection is the default: dependencies arrive as constructor params, fields are final.

  2. 02

    Interface + implementation pattern (ports & adapters): domain depends on the port; the adapter (Postgres, Redis, HTTP) implements it.

  3. 03

    Without DI, testing a service means mocking static calls; with DI you inject a fake implementation.

  4. 04

    Spring resolves the wiring at startup: @Component on implementations, constructor injection everywhere else.

  5. 05

    DIP is not the same as DI: DIP is the principle (depend on abstractions); DI is the mechanism (inject the concrete behind the abstraction).

Java / Spring map

  • →

    Spring: @Service CheckoutService(PaymentGateway gateway) — the container hands it the bean.

Code & diagrams

DependencyInjection.javajava

The full port/adapter chain the syllabus calls 'interface → implementation → constructor injection → Spring Bean'.

// 1. DOMAIN PORT (interface — the abstraction the domain depends on)
public interface TicketRepository {
  Optional<Ticket> findById(String id);
  Ticket save(Ticket t);
}

// 2. ADAPTER IMPLEMENTATION
@Repository
public class JpaTicketRepository implements TicketRepository {
  private final TicketJpaRepository jpa;
  public JpaTicketRepository(TicketJpaRepository jpa) { this.jpa = jpa; }  // framework's own DI
  public Optional<Ticket> findById(String id) { return jpa.findById(id); }
  public Ticket save(Ticket t) { return jpa.save(t); }
}

// 3. CONSTRUCTOR INJECTION INTO THE SERVICE
@Service
public class ParkingService {
  private final TicketRepository tickets;      // depends on the PORT
  public ParkingService(TicketRepository tickets) { this.tickets = tickets; }  // constructor injection

  public Ticket assign(String plate) {
    Ticket t = new Ticket(plate, System.currentTimeMillis());
    return tickets.save(t);                    // can't tell which adapter runs → testable
  }
}

// TEST — swap the adapter:
// ParkingService svc = new ParkingService(new InMemoryTicketRepository());
// No Spring, no database, no mocks. The design earns its keep in the test.

Explain without notes

01

What breaks in tests if TicketRepository is a concrete class with static methods?

Practice

01

Convert a service with new JdbcThing() inside to constructor-injected port/adapter.

Trade-offs

  • ↔

    Framework magic obscures wiring errors; constructor injection keeps it visible and compile-safe.

Completion checklist

  • I can draw interface → impl → constructor → Spring bean and say why each hop exists.

Back to phase