Step 1: Basic container with manual registration
This commit is contained in:
9
DependencyInjection/src/Step1/Database.ts
Normal file
9
DependencyInjection/src/Step1/Database.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export class Database {
|
||||
public connect() {
|
||||
return '[Database] connected to database';
|
||||
}
|
||||
|
||||
public query(sql) {
|
||||
return `Executed [${sql}]`;
|
||||
}
|
||||
}
|
||||
23
DependencyInjection/src/Step1/Logger.ts
Normal file
23
DependencyInjection/src/Step1/Logger.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export class Logger {
|
||||
private _category: string;
|
||||
|
||||
constructor(category: string) {
|
||||
this._category = category;
|
||||
}
|
||||
|
||||
public info(message: string, ...optionalArgs: unknown[]): void {
|
||||
const formattedMessage = this.formatMessage('INFO', message);
|
||||
|
||||
console.log(formattedMessage, ...optionalArgs);
|
||||
}
|
||||
|
||||
public error(message: string, ...optionalArgs: unknown[]): void {
|
||||
const formattedMessage = this.formatMessage('ERROR', message);
|
||||
|
||||
console.error(formattedMessage, ...optionalArgs);
|
||||
}
|
||||
|
||||
private formatMessage(level: string, message: string): string {
|
||||
return `${new Date().toISOString()} [${this._category}] [${level}] ${message}`;
|
||||
}
|
||||
}
|
||||
49
DependencyInjection/src/Step1/SimpleContainer.ts
Normal file
49
DependencyInjection/src/Step1/SimpleContainer.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* STEP 1: Basic DI Container
|
||||
*
|
||||
* Learning Goals:
|
||||
* - Understand the core concept of a container
|
||||
* - Register and resolve simple dependencies
|
||||
* - Manual instantiation
|
||||
*/
|
||||
|
||||
export class SimpleContainer {
|
||||
private readonly _services: Map<string, any>;
|
||||
|
||||
constructor() {
|
||||
this._services = new Map();
|
||||
}
|
||||
|
||||
/* Register a service
|
||||
* @param {string} name - Service name/identifier
|
||||
* @param {*} instance - The service instance
|
||||
*/
|
||||
public register(name: string, instance) {
|
||||
if (this._services.has(name)) {
|
||||
throw new Error(`Service [${name}] is already registered`);
|
||||
}
|
||||
|
||||
this._services.set(name, instance);
|
||||
console.log(`[SimpleContainer] Registered [${name}]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve (retrieve) a service
|
||||
* @param {string} name - Service name/identifier
|
||||
* @returns {*} The service instance
|
||||
*/
|
||||
public resolve(name) {
|
||||
if (!this._services.has(name)) {
|
||||
throw new Error(`Service [${name}] is not registered`);
|
||||
}
|
||||
|
||||
return this._services.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a service is registered
|
||||
*/
|
||||
public has(name): boolean {
|
||||
return this._services.has(name);
|
||||
}
|
||||
}
|
||||
20
DependencyInjection/src/Step1/UserService.ts
Normal file
20
DependencyInjection/src/Step1/UserService.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {Logger} from './Logger';
|
||||
import {Database} from './Database';
|
||||
|
||||
export class UserService {
|
||||
private _logger: Logger;
|
||||
private _database: Database;
|
||||
|
||||
constructor(logger: Logger, database: Database) {
|
||||
this._logger = logger;
|
||||
this._database = database;
|
||||
}
|
||||
|
||||
public getUser(id: number) {
|
||||
this._logger.info(`Getting user with id [${id}]`);
|
||||
|
||||
const result = this._database.query(`SELECT * FROM users WHERE id = ${id}`);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
56
DependencyInjection/src/Step1/index.ts
Normal file
56
DependencyInjection/src/Step1/index.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import {Logger} from './Logger';
|
||||
import {Database} from './Database';
|
||||
import {UserService} from './UserService.ts';
|
||||
import {SimpleContainer} from './SimpleContainer';
|
||||
|
||||
console.log('===== STEP 1: Basic DI Container =====\n');
|
||||
|
||||
// Create the Container
|
||||
const container = new SimpleContainer();
|
||||
|
||||
// Create instances manually
|
||||
const logger = new Logger('STEP 1');
|
||||
const database = new Database();
|
||||
const userService = new UserService(logger, database);
|
||||
|
||||
// Register them in the container
|
||||
container.register('logger', logger);
|
||||
container.register('database', database);
|
||||
container.register('userService', userService);
|
||||
|
||||
console.log('\n ---- User Services ---- \n');
|
||||
|
||||
const resolvedLogger = container.resolve('logger');
|
||||
resolvedLogger.info('Hello from DI container');
|
||||
|
||||
const resolvedUserService = container.resolve('userService');
|
||||
const user = resolvedUserService.getUser(123);
|
||||
|
||||
console.log(`Result [${user}]`);
|
||||
|
||||
// THE PROBLEM WITH THIS APPROACH
|
||||
console.log('\n--- Problems with Step 1 ---\n');
|
||||
console.log('❌ We still create instances manually');
|
||||
console.log('❌ We must register them in correct order (dependencies first)');
|
||||
console.log("❌ Container doesn't know about dependencies");
|
||||
console.log('❌ No automatic instantiation');
|
||||
console.log('\n👉 Step 2 will solve this with constructor injection!\n');
|
||||
|
||||
// ============================================================================
|
||||
// KEY LEARNINGS
|
||||
// ============================================================================
|
||||
/**
|
||||
* What we learned:
|
||||
*
|
||||
* 1. A DI container is essentially a registry/map of services
|
||||
* 2. We can register services with a name/key
|
||||
* 3. We can resolve (retrieve) services by name
|
||||
* 4. But we still create objects manually - not very useful yet!
|
||||
*
|
||||
* Next step: Make the container smart enough to:
|
||||
* - Understand dependencies
|
||||
* - Automatically instantiate classes
|
||||
* - Resolve dependency chains
|
||||
*/
|
||||
|
||||
export {}
|
||||
Reference in New Issue
Block a user