\section{DIC}

\begin{frame}{Mi az a Dependency Injection Container?}
  \begin{block}{DI Container (DIC) / IoC Container}
    \begin{itemize}
      \item Automatizálja a dependency injection-t
      \item Központi hely a függőségek kezelésére
      \item Automatikus dependency resolution
      \item Lifecycle management (singleton, transient, scoped)
      \item Inversion of Control (IoC) megvalósítása
    \end{itemize}
  \end{block}
  
  \begin{block}{Miért használjunk DI Container-t?}
    \begin{itemize}
      \item \textbf{Manuális DI problémái:} sok függőség esetén bonyolult az instanciálás
      \item \textbf{Automatizálás:} a container automatikusan feloldja a függőségeket
      \item \textbf{Konfiguráció:} egy helyen konfiguráljuk az összes service-t
      \item \textbf{Tesztelhetőség:} könnyű mock service-eket injektálni
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Probléma manuális DI-val (1/3)}
  \begin{alertblock}{Dependency hell - Alapvető szolgáltatások}
\small
\begin{lstlisting}[language=JavaScript]
const database = new Database(config.db);
const logger = new Logger(config.log);
const cache = new RedisCache(config.redis);

const userRepository = 
  new UserRepository(database, logger);
const productRepository = 
  new ProductRepository(database, logger);
const orderRepository = 
  new OrderRepository(database, logger);
\end{lstlisting}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Probléma manuális DI-val (2/3)}
  \begin{alertblock}{Dependency hell - Service-ek}
\small
\begin{lstlisting}[language=JavaScript]
const emailService = 
  new EmailService(config.email, logger);
const paymentService = 
  new PaymentService(config.payment, logger);

const userService = new UserService(
  userRepository, emailService, logger);
const productService = new ProductService(
  productRepository, cache, logger);
\end{lstlisting}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Probléma manuális DI-val (3/3)}
  \begin{alertblock}{Dependency hell - Komplex függőségek}
\small
\begin{lstlisting}[language=JavaScript]
const orderService = new OrderService(
  orderRepository, 
  productService,
  userService,
  paymentService,
  emailService,
  logger
);
// És még tovább...
\end{lstlisting}
  \end{alertblock}
  \begin{alertblock}{Problémák}
    Bonyolult, hibára hajlamos, nehéz karbantartani, függőségi sorrend fontos
  \end{alertblock}
\end{frame}

\begin{frame}{DI Container életciklusok}
  \begin{block}{Service Lifetimes}
    \begin{description}
      \item[Transient] Minden kérésnél új instance
        \begin{itemize}
          \item Lightweight, stateless service-ek
          \item Példa: validators, utilities
        \end{itemize}
      \item[Singleton] Egy instance az egész alkalmazásra
        \begin{itemize}
          \item Shared state, resource-intenzív
          \item Példa: database connection pool, logger, config
        \end{itemize}
      \item[Scoped] Egy instance per request/scope
        \begin{itemize}
          \item HTTP request cycle alatt ugyanaz
          \item Példa: database transaction, user context
        \end{itemize}
    \end{description}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Népszerű DI Container-ek Node.js-hez}
  \begin{block}{JavaScript/TypeScript DI library-k}
    \begin{description}
      \item[InversifyJS] TypeScript-friendly, decorator-alapú
      \item[Awilix] Lightweight, flexible, Node.js-specifikus
      \item[TSyringe] Microsoft TypeScript DI container
      \item[TypeDI] Decorator-alapú, TypeScript support
      \item[BottleJS] Egyszerű DI container pure JS-hez
    \end{description}
  \end{block}
  
  \begin{exampleblock}{Választás}
    TypeScript projekt: \textbf{InversifyJS} vagy \textbf{TSyringe}\\
    JavaScript projekt: \textbf{Awilix}\\
    Egyszerű projektek: manuális DI vagy \textbf{BottleJS}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Lightweight DI Container}
  \begin{block}{Telepítés}
\small
\begin{lstlisting}[language=bash]
npm install awilix
\end{lstlisting}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Awilix - Alapvető használat (1/2)}
  \begin{exampleblock}{Container létrehozása és singleton-ok}
\small
\begin{lstlisting}[language=JavaScript]
const { createContainer, asClass, 
  asFunction, asValue } = require('awilix');

const container = createContainer();

container.register({
  // Singleton - egy instance
  database: asClass(Database).singleton(),
  logger: asClass(Logger).singleton()
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Alapvető használat (2/3)}
  \begin{exampleblock}{Transient és Scoped}
\small
\begin{lstlisting}[language=JavaScript]
container.register({
  // Transient - minden resolve-nál új instance
  userService: asClass(UserService).transient(),
  
  // Scoped - scope-on belül ugyanaz
  orderService: asClass(OrderService).scoped()
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Alapvető használat (3/3)}
  \begin{exampleblock}{Value injection}
\small
\begin{lstlisting}[language=JavaScript]
container.register({
  // Value injection - konstans értékek
  config: asValue({
    dbUrl: process.env.DATABASE_URL,
    port: process.env.PORT || 3000
  })
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Service-ek használata (1/4)}
  \begin{exampleblock}{Osztály definíciók - Repository rész 1}
\small
\begin{lstlisting}[language=JavaScript]
class UserRepository {
  constructor({ database, logger }) {
    this.database = database;
    this.logger = logger;
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Service-ek használata (2/4)}
  \begin{exampleblock}{Osztály definíciók - Repository rész 2}
\small
\begin{lstlisting}[language=JavaScript]
  async findById(id) {
    this.logger.log(`Finding user ${id}`);
    return await this.database.query(
      'SELECT * FROM users WHERE id = ?', [id]
    );
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Service-ek használata (3/5)}
  \begin{exampleblock}{Osztály definíciók - Service rész 1}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
  constructor({ userRepository, emailService, 
    logger }) {
    this.userRepository = userRepository;
    this.emailService = emailService;
    this.logger = logger;
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Service-ek használata (4/5)}
  \begin{exampleblock}{Osztály definíciók - Service rész 2}
\small
\begin{lstlisting}[language=JavaScript]
  async createUser(userData) {
    this.logger.log('Creating user');
    const user = await this.userRepository
      .create(userData);
    await this.emailService.sendWelcomeEmail(
      user);
    return user;
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Service-ek használata (5/5)}
  \begin{exampleblock}{Dependency resolution}
\small
\begin{lstlisting}[language=JavaScript]
// Awilix automatikusan feloldja 
// a függőségeket
const userService = 
  container.resolve('userService');
// userRepository, emailService, logger 
// automatikusan injektálva
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Auto-loading (1/3)}
  \begin{exampleblock}{Setup}
\small
\begin{lstlisting}[language=JavaScript]
const { createContainer, asClass } = 
  require('awilix');

const container = createContainer();
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Auto-loading (2/4)}
  \begin{exampleblock}{Automatikus service regisztráció}
\small
\begin{lstlisting}[language=JavaScript]
container.loadModules([
  'services/**/*.js',
  'repositories/**/*.js',
  'controllers/**/*.js'
], {
  formatName: 'camelCase',
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Auto-loading (3/4)}
  \begin{exampleblock}{Resolver options}
\small
\begin{lstlisting}[language=JavaScript]
  resolverOptions: {
    lifetime: 'SINGLETON',
    register: asClass
  }
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Auto-loading (3/4)}
  \begin{exampleblock}{Egyedi konfiguráció}
\small
\begin{lstlisting}[language=JavaScript]
container.loadModules([
  ['services/**/*.js', 
    { register: asClass, lifetime: 'SINGLETON' }],
  ['repositories/**/*.js', 
    { register: asClass, lifetime: 'SCOPED' }],
  ['utils/**/*.js', 
    { register: asFunction, lifetime: 'SINGLETON' }]
]);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix - Auto-loading (4/4)}
  \begin{alertblock}{Fájl struktúra}
    \texttt{services/userService.js} $\rightarrow$ \texttt{userService}\\
    \texttt{repositories/userRepository.js} $\rightarrow$ \texttt{userRepository}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Awilix Express integráció (1/2)}
  \begin{exampleblock}{Setup és container}
\small
\begin{lstlisting}[language=JavaScript]
const express = require('express');
const { createContainer, asClass } = 
  require('awilix');
const { loadControllers, scopePerRequest } = 
  require('awilix-express');

const app = express();
const container = createContainer();
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix Express integráció (2/2)}
  \begin{exampleblock}{Service regisztráció}
\small
\begin{lstlisting}[language=JavaScript]
container.register({
  userService: asClass(UserService).singleton(),
  orderService: asClass(OrderService)
    .singleton()
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix Express - Middleware}
  \begin{exampleblock}{Scope és controller betöltés}
\small
\begin{lstlisting}[language=JavaScript]
// Scope per request middleware
app.use(scopePerRequest(container));

// Controller-ek automatikus betöltése
app.use(loadControllers('controllers/*.js', 
  { cwd: __dirname }));

app.listen(3000);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix Controller példa (1/4)}
  \begin{exampleblock}{controllers/userController.js - Import}
\small
\begin{lstlisting}[language=JavaScript]
const { createController } = 
  require('awilix-express');
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix Controller példa (2/4)}
  \begin{exampleblock}{Osztály definíció}
\small
\begin{lstlisting}[language=JavaScript]
class UserController {
  constructor({ userService, logger }) {
    // Awilix automatikusan injektálja
    this.userService = userService;
    this.logger = logger;
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix Controller példa (3/7)}
  \begin{exampleblock}{GetUser metódus - try block}
\small
\begin{lstlisting}[language=JavaScript]
  async getUser(req, res) {
    try {
      const user = await this.userService
        .getUserById(req.params.id);
      res.json(user);
    } catch (error) {
      this.logger.error(error);
\end{lstlisting}
  \end{exampleblock}
\end{frame}  

\begin{frame}[fragile]{Awilix Controller példa (4/7)}
  \begin{exampleblock}{GetUser metódus - error handling}
\small
\begin{lstlisting}[language=JavaScript]
      res.status(500).json({ 
        error: 'Internal server error' });
    }
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix Controller példa (5/7)}
  \begin{exampleblock}{CreateUser metódus - rész 1}
\small
\begin{lstlisting}[language=JavaScript]
  async createUser(req, res) {
    try {
      const user = await this.userService
        .createUser(req.body);
      res.status(201).json(user);
    } catch (error) {
      this.logger.error(error);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix Controller példa (6/7)}
  \begin{exampleblock}{CreateUser metódus - rész 2}
\small
\begin{lstlisting}[language=JavaScript]
      res.status(500).json({ 
        error: 'Internal server error' });
    }
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Awilix Controller példa (7/7)}
  \begin{exampleblock}{Route mapping}
\small
\begin{lstlisting}[language=JavaScript]
module.exports = createController(UserController)
  .prefix('/users')
  .get('/:id', 'getUser')
  .post('/', 'createUser');
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - TypeScript DI Container}
  \begin{block}{Telepítés}
\small
\begin{lstlisting}[language=bash]
npm install inversify reflect-metadata
\end{lstlisting}
  \end{block}
  
  \begin{exampleblock}{tsconfig.json beállítások}
\small
\begin{lstlisting}[language=JSON]
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Alapvető setup}
  \begin{exampleblock}{Setup}
\small
\begin{lstlisting}[language=JavaScript]
import 'reflect-metadata';
import { Container } from 'inversify';

const container = new Container();

// Az app belépési pontján
// import 'reflect-metadata' kell lennie legelső sorban
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Decorator-alapú DI (1/4)}
  \begin{exampleblock}{Symbol-ok definíciója}
\small
\begin{lstlisting}[language=JavaScript]
import { injectable, inject } from 'inversify';
import 'reflect-metadata';

const TYPES = {
  Database: Symbol.for('Database'),
  Logger: Symbol.for('Logger'),
  UserRepository: Symbol.for('UserRepository'),
  UserService: Symbol.for('UserService')
};
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Decorator-alapú DI (2/4)}
  \begin{exampleblock}{Database osztály}
\small
\begin{lstlisting}[language=JavaScript]
@injectable()
class Database {
  query(sql: string) { /* ... */ }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Decorator-alapú DI (2/3)}
  \begin{exampleblock}{Logger osztály}
\small
\begin{lstlisting}[language=JavaScript]
@injectable()
class Logger {
  log(message: string) { 
    console.log(message); 
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Decorator-alapú DI (3/4)}
  \begin{exampleblock}{UserRepository constructor}
\small
\begin{lstlisting}[language=JavaScript]
@injectable()
class UserRepository {
  constructor(
    @inject(TYPES.Database) 
      private database: Database,
    @inject(TYPES.Logger) 
      private logger: Logger
  ) {}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Decorator-alapú DI (4/4)}
  \begin{exampleblock}{UserRepository metódusok}
\small
\begin{lstlisting}[language=JavaScript]
  async findById(id: string) {
    this.logger.log(`Finding user ${id}`);
    return await this.database.query(
      `SELECT * FROM users WHERE id = ${id}`);
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Container konfiguráció (1/3)}
  \begin{exampleblock}{Setup}
\small
\begin{lstlisting}[language=JavaScript]
import { Container } from 'inversify';

const container = new Container();
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Container konfiguráció (2/3)}
  \begin{exampleblock}{Singleton és Transient binding}
\small
\begin{lstlisting}[language=JavaScript]
// Singleton binding
container.bind<Database>(TYPES.Database)
  .to(Database).inSingletonScope();
container.bind<Logger>(TYPES.Logger)
  .to(Logger).inSingletonScope();

// Transient binding (default)
container.bind<UserRepository>(
  TYPES.UserRepository).to(UserRepository);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Container konfiguráció (3/4)}
  \begin{exampleblock}{Scoped és Constant binding}
\small
\begin{lstlisting}[language=JavaScript]
// Scoped binding (request scope)
container.bind<UserService>(TYPES.UserService)
  .to(UserService).inRequestScope();

// Constant value
container.bind<Config>(TYPES.Config)
  .toConstantValue({
    dbUrl: process.env.DATABASE_URL,
    port: 3000
  });
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Container konfiguráció (4/4)}
  \begin{exampleblock}{Factory binding}
\small
\begin{lstlisting}[language=JavaScript]
// Factory binding
container.bind<EmailService>(TYPES.EmailService)
  .toFactory((context) => {
    return () => new EmailService(
      context.container.get(TYPES.Config));
  });

export { container, TYPES };
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Service használat (1/3)}
  \begin{exampleblock}{Service lekérése}
\small
\begin{lstlisting}[language=JavaScript]
import { container, TYPES } 
  from './container';

// Service lekérése
const userService = 
  container.get<UserService>(TYPES.UserService);
await userService.createUser({ 
  name: 'John', email: 'john@example.com' 
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Service használat (2/3)}
  \begin{exampleblock}{Express route-okban}
\small
\begin{lstlisting}[language=JavaScript]
app.get('/users/:id', async (req, res) => {
  const userService = 
    container.get<UserService>(TYPES.UserService);
  const user = await userService.getUserById(
    req.params.id);
  res.json(user);
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Service használat (3/4)}
  \begin{exampleblock}{Middleware factory}
\small
\begin{lstlisting}[language=JavaScript]
function injectService(type: symbol) {
  return (req, res, next) => {
    req.service = container.get(type);
    next();
  };
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Service használat (4/4)}
  \begin{exampleblock}{Middleware használat}
\small
\begin{lstlisting}[language=JavaScript]
app.get('/users/:id', 
  injectService(TYPES.UserService),
  async (req, res) => {
    const user = await req.service.getUserById(
      req.params.id);
    res.json(user);
  }
);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Interface binding (1/3)}
  \begin{exampleblock}{Interface definíció}
\small
\begin{lstlisting}[language=JavaScript]
interface ILogger {
  log(message: string): void;
  error(message: string): void;
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Interface binding (2/4)}
  \begin{exampleblock}{ConsoleLogger implementáció}
\small
\begin{lstlisting}[language=JavaScript]
@injectable()
class ConsoleLogger implements ILogger {
  log(message: string) { 
    console.log(message); 
  }
  error(message: string) { 
    console.error(message); 
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Interface binding (3/4)}
  \begin{exampleblock}{FileLogger implementáció}
\small
\begin{lstlisting}[language=JavaScript]
@injectable()
class FileLogger implements ILogger {
  log(message: string) { 
    /* write to file */ 
  }
  error(message: string) { 
    /* write to file */ 
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Interface binding (4/5)}
  \begin{exampleblock}{Container konfiguráció}
\small
\begin{lstlisting}[language=JavaScript]
const TYPES = { ILogger: Symbol.for('ILogger') };

// Development környezet
if (process.env.NODE_ENV === 'development') {
  container.bind<ILogger>(TYPES.ILogger)
    .to(ConsoleLogger).inSingletonScope();
} else {
  container.bind<ILogger>(TYPES.ILogger)
    .to(FileLogger).inSingletonScope();
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{InversifyJS - Interface binding (5/5)}
  \begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
@injectable()
class UserService {
  constructor(@inject(TYPES.ILogger) 
    private logger: ILogger) {}
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Container scope és lifecycle (1/2)}
  \begin{exampleblock}{Container setup}
\small
\begin{lstlisting}[language=JavaScript]
const { createContainer, asClass } = 
  require('awilix');
const { scopePerRequest } = 
  require('awilix-express');

const container = createContainer();
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Container scope és lifecycle (2/2)}
  \begin{exampleblock}{Lifetime regisztráció}
\small
\begin{lstlisting}[language=JavaScript]
container.register({
  // Singleton - egy instance az egész app-ra
  database: asClass(Database).singleton(),
  // Scoped - egy instance per HTTP request
  userContext: asClass(UserContext).scoped(),
  // Transient - mindig új instance
  validator: asClass(Validator).transient()
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Request scope - Használat (1/2)}
  \begin{exampleblock}{Middleware setup}
\small
\begin{lstlisting}[language=JavaScript]
app.use(scopePerRequest(container));

// Minden request új scope-ot kap
app.get('/users', (req, res) => {
  // req.container - scoped container
  const userContext = 
    req.container.resolve('userContext');
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Request scope - Használat (2/2)}
  \begin{exampleblock}{Scope életciklus}
\small
\begin{lstlisting}[language=JavaScript]
  // Ugyanaz a userContext instance 
  // a request során
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Testing DI Container-rel (1/5)}
  \begin{exampleblock}{Import és describe}
\small
\begin{lstlisting}[language=JavaScript]
const { createContainer, asClass, asValue } = 
  require('awilix');

describe('UserService', () => {
  let container;
  let userService;
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Testing DI Container-rel (2/5)}
  \begin{exampleblock}{BeforeEach setup}
\small
\begin{lstlisting}[language=JavaScript]
  beforeEach(() => {
    container = createContainer();
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Testing DI Container-rel (3/5)}
  \begin{exampleblock}{Mock repository}
\small
\begin{lstlisting}[language=JavaScript]
    const mockRepository = {
      findById: jest.fn().mockResolvedValue(
        { id: 1, name: 'John' }),
      create: jest.fn().mockResolvedValue(
        { id: 2, name: 'Jane' })
    };
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Testing DI Container-rel (4/5)}
  \begin{exampleblock}{Mock email és logger}
\small
\begin{lstlisting}[language=JavaScript]
    const mockEmailService = {
      sendWelcomeEmail: jest.fn()
        .mockResolvedValue(true)
    };
    const mockLogger = {
      log: jest.fn(), error: jest.fn()
    };
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Testing DI Container-rel (5/6)}
  \begin{exampleblock}{Container regisztráció}
\small
\begin{lstlisting}[language=JavaScript]
    container.register({
      userRepository: asValue(mockRepository),
      emailService: asValue(mockEmailService),
      logger: asValue(mockLogger),
      userService: asClass(UserService)
    });
    userService = container.resolve('userService');
  });
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Testing DI Container-rel (6/6)}
  \begin{exampleblock}{Teszt}
\small
\begin{lstlisting}[language=JavaScript]
  test('createUser sends welcome email', 
    async () => {
    await userService.createUser(
      { name: 'Jane', email: 'jane@ex.com' });
    expect(container.resolve('emailService')
      .sendWelcomeEmail).toHaveBeenCalled();
  });
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI Container projekt struktúra (1/3)}
  \begin{exampleblock}{Config és services}
\small
\begin{lstlisting}[language=bash]
src/
  ├── config/
  │   ├── database.js
  │   └── email.js
  ├── services/
  │   ├── userService.js
  │   ├── emailService.js
  │   └── authService.js
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI Container projekt struktúra (2/3)}
  \begin{exampleblock}{Repositories és controllers}
\small
\begin{lstlisting}[language=bash]
  ├── repositories/
  │   ├── userRepository.js
  │   └── orderRepository.js
  ├── controllers/
  │   ├── userController.js
  │   └── orderController.js
  ├── container.js
  └── app.js
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI Container projekt struktúra (3/4)}
  \begin{exampleblock}{container.js - Setup}
\small
\begin{lstlisting}[language=JavaScript]
const { createContainer, asClass, asValue } = 
  require('awilix');
const config = require('./config');

function setupContainer() {
  const container = createContainer();
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI Container projekt struktúra (4/5)}
  \begin{exampleblock}{container.js - LoadModules}
\small
\begin{lstlisting}[language=JavaScript]
  container.loadModules([
    'services/**/*.js',
    'repositories/**/*.js'
  ], {
    formatName: 'camelCase',
    resolverOptions: {
      lifetime: 'SINGLETON',
      register: asClass
    }
  });
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI Container projekt struktúra (5/5)}
  \begin{exampleblock}{container.js - Export}
\small
\begin{lstlisting}[language=JavaScript]
  container.register({ config: asValue(config) });
  return container;
}
module.exports = setupContainer;
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Advanced: Multi-tenancy DI (1/3)}
  \begin{exampleblock}{Setup}
\small
\begin{lstlisting}[language=JavaScript]
const { createContainer, asClass } = 
  require('awilix');

function tenantMiddleware(req, res, next) {
  const tenantId = req.headers['x-tenant-id'];
  
  // Tenant-specifikus scope
  req.tenantScope = req.container.createScope();
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Advanced: Multi-tenancy DI (2/3)}
  \begin{exampleblock}{Tenant regisztráció}
\small
\begin{lstlisting}[language=JavaScript]
  req.tenantScope.register({
    tenantId: asValue(tenantId),
    tenantDatabase: asClass(TenantDatabase)
      .scoped(),
    tenantConfig: asClass(TenantConfig).scoped()
  });
  next();
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Advanced: Multi-tenancy DI (3/4)}
  \begin{exampleblock}{Middleware setup}
\small
\begin{lstlisting}[language=JavaScript]
app.use(scopePerRequest(container));
app.use(tenantMiddleware);

app.get('/data', async (req, res) => {
  // Tenant-specifikus database
  const db = req.tenantScope.resolve(
    'tenantDatabase');
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Advanced: Multi-tenancy DI (4/4)}
  \begin{exampleblock}{Query használat}
\small
\begin{lstlisting}[language=JavaScript]
  const data = await db.query(
    'SELECT * FROM tenant_data');
  res.json(data);
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}{DI Container Best Practices}
  \begin{block}{Ajánlások}
    \footnotesize
    \begin{enumerate}
      \item \textbf{Egy container per alkalmazás} - singleton pattern
      \item \textbf{Centralizált konfiguráció} - egy helyen regisztráld a service-eket
      \item \textbf{Helyes lifetime választás}
        \begin{itemize}
          \item Singleton: stateless, expensive resources
          \item Scoped: request-specific state
          \item Transient: lightweight, stateless
        \end{itemize}
      \item \textbf{Ne add tovább a container-t} - ne injektáld service-ekbe (service locator antipattern)
      \item \textbf{Explicit függőségek} - konstruktor injection
      \item \textbf{Interface-alapú programozás} - TypeScript interface-ek
      \item \textbf{Testing} - mock service-ek könnyű injektálása
    \end{enumerate}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Antipattern - Service Locator (1/3)}
  \begin{alertblock}{Rossz: Container injection - Constructor}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
  constructor({ container }) {
    this.container = container;  // NE!
  }
\end{lstlisting}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Antipattern - Service Locator (2/3)}
  \begin{alertblock}{Rossz: Rejtett függőségek}
\small
\begin{lstlisting}[language=JavaScript]
  async createUser(userData) {
    // Rejtett függőségek
    const repo = this.container.resolve(
      'userRepository');
    const email = this.container.resolve(
      'emailService');
    // ...
  }
}
\end{lstlisting}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Antipattern - Service Locator (3/4)}
  \begin{exampleblock}{Jó: Explicit constructor}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
  constructor({ userRepository, emailService, 
    logger }) {
    this.userRepository = userRepository;
    this.emailService = emailService;
    this.logger = logger;
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Antipattern - Service Locator (4/4)}
  \begin{exampleblock}{Jó: Explicit függőségek}
\small
\begin{lstlisting}[language=JavaScript]
  async createUser(userData) {
    // Explicit függőségek
    const user = await this.userRepository
      .create(userData);
    await this.emailService.sendWelcome(user);
    return user;
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}{Mikor használj DI Container-t?}
  \begin{block}{Használd, ha:}
    \begin{itemize}
      \item Közepes vagy nagy projekt sok függőséggel
      \item TypeScript projektek (InversifyJS, TSyringe)
      \item Komplex service lifecycle management szükséges
      \item Enterprise alkalmazások
      \item Microservices architektúra
    \end{itemize}
  \end{block}
  
  \begin{block}{Ne használd, ha:}
    \begin{itemize}
      \item Kis projektek, kevés függőséggel
      \item Egyszerű CRUD alkalmazások
      \item Learning project - manuális DI jobb a megértéshez
      \item Overhead túl nagy a project méretéhez képest
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}{DI Container - Alternatívák}
  \begin{alertblock}{Alternatíva}
    Kis projektekhez: manuális DI factory pattern-nel vagy egyszerű service registry
  \end{alertblock}
\end{frame}

\begin{frame}{Összefoglalás (1/2)}
  \begin{block}{DI Container lényege}
    \begin{itemize}
      \item Automatizált dependency injection
      \item Központi konfiguráció
      \item Lifecycle management (singleton, scoped, transient)
      \item Inversion of Control (IoC)
    \end{itemize}
  \end{block}
  
  \begin{block}{Népszerű library-k}
    \begin{itemize}
      \item \textbf{Awilix} - Lightweight, JavaScript-friendly
      \item \textbf{InversifyJS} - TypeScript, decorator-alapú
      \item \textbf{TSyringe} - Microsoft TypeScript DI
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}{Összefoglalás (2/2)}
  \begin{block}{Előnyök}
    \begin{itemize}
      \item Automatikus dependency resolution
      \item Könnyű tesztelhetőség
      \item Tiszta architektúra
      \item Skálázható kódbázis
    \end{itemize}
  \end{block}
\end{frame}

