From f7c67e62536c0c9e555fe597c79135c2fa06602b Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Tue, 14 Oct 2025 22:53:28 +0200 Subject: [PATCH] Implements dependency injection with Brandi Introduces Brandi DI container for managing application dependencies. This change replaces the existing factory pattern with a proper dependency injection mechanism, improving code modularity, testability, and maintainability. It configures all managers and core services within the container, ensuring proper dependency resolution. --- package-lock.json | 7 ++++ package.json | 1 + src/di/container.ts | 85 +++++++++++++++++++++++++++++++++++++++++++++ src/di/tokens.ts | 43 +++++++++++++++++++++++ src/index.ts | 58 ++++++++++++++++++------------- 5 files changed, 170 insertions(+), 24 deletions(-) create mode 100644 src/di/container.ts create mode 100644 src/di/tokens.ts diff --git a/package-lock.json b/package-lock.json index d6b6f5a..cb509bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "@rollup/rollup-win32-x64-msvc": "^4.52.2", + "brandi": "^5.0.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "fuse.js": "^7.1.0" @@ -1124,6 +1125,12 @@ "require-from-string": "^2.0.2" } }, + "node_modules/brandi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/brandi/-/brandi-5.0.0.tgz", + "integrity": "sha512-oztvITQgvuFb2K+NWdHLx0mMH8TGO3ASrQ43FZzmfiq5rCj0DRlsuZ6Efi/yeu3hyGx/Y+Z1xLGp2qzDWpiNYA==", + "license": "ISC" + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", diff --git a/package.json b/package.json index 6beb926..c17df2f 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@rollup/rollup-win32-x64-msvc": "^4.52.2", + "brandi": "^5.0.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "fuse.js": "^7.1.0" diff --git a/src/di/container.ts b/src/di/container.ts new file mode 100644 index 0000000..572e0a8 --- /dev/null +++ b/src/di/container.ts @@ -0,0 +1,85 @@ +/** + * Brandi Dependency Injection Container Configuration + * Configures all bindings with proper dependency resolution + */ + +import { Container } from 'brandi'; +import { TOKENS } from './tokens'; +import { eventBus } from '../core/EventBus'; + +// Import all managers +import { EventManager } from '../managers/EventManager'; +import { EventRenderingService } from '../renderers/EventRendererManager'; +import { GridManager } from '../managers/GridManager'; +import { ScrollManager } from '../managers/ScrollManager'; +import { NavigationManager } from '../managers/NavigationManager'; +import { ViewManager } from '../managers/ViewManager'; +import { CalendarManager } from '../managers/CalendarManager'; +import { DragDropManager } from '../managers/DragDropManager'; +import { AllDayManager } from '../managers/AllDayManager'; +import { ResizeHandleManager } from '../managers/ResizeHandleManager'; +import { EdgeScrollManager } from '../managers/EdgeScrollManager'; +import { DragHoverManager } from '../managers/DragHoverManager'; +import { HeaderManager } from '../managers/HeaderManager'; + +/** + * Create and configure the DI container + * Using manual instantiation instead of automatic injection + */ +export function createContainer(): Container { + const container = new Container(); + + // Bind core services as constant + container.bind(TOKENS.eventBus).toConstant(eventBus); + + // Create instances manually and bind as constants (singletons) + // This is simpler than using decorators or complex factory setup + + const eventManager = new EventManager(eventBus); + container.bind(TOKENS.eventManager).toConstant(eventManager); + + const eventRenderer = new EventRenderingService(eventBus, eventManager); + container.bind(TOKENS.eventRenderer).toConstant(eventRenderer); + + const gridManager = new GridManager(); + container.bind(TOKENS.gridManager).toConstant(gridManager); + + const scrollManager = new ScrollManager(); + container.bind(TOKENS.scrollManager).toConstant(scrollManager); + + const navigationManager = new NavigationManager(eventBus, eventRenderer); + container.bind(TOKENS.navigationManager).toConstant(navigationManager); + + const viewManager = new ViewManager(eventBus); + container.bind(TOKENS.viewManager).toConstant(viewManager); + + const dragDropManager = new DragDropManager(eventBus); + container.bind(TOKENS.dragDropManager).toConstant(dragDropManager); + + const allDayManager = new AllDayManager(eventManager); + container.bind(TOKENS.allDayManager).toConstant(allDayManager); + + const resizeHandleManager = new ResizeHandleManager(); + container.bind(TOKENS.resizeHandleManager).toConstant(resizeHandleManager); + + const edgeScrollManager = new EdgeScrollManager(eventBus); + container.bind(TOKENS.edgeScrollManager).toConstant(edgeScrollManager); + + const dragHoverManager = new DragHoverManager(eventBus); + container.bind(TOKENS.dragHoverManager).toConstant(dragHoverManager); + + const headerManager = new HeaderManager(); + container.bind(TOKENS.headerManager).toConstant(headerManager); + + // CalendarManager depends on multiple managers + const calendarManager = new CalendarManager( + eventBus, + eventManager, + gridManager, + eventRenderer, + scrollManager + ); + container.bind(TOKENS.calendarManager).toConstant(calendarManager); + + return container; +} diff --git a/src/di/tokens.ts b/src/di/tokens.ts new file mode 100644 index 0000000..fd1db0b --- /dev/null +++ b/src/di/tokens.ts @@ -0,0 +1,43 @@ +/** + * Dependency Injection Tokens for Brandi Container + * Type-safe tokens for all managers and services + */ + +import { token } from 'brandi'; +import { IEventBus } from '../types/CalendarTypes'; +import { EventManager } from '../managers/EventManager'; +import { EventRenderingService } from '../renderers/EventRendererManager'; +import { GridManager } from '../managers/GridManager'; +import { ScrollManager } from '../managers/ScrollManager'; +import { NavigationManager } from '../managers/NavigationManager'; +import { ViewManager } from '../managers/ViewManager'; +import { CalendarManager } from '../managers/CalendarManager'; +import { DragDropManager } from '../managers/DragDropManager'; +import { AllDayManager } from '../managers/AllDayManager'; +import { ResizeHandleManager } from '../managers/ResizeHandleManager'; +import { EdgeScrollManager } from '../managers/EdgeScrollManager'; +import { DragHoverManager } from '../managers/DragHoverManager'; +import { HeaderManager } from '../managers/HeaderManager'; + +/** + * DI Tokens - Type-safe identifiers for dependency injection + */ +export const TOKENS = { + // Core services + eventBus: token('eventBus'), + + // Managers + eventManager: token('eventManager'), + eventRenderer: token('eventRenderer'), + gridManager: token('gridManager'), + scrollManager: token('scrollManager'), + navigationManager: token('navigationManager'), + viewManager: token('viewManager'), + calendarManager: token('calendarManager'), + dragDropManager: token('dragDropManager'), + allDayManager: token('allDayManager'), + resizeHandleManager: token('resizeHandleManager'), + edgeScrollManager: token('edgeScrollManager'), + dragHoverManager: token('dragHoverManager'), + headerManager: token('headerManager'), +}; diff --git a/src/index.ts b/src/index.ts index ffb3cd8..83d6b39 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,24 +2,25 @@ import { eventBus } from './core/EventBus'; import { calendarConfig } from './core/CalendarConfig'; import { CalendarTypeFactory } from './factories/CalendarTypeFactory'; -import { ManagerFactory } from './factories/ManagerFactory'; +import { createContainer } from './di/container'; +import { TOKENS } from './di/tokens'; import { URLManager } from './utils/URLManager'; -import { CalendarManagers } from './types/ManagerTypes'; +import { EventManager } from './managers/EventManager'; /** * Handle deep linking functionality after managers are initialized */ -async function handleDeepLinking(managers: CalendarManagers): Promise { +async function handleDeepLinking(eventManager: EventManager): Promise { try { const urlManager = new URLManager(eventBus); const eventId = urlManager.parseEventIdFromURL(); - + if (eventId) { console.log(`Deep linking to event ID: ${eventId}`); - + // Wait a bit for managers to be fully ready setTimeout(() => { - const success = managers.eventManager.navigateToEvent(eventId); + const success = eventManager.navigateToEvent(eventId); if (!success) { console.warn(`Deep linking failed: Event with ID ${eventId} not found`); } @@ -31,40 +32,49 @@ async function handleDeepLinking(managers: CalendarManagers): Promise { } /** - * Initialize the calendar application with simple direct calls + * Initialize the calendar application using Brandi DI */ async function initializeCalendar(): Promise { - try { // Use the singleton calendar configuration const config = calendarConfig; - + // Initialize the CalendarTypeFactory before creating managers CalendarTypeFactory.initialize(); - - // Create managers using factory pattern - const managerFactory = ManagerFactory.getInstance(); - const managers = managerFactory.createManagers(eventBus); - + + // Create Brandi DI container (all managers instantiated here) + const container = createContainer(); + // Enable debug mode for development eventBus.setDebug(true); - - // Initialize all managers - await managerFactory.initializeManagers(managers); - + + // Get managers from container + const calendarManager = container.get(TOKENS.calendarManager); + const eventManager = container.get(TOKENS.eventManager); + const resizeHandleManager = container.get(TOKENS.resizeHandleManager); + + // Initialize managers + await calendarManager.initialize?.(); + await resizeHandleManager.initialize?.(); + // Handle deep linking after managers are initialized - await handleDeepLinking(managers); - + await handleDeepLinking(eventManager); + // Expose to window for debugging (with proper typing) - (window as Window & { + (window as Window & { calendarDebug?: { eventBus: typeof eventBus; - } & CalendarManagers; + container: typeof container; + calendarManager: typeof calendarManager; + eventManager: typeof eventManager; + }; }).calendarDebug = { eventBus, - ...managers + container, + calendarManager, + eventManager, }; - + } catch (error) { throw error; }