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