phpFilesUnder('src/Kernel') as $file) { foreach ($this->importedClasses($file) as $import) { $importsFeatureModule = false; foreach ($featurePrefixes as $prefix) { if (str_starts_with($import, $prefix)) { $importsFeatureModule = true; break; } } if (!$importsFeatureModule) { continue; } $violations[] = $this->relativePath($file) . ' -> ' . $import; } } $this->assertNoArchitectureViolations( $violations, 'Kernel must not import feature modules directly', ); } public function testApplicationDomainAndInfrastructureUseOnlyApprovedCrossModuleContracts(): void { $violations = []; $allowedExternalImports = [ 'Identity' => [], 'Settings' => [], 'AuditLog' => [], 'Notifications' => [], 'Taxonomy' => [], 'Media' => [], ]; foreach (self::FEATURE_MODULES as $module) { $directories = [ $this->projectPath('src/' . $module . '/Application'), $this->projectPath('src/' . $module . '/Domain'), $this->projectPath('src/' . $module . '/Infrastructure'), ]; foreach ($directories as $directory) { if (!is_dir($directory)) { continue; } $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory)); foreach ($iterator as $fileInfo) { if (!$fileInfo->isFile() || $fileInfo->getExtension() !== 'php') { continue; } $file = $fileInfo->getPathname(); $imports = $this->importedClasses($file); foreach ($imports as $import) { if (!str_starts_with($import, 'Netig\\Netslim\\')) { continue; } if (str_starts_with($import, 'Netig\\Netslim\\Kernel\\') || str_starts_with($import, 'Netig\\Netslim\\' . $module . '\\')) { continue; } $isAllowed = false; foreach ($allowedExternalImports[$module] as $allowedPrefix) { if (str_starts_with($import, $allowedPrefix)) { $isAllowed = true; break; } } if ($isAllowed) { continue; } $violations[] = $this->relativePath($file) . ' -> ' . $import; } } } } $this->assertNoArchitectureViolations( $violations, 'Application, Domain and Infrastructure layers must only cross module boundaries through approved public contracts', ); } }