/** * @angular-modernizer/plugin-angular - Architecture Configuration * * Configurable architecture definitions for different project types. * Allows teams to define their own layer boundaries and architectural rules. */ import { ArchitectureLayer, ModuleType, type ArchitectureConfig, } from './types.js'; /** * Predefined architecture configurations for common project types */ export const ARCHITECTURE_PRESETS = { /** * Standard Angular Architecture (as used in the analyzed codebase) */ angular: { layers: { [ArchitectureLayer.CORE]: { canImportFrom: [], pathPatterns: ['**/core/**', '**/@core/**'], allowedTypes: [ ModuleType.SERVICE, ModuleType.COMPONENT, ModuleType.DIRECTIVE, ModuleType.PIPE, ModuleType.UTILITY, ], }, [ArchitectureLayer.SHARED]: { canImportFrom: [ArchitectureLayer.CORE], pathPatterns: [ '**/shared/**', '**/enterprise-shared/**', '**/@shared/**', '**/@enterprise-shared/**', ], allowedTypes: [ ModuleType.SERVICE, ModuleType.COMPONENT, ModuleType.DIRECTIVE, ModuleType.PIPE, ModuleType.UTILITY, ModuleType.MODEL, ], }, [ArchitectureLayer.FEATURES]: { canImportFrom: [ArchitectureLayer.CORE, ArchitectureLayer.SHARED], pathPatterns: ['**/features/**', '**/@features/**'], allowedTypes: [ ModuleType.SERVICE, ModuleType.COMPONENT, ModuleType.DIRECTIVE, ModuleType.PIPE, ModuleType.MODULE, ModuleType.MODEL, ], }, [ArchitectureLayer.MAIN]: { canImportFrom: [ ArchitectureLayer.CORE, ArchitectureLayer.SHARED, ArchitectureLayer.FEATURES, ], pathPatterns: [ '**/main/**', '**/app/**', '**/@main/**', '**/@app/**', '**/offline/**', ], allowedTypes: [ ModuleType.SERVICE, ModuleType.COMPONENT, ModuleType.DIRECTIVE, ModuleType.PIPE, ModuleType.MODULE, ModuleType.UTILITY, ModuleType.CONFIG, ], }, [ArchitectureLayer.OFFLINE]: { canImportFrom: [ ArchitectureLayer.CORE, ArchitectureLayer.SHARED, ArchitectureLayer.MAIN, ], pathPatterns: ['**/offline/**', '**/main/offline/**'], allowedTypes: [ ModuleType.SERVICE, ModuleType.COMPONENT, ModuleType.UTILITY, ModuleType.CONFIG, ], }, }, boundaries: { maxCircularDepth: 3, maxClassSize: 20, maxServiceDependencies: 5, }, } as ArchitectureConfig, /** * Clean Architecture (Hexagonal) */ cleanArchitecture: { layers: { [ArchitectureLayer.CORE]: { canImportFrom: [], pathPatterns: ['**/domain/**', '**/entities/**'], allowedTypes: [ModuleType.MODEL, ModuleType.UTILITY], }, [ArchitectureLayer.SHARED]: { canImportFrom: [ArchitectureLayer.CORE], pathPatterns: ['**/shared/**', '**/common/**'], allowedTypes: [ModuleType.UTILITY, ModuleType.SERVICE], }, [ArchitectureLayer.FEATURES]: { canImportFrom: [ArchitectureLayer.CORE, ArchitectureLayer.SHARED], pathPatterns: ['**/use-cases/**', '**/application/**'], allowedTypes: [ModuleType.SERVICE, ModuleType.UTILITY], }, [ArchitectureLayer.MAIN]: { canImportFrom: [ ArchitectureLayer.CORE, ArchitectureLayer.SHARED, ArchitectureLayer.FEATURES, ], pathPatterns: [ '**/infrastructure/**', '**/adapters/**', '**/framework/**', ], allowedTypes: [ ModuleType.SERVICE, ModuleType.COMPONENT, ModuleType.DIRECTIVE, ModuleType.PIPE, ModuleType.CONFIG, ], }, [ArchitectureLayer.OFFLINE]: { canImportFrom: [ ArchitectureLayer.CORE, ArchitectureLayer.SHARED, ArchitectureLayer.FEATURES, ArchitectureLayer.MAIN, ], pathPatterns: ['**/offline/**', '**/persistence/**'], allowedTypes: [ModuleType.SERVICE, ModuleType.UTILITY], }, }, boundaries: { maxCircularDepth: 2, maxClassSize: 15, maxServiceDependencies: 3, }, } as ArchitectureConfig, /** * Onion Architecture */ onionArchitecture: { layers: { [ArchitectureLayer.CORE]: { canImportFrom: [], pathPatterns: ['**/domain/**', '**/core/**'], allowedTypes: [ModuleType.MODEL, ModuleType.SERVICE], }, [ArchitectureLayer.SHARED]: { canImportFrom: [ArchitectureLayer.CORE], pathPatterns: ['**/application/**', '**/services/**'], allowedTypes: [ModuleType.SERVICE, ModuleType.UTILITY], }, [ArchitectureLayer.FEATURES]: { canImportFrom: [ArchitectureLayer.CORE, ArchitectureLayer.SHARED], pathPatterns: ['**/infrastructure/**', '**/external/**'], allowedTypes: [ ModuleType.SERVICE, ModuleType.COMPONENT, ModuleType.UTILITY, ], }, [ArchitectureLayer.MAIN]: { canImportFrom: [ ArchitectureLayer.CORE, ArchitectureLayer.SHARED, ArchitectureLayer.FEATURES, ], pathPatterns: ['**/presentation/**', '**/ui/**', '**/controllers/**'], allowedTypes: [ ModuleType.COMPONENT, ModuleType.DIRECTIVE, ModuleType.PIPE, ModuleType.SERVICE, ], }, [ArchitectureLayer.OFFLINE]: { canImportFrom: [ ArchitectureLayer.CORE, ArchitectureLayer.SHARED, ArchitectureLayer.FEATURES, ArchitectureLayer.MAIN, ], pathPatterns: ['**/persistence/**', '**/repositories/**'], allowedTypes: [ModuleType.SERVICE, ModuleType.UTILITY], }, }, boundaries: { maxCircularDepth: 2, maxClassSize: 12, maxServiceDependencies: 4, }, } as ArchitectureConfig, /** * Custom configuration for teams to define their own architecture */ custom: { layers: {}, boundaries: { maxCircularDepth: 3, maxClassSize: 20, maxServiceDependencies: 5, }, } as ArchitectureConfig, }; /** * Architecture Configuration Builder * Allows teams to create custom architecture configurations */ export class ArchitectureConfigBuilder { private config: ArchitectureConfig = { layers: {}, boundaries: { maxCircularDepth: 3, maxClassSize: 20, maxServiceDependencies: 5, }, }; /** * Add a layer definition */ addLayer( layer: ArchitectureLayer, canImportFrom: ArchitectureLayer[], pathPatterns: string[], allowedTypes: ModuleType[], ): this { this.config.layers[layer] = { canImportFrom, pathPatterns, allowedTypes, }; return this; } /** * Set boundary rules */ setBoundaries(boundaries: { maxCircularDepth?: number; maxClassSize?: number; maxServiceDependencies?: number; }): this { this.config.boundaries = { ...this.config.boundaries, ...boundaries, }; return this; } /** * Build the configuration */ build(): ArchitectureConfig { return { ...this.config }; } /** * Create from preset */ static fromPreset( presetName: keyof typeof ARCHITECTURE_PRESETS, ): ArchitectureConfigBuilder { const preset = ARCHITECTURE_PRESETS[presetName]; const builder = new ArchitectureConfigBuilder(); // Copy preset configuration // Disable no-unsafe-assignment: JSON.parse() returns 'any' but preset is a statically typed ArchitectureConfig from ARCHITECTURE_PRESETS (validated at compile time) builder.config = JSON.parse(JSON.stringify(preset)); // eslint-disable-line @typescript-eslint/no-unsafe-assignment return builder; } } /** * Default configuration (Angular preset) */ export const DEFAULT_ARCHITECTURE_CONFIG = ARCHITECTURE_PRESETS.angular;