Skip to main content
Glama
architectureAnalyzer.js24.2 kB
export async function analyzeArchitectureCompliance(args) { const { projectPath, architectureType = 'clean', strictMode = true } = args; try { const analysis = { compliance: {}, violations: [], dependencies: {}, layering: {}, metrics: {}, recommendations: [], }; // Analyze project structure const structure = await analyzeProjectStructure(projectPath); // Check architecture compliance analysis.compliance = checkArchitectureCompliance(structure, architectureType); // Find violations analysis.violations = findArchitectureViolations(structure, architectureType, strictMode); // Analyze dependencies analysis.dependencies = analyzeDependencyFlow(structure); // Check layer separation analysis.layering = analyzeLayerSeparation(structure); // Calculate metrics analysis.metrics = calculateArchitectureMetrics(structure); // Generate recommendations analysis.recommendations = generateArchitectureRecommendations(analysis); // Generate compliance report const report = generateComplianceReport(analysis, architectureType); // Generate refactoring suggestions const refactoring = generateRefactoringSuggestions(analysis.violations); return { content: [ { type: 'text', text: JSON.stringify({ analysis, report, refactoring, score: calculateArchitectureScore(analysis), diagram: generateArchitectureDiagram(structure), }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error analyzing architecture compliance: ${error.message}`, }, ], }; } } async function analyzeProjectStructure(projectPath) { // Simulate project structure analysis return { layers: { presentation: { files: 45, components: ['pages', 'widgets', 'controllers', 'view_models'], dependencies: ['domain', 'flutter', 'state_management'], }, domain: { files: 28, components: ['entities', 'use_cases', 'repositories'], dependencies: [], // Should have no dependencies }, data: { files: 32, components: ['models', 'repositories', 'datasources', 'mappers'], dependencies: ['domain', 'dio', 'sqflite'], }, infrastructure: { files: 15, components: ['services', 'adapters', 'factories'], dependencies: ['domain', 'data'], }, }, modules: [ { name: 'auth', layers: ['presentation', 'domain', 'data'], crossReferences: ['user', 'profile'], }, { name: 'user', layers: ['presentation', 'domain', 'data'], crossReferences: ['auth'], }, { name: 'product', layers: ['presentation', 'domain', 'data'], crossReferences: ['cart', 'catalog'], }, ], violations: [], }; } function checkArchitectureCompliance(structure, architectureType) { const compliance = { overallCompliance: 0, layerCompliance: {}, principleCompliance: {}, patternCompliance: {}, }; if (architectureType === 'clean') { // Check Clean Architecture compliance compliance.layerCompliance = { presentation: checkPresentationLayerCompliance(structure.layers.presentation), domain: checkDomainLayerCompliance(structure.layers.domain), data: checkDataLayerCompliance(structure.layers.data), }; compliance.principleCompliance = { dependencyRule: checkDependencyRule(structure), singleResponsibility: checkSingleResponsibility(structure), interfaceSegregation: checkInterfaceSegregation(structure), dependencyInversion: checkDependencyInversion(structure), }; compliance.patternCompliance = { repository: checkRepositoryPattern(structure), useCase: checkUseCasePattern(structure), factory: checkFactoryPattern(structure), }; } else if (architectureType === 'mvvm') { compliance.layerCompliance = { model: checkModelCompliance(structure), view: checkViewCompliance(structure), viewModel: checkViewModelCompliance(structure), }; } else if (architectureType === 'mvc') { compliance.layerCompliance = { model: checkModelCompliance(structure), view: checkViewCompliance(structure), controller: checkControllerCompliance(structure), }; } // Calculate overall compliance const scores = Object.values(compliance.layerCompliance); compliance.overallCompliance = scores.reduce((a, b) => a + b, 0) / scores.length; return compliance; } function checkPresentationLayerCompliance(layer) { let score = 100; // Check for business logic in presentation if (layer.components.includes('business_logic')) { score -= 20; } // Check for direct data access if (layer.dependencies.includes('data')) { score -= 30; } // Check for proper state management if (!layer.dependencies.some(d => ['provider', 'bloc', 'riverpod', 'getx'].includes(d))) { score -= 10; } return Math.max(0, score); } function checkDomainLayerCompliance(layer) { let score = 100; // Domain should have no dependencies if (layer.dependencies.length > 0) { score -= layer.dependencies.length * 20; } // Check for required components const requiredComponents = ['entities', 'use_cases', 'repositories']; requiredComponents.forEach(comp => { if (!layer.components.includes(comp)) { score -= 20; } }); return Math.max(0, score); } function checkDataLayerCompliance(layer) { let score = 100; // Should depend only on domain const allowedDeps = ['domain', 'dio', 'sqflite', 'shared_preferences']; layer.dependencies.forEach(dep => { if (!allowedDeps.includes(dep)) { score -= 10; } }); // Check for required components if (!layer.components.includes('repositories')) { score -= 25; } if (!layer.components.includes('models')) { score -= 15; } return Math.max(0, score); } function findArchitectureViolations(structure, architectureType, strictMode) { const violations = []; // Check dependency violations if (structure.layers.domain.dependencies.length > 0) { violations.push({ type: 'dependency_violation', severity: 'critical', layer: 'domain', message: 'Domain layer has external dependencies', dependencies: structure.layers.domain.dependencies, fix: 'Remove all dependencies from domain layer', }); } // Check presentation accessing data directly if (structure.layers.presentation.dependencies.includes('data')) { violations.push({ type: 'layer_violation', severity: 'high', layer: 'presentation', message: 'Presentation layer directly accesses data layer', fix: 'Use domain layer use cases instead', }); } // Check for circular dependencies structure.modules.forEach(module => { module.crossReferences.forEach(ref => { const refModule = structure.modules.find(m => m.name === ref); if (refModule && refModule.crossReferences.includes(module.name)) { violations.push({ type: 'circular_dependency', severity: 'high', modules: [module.name, ref], message: `Circular dependency between ${module.name} and ${ref}`, fix: 'Extract common functionality to shared module', }); } }); }); // Check for god classes if (strictMode) { checkForGodClasses(structure, violations); checkForFeatureEnvy(structure, violations); checkForInappropriateDependencies(structure, violations); } return violations; } function checkForGodClasses(structure, violations) { // Simulate checking for god classes const suspectedGodClasses = [ { name: 'AppController', methods: 45, dependencies: 12 }, { name: 'UserService', methods: 38, dependencies: 8 }, ]; suspectedGodClasses.forEach(cls => { if (cls.methods > 20 || cls.dependencies > 5) { violations.push({ type: 'god_class', severity: 'medium', class: cls.name, metrics: { methods: cls.methods, dependencies: cls.dependencies, }, message: `${cls.name} has too many responsibilities`, fix: 'Split into smaller, focused classes', }); } }); } function checkForFeatureEnvy(structure, violations) { // Check for classes that use other classes' data excessively const featureEnvyInstances = [ { class: 'OrderController', accessing: 'UserModel', frequency: 15, }, ]; featureEnvyInstances.forEach(instance => { if (instance.frequency > 10) { violations.push({ type: 'feature_envy', severity: 'medium', class: instance.class, message: `${instance.class} accesses ${instance.accessing} excessively`, fix: 'Move behavior to the data owner or use proper abstraction', }); } }); } function checkForInappropriateDependencies(structure, violations) { const inappropriateDeps = [ { from: 'domain/entities/user.dart', imports: 'package:flutter/material.dart', }, { from: 'data/repositories/product_repository.dart', imports: 'presentation/widgets/product_card.dart', }, ]; inappropriateDeps.forEach(dep => { violations.push({ type: 'inappropriate_dependency', severity: 'critical', file: dep.from, import: dep.imports, message: 'Layer boundary violation', fix: 'Remove inappropriate import and use proper abstraction', }); }); } function analyzeDependencyFlow(structure) { const flow = { direction: 'inward', // Should be inward for clean architecture violations: [], graph: {}, }; // Build dependency graph Object.entries(structure.layers).forEach(([layer, data]) => { flow.graph[layer] = data.dependencies; }); // Check flow direction if (structure.layers.domain.dependencies.length > 0) { flow.direction = 'mixed'; flow.violations.push('Domain layer has outward dependencies'); } if (structure.layers.presentation.dependencies.includes('data')) { flow.direction = 'mixed'; flow.violations.push('Presentation bypasses domain layer'); } // Calculate metrics flow.metrics = { coupling: calculateCoupling(flow.graph), cohesion: calculateCohesion(structure), instability: calculateInstability(flow.graph), }; return flow; } function analyzeLayerSeparation(structure) { const separation = { score: 0, issues: [], boundaries: {}, }; // Check layer boundaries separation.boundaries = { presentationDomain: checkBoundary(structure.layers.presentation, structure.layers.domain), domainData: checkBoundary(structure.layers.domain, structure.layers.data), dataDomain: checkBoundary(structure.layers.data, structure.layers.domain), }; // Check for leaked abstractions if (structure.layers.presentation.components.includes('sql_queries')) { separation.issues.push({ type: 'leaked_abstraction', layer: 'presentation', issue: 'SQL queries in presentation layer', }); } // Check for proper interfaces if (!structure.layers.domain.components.includes('repositories')) { separation.issues.push({ type: 'missing_abstraction', layer: 'domain', issue: 'Missing repository interfaces', }); } // Calculate separation score separation.score = 100 - (separation.issues.length * 15); return separation; } function checkBoundary(fromLayer, toLayer) { return { clean: !fromLayer.dependencies.some(dep => toLayer.components.includes(dep) ), interfaces: true, // Assume interfaces are used abstraction: 'repository', // Type of abstraction used }; } function calculateArchitectureMetrics(structure) { const metrics = { modularity: 0, maintainability: 0, testability: 0, reusability: 0, complexity: 0, }; // Calculate modularity const totalFiles = Object.values(structure.layers).reduce((sum, layer) => sum + layer.files, 0); const avgFilesPerLayer = totalFiles / Object.keys(structure.layers).length; metrics.modularity = Math.min(100, (avgFilesPerLayer / totalFiles) * 300); // Calculate maintainability const violations = structure.violations?.length || 0; metrics.maintainability = Math.max(0, 100 - (violations * 10)); // Calculate testability const hasUseCases = structure.layers.domain.components.includes('use_cases'); const hasInterfaces = structure.layers.domain.components.includes('repositories'); metrics.testability = (hasUseCases ? 50 : 0) + (hasInterfaces ? 50 : 0); // Calculate reusability const domainIndependent = structure.layers.domain.dependencies.length === 0; metrics.reusability = domainIndependent ? 90 : 40; // Calculate complexity const avgDependencies = Object.values(structure.layers) .reduce((sum, layer) => sum + layer.dependencies.length, 0) / Object.keys(structure.layers).length; metrics.complexity = Math.max(0, 100 - (avgDependencies * 20)); return metrics; } function generateArchitectureRecommendations(analysis) { const recommendations = []; // Based on compliance scores Object.entries(analysis.compliance.layerCompliance).forEach(([layer, score]) => { if (score < 80) { recommendations.push({ category: 'compliance', layer, priority: score < 50 ? 'high' : 'medium', message: `${layer} layer compliance is low (${score}%)`, actions: getLayerImprovementActions(layer, score), }); } }); // Based on violations const criticalViolations = analysis.violations.filter(v => v.severity === 'critical'); if (criticalViolations.length > 0) { recommendations.push({ category: 'violations', priority: 'critical', message: `${criticalViolations.length} critical architecture violations found`, actions: criticalViolations.map(v => v.fix), }); } // Based on metrics if (analysis.metrics.testability < 70) { recommendations.push({ category: 'testability', priority: 'high', message: 'Low testability score', actions: [ 'Introduce use cases for business logic', 'Use dependency injection', 'Create interfaces for external dependencies', ], }); } if (analysis.dependencies.flow.direction !== 'inward') { recommendations.push({ category: 'dependencies', priority: 'critical', message: 'Dependency flow is not following inward direction', actions: [ 'Remove outward dependencies from inner layers', 'Use dependency inversion principle', 'Introduce proper abstractions', ], }); } return recommendations; } function getLayerImprovementActions(layer, score) { const actions = { presentation: [ 'Remove business logic from UI', 'Use proper state management', 'Access data only through domain layer', ], domain: [ 'Remove all external dependencies', 'Define clear use cases', 'Create repository interfaces', ], data: [ 'Implement repository pattern', 'Separate models from entities', 'Use data sources for external access', ], }; return actions[layer] || ['Review layer responsibilities']; } function generateComplianceReport(analysis, architectureType) { const report = { summary: { architecture: architectureType, overallScore: analysis.compliance.overallCompliance, status: getComplianceStatus(analysis.compliance.overallCompliance), date: new Date().toISOString(), }, details: { layerScores: analysis.compliance.layerCompliance, principleScores: analysis.compliance.principleCompliance, patternScores: analysis.compliance.patternCompliance, }, violations: { total: analysis.violations.length, bySeverity: groupViolationsBySeverity(analysis.violations), byType: groupViolationsByType(analysis.violations), }, metrics: analysis.metrics, recommendations: { immediate: analysis.recommendations.filter(r => r.priority === 'critical'), shortTerm: analysis.recommendations.filter(r => r.priority === 'high'), longTerm: analysis.recommendations.filter(r => r.priority === 'medium'), }, }; return report; } function getComplianceStatus(score) { if (score >= 90) return 'Excellent'; if (score >= 75) return 'Good'; if (score >= 60) return 'Fair'; if (score >= 40) return 'Poor'; return 'Critical'; } function groupViolationsBySeverity(violations) { return violations.reduce((acc, violation) => { acc[violation.severity] = (acc[violation.severity] || 0) + 1; return acc; }, {}); } function groupViolationsByType(violations) { return violations.reduce((acc, violation) => { acc[violation.type] = (acc[violation.type] || 0) + 1; return acc; }, {}); } function generateRefactoringSuggestions(violations) { const suggestions = []; // Group violations by type for batch refactoring const groupedViolations = {}; violations.forEach(violation => { if (!groupedViolations[violation.type]) { groupedViolations[violation.type] = []; } groupedViolations[violation.type].push(violation); }); // Generate refactoring plans Object.entries(groupedViolations).forEach(([type, typeViolations]) => { suggestions.push({ type, count: typeViolations.length, plan: generateRefactoringPlan(type, typeViolations), effort: estimateRefactoringEffort(type, typeViolations.length), impact: 'high', }); }); return { suggestions, totalEffort: suggestions.reduce((sum, s) => sum + s.effort.hours, 0), priority: determinRefactoringPriority(violations), }; } function generateRefactoringPlan(type, violations) { const plans = { dependency_violation: [ 'Identify improper dependencies', 'Create abstraction interfaces', 'Implement dependency inversion', 'Update imports throughout codebase', 'Run tests to verify functionality', ], layer_violation: [ 'Map current layer interactions', 'Identify bypass points', 'Introduce proper use cases', 'Refactor direct access to use domain layer', 'Update affected components', ], circular_dependency: [ 'Analyze dependency cycle', 'Identify shared functionality', 'Extract to common module', 'Update references in both modules', 'Remove circular references', ], god_class: [ 'Analyze class responsibilities', 'Group related methods', 'Extract to focused classes', 'Update dependencies', 'Refactor calling code', ], }; return plans[type] || ['Analyze specific violation', 'Plan refactoring approach', 'Implement changes']; } function estimateRefactoringEffort(type, count) { const baseEfforts = { dependency_violation: 4, layer_violation: 6, circular_dependency: 8, god_class: 12, feature_envy: 4, inappropriate_dependency: 2, }; const baseHours = baseEfforts[type] || 4; const totalHours = baseHours * count; return { hours: totalHours, days: Math.ceil(totalHours / 8), complexity: totalHours > 40 ? 'high' : totalHours > 16 ? 'medium' : 'low', }; } function determinRefactoringPriority(violations) { const criticalCount = violations.filter(v => v.severity === 'critical').length; const highCount = violations.filter(v => v.severity === 'high').length; if (criticalCount > 0) return 'immediate'; if (highCount > 3) return 'high'; if (violations.length > 5) return 'medium'; return 'low'; } function calculateArchitectureScore(analysis) { const weights = { compliance: 0.4, violations: 0.3, metrics: 0.2, dependencies: 0.1, }; const scores = { compliance: analysis.compliance.overallCompliance, violations: Math.max(0, 100 - (analysis.violations.length * 10)), metrics: Object.values(analysis.metrics).reduce((a, b) => a + b, 0) / Object.keys(analysis.metrics).length, dependencies: analysis.dependencies.flow.direction === 'inward' ? 100 : 50, }; const weightedScore = Object.entries(weights).reduce((total, [key, weight]) => { return total + (scores[key] * weight); }, 0); return { overall: Math.round(weightedScore), breakdown: scores, grade: getGrade(weightedScore), }; } function getGrade(score) { if (score >= 90) return 'A'; if (score >= 80) return 'B'; if (score >= 70) return 'C'; if (score >= 60) return 'D'; return 'F'; } function generateArchitectureDiagram(structure) { return { type: 'layered', layers: Object.keys(structure.layers).map(layer => ({ name: layer, components: structure.layers[layer].components, dependencies: structure.layers[layer].dependencies, })), mermaidCode: generateMermaidDiagram(structure), }; } function generateMermaidDiagram(structure) { let diagram = 'graph TD\n'; // Add layers Object.entries(structure.layers).forEach(([layer, data]) => { diagram += ` subgraph ${layer}\n`; data.components.forEach(comp => { diagram += ` ${layer}_${comp}[${comp}]\n`; }); diagram += ` end\n`; }); // Add dependencies Object.entries(structure.layers).forEach(([layer, data]) => { data.dependencies.forEach(dep => { if (structure.layers[dep]) { diagram += ` ${layer} --> ${dep}\n`; } }); }); return diagram; } // Helper functions function checkDependencyRule(structure) { // Domain should have no dependencies if (structure.layers.domain.dependencies.length === 0) { return 100; } return 0; } function checkSingleResponsibility(structure) { // Check if components are focused let score = 100; // Penalize for mixed responsibilities if (structure.layers.presentation.components.includes('repositories')) { score -= 30; } return score; } function checkInterfaceSegregation(structure) { // Check for proper interfaces if (structure.layers.domain.components.includes('repositories')) { return 90; } return 50; } function checkDependencyInversion(structure) { // Check if dependencies point to abstractions if (!structure.layers.data.dependencies.includes('presentation')) { return 95; } return 40; } function checkRepositoryPattern(structure) { const hasRepoInterface = structure.layers.domain.components.includes('repositories'); const hasRepoImpl = structure.layers.data.components.includes('repositories'); if (hasRepoInterface && hasRepoImpl) { return 100; } else if (hasRepoInterface || hasRepoImpl) { return 50; } return 0; } function checkUseCasePattern(structure) { if (structure.layers.domain.components.includes('use_cases')) { return 100; } return 0; } function checkFactoryPattern(structure) { // Check for factory usage const hasFactories = structure.layers.some(layer => layer.components.includes('factories') || layer.components.includes('builders') ); return hasFactories ? 80 : 40; } function checkModelCompliance(structure) { // For MVVM/MVC return 85; } function checkViewCompliance(structure) { // For MVVM/MVC return 90; } function checkViewModelCompliance(structure) { // For MVVM return 88; } function checkControllerCompliance(structure) { // For MVC return 82; } function calculateCoupling(graph) { const totalDeps = Object.values(graph).reduce((sum, deps) => sum + deps.length, 0); const layerCount = Object.keys(graph).length; return totalDeps / layerCount; } function calculateCohesion(structure) { // Simplified cohesion calculation return 75; } function calculateInstability(graph) { // Ce / (Ca + Ce) where Ce = efferent coupling, Ca = afferent coupling return 0.3; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dvillegastech/flutter_mcp_2'

If you have feedback or need assistance with the MCP directory API, please join our Discord server