Files
sa-lessons/modules/module7/sections/01-architecture-documentation-basics.md
Alexander Abakumov 15e744da49 Initial commit: Complete Software Architecture course with 11 modules
- Added comprehensive course structure with 11 modules
- Included theoretical content, practical examples, and tasks
- Multi-language code examples (Java, Rust, Node.js, Python)
- Detailed analysis of architectural approaches and trade-offs
- Complete caching strategies documentation
- Architecture examples from real-world systems (Twitter, Netflix, Uber)
- Structured navigation and learning path
- Ready for educational use
2025-09-09 22:08:07 +03:00

16 KiB
Raw Permalink Blame History

Основы архитектурной документации

Навигация


Зачем нужна архитектурная документация

1. Коммуникация

  • Общий язык для команды
  • Понимание системы новыми участниками
  • Обсуждение архитектурных решений

2. Планирование

  • Основа для принятия решений
  • Анализ влияния изменений
  • Планирование миграций

3. Поддержка

  • Быстрое понимание системы
  • Отладка проблем
  • Обучение команды

Принципы архитектурной документации

1. Living Documentation

Принцип: Документация должна быть живой и актуальной.

Технический пример - Living Documentation с автоматизацией:

# Архитектура системы E-Commerce Platform

> **Последнее обновление:** 2024-01-15  
> **Версия:** 2.1  
> **Ответственный:** Архитектор команды  
> **Статус:** Активная разработка  
> **Следующий обзор:** 2024-02-15  

## Изменения в версии 2.1
- ✅ Добавлен новый микросервис Payment Service
- ✅ Обновлена схема базы данных (добавлены индексы для производительности)
- ✅ Изменена стратегия кэширования (Redis Cluster)
- 🔄 В процессе: Миграция на Kubernetes
- 📋 Планируется: Внедрение Event Sourcing

## Архитектурные решения
- [ADR-001: Выбор микросервисной архитектуры](./adr/001-microservices-architecture.md)
- [ADR-002: Использование PostgreSQL](./adr/002-postgresql-database.md)
- [ADR-003: Redis для кэширования](./adr/003-redis-caching.md)
- [ADR-004: Kubernetes для оркестрации](./adr/004-kubernetes-orchestration.md)

## Метрики системы
- **Uptime:** 99.9% (последние 30 дней)
- **Response Time:** P95 < 200ms
- **Throughput:** 10,000 RPS
- **Error Rate:** < 0.1%

Технический пример - Automated Documentation Generation:

// Architecture Documentation Generator
@Component
public class ArchitectureDocumentationGenerator {
    
    @Autowired
    private ServiceRegistry serviceRegistry;
    
    @Autowired
    private DatabaseSchemaAnalyzer schemaAnalyzer;
    
    @Autowired
    private ApiSpecificationGenerator apiGenerator;
    
    @Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00
    public void generateDocumentation() {
        log.info("Starting architecture documentation generation");
        
        try {
            // Генерируем обновленную документацию
            generateSystemOverview();
            generateServiceCatalog();
            generateDatabaseSchema();
            generateApiDocumentation();
            generateDeploymentDiagrams();
            
            // Обновляем метрики
            updateSystemMetrics();
            
            // Отправляем уведомления
            notifyTeamAboutUpdates();
            
            log.info("Architecture documentation generation completed");
            
        } catch (Exception e) {
            log.error("Failed to generate architecture documentation", e);
        }
    }
    
    private void generateSystemOverview() {
        SystemOverview overview = SystemOverview.builder()
            .systemName("E-Commerce Platform")
            .version(getCurrentVersion())
            .lastUpdated(LocalDateTime.now())
            .services(serviceRegistry.getAllServices())
            .databases(schemaAnalyzer.getDatabaseInfo())
            .build();
        
        String markdown = convertToMarkdown(overview);
        writeToFile("docs/system-overview.md", markdown);
    }
    
    private void generateServiceCatalog() {
        List<ServiceInfo> services = serviceRegistry.getAllServices();
        
        StringBuilder catalog = new StringBuilder();
        catalog.append("# Service Catalog\n\n");
        catalog.append("| Service | Version | Status | Health | Dependencies |\n");
        catalog.append("|---------|---------|--------|--------|-------------|\n");
        
        for (ServiceInfo service : services) {
            catalog.append(String.format("| %s | %s | %s | %s | %s |\n",
                service.getName(),
                service.getVersion(),
                service.getStatus(),
                service.getHealthStatus(),
                String.join(", ", service.getDependencies())
            ));
        }
        
        writeToFile("docs/service-catalog.md", catalog.toString());
    }
    
    private void generateDatabaseSchema() {
        DatabaseSchema schema = schemaAnalyzer.analyzeSchema();
        
        StringBuilder schemaDoc = new StringBuilder();
        schemaDoc.append("# Database Schema\n\n");
        
        for (TableInfo table : schema.getTables()) {
            schemaDoc.append(String.format("## %s\n\n", table.getName()));
            schemaDoc.append(String.format("**Description:** %s\n\n", table.getDescription()));
            schemaDoc.append("| Column | Type | Nullable | Key | Description |\n");
            schemaDoc.append("|--------|------|----------|-----|-------------|\n");
            
            for (ColumnInfo column : table.getColumns()) {
                schemaDoc.append(String.format("| %s | %s | %s | %s | %s |\n",
                    column.getName(),
                    column.getType(),
                    column.isNullable() ? "Yes" : "No",
                    column.getKeyType(),
                    column.getDescription()
                ));
            }
            schemaDoc.append("\n");
        }
        
        writeToFile("docs/database-schema.md", schemaDoc.toString());
    }
    
    private void generateApiDocumentation() {
        List<ApiSpecification> apiSpecs = apiGenerator.generateSpecifications();
        
        for (ApiSpecification spec : apiSpecs) {
            String openApiSpec = convertToOpenApi(spec);
            writeToFile(String.format("docs/api/%s-openapi.yaml", spec.getServiceName()), openApiSpec);
            
            String markdownDoc = convertToMarkdown(spec);
            writeToFile(String.format("docs/api/%s.md", spec.getServiceName()), markdownDoc);
        }
    }
    
    private void generateDeploymentDiagrams() {
        // Генерируем PlantUML диаграммы
        String deploymentDiagram = generatePlantUMLDeploymentDiagram();
        writeToFile("docs/diagrams/deployment.puml", deploymentDiagram);
        
        // Конвертируем в изображения
        convertPlantUMLToImages("docs/diagrams/deployment.puml");
    }
    
    private void updateSystemMetrics() {
        SystemMetrics metrics = collectSystemMetrics();
        
        StringBuilder metricsDoc = new StringBuilder();
        metricsDoc.append("# System Metrics\n\n");
        metricsDoc.append(String.format("**Last Updated:** %s\n\n", LocalDateTime.now()));
        metricsDoc.append("## Performance Metrics\n");
        metricsDoc.append(String.format("- **Uptime:** %.2f%%\n", metrics.getUptime()));
        metricsDoc.append(String.format("- **Response Time P95:** %dms\n", metrics.getResponseTimeP95()));
        metricsDoc.append(String.format("- **Throughput:** %d RPS\n", metrics.getThroughput()));
        metricsDoc.append(String.format("- **Error Rate:** %.2f%%\n", metrics.getErrorRate()));
        
        writeToFile("docs/system-metrics.md", metricsDoc.toString());
    }
    
    private void notifyTeamAboutUpdates() {
        // Отправляем уведомления в Slack/Teams
        notificationService.sendNotification(
            "Architecture documentation has been updated",
            "docs/system-overview.md"
        );
    }
}

// Service Registry для автоматического обнаружения сервисов
@Component
public class ServiceRegistry {
    
    @Autowired
    private DiscoveryClient discoveryClient;
    
    @Autowired
    private RestTemplate restTemplate;
    
    public List<ServiceInfo> getAllServices() {
        List<ServiceInfo> services = new ArrayList<>();
        
        List<String> serviceNames = discoveryClient.getServices();
        
        for (String serviceName : serviceNames) {
            List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
            
            if (!instances.isEmpty()) {
                ServiceInstance instance = instances.get(0);
                
                try {
                    ServiceInfo serviceInfo = getServiceInfo(instance);
                    services.add(serviceInfo);
                } catch (Exception e) {
                    log.warn("Failed to get info for service: {}", serviceName, e);
                }
            }
        }
        
        return services;
    }
    
    private ServiceInfo getServiceInfo(ServiceInstance instance) {
        String healthUrl = String.format("http://%s:%d/actuator/health", 
            instance.getHost(), instance.getPort());
        
        ResponseEntity<Map> healthResponse = restTemplate.getForEntity(healthUrl, Map.class);
        
        String infoUrl = String.format("http://%s:%d/actuator/info", 
            instance.getHost(), instance.getPort());
        
        ResponseEntity<Map> infoResponse = restTemplate.getForEntity(infoUrl, Map.class);
        
        return ServiceInfo.builder()
            .name(instance.getServiceId())
            .version(getVersionFromInfo(infoResponse.getBody()))
            .status(healthResponse.getStatusCode().is2xxSuccessful() ? "UP" : "DOWN")
            .healthStatus(healthResponse.getBody().get("status").toString())
            .dependencies(getDependenciesFromInfo(infoResponse.getBody()))
            .build();
    }
    
    private String getVersionFromInfo(Map<String, Object> info) {
        Map<String, Object> build = (Map<String, Object>) info.get("build");
        return build != null ? build.get("version").toString() : "unknown";
    }
    
    private List<String> getDependenciesFromInfo(Map<String, Object> info) {
        // Логика извлечения зависимостей из actuator/info
        return new ArrayList<>();
    }
}

// Database Schema Analyzer
@Component
public class DatabaseSchemaAnalyzer {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public DatabaseSchema analyzeSchema() {
        List<TableInfo> tables = new ArrayList<>();
        
        // Получаем список таблиц
        List<String> tableNames = getTableNames();
        
        for (String tableName : tableNames) {
            TableInfo table = analyzeTable(tableName);
            tables.add(table);
        }
        
        return new DatabaseSchema(tables);
    }
    
    private List<String> getTableNames() {
        String sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'";
        return jdbcTemplate.queryForList(sql, String.class);
    }
    
    private TableInfo analyzeTable(String tableName) {
        // Получаем информацию о колонках
        String columnSql = """
            SELECT 
                column_name,
                data_type,
                is_nullable,
                column_key,
                column_comment
            FROM information_schema.columns 
            WHERE table_name = ? AND table_schema = 'public'
            """;
        
        List<ColumnInfo> columns = jdbcTemplate.query(columnSql, 
            new Object[]{tableName}, 
            (rs, rowNum) -> new ColumnInfo(
                rs.getString("column_name"),
                rs.getString("data_type"),
                "YES".equals(rs.getString("is_nullable")),
                rs.getString("column_key"),
                rs.getString("column_comment")
            )
        );
        
        // Получаем информацию о таблице
        String tableSql = """
            SELECT 
                table_comment,
                table_rows,
                data_length
            FROM information_schema.tables 
            WHERE table_name = ? AND table_schema = 'public'
            """;
        
        Map<String, Object> tableInfo = jdbcTemplate.queryForMap(tableSql, tableName);
        
        return new TableInfo(
            tableName,
            (String) tableInfo.get("table_comment"),
            columns
        );
    }
}

// API Specification Generator
@Component
public class ApiSpecificationGenerator {
    
    @Autowired
    private RestTemplate restTemplate;
    
    public List<ApiSpecification> generateSpecifications() {
        List<ApiSpecification> specifications = new ArrayList<>();
        
        List<ServiceInfo> services = serviceRegistry.getAllServices();
        
        for (ServiceInfo service : services) {
            try {
                ApiSpecification spec = generateSpecificationForService(service);
                specifications.add(spec);
            } catch (Exception e) {
                log.warn("Failed to generate API spec for service: {}", service.getName(), e);
            }
        }
        
        return specifications;
    }
    
    private ApiSpecification generateSpecificationForService(ServiceInfo service) {
        // Получаем OpenAPI спецификацию от сервиса
        String openApiUrl = String.format("http://%s:%d/v3/api-docs", 
            service.getHost(), service.getPort());
        
        ResponseEntity<Map> response = restTemplate.getForEntity(openApiUrl, Map.class);
        Map<String, Object> openApiSpec = response.getBody();
        
        return ApiSpecification.builder()
            .serviceName(service.getName())
            .version(service.getVersion())
            .openApiSpec(openApiSpec)
            .endpoints(extractEndpoints(openApiSpec))
            .build();
    }
    
    private List<EndpointInfo> extractEndpoints(Map<String, Object> openApiSpec) {
        // Логика извлечения endpoints из OpenAPI спецификации
        return new ArrayList<>();
    }
}

2. Just Enough Documentation

Принцип: Документируйте достаточно, но не слишком много.

3. Audience-Driven

Принцип: Документация должна быть ориентирована на аудиторию.

Типы архитектурной документации

1. High-Level Architecture

  • Общий обзор системы
  • Основные компоненты
  • Ключевые взаимодействия

2. Detailed Design

  • Детальное описание компонентов
  • API спецификации
  • Схемы баз данных

3. Deployment Documentation

  • Схемы развертывания
  • Конфигурация инфраструктуры
  • Процедуры развертывания

4. Operational Documentation

  • Мониторинг и логирование
  • Процедуры восстановления
  • Руководства по эксплуатации

Заключение

Архитектурная документация является критически важным компонентом успешной разработки:

  1. Коммуникация — обеспечивает общий язык для команды
  2. Планирование — основа для принятия решений
  3. Поддержка — упрощает понимание и поддержку системы
  4. Автоматизация — современные инструменты позволяют автоматизировать генерацию документации
  5. Живая документация — должна обновляться вместе с системой

Правильная организация и ведение архитектурной документации значительно повышает эффективность команды разработки.