Vkladanie závislostí
Milan Herda
November 2025
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
// 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}`;
},
};
}
DB Query objekt
Session
Logiku pre vytvorenie User objektu
Cudziu factory funciu
DSN string
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> {
// ...
}// 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
A keď majú kontrolu funkcie, ...
...neznamená to zároveň,...
...že moja aplikácia...
Naše aplikácie sú zložené z množstva funkcí
Funkcia požiada klientov (volajúci kód), aby jej odovzdali (vložili, inject) tieto závislosti
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?
// 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}`;
},
};
}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);
// 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]);
}
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
Námietka: ako často potrebuješ vymeniť jednu implementáciu za inú?
Takmer vždy :)
Pretože dobrý programátor píše testy
// 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í
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.
Možno niekedy nabudúce
Kde to má koniec?