Step 1: Basic container with manual registration

This commit is contained in:
2025-10-24 00:57:40 -04:00
parent c3736be4f7
commit 3cd06003d5
5 changed files with 157 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
export class Database {
public connect() {
return '[Database] connected to database';
}
public query(sql) {
return `Executed [${sql}]`;
}
}

View 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}`;
}
}

View 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);
}
}

View 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;
}
}

View 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 {}