Implements dependency injection with Brandi

Replaces manual manager creation with Brandi DI container
for improved dependency management and testability.

Removes the ManagerFactory and its usages.
This commit is contained in:
Janus C. H. Knudsen 2025-10-15 20:09:12 +02:00
parent b3b930c1f9
commit a80e4a7603
4 changed files with 93 additions and 132 deletions

View file

@ -3,7 +3,7 @@
* Configures all bindings with proper dependency resolution * Configures all bindings with proper dependency resolution
*/ */
import { Container } from 'brandi'; import { Container, injected } from 'brandi';
import { TOKENS } from './tokens'; import { TOKENS } from './tokens';
import { eventBus } from '../core/EventBus'; import { eventBus } from '../core/EventBus';
import { calendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
@ -57,57 +57,103 @@ export function createContainer(): Container {
container.bind(TOKENS.eventRendererStrategy).toConstant(new DateEventRenderer()); container.bind(TOKENS.eventRendererStrategy).toConstant(new DateEventRenderer());
} }
// Create instances manually and bind as constants (singletons) // Proper Brandi DI bindings with injected() declarations
// This is simpler than using decorators or complex factory setup
const eventManager = new EventManager(eventBus); // EventManager
container.bind(TOKENS.eventManager).toConstant(eventManager); injected(EventManager, TOKENS.eventBus);
container
.bind(TOKENS.eventManager)
.toInstance(EventManager)
.inSingletonScope();
const eventRenderer = new EventRenderingService(eventBus, eventManager, container.get(TOKENS.eventRendererStrategy)); // EventRenderingService
container.bind(TOKENS.eventRenderer).toConstant(eventRenderer); injected(EventRenderingService, TOKENS.eventBus, TOKENS.eventManager, TOKENS.eventRendererStrategy);
container
.bind(TOKENS.eventRenderer)
.toInstance(EventRenderingService)
.inSingletonScope();
const gridRenderer = new GridRenderer(container.get(TOKENS.columnRenderer)); // GridRenderer
container.bind(TOKENS.gridRenderer).toConstant(gridRenderer); injected(GridRenderer, TOKENS.columnRenderer);
container
.bind(TOKENS.gridRenderer)
.toInstance(GridRenderer)
.inSingletonScope();
const gridManager = new GridManager(gridRenderer); // GridManager
container.bind(TOKENS.gridManager).toConstant(gridManager); injected(GridManager, TOKENS.gridRenderer);
container
.bind(TOKENS.gridManager)
.toInstance(GridManager)
.inSingletonScope();
const scrollManager = new ScrollManager(); // ScrollManager (no dependencies)
container.bind(TOKENS.scrollManager).toConstant(scrollManager); container
.bind(TOKENS.scrollManager)
.toInstance(ScrollManager)
.inSingletonScope();
const navigationManager = new NavigationManager(eventBus, eventRenderer, gridRenderer); // NavigationManager
container.bind(TOKENS.navigationManager).toConstant(navigationManager); injected(NavigationManager, TOKENS.eventBus, TOKENS.eventRenderer, TOKENS.gridRenderer);
container
.bind(TOKENS.navigationManager)
.toInstance(NavigationManager)
.inSingletonScope();
const viewManager = new ViewManager(eventBus); // ViewManager
container.bind(TOKENS.viewManager).toConstant(viewManager); injected(ViewManager, TOKENS.eventBus);
container
.bind(TOKENS.viewManager)
.toInstance(ViewManager)
.inSingletonScope();
const dragDropManager = new DragDropManager(eventBus); // DragDropManager
container.bind(TOKENS.dragDropManager).toConstant(dragDropManager); injected(DragDropManager, TOKENS.eventBus);
container
.bind(TOKENS.dragDropManager)
.toInstance(DragDropManager)
.inSingletonScope();
const allDayManager = new AllDayManager(eventManager); // AllDayManager
container.bind(TOKENS.allDayManager).toConstant(allDayManager); injected(AllDayManager, TOKENS.eventManager);
container
.bind(TOKENS.allDayManager)
.toInstance(AllDayManager)
.inSingletonScope();
const resizeHandleManager = new ResizeHandleManager(); // ResizeHandleManager (no dependencies)
container.bind(TOKENS.resizeHandleManager).toConstant(resizeHandleManager); container
.bind(TOKENS.resizeHandleManager)
.toInstance(ResizeHandleManager)
.inSingletonScope();
const edgeScrollManager = new EdgeScrollManager(eventBus); // EdgeScrollManager
container.bind(TOKENS.edgeScrollManager).toConstant(edgeScrollManager); injected(EdgeScrollManager, TOKENS.eventBus);
container
.bind(TOKENS.edgeScrollManager)
.toInstance(EdgeScrollManager)
.inSingletonScope();
const dragHoverManager = new DragHoverManager(eventBus); // DragHoverManager
container.bind(TOKENS.dragHoverManager).toConstant(dragHoverManager); injected(DragHoverManager, TOKENS.eventBus);
container
.bind(TOKENS.dragHoverManager)
.toInstance(DragHoverManager)
.inSingletonScope();
const headerManager = new HeaderManager(container.get(TOKENS.headerRenderer)); // HeaderManager
container.bind(TOKENS.headerManager).toConstant(headerManager); injected(HeaderManager, TOKENS.headerRenderer);
container
.bind(TOKENS.headerManager)
.toInstance(HeaderManager)
.inSingletonScope();
// CalendarManager depends on multiple managers // CalendarManager
const calendarManager = new CalendarManager( injected(CalendarManager, TOKENS.eventBus, TOKENS.eventManager, TOKENS.gridManager, TOKENS.eventRenderer, TOKENS.scrollManager);
eventBus, container
eventManager, .bind(TOKENS.calendarManager)
gridManager, .toInstance(CalendarManager)
eventRenderer, .inSingletonScope();
scrollManager
);
container.bind(TOKENS.calendarManager).toConstant(calendarManager);
return container; return container;
} }

View file

@ -1,92 +0,0 @@
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 { CalendarManagers } from '../types/ManagerTypes';
import { HeaderManager } from '../managers/HeaderManager';
/**
* Factory for creating and managing calendar managers with proper dependency injection
*/
export class ManagerFactory {
private static instance: ManagerFactory;
private constructor() {}
public static getInstance(): ManagerFactory {
if (!ManagerFactory.instance) {
ManagerFactory.instance = new ManagerFactory();
}
return ManagerFactory.instance;
}
/**
* Create all managers with proper dependency injection
*/
public createManagers(eventBus: IEventBus): CalendarManagers {
// Create managers in dependency order
const eventManager = new EventManager(eventBus);
const eventRenderer = new EventRenderingService(eventBus, eventManager);
const gridManager = new GridManager();
const scrollManager = new ScrollManager();
const navigationManager = new NavigationManager(eventBus, eventRenderer);
const viewManager = new ViewManager(eventBus);
const dragDropManager = new DragDropManager(eventBus);
const allDayManager = new AllDayManager(eventManager);
const resizeHandleManager = new ResizeHandleManager();
const edgeScrollManager = new EdgeScrollManager(eventBus);
const dragHoverManager = new DragHoverManager(eventBus);
// HeaderManager now requires HeaderRenderer via DI - will be created in Brandi container
// const headerManager = new HeaderManager();
// CalendarManager depends on all other managers
const calendarManager = new CalendarManager(
eventBus,
eventManager,
gridManager,
eventRenderer,
scrollManager
);
return {
eventManager,
eventRenderer,
gridManager,
scrollManager,
navigationManager,
viewManager,
calendarManager,
dragDropManager,
allDayManager,
resizeHandleManager,
edgeScrollManager,
dragHoverManager,
headerManager: null as any // HeaderManager created in Brandi container
};
}
/**
* Initialize all managers in the correct order
*/
public async initializeManagers(managers: CalendarManagers): Promise<void> {
try {
await managers.calendarManager.initialize?.();
await managers.resizeHandleManager.initialize?.();
} catch (error) {
throw error;
}
}
}

View file

@ -52,6 +52,13 @@ async function initializeCalendar(): Promise<void> {
const calendarManager = container.get(TOKENS.calendarManager); const calendarManager = container.get(TOKENS.calendarManager);
const eventManager = container.get(TOKENS.eventManager); const eventManager = container.get(TOKENS.eventManager);
const resizeHandleManager = container.get(TOKENS.resizeHandleManager); const resizeHandleManager = container.get(TOKENS.resizeHandleManager);
const headerManager = container.get(TOKENS.headerManager);
const dragDropManager = container.get(TOKENS.dragDropManager);
const viewManager = container.get(TOKENS.viewManager);
const navigationManager = container.get(TOKENS.navigationManager);
const edgeScrollManager = container.get(TOKENS.edgeScrollManager);
const dragHoverManager = container.get(TOKENS.dragHoverManager);
const allDayManager = container.get(TOKENS.allDayManager);
// Initialize managers // Initialize managers
await calendarManager.initialize?.(); await calendarManager.initialize?.();
@ -84,7 +91,7 @@ async function initializeCalendar(): Promise<void> {
if (document.readyState === 'loading') { if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initializeCalendar().catch(error => { initializeCalendar().catch(error => {
}); });
}); });
} else { } else {
initializeCalendar().catch(error => { initializeCalendar().catch(error => {

View file

@ -1,7 +1,7 @@
import { calendarConfig } from '../core/CalendarConfig'; import { calendarConfig } from '../core/CalendarConfig';
import { ResourceCalendarData, CalendarView } from '../types/CalendarTypes'; import { ResourceCalendarData, CalendarView } from '../types/CalendarTypes';
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
import { ColumnRenderContext } from './ColumnRenderer'; import { ColumnRenderer, ColumnRenderContext } from './ColumnRenderer';
import { eventBus } from '../core/EventBus'; import { eventBus } from '../core/EventBus';
import { DateService } from '../utils/DateService'; import { DateService } from '../utils/DateService';
import { CoreEvents } from '../constants/CoreEvents'; import { CoreEvents } from '../constants/CoreEvents';