Dependency Injection
Vkladanie závislostí
Milan Herda
November 2025
- DI
- IoC
- DIP
- Dependency Injection
- Inversion of Control
- Dependency Inversion Principle

O čom budeme rozprávať
- podobá sa na iné C-čkovské jazyky
- vyzerá ľahký na naučenie
- treba investovať veľa času na zorientovanie sa v ekosystéme
- veľa sa toho deje
- nezostáva čas sa zamyslieť nad architektúrou
Prečo o tom budeme rozprávať
JavaScript má problémy
DI na vedľajšej koľaji
// src/services/userRepository.ts
export async function getCurrentUser(): Promise<User> {
// ...
}
Netušíme
Nie je to napísané v jej podpise a nemá dokumentačný komentár
Čo táto funkcia potrebuje k svojmu behu?
// src/services/userRepository.ts
export async function getCurrentUser(): Promise<User> {
const db = createDbConnection("mysql://user:password@localhost:3306/mydb");
const rows = await db
.select("*")
.from("users")
.where("id = :id", { id: session.userId })
.execute<UserData>();
if (rows.length === 0) {
throw new Error("User not found");
}
const userData = rows[0];
return {
id: userData.id,
name: userData.name,
surname: userData.surname,
email: userData.email,
getFullName() {
return `${this.name} ${this.surname}`;
},
};
}
Čo táto funkcia potrebuje k svojmu behu?
DB Query objekt
Session
Logiku pre vytvorenie User objektu
Cudziu factory funciu
DSN string
Funkcia tieto objekty a functionalitu potrebuje
Vyžaduje ich, aby mohla fungovať správne
Závisí na nich
Sú to jej závislosti
Dependency Injection
// src/services/userRepository.ts
export async function getCurrentUser(): Promise<User> {
// ...
}- nevieme, čo všetko potrebujeme skôr, než funkciu zavoláme
- prepoužitie je problematické
- ťažké povedať, čo potrebuje na fungovanie
- podobný kód môže byť zduplikovaný v iných funkciách
- aj zmena, ktorá sa netýka primárnej zodpovednosti sa musí robiť v tele tejto funkcie
Je problém, keď závislosti nevidíme
// src/services/userRepository.ts
export async function getCurrentUser(): Promise<User> {
const db = createDbConnection("mysql://user:password@localhost:3306/mydb");
const rows = await db
.select("*")
.from("users")
.where("id = :id", { id: session.userId })
.execute<UserData>();
if (rows.length === 0) {
throw new Error("User not found");
}
const userData = rows[0];
return {
id: userData.id,
name: userData.name,
surname: userData.surname,
email: userData.email,
getFullName() {
return `${this.name} ${this.surname}`;
},
};
}
Táto funkcia
Čo má kontrolu nad závislosťami?

A keď majú kontrolu funkcie, ...
...neznamená to zároveň,...
...že moja aplikácia...
nemá kontrolu?
Naše aplikácie sú zložené z množstva funkcí
- vezmeme kontrolu z malých jednotiek a dáme ju veľkým

Inversion of Control
- funkcia si nebude závislosti manažovať sama
- požiada o ne
- nestará sa o pôvod závislostí
- len dá vedieť volajúcemu, čo potrebuje
Funkcia požiada klientov (volajúci kód), aby jej odovzdali (vložili, inject) tieto závislosti
Inversion of Control
Dependency Injection
// src/services/userRepository.ts
export async function getCurrentUser(db: DbQuery, userId: number): Promise<User> {
const rows = await db
.select("*")
.from("users")
.where("id = :id", { id: userId })
.execute<UserData>();
if (rows.length === 0) {
throw new Error("User not found");
}
const userData = rows[0];
return {
id: userData.id,
name: userData.name,
surname: userData.surname,
email: userData.email,
getFullName() {
return `${this.name} ${this.surname}`;
},
};
}
Čo s touto "závislosťou" na algoritme?
Presun závislostí do parametrov
// src/services/userRepository.ts
export async function getCurrentUser(db: DbQuery, userId: number, createUser: UserFactory): Promise<User> {
const rows = await db
.select("*")
.from("users")
.where("id = :id", { id: userId })
.execute<UserData>();
if (rows.length === 0) {
throw new Error("User not found");
}
return createUser(rows[0]);
}
// src/services/userFactory.ts
export type UserFactory = (data: UserData) => User;
function createUser(data: UserData): User {
return {
id: userData.id,
name: userData.name,
surname: userData.surname,
email: userData.email,
getFullName() {
return `${this.name} ${this.surname}`;
},
};
}Extrakcia skrytej závislosti
Aplikujeme
Single Responsibility Principle
A extrahujeme algoritmus do vlastnej funkcie a súboru
Použijeme základné techniky
SOLID princípy
// src/somewhere/else.ts
import session from './services/session';
import createUser from './services/userFactory';
import { getCurrentUser } from './services/userRepository';
const db = createDbConnection("mysql://user:password@localhost:3306/mydb");
const currentUser = getCurrentUser(db, session.userId, createUser);
Vkladanie závislostí
// src/services/userRepository.ts
export async function getCurrentUser(
db: DbQuery,
userId: number,
createUser: UserFactory
): Promise<User> {
const rows = await db
.select("*")
.from("users")
.where("id = :id", { id: userId })
.execute<UserData>();
if (rows.length === 0) {
throw new Error("User not found");
}
return createUser(rows[0]);
}
Čo keby sme kód napísali takto?
// src/services/userRepository.ts
import db from './.../dbConnection';
import createUser from './.../userFactory';
export async function getCurrentUser(
userId: number
): Promise<User> {
const rows = await db
.select("*")
.from("users")
.where("id = :id", { id: userId })
.execute<UserData>();
if (rows.length === 0) {
throw new Error("User not found");
}
return createUser(rows[0]);
}
Kód nám takto síce funguje, ale je tam problém
Funkcia teraz závisí na konkrétnej implementácii pre databázovú query a továrničku pre používateľa
V prípade, že by sme ich chceli vymeniť, musíme zasiahnúť do kódu userRepository
- piaty zo SOLID princípov
- učí nás, že máme závisieť na abstrakciách
- náš kód sa tak stáva viac konfigurovateľný
- pretože je oveľa jednoduchšie vymeniť abstrakciu, než konkrétnu implementáciu
Dependency Inversion Principle
Námietka: ako často potrebuješ vymeniť jednu implementáciu za inú?
Takmer vždy :)
Pretože dobrý programátor píše testy
Ktorý kód sa bude ľahšie unit-testovať?
// src/services/userRepository.ts
export async function getCurrentUser(
db: DbQuery,
userId: number,
createUser: UserFactory
): Promise<User> {
const rows = await db
.select("*")
.from("users")
.where("id = :id", { id: userId })
.execute<UserData>();
if (rows.length === 0) {
throw new Error("User not found");
}
return createUser(rows[0]);
}
// src/services/userRepository.ts
import db from './.../dbConnection';
import createUser from './.../userFactory';
export async function getCurrentUser(
userId: number
): Promise<User> {
const rows = await db
.select("*")
.from("users")
.where("id = :id", { id: userId })
.execute<UserData>();
if (rows.length === 0) {
throw new Error("User not found");
}
return createUser(rows[0]);
}
Musíme mockovať moduly
Žiadne mockovanie modulov


Inversion of Control hovorí o zobraní kontroly z malých jednotiek a jej presunu na tie väčšie (aplikácia, framework)
Dependency Injection je o poskytovaní závislostí funkciám, objektom a modulom. Je to v podstate len odovzdávanie argumentov.
Dependency Inversion Principle nás učí, aby sme záviseli na abstraktných veciach (interface, typ) namiesto konkrétnych implementácií
Čo sme sa dnes naučili

Pokiaľ zodpovedne vo svojom kóde aplikujem DI, IoC a DIP, tak...
Keď si všetky funkcie len pýtajú závislosti cez parametre, tak niečo niekde musí byť ultimatívne zodpovedné za ich vytváranie a manažment.
Service Container
Možno niekedy nabudúce
Kde to má koniec?
Ďakujem za pozornosť
Milan Herda
- programátor
- Alma Career (Profesia)
- Lead Engineer
- Vedúci "Expertného tímu pre frontend a JavaScript"
- školím a vzdelávam
- programujem webové hry
- mám rád spoločenské hry
- https://perunhq.org/
Dependency Injection
By Milan Herda
Dependency Injection
- 11