From 15e744da49f6c5597851101c76e0fe6c76a4cd7a Mon Sep 17 00:00:00 2001 From: Alexander Abakumov Date: Tue, 9 Sep 2025 22:08:07 +0300 Subject: [PATCH] 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 --- .gitignore | 96 + LICENSE | 21 + README.md | 81 + course_plan.md | 340 + modules/module1/README.md | 105 + .../sections/01-role-and-responsibilities.md | 326 + ...2-introduction-to-software-architecture.md | 1335 ++++ .../sections/03-domain-driven-design.md | 683 ++ .../module1/sections/04-design-principles.md | 1040 +++ .../05-architectural-styles-patterns.md | 552 ++ .../module1/sections/06-quality-attributes.md | 416 ++ .../sections/07-architect-role-in-team.md | 517 ++ .../module1/sections/08-practical-tasks.md | 22 + .../sections/09-architecture-examples.md | 844 +++ modules/module1/theory.md | 6365 +++++++++++++++++ modules/module10/README.md | 80 + .../sections/01-performance-fundamentals.md | 363 + .../sections/02-profiling-analysis.md | 169 + .../sections/03-database-optimization.md | 145 + modules/module10/sections/04-caching.md | 310 + .../05-code-optimization-minification.md | 357 + .../sections/06-network-optimization.md | 102 + .../module10/sections/07-capacity-planning.md | 116 + .../sections/08-performance-testing.md | 174 + .../module10/sections/08-practical-tasks.md | 24 + .../module10/sections/09-practical-tasks.md | 42 + modules/module10/theory.md | 2141 ++++++ modules/module11/README.md | 70 + .../module11/sections/01-project-overview.md | 95 + .../sections/02-architecture-design.md | 243 + .../module11/sections/03-implementation.md | 364 + modules/module11/sections/04-documentation.md | 159 + modules/module11/sections/05-presentation.md | 113 + .../sections/06-interview-preparation.md | 203 + .../sections/07-evaluation-reflection.md | 155 + .../module11/sections/08-practical-tasks.md | 27 + modules/module11/theory.md | 1831 +++++ modules/module2/README.md | 102 + .../sections/01-microservices-fundamentals.md | 967 +++ .../sections/02-domain-driven-design.md | 173 + .../sections/03-service-communication.md | 541 ++ .../module2/sections/04-data-management.md | 201 + .../sections/05-serverless-architecture.md | 257 + modules/module2/sections/06-microfrontends.md | 240 + .../module2/sections/08-practical-tasks.md | 31 + modules/module2/theory.md | 3367 +++++++++ modules/module3/README.md | 108 + .../sections/01-containerization-basics.md | 60 + modules/module3/sections/02-docker.md | 369 + modules/module3/sections/03-kubernetes.md | 486 ++ .../sections/04-infrastructure-as-code.md | 177 + .../module3/sections/05-cicd-containers.md | 61 + .../sections/06-container-monitoring.md | 68 + .../module3/sections/08-practical-tasks.md | 25 + modules/module3/theory.md | 2008 ++++++ modules/module4/README.md | 122 + .../module4/sections/01-scalability-basics.md | 439 ++ .../module4/sections/08-practical-tasks.md | 18 + modules/module4/theory.md | 3838 ++++++++++ modules/module5/README.md | 113 + .../module5/sections/01-monitoring-basics.md | 187 + .../module5/sections/02-prometheus-grafana.md | 155 + modules/module5/sections/03-logging.md | 238 + .../sections/04-distributed-tracing.md | 102 + modules/module5/sections/05-observability.md | 92 + modules/module5/sections/06-alerting.md | 76 + modules/module5/sections/07-sli-slo-sla.md | 80 + .../module5/sections/08-practical-tasks.md | 24 + modules/module5/theory.md | 2009 ++++++ modules/module6/README.md | 134 + .../02-vulnerabilities-inter-service.md | 723 ++ .../sections/03-encryption-key-management.md | 766 ++ .../04-code-obfuscation-protection.md | 555 ++ .../module6/sections/05-security-by-design.md | 745 ++ .../module6/sections/08-practical-tasks.md | 24 + modules/module6/theory.md | 4009 +++++++++++ modules/module7/README.md | 102 + .../01-architecture-documentation-basics.md | 428 ++ modules/module7/sections/02-c4-model.md | 152 + modules/module7/sections/03-plantuml.md | 201 + .../04-architecture-decision-records.md | 280 + .../sections/05-technical-documentation.md | 180 + .../sections/06-architecture-communication.md | 767 ++ .../module7/sections/08-practical-tasks.md | 24 + modules/module7/theory.md | 2612 +++++++ modules/module8/README.md | 120 + .../module8/sections/01-cloud-fundamentals.md | 701 ++ modules/module8/sections/02-aws-services.md | 561 ++ modules/module8/sections/03-azure-services.md | 412 ++ modules/module8/sections/04-gcp-services.md | 507 ++ .../sections/05-cloud-native-architecture.md | 721 ++ .../06-multi-cloud-hybrid-strategies.md | 669 ++ .../sections/07-migration-strategies.md | 672 ++ .../08-cloud-economics-cost-calculator.md | 621 ++ .../module8/sections/08-practical-tasks.md | 24 + modules/module8/theory.md | 3612 ++++++++++ modules/module9/README.md | 123 + .../sections/01-technical-leadership.md | 255 + .../02-architecture-process-management.md | 296 + .../module9/sections/03-agile-architecture.md | 269 + modules/module9/sections/04-devops-culture.md | 429 ++ .../05-code-review-architecture-review.md | 333 + .../sections/06-coding-standards-tools.md | 516 ++ .../sections/07-technical-debt-management.md | 392 + .../08-communication-presentations.md | 125 + .../module9/sections/08-practical-tasks.md | 24 + modules/module9/theory.md | 1918 +++++ 107 files changed, 61762 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 course_plan.md create mode 100644 modules/module1/README.md create mode 100644 modules/module1/sections/01-role-and-responsibilities.md create mode 100644 modules/module1/sections/02-introduction-to-software-architecture.md create mode 100644 modules/module1/sections/03-domain-driven-design.md create mode 100644 modules/module1/sections/04-design-principles.md create mode 100644 modules/module1/sections/05-architectural-styles-patterns.md create mode 100644 modules/module1/sections/06-quality-attributes.md create mode 100644 modules/module1/sections/07-architect-role-in-team.md create mode 100644 modules/module1/sections/08-practical-tasks.md create mode 100644 modules/module1/sections/09-architecture-examples.md create mode 100644 modules/module1/theory.md create mode 100644 modules/module10/README.md create mode 100644 modules/module10/sections/01-performance-fundamentals.md create mode 100644 modules/module10/sections/02-profiling-analysis.md create mode 100644 modules/module10/sections/03-database-optimization.md create mode 100644 modules/module10/sections/04-caching.md create mode 100644 modules/module10/sections/05-code-optimization-minification.md create mode 100644 modules/module10/sections/06-network-optimization.md create mode 100644 modules/module10/sections/07-capacity-planning.md create mode 100644 modules/module10/sections/08-performance-testing.md create mode 100644 modules/module10/sections/08-practical-tasks.md create mode 100644 modules/module10/sections/09-practical-tasks.md create mode 100644 modules/module10/theory.md create mode 100644 modules/module11/README.md create mode 100644 modules/module11/sections/01-project-overview.md create mode 100644 modules/module11/sections/02-architecture-design.md create mode 100644 modules/module11/sections/03-implementation.md create mode 100644 modules/module11/sections/04-documentation.md create mode 100644 modules/module11/sections/05-presentation.md create mode 100644 modules/module11/sections/06-interview-preparation.md create mode 100644 modules/module11/sections/07-evaluation-reflection.md create mode 100644 modules/module11/sections/08-practical-tasks.md create mode 100644 modules/module11/theory.md create mode 100644 modules/module2/README.md create mode 100644 modules/module2/sections/01-microservices-fundamentals.md create mode 100644 modules/module2/sections/02-domain-driven-design.md create mode 100644 modules/module2/sections/03-service-communication.md create mode 100644 modules/module2/sections/04-data-management.md create mode 100644 modules/module2/sections/05-serverless-architecture.md create mode 100644 modules/module2/sections/06-microfrontends.md create mode 100644 modules/module2/sections/08-practical-tasks.md create mode 100644 modules/module2/theory.md create mode 100644 modules/module3/README.md create mode 100644 modules/module3/sections/01-containerization-basics.md create mode 100644 modules/module3/sections/02-docker.md create mode 100644 modules/module3/sections/03-kubernetes.md create mode 100644 modules/module3/sections/04-infrastructure-as-code.md create mode 100644 modules/module3/sections/05-cicd-containers.md create mode 100644 modules/module3/sections/06-container-monitoring.md create mode 100644 modules/module3/sections/08-practical-tasks.md create mode 100644 modules/module3/theory.md create mode 100644 modules/module4/README.md create mode 100644 modules/module4/sections/01-scalability-basics.md create mode 100644 modules/module4/sections/08-practical-tasks.md create mode 100644 modules/module4/theory.md create mode 100644 modules/module5/README.md create mode 100644 modules/module5/sections/01-monitoring-basics.md create mode 100644 modules/module5/sections/02-prometheus-grafana.md create mode 100644 modules/module5/sections/03-logging.md create mode 100644 modules/module5/sections/04-distributed-tracing.md create mode 100644 modules/module5/sections/05-observability.md create mode 100644 modules/module5/sections/06-alerting.md create mode 100644 modules/module5/sections/07-sli-slo-sla.md create mode 100644 modules/module5/sections/08-practical-tasks.md create mode 100644 modules/module5/theory.md create mode 100644 modules/module6/README.md create mode 100644 modules/module6/sections/02-vulnerabilities-inter-service.md create mode 100644 modules/module6/sections/03-encryption-key-management.md create mode 100644 modules/module6/sections/04-code-obfuscation-protection.md create mode 100644 modules/module6/sections/05-security-by-design.md create mode 100644 modules/module6/sections/08-practical-tasks.md create mode 100644 modules/module6/theory.md create mode 100644 modules/module7/README.md create mode 100644 modules/module7/sections/01-architecture-documentation-basics.md create mode 100644 modules/module7/sections/02-c4-model.md create mode 100644 modules/module7/sections/03-plantuml.md create mode 100644 modules/module7/sections/04-architecture-decision-records.md create mode 100644 modules/module7/sections/05-technical-documentation.md create mode 100644 modules/module7/sections/06-architecture-communication.md create mode 100644 modules/module7/sections/08-practical-tasks.md create mode 100644 modules/module7/theory.md create mode 100644 modules/module8/README.md create mode 100644 modules/module8/sections/01-cloud-fundamentals.md create mode 100644 modules/module8/sections/02-aws-services.md create mode 100644 modules/module8/sections/03-azure-services.md create mode 100644 modules/module8/sections/04-gcp-services.md create mode 100644 modules/module8/sections/05-cloud-native-architecture.md create mode 100644 modules/module8/sections/06-multi-cloud-hybrid-strategies.md create mode 100644 modules/module8/sections/07-migration-strategies.md create mode 100644 modules/module8/sections/08-cloud-economics-cost-calculator.md create mode 100644 modules/module8/sections/08-practical-tasks.md create mode 100644 modules/module8/theory.md create mode 100644 modules/module9/README.md create mode 100644 modules/module9/sections/01-technical-leadership.md create mode 100644 modules/module9/sections/02-architecture-process-management.md create mode 100644 modules/module9/sections/03-agile-architecture.md create mode 100644 modules/module9/sections/04-devops-culture.md create mode 100644 modules/module9/sections/05-code-review-architecture-review.md create mode 100644 modules/module9/sections/06-coding-standards-tools.md create mode 100644 modules/module9/sections/07-technical-debt-management.md create mode 100644 modules/module9/sections/08-communication-presentations.md create mode 100644 modules/module9/sections/08-practical-tasks.md create mode 100644 modules/module9/theory.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f72fb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,96 @@ +# Временные файлы +*.tmp +*.temp +temp_* +.DS_Store +Thumbs.db + +# IDE файлы +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Логи +*.log +logs/ + +# Кэш +.cache/ +node_modules/ +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Rust +target/ +Cargo.lock + +# Java +*.class +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar +hs_err_pid* + +# Node.js +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.npm +.eslintcache + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Базы данных +*.db +*.sqlite +*.sqlite3 + +# Конфигурационные файлы с секретами +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +config/secrets.yml +secrets.json + +# Документация сборки +docs/_build/ +site/ + +# Временные файлы проекта +temp_restructure.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..82a25eb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 SA Lessons Course + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..728e6e3 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# Курс "Архитектор программного обеспечения" + +## Описание + +Этот репозиторий содержит полный курс по архитектуре программного обеспечения, разработанный на основе программы Yandex.Practicum и дополненный современными практиками и технологиями. + +## Структура курса + +Курс состоит из 11 модулей, каждый из которых содержит: + +- **Теоретический материал** — основы архитектуры ПО +- **Практические примеры** — код на Java, Rust, Node.js и Python +- **Практические задания** — для закрепления материала +- **Дополнительные ресурсы** — ссылки на полезные материалы + +## Модули курса + +1. **Основы архитектуры ПО** — принципы, SOLID, архитектурные стили +2. **Микросервисная архитектура** — проектирование и реализация микросервисов +3. **Контейнеризация и оркестрация** — Docker, Kubernetes, Infrastructure as Code +4. **Масштабируемость и отказоустойчивость** — паттерны масштабирования +5. **Мониторинг и наблюдаемость** — Prometheus, Grafana, логирование +6. **Безопасность и соответствие требованиям** — аутентификация, шифрование, уязвимости +7. **Документирование архитектуры** — C4 Model, PlantUML, ADR +8. **Облачные технологии и миграция** — AWS, Azure, GCP, стратегии миграции +9. **Управление командой и процессы** — техническое лидерство, Agile, DevOps +10. **Производительность и оптимизация** — профилирование, оптимизация БД +11. **Финальный проект** — архитектурное проектирование и реализация + +## Технологии + +Курс охватывает следующие технологии: + +- **Языки программирования**: Java, Rust, Node.js, Python +- **Фреймворки**: Spring Boot, Actix Web, Fastify, FastAPI +- **Базы данных**: PostgreSQL, MongoDB, Redis +- **Контейнеризация**: Docker, Kubernetes +- **Облачные платформы**: AWS, Azure, Google Cloud +- **Мониторинг**: Prometheus, Grafana, ELK Stack + +## Как использовать + +1. Клонируйте репозиторий: + ```bash + git clone https://git.mylocaldomain.online/administrator/sa-lessons.git + cd sa-lessons + ``` + +2. Изучайте модули последовательно, начиная с [Модуля 1](modules/module1/README.md) + +3. Выполняйте практические задания в каждом модуле + +4. Используйте примеры кода как справочный материал + +## Структура репозитория + +``` +├── modules/ # Модули курса +│ ├── module1/ # Основы архитектуры ПО +│ ├── module2/ # Микросервисная архитектура +│ └── ... +├── README.md # Этот файл +├── .gitignore # Игнорируемые файлы +└── course_plan.md # План курса +``` + +## Автор + +Курс разработан на основе программы Yandex.Practicum "Архитектор программного обеспечения" с дополнениями и современными практиками. + +## Лицензия + +Этот проект распространяется под лицензией MIT. См. файл [LICENSE](LICENSE) для подробностей. + +## Вклад в проект + +Мы приветствуем вклад в развитие курса! Пожалуйста, создавайте Issues и Pull Requests для предложений по улучшению. + +## Поддержка + +Если у вас есть вопросы по курсу, создайте Issue в этом репозитории. \ No newline at end of file diff --git a/course_plan.md b/course_plan.md new file mode 100644 index 0000000..0743a09 --- /dev/null +++ b/course_plan.md @@ -0,0 +1,340 @@ +# План курса "Архитектор программного обеспечения" + +## Общая информация +- **Длительность**: 6 месяцев +- **Нагрузка**: 10-15 часов в неделю +- **Количество проектов**: 11 практических проектов +- **Целевая аудитория**: Разработчики с опытом, технические лидеры, начинающие архитекторы + +## Модуль 1: Основы архитектуры ПО (2 недели) + +### Компетенции: +- Понимание принципов архитектуры ПО +- Знание архитектурных стилей и паттернов +- Умение анализировать требования и ограничения + +### Темы: +1. **Введение в архитектуру ПО** + - Определение архитектуры ПО + - Роль архитектора в команде + - Архитектурные стили (монолит, микросервисы, SOA) + - Качественные атрибуты (производительность, масштабируемость, надежность) + +2. **Принципы проектирования** + - SOLID принципы + - DRY, KISS, YAGNI + - Принципы модульности и слабой связанности + - Закон Конвея + +### Практический проект: +Создание архитектурного обзора существующей системы с анализом качественных атрибутов. + +## Модуль 2: Микросервисная архитектура (3 недели) + +### Компетенции: +- Проектирование микросервисных систем +- Управление взаимодействием сервисов +- Решение проблем распределенных систем + +### Темы: +1. **Основы микросервисов** + - Преимущества и недостатки микросервисов + - Критерии декомпозиции системы + - Паттерны коммуникации между сервисами + - Управление данными в микросервисах + +2. **Domain-Driven Design (DDD)** + - Стратегическое проектирование + - Тактическое проектирование + - Контекстные карты + - Антипаттерны DDD + +3. **Микрофронтенды** + - Архитектура фронтенда + - Интеграция микросервисов и микрофронтендов + - Управление состоянием + +### Практический проект: +Проектирование и реализация микросервисной системы с использованием DDD подходов. + +## Модуль 3: Контейнеризация и оркестрация (3 недели) + +### Компетенции: +- Работа с Docker и контейнеризацией +- Управление оркестрацией с Kubernetes +- Infrastructure as Code + +### Темы: +1. **Docker и контейнеризация** + - Основы контейнеризации + - Создание и оптимизация Docker образов + - Docker Compose для локальной разработки + - Лучшие практики контейнеризации + +2. **Kubernetes** + - Основы оркестрации + - Pods, Services, Deployments + - ConfigMaps и Secrets + - Ingress и Service Mesh + +3. **Infrastructure as Code** + - Terraform основы + - Управление инфраструктурой + - CI/CD пайплайны + - GitOps подходы + +### Практический проект: +Развертывание микросервисного приложения в Kubernetes с использованием Terraform. + +## Модуль 4: Масштабируемость и отказоустойчивость (3 недели) + +### Компетенции: +- Проектирование масштабируемых систем +- Обеспечение отказоустойчивости +- Оптимизация производительности + +### Темы: +1. **Паттерны масштабируемости** + - Горизонтальное и вертикальное масштабирование + - Load balancing + - Caching стратегии + - CDN и edge computing + +2. **Отказоустойчивость** + - Circuit Breaker паттерн + - Retry и timeout механизмы + - Graceful degradation + - Chaos Engineering + +3. **Управление данными** + - Репликация и шардирование + - CAP теорема + - Event Sourcing и CQRS + - Обработка данных в реальном времени + +### Практический проект: +Проектирование высоконагруженной системы с обеспечением отказоустойчивости. + +## Модуль 5: Мониторинг и наблюдаемость (2 недели) + +### Компетенции: +- Настройка систем мониторинга +- Анализ метрик и логов +- Проактивное управление системой + +### Темы: +1. **Мониторинг и метрики** + - Prometheus и Grafana + - Типы метрик (counter, gauge, histogram) + - Алертинг и уведомления + - SLI/SLO/SLA концепции + +2. **Логирование и трейсинг** + - Централизованное логирование + - Distributed tracing + - OpenTelemetry + - Анализ производительности + +3. **Observability** + - Три столпа observability + - Инструментирование приложений + - Debugging в продакшене + +### Практический проект: +Настройка полного стека мониторинга для микросервисного приложения. + +## Модуль 6: Безопасность и соответствие требованиям (2 недели) + +### Компетенции: +- Интеграция безопасности в архитектуру +- Управление доступом и идентификацией +- Соответствие стандартам безопасности + +### Темы: +1. **Архитектурная безопасность** + - Security by Design + - Threat modeling + - OWASP Top 10 + - Secure coding practices + +2. **Управление доступом** + - IAM (Identity and Access Management) + - OAuth 2.0 и OpenID Connect + - RBAC и ABAC + - Zero Trust архитектура + +3. **Соответствие требованиям** + - GDPR, SOX, PCI DSS + - Аудит и комплаенс + - Data privacy и protection + +### Практический проект: +Проектирование безопасной архитектуры с учетом требований соответствия. + +## Модуль 7: Документирование архитектуры (2 недели) + +### Компетенции: +- Создание архитектурной документации +- Использование инструментов моделирования +- Коммуникация архитектурных решений + +### Темы: +1. **C4 Model** + - Context, Container, Component, Code диаграммы + - Архитектурные диаграммы + - Документирование решений + +2. **PlantUML и диаграммы** + - UML диаграммы + - Sequence диаграммы + - Deployment диаграммы + - Архитектурные решения (ADR) + +3. **Коммуникация** + - Презентация архитектуры стейкхолдерам + - Техническое лидерство + - Code review и архитектурные ревью + +### Практический проект: +Создание полной архитектурной документации с использованием C4 и PlantUML. + +## Модуль 8: Облачные технологии и миграция (3 недели) + +### Компетенции: +- Работа с облачными платформами +- Стратегии миграции в облако +- Cloud-native архитектура + +### Темы: +1. **Облачные платформы** + - AWS, Azure, GCP обзор + - Serverless архитектура + - Managed services + - Cost optimization + +2. **Cloud-native паттерны** + - 12-factor app + - Cloud-native архитектура + - Event-driven архитектура + - API Gateway и Service Mesh + +3. **Миграция в облако** + - Стратегии миграции + - Lift and shift vs. refactoring + - Hybrid cloud архитектура + - Disaster recovery + +### Практический проект: +Миграция монолитного приложения в облачную микросервисную архитектуру. + +## Модуль 9: Управление командой и процессы (2 недели) + +### Компетенции: +- Техническое лидерство +- Управление архитектурными процессами +- Agile и DevOps практики + +### Темы: +1. **Техническое лидерство** + - Роль архитектора в команде + - Принятие архитектурных решений + - Техническое менторство + - Управление техническим долгом + +2. **Процессы разработки** + - Agile архитектура + - Continuous Architecture + - Architecture Decision Records (ADR) + - Architecture governance + +3. **DevOps и CI/CD** + - DevOps культура + - Continuous Integration/Deployment + - Blue-green и canary deployments + - Feature flags + +### Практический проект: +Организация архитектурных процессов в команде разработки. + +## Модуль 10: Производительность и оптимизация (2 недели) + +### Компетенции: +- Профилирование и оптимизация +- Анализ производительности +- Capacity planning + +### Темы: +1. **Профилирование систем** + - Методы профилирования + - Анализ узких мест + - Performance testing + - Load testing + +2. **Оптимизация** + - Database optimization + - Caching стратегии + - CDN и edge optimization + - Resource optimization + +3. **Capacity Planning** + - Прогнозирование нагрузки + - Scaling стратегии + - Cost optimization + - Resource management + +### Практический проект: +Оптимизация производительности высоконагруженной системы. + +## Модуль 11: Финальный проект (3 недели) + +### Компетенции: +- Комплексное применение всех изученных навыков +- Проектирование полной архитектуры +- Презентация решения + +### Задача: +Проектирование и документирование архитектуры крупной распределенной системы с учетом всех изученных аспектов: +- Микросервисная архитектура +- Облачное развертывание +- Мониторинг и безопасность +- Масштабируемость и отказоустойчивость +- Документирование архитектуры + +## Итоговые компетенции выпускника: + +### Технические компетенции: +- Проектирование микросервисных архитектур +- Работа с Docker и Kubernetes +- Настройка мониторинга и observability +- Обеспечение безопасности архитектуры +- Документирование архитектурных решений +- Оптимизация производительности систем + +### Soft Skills: +- Техническое лидерство +- Коммуникация с бизнесом +- Управление архитектурными процессами +- Принятие архитектурных решений +- Менторство и развитие команды + +### Инструменты и технологии: +- Docker, Kubernetes, Terraform +- Prometheus, Grafana, ELK Stack +- AWS/Azure/GCP +- PlantUML, C4 Model +- Git, CI/CD инструменты +- Monitoring и logging инструменты + +## Формат обучения: +- Онлайн лекции и вебинары +- Практические лабораторные работы +- Индивидуальные проекты +- Peer review и code review +- Менторство от практикующих архитекторов +- Доступ к корпоративным кейсам + +## Оценка и сертификация: +- Проектные работы (70%) +- Теоретические тесты (20%) +- Участие в обсуждениях и peer review (10%) +- Итоговый сертификат при успешном завершении всех модулей diff --git a/modules/module1/README.md b/modules/module1/README.md new file mode 100644 index 0000000..8c59abc --- /dev/null +++ b/modules/module1/README.md @@ -0,0 +1,105 @@ +# Модуль 1: Основы архитектуры программного обеспечения + +## Навигация +- [🏠 Главная страница курса](../../README.md) +- [📋 Практические задания](sections/08-practical-tasks.md) +- [🏗️ Примеры архитектур](sections/09-architecture-examples.md) + +## Содержание модуля + +1. [Роль и обязанности Solution Architect](sections/01-role-and-responsibilities.md) - Кто такой Solution Architect, его роль и обязанности в команде +2. [Введение в архитектуру ПО](sections/02-introduction-to-software-architecture.md) - Основы архитектуры программного обеспечения +3. [Доменная модель и Domain-Driven Design](sections/03-domain-driven-design.md) - Принципы DDD и доменное моделирование +4. [Принципы проектирования](sections/04-design-principles.md) - SOLID принципы и принципы проектирования +5. [Архитектурные стили и паттерны](sections/05-architectural-styles-patterns.md) - Монолит, микросервисы, event-driven, CQRS, hexagonal +6. [Качественные атрибуты](sections/06-quality-attributes.md) - Производительность, масштабируемость, надежность +7. [Роль архитектора в команде](sections/07-architect-role-in-team.md) - Взаимодействие с различными ролями в команде +8. [Практические задания](sections/08-practical-tasks.md) - Задания для закрепления материала +9. [Примеры архитектур известных систем](sections/09-architecture-examples.md) - Анализ архитектур Twitter, Netflix, Uber + +## Время изучения + +- **Общая длительность**: 2 недели +- **Время на раздел**: 2-3 часа +- **Практические задания**: 4-6 часов +- **Итого**: 20-30 часов + +## Описание модуля + +Этот модуль закладывает фундаментальные основы для понимания роли Solution Architect и принципов архитектуры программного обеспечения. Студенты изучат: + +- Кто такой Solution Architect и чем он занимается +- Основы архитектуры ПО и архитектурные решения +- Domain-Driven Design и доменное моделирование +- SOLID принципы и принципы проектирования +- Различные архитектурные стили и паттерны (монолит, микросервисы, event-driven, CQRS, hexagonal) +- Качественные атрибуты системы +- Роль архитектора в команде разработки +- Анализ архитектур известных систем (Twitter, Netflix, Uber) + +## Ключевые компетенции + +После изучения модуля студенты будут знать: + +- ✅ Роль и обязанности Solution Architect +- ✅ Основы архитектуры программного обеспечения +- ✅ Принципы Domain-Driven Design и доменное моделирование +- ✅ SOLID принципы и принципы проектирования +- ✅ Различные архитектурные стили (монолит, микросервисы, event-driven, CQRS, hexagonal) +- ✅ Качественные атрибуты системы +- ✅ Роль архитектора в команде и взаимодействие с различными ролями +- ✅ Анализ архитектур известных систем и их паттернов + +## Практические задания + +1. **Анализ архитектуры известных систем** — анализ архитектуры Twitter, Netflix, Uber и определение архитектурных стилей, компонентов и качественных атрибутов +2. **Применение SOLID принципов** — рефакторинг кода с применением SOLID принципов +3. **Trade-off анализ** — рассмотрение конкретного случая выбора между качественными атрибутами и обоснование решения + +## Предварительные требования + +- Опыт программирования (2+ года) +- Понимание основ объектно-ориентированного программирования +- Базовые знания о базах данных +- Опыт работы в команде разработки + +## Дополнительные ресурсы + +- [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) - Robert C. Martin +- [Domain-Driven Design](https://martinfowler.com/bliki/DomainDrivenDesign.html) - Martin Fowler +- [Microservices](https://martinfowler.com/articles/microservices.html) - Martin Fowler +- [SOLID Principles](https://en.wikipedia.org/wiki/SOLID) - Wikipedia +- [Architecture Patterns](https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/) - O'Reilly + +## Результаты обучения + +После завершения модуля студенты смогут: + +- Понимать роль и обязанности Solution Architect +- Применять принципы Domain-Driven Design +- Выбирать подходящие архитектурные стили для проектов +- Анализировать качественные атрибуты системы +- Применять SOLID принципы в проектировании +- Анализировать архитектуры известных систем + +## Оценка + +Модуль оценивается по следующим критериям: + +- **Теоретические знания** (40%) — понимание концепций и принципов +- **Практические навыки** (40%) — применение знаний на практике +- **Аналитические способности** (20%) — анализ и обоснование решений + +## Следующие модули + +После изучения основ архитектуры ПО, студенты переходят к: + +- [Модуль 2: Микросервисная архитектура](../module2/README.md) - Детали проектирования и реализации микросервисных систем +- [Модуль 3: Контейнеризация и оркестрация](../module3/README.md) - Docker, Kubernetes, Infrastructure as Code +- [Модуль 4: Масштабируемость и отказоустойчивость](../module4/README.md) - Паттерны масштабирования и отказоустойчивости + +## Быстрая навигация + +- [🏠 Главная страница курса](../../README.md) +- [📚 Все модули](../../README.md#модули-курса) +- [🛣️ Рекомендуемый путь изучения](../../README.md#рекомендуемый-путь-изучения) diff --git a/modules/module1/sections/01-role-and-responsibilities.md b/modules/module1/sections/01-role-and-responsibilities.md new file mode 100644 index 0000000..e82179d --- /dev/null +++ b/modules/module1/sections/01-role-and-responsibilities.md @@ -0,0 +1,326 @@ +# Роль и обязанности Solution Architect + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +## Кто такой Solution Architect? + +**Solution Architect** — это специалист, который проектирует комплексные технические решения для решения бизнес-задач. Он является мостом между бизнес-требованиями и технической реализацией. + +## Ключевые обязанности Solution Architect + +### 1. Анализ и проектирование решений +- **Анализ бизнес-требований** и перевод их в технические решения +- **Проектирование архитектуры** с учетом всех ограничений и требований +- **Выбор технологий** и инструментов для реализации +- **Создание технических спецификаций** и архитектурной документации + +### 2. Управление техническими решениями +- **Принятие архитектурных решений** (Architecture Decision Records - ADR) +- **Оценка рисков** и компромиссов технических решений +- **Управление техническим долгом** и эволюцией системы +- **Обеспечение соответствия** стандартам и лучшим практикам + +### 3. Коммуникация и координация +- **Взаимодействие со стейкхолдерами** (бизнес, разработчики, DevOps, QA) +- **Презентация архитектурных решений** на разных уровнях +- **Техническое лидерство** и менторство команды +- **Координация между командами** разработки + +## Отличия от других типов архитекторов + +### Solution Architect против Enterprise Architect + +| Аспект | Solution Architect | Enterprise Architect | +|--------|-------------------|---------------------| +| **Фокус** | Конкретное решение/продукт | Вся организация | +| **Масштаб** | Один проект/система | Множество систем | +| **Временной горизонт** | 6-18 месяцев | 2-5 лет | +| **Детализация** | Высокая техническая детализация | Высокий уровень абстракции | +| **Взаимодействие** | Команды разработки | C-level, IT-директора | + +**Пример различий:** + +```java +// Solution Architect - детальное техническое решение +@RestController +@RequestMapping("/api/orders") +public class OrderController { + + @Autowired + private OrderService orderService; + + @Autowired + private PaymentService paymentService; + + @PostMapping + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + // Детальная реализация бизнес-логики + Order order = orderService.createOrder(request); + PaymentResult payment = paymentService.processPayment(order); + + if (payment.isSuccess()) { + return ResponseEntity.ok(OrderResponse.fromOrder(order)); + } else { + throw new PaymentException("Payment failed"); + } + } +} + +// Enterprise Architect - высокоуровневая диаграмма +/* +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ E-commerce │ │ Payment │ │ Inventory │ +│ Platform │◄──►│ Gateway │◄──►│ Management │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Customer │ │ Order │ │ Fulfillment │ +│ Management │ │ Processing │ │ System │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +*/ +``` + +### Solution Architect против Technical Architect + +| Аспект | Solution Architect | Technical Architect | +|--------|-------------------|-------------------| +| **Фокус** | Бизнес-решение | Техническая реализация | +| **Уровень** | Системный | Компонентный | +| **Взаимодействие** | Бизнес + Технические команды | Только технические команды | +| **Решения** | Архитектурные паттерны, технологии | Алгоритмы, структуры данных | + +## Жизненный цикл проектирования архитектуры + +### 1. Анализ требований (Requirements Analysis) + +**Входные данные:** +- Бизнес-требования (Business Requirements) +- Функциональные требования (Functional Requirements) +- Нефункциональные требования (Non-Functional Requirements) +- Ограничения (Constraints) + +**Процесс анализа:** + +```java +// Пример анализа требований +public class RequirementsAnalyzer { + + public ArchitectureRequirements analyzeRequirements(BusinessRequirements businessReq) { + ArchitectureRequirements archReq = new ArchitectureRequirements(); + + // Анализ функциональных требований + analyzeFunctionalRequirements(businessReq, archReq); + + // Анализ нефункциональных требований + analyzeNonFunctionalRequirements(businessReq, archReq); + + // Анализ ограничений + analyzeConstraints(businessReq, archReq); + + return archReq; + } + + private void analyzeNonFunctionalRequirements(BusinessRequirements businessReq, + ArchitectureRequirements archReq) { + // Производительность + if (businessReq.getExpectedUsers() > 10000) { + archReq.addRequirement("HIGH_PERFORMANCE", "System must handle 10k+ concurrent users"); + } + + // Масштабируемость + if (businessReq.isGlobalDeployment()) { + archReq.addRequirement("SCALABILITY", "System must scale globally"); + } + + // Безопасность + if (businessReq.isFinancialData()) { + archReq.addRequirement("SECURITY", "Financial data must be encrypted"); + } + } +} +``` + +### 2. Проектирование архитектуры (Architecture Design) + +**Этапы проектирования:** + +1. **Высокоуровневое проектирование** + - Определение основных компонентов + - Выбор архитектурного стиля + - Определение интерфейсов + +2. **Детальное проектирование** + - Проектирование компонентов + - Выбор технологий + - Определение паттернов взаимодействия + +3. **Валидация архитектуры** + - Проверка соответствия требованиям + - Анализ рисков + - Прототипирование критических компонентов + +### 3. Документирование (Documentation) + +**Типы архитектурной документации:** + +```markdown +# Solution Architecture Document + +## 1. Executive Summary +- Краткое описание решения +- Ключевые преимущества +- Основные риски + +## 2. Business Context +- Бизнес-цели +- Пользователи системы +- Ключевые процессы + +## 3. Architecture Overview +- Высокоуровневая диаграмма +- Основные компоненты +- Технологический стек + +## 4. Detailed Design +- Детальные диаграммы компонентов +- API спецификации +- Схемы данных + +## 5. Non-Functional Requirements +- Производительность +- Безопасность +- Масштабируемость +- Надежность + +## 6. Implementation Plan +- Этапы реализации +- Ресурсы +- Временные рамки + +## 7. Risks and Mitigation +- Технические риски +- Планы по их устранению +``` + +### 4. Реализация и контроль (Implementation & Governance) + +**Роль Solution Architect в реализации:** + +- **Техническое лидерство** команды разработки +- **Контроль качества** архитектурных решений +- **Управление изменениями** в архитектуре +- **Решение технических проблем** + +## Нефункциональные требования (NFR) в фокусе Solution Architect + +### 1. Масштабируемость (Scalability) + +**Определение:** Способность системы обрабатывать увеличение нагрузки. + +**Метрики:** +- Количество одновременных пользователей +- Объем обрабатываемых данных +- Количество транзакций в секунду + +**Примеры требований:** +```yaml +scalability: + concurrent_users: 10000 + data_volume: "1TB per day" + transactions_per_second: 1000 + response_time: "< 200ms for 95% of requests" + availability: "99.9%" +``` + +### 2. Надежность (Reliability) + +**Определение:** Способность системы выполнять требуемые функции в заданных условиях. + +**Аспекты:** +- Отказоустойчивость (Fault Tolerance) +- Восстановление после сбоев (Recovery) +- Предотвращение потери данных (Data Integrity) + +### 3. Безопасность (Security) + +**Определение:** Защита системы от угроз и несанкционированного доступа. + +**Компоненты:** +- Аутентификация и авторизация +- Шифрование данных +- Защита от атак +- Соответствие стандартам + +### 4. Производительность (Performance) + +**Определение:** Скорость и эффективность выполнения операций. + +**Метрики:** +- Время отклика (Response Time) +- Пропускная способность (Throughput) +- Использование ресурсов (Resource Utilization) + +## Ключевые навыки Solution Architect + +### Технические навыки + +1. **Архитектурные паттерны и стили** + - Микросервисы, SOA, Event-driven + - CQRS, Event Sourcing, Saga + - API Gateway, Service Mesh + +2. **Облачные технологии** + - AWS, Azure, GCP + - Containerization (Docker, Kubernetes) + - Serverless архитектура + +3. **Интеграционные технологии** + - REST, GraphQL, gRPC + - Message Queues (RabbitMQ, Kafka) + - API Management + +### Мягкие навыки + +1. **Коммуникация** + - Презентация технических решений + - Взаимодействие с бизнесом + - Техническое письмо + +2. **Лидерство** + - Техническое лидерство + - Менторство + - Управление конфликтами + +3. **Аналитическое мышление** + - Анализ требований + - Принятие решений + - Управление рисками + +## Карьерный путь Solution Architect + +### Уровни развития + +1. **Junior Solution Architect** (2-3 года опыта) + - Помощь в проектировании решений + - Изучение архитектурных паттернов + - Работа под руководством Senior Architect + +2. **Solution Architect** (3-7 лет опыта) + - Независимое проектирование решений + - Взаимодействие с бизнесом + - Техническое лидерство команды + +3. **Senior Solution Architect** (7+ лет опыта) + - Сложные enterprise решения + - Архитектурная стратегия + - Менторство других архитекторов + +4. **Principal/Lead Solution Architect** (10+ лет опыта) + - Архитектурная стратегия компании + - Управление архитектурной командой + - Влияние на технологическую стратегию diff --git a/modules/module1/sections/02-introduction-to-software-architecture.md b/modules/module1/sections/02-introduction-to-software-architecture.md new file mode 100644 index 0000000..8b7a8ac --- /dev/null +++ b/modules/module1/sections/02-introduction-to-software-architecture.md @@ -0,0 +1,1335 @@ +# Введение в архитектуру ПО + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +## Определение архитектуры ПО + +**Архитектура программного обеспечения** — это структура системы, включающая: +- **Компоненты системы и их взаимодействие** — основные части системы и способы их связи +- **Внешние интерфейсы** — точки взаимодействия с внешними системами и пользователями +- **Ограничения и требования** — технические и бизнес-ограничения, которым должна соответствовать система +- **Принципы проектирования** — руководящие принципы для принятия архитектурных решений + +> **💡 Важно**: Архитектура — это не только код, но и решения о том, как система будет развиваться, масштабироваться и поддерживаться. + +## Ключевые характеристики архитектуры + +1. **Структурность** — архитектура определяет структуру системы + - Организует компоненты в логические группы + - Определяет иерархию и зависимости между компонентами + - Создает основу для понимания системы + +2. **Абстрактность** — скрывает детали реализации + - Фокусируется на "что" делает система, а не "как" + - Позволяет обсуждать систему на высоком уровне + - Упрощает понимание для разных ролей в команде + +3. **Системность** — рассматривает систему как целое + - Учитывает взаимодействие всех компонентов + - Анализирует влияние изменений на всю систему + - Обеспечивает целостность архитектурных решений + +4. **Эволюционность** — может изменяться со временем + - Адаптируется к изменяющимся требованиям + - Позволяет постепенное развитие системы + - Учитывает технический долг и рефакторинг + +## Зачем нужна архитектура? + +- **Управление сложностью** — разбиение сложной системы на понятные части + - Разделение ответственности между компонентами + - Создание четких границ и интерфейсов + - Упрощение понимания и поддержки системы + +- **Коммуникация** — общий язык для обсуждения системы + - Единая терминология для команды разработки + - Визуальное представление системы через диаграммы + - Облегчение передачи знаний между разработчиками + +- **Планирование** — основа для принятия технических решений + - Анализ влияния изменений на систему + - Оценка рисков и последствий решений + - Планирование развития и масштабирования + +- **Переиспользование** — возможность использования компонентов в других проектах + - Создание переиспользуемых модулей + - Стандартизация подходов к решению задач + - Ускорение разработки новых проектов + +## Архитектурные решения + +**Архитектурное решение** — это выбор, который: +- **Влияет на структуру системы** — изменяет организацию компонентов и их взаимодействие +- **Имеет долгосрочные последствия** — последствия решения будут ощущаться месяцы и годы +- **Трудно изменить в будущем** — требует значительных усилий и времени для изменения +- **Влияет на качественные атрибуты** — производительность, масштабируемость, надежность, безопасность + +> **⚠️ Важно**: Архитектурные решения должны приниматься осознанно, с пониманием всех последствий и альтернатив. + +### Типы архитектурных решений: + +1. **Структурные решения** — выбор архитектурных стилей и паттернов +2. **Технологические решения** — выбор технологий и инструментов +3. **Интеграционные решения** — способы взаимодействия с внешними системами +4. **Операционные решения** — подходы к развертыванию и мониторингу + +## Анализ плюсов и минусов подходов + +### 1. Монолит vs Микросервисы + +#### Монолитная архитектура + +**✅ Плюсы:** +- **Простота разработки** — один репозиторий, одна база данных, один процесс +- **Простота тестирования** — все компоненты в одном процессе +- **Простота развертывания** — один артефакт для развертывания +- **Производительность** — нет сетевых вызовов между компонентами +- **Консистентность данных** — ACID транзакции в рамках одной БД +- **Простота отладки** — все логи в одном месте +- **Быстрый старт** — идеально для MVP и небольших команд + +**❌ Минусы:** +- **Сложность масштабирования** — нужно масштабировать весь монолит +- **Технологическая привязка** — сложно использовать разные технологии +- **Риск каскадных сбоев** — сбой одного компонента влияет на весь монолит +- **Сложность команд** — большие команды работают над одним кодом +- **Медленное развертывание** — изменение одного компонента требует пересборки всего +- **Технический долг** — сложно рефакторить большие монолиты + +#### Микросервисная архитектура + +**✅ Плюсы:** +- **Независимое масштабирование** — каждый сервис масштабируется отдельно +- **Технологическое разнообразие** — каждый сервис может использовать свою технологию +- **Независимые команды** — команды могут работать независимо +- **Отказоустойчивость** — сбой одного сервиса не влияет на другие +- **Быстрое развертывание** — можно развертывать сервисы независимо +- **Переиспользование** — сервисы можно переиспользовать в разных проектах + +**❌ Минусы:** +- **Сложность разработки** — нужно управлять множеством сервисов +- **Сетевая задержка** — вызовы между сервисами по сети +- **Сложность тестирования** — нужно тестировать интеграцию между сервисами +- **Сложность отладки** — логи разбросаны по разным сервисам +- **Проблемы с данными** — сложно обеспечить консистентность между сервисами +- **Операционная сложность** — нужно управлять множеством сервисов в продакшене + +#### Примеры реализации + +**Java (Spring Boot)** + +```java +// Монолитная архитектура +@SpringBootApplication +public class EcommerceApplication { + public static void main(String[] args) { + SpringApplication.run(EcommerceApplication.class, args); + } +} + +// Все сервисы в одном приложении +@RestController +@RequestMapping("/api") +public class EcommerceController { + + @Autowired + private UserService userService; + + @Autowired + private OrderService orderService; + + @Autowired + private PaymentService paymentService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + // Вся логика в одном месте + User user = userService.findById(request.getUserId()); + Order order = orderService.createOrder(request); + PaymentResult payment = paymentService.processPayment(order); + return ResponseEntity.ok(order); + } +} + +// Микросервисная архитектура +// User Service +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + User user = userService.findById(id); + return ResponseEntity.ok(user); + } +} + +// Order Service +@RestController +@RequestMapping("/api/orders") +public class OrderController { + @Autowired + private OrderService orderService; + + @PostMapping + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + Order order = orderService.createOrder(request); + return ResponseEntity.ok(order); + } +} +``` + +**Rust (Actix Web)** + +```rust +// Монолитная архитектура +use actix_web::{web, App, HttpServer, Result}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +struct User { + id: u64, + name: String, + email: String, +} + +#[derive(Serialize, Deserialize)] +struct Order { + id: u64, + user_id: u64, + status: String, + total: f64, +} + +// Все сервисы в одном приложении +async fn create_order( + order_data: web::Json, + user_service: web::Data, + order_service: web::Data, +) -> Result> { + // Вся логика в одном месте + let user = user_service.find_by_id(order_data.user_id).await?; + let order = order_service.create_order(order_data.into_inner()).await?; + Ok(web::Json(order)) +} + +// Микросервисная архитектура +// User Service +async fn get_user( + path: web::Path, + user_service: web::Data, +) -> Result> { + let user = user_service.find_by_id(path.into_inner()).await?; + Ok(web::Json(user)) +} + +// Order Service +async fn create_order( + order_data: web::Json, + order_service: web::Data, +) -> Result> { + let order = order_service.create_order(order_data.into_inner()).await?; + Ok(web::Json(order)) +} +``` + +**Node.js (Fastify)** + +```javascript +// Монолитная архитектура +const fastify = require('fastify')({ logger: true }); + +// Все сервисы в одном приложении +fastify.post('/api/orders', async (request, reply) => { + // Вся логика в одном месте + const user = await userService.findById(request.body.userId); + const order = await orderService.createOrder(request.body); + const payment = await paymentService.processPayment(order); + return order; +}); + +// Микросервисная архитектура +// User Service +fastify.get('/api/users/:id', async (request, reply) => { + const user = await userService.findById(request.params.id); + return user; +}); + +// Order Service +fastify.post('/api/orders', async (request, reply) => { + const order = await orderService.createOrder(request.body); + return order; +}); +``` + +**Python (FastAPI)** + +```python +# Монолитная архитектура +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel + +app = FastAPI() + +# Все сервисы в одном приложении +@app.post("/api/orders") +async def create_order(request: CreateOrderRequest): + # Вся логика в одном месте + user = await user_service.find_by_id(request.user_id) + order = await order_service.create_order(request) + payment = await payment_service.process_payment(order) + return order + +# Микросервисная архитектура +# User Service +@app.get("/api/users/{user_id}") +async def get_user(user_id: int): + user = await user_service.find_by_id(user_id) + return user + +# Order Service +@app.post("/api/orders") +async def create_order(request: CreateOrderRequest): + order = await order_service.create_order(request) + return order +``` + +### 2. SQL vs NoSQL + +#### SQL базы данных (PostgreSQL, MySQL) + +**✅ Плюсы:** +- **ACID транзакции** — гарантированная консистентность данных +- **Сложные запросы** — мощный SQL для аналитики и отчетов +- **Схема данных** — четкая структура и валидация данных +- **Зрелость** — проверенные временем решения +- **Стандартизация** — SQL стандарт, легко мигрировать между БД +- **Инструменты** — богатая экосистема инструментов +- **Сообщество** — большое сообщество и документация + +**❌ Минусы:** +- **Сложность масштабирования** — вертикальное масштабирование дорого +- **Схема данных** — сложно изменять структуру в больших системах +- **Производительность** — может быть медленнее для простых операций +- **Горизонтальное масштабирование** — сложно шардировать +- **Стоимость** — лицензии и ресурсы для больших систем + +#### NoSQL базы данных (MongoDB, Cassandra) + +**✅ Плюсы:** +- **Горизонтальное масштабирование** — легко масштабировать горизонтально +- **Гибкая схема** — можно изменять структуру данных без миграций +- **Производительность** — оптимизированы для конкретных паттернов доступа +- **Простота разработки** — документная модель близка к объектам в коде +- **Высокая доступность** — репликация из коробки +- **Стоимость** — часто дешевле для больших объемов данных + +**❌ Минусы:** +- **Ограниченная консистентность** — eventual consistency вместо ACID +- **Сложные запросы** — ограниченные возможности для аналитики +- **Дублирование данных** — данные могут дублироваться между документами +- **Ограниченные транзакции** — сложно обеспечить ACID между документами +- **Специфичность** — каждая NoSQL БД имеет свои особенности +- **Обучение** — команде нужно изучать новые концепции + +#### Примеры реализации + +**Java (Spring Boot + JPA)** + +```java +// SQL подход (PostgreSQL) +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String name; + + @Column(unique = true, nullable = false) + private String email; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + private List orders = new ArrayList<>(); + + // ACID транзакции + @Transactional + public User createUserWithOrder(CreateUserRequest request) { + User user = new User(); + user.setName(request.getName()); + user.setEmail(request.getEmail()); + user = userRepository.save(user); + + Order order = new Order(); + order.setUser(user); + order.setStatus("pending"); + order.setTotal(0.0); + orderRepository.save(order); + + return user; + } +} + +// NoSQL подход (MongoDB) +@Document(collection = "users") +public class MongoUser { + @Id + private String id; + + private String name; + private String email; + + // Встроенные документы + private List orders = new ArrayList<>(); + + // Гибкая схема данных + private Map additionalProperties = new HashMap<>(); + + // Простая работа с данными + public void addOrder(Order order) { + this.orders.add(order); + } +} +``` + +**Rust (SQLx + MongoDB)** + +```rust +// SQL подход (PostgreSQL) +use sqlx::{PgPool, Row}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +struct User { + id: i64, + name: String, + email: String, +} + +// ACID транзакции +async fn create_user_with_order( + pool: &PgPool, + request: CreateUserRequest, +) -> Result { + let mut tx = pool.begin().await?; + + // Создаем пользователя + let user = sqlx::query_as!( + User, + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email", + request.name, + request.email + ) + .fetch_one(&mut *tx) + .await?; + + // Создаем заказ + sqlx::query!( + "INSERT INTO orders (user_id, status, total) VALUES ($1, $2, $3)", + user.id, + "pending", + 0.0 + ) + .execute(&mut *tx) + .await?; + + tx.commit().await?; + Ok(user) +} + +// NoSQL подход (MongoDB) +use mongodb::bson::{doc, Document}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +struct MongoUser { + #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] + id: Option, + name: String, + email: String, + orders: Vec, + // Гибкая схема данных + additional_properties: Option, +} + +// Простая работа с данными +async fn create_user_with_order( + collection: &mongodb::Collection, + request: CreateUserRequest, +) -> Result { + let user = MongoUser { + id: None, + name: request.name, + email: request.email, + orders: vec![Order { + id: None, + status: "pending".to_string(), + total: 0.0, + }], + additional_properties: None, + }; + + let result = collection.insert_one(&user, None).await?; + Ok(user) +} +``` + +**Node.js (pg + Mongoose)** + +```javascript +// SQL подход (PostgreSQL) +const { Pool } = require('pg'); +const mongoose = require('mongoose'); + +// Подключение к PostgreSQL +const pool = new Pool({ + user: 'user', + host: 'localhost', + database: 'ecommerce', + password: 'password', + port: 5432, +}); + +// SQL модели (без ORM) +class User { + constructor(id, name, email) { + this.id = id; + this.name = name; + this.email = email; + } +} + +// ACID транзакции +async function createUserWithOrderSQL(request) { + const client = await pool.connect(); + + try { + await client.query('BEGIN'); + + // Создаем пользователя + const userResult = await client.query( + 'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email', + [request.name, request.email] + ); + const user = new User(userResult.rows[0].id, userResult.rows[0].name, userResult.rows[0].email); + + // Создаем заказ + const orderResult = await client.query( + 'INSERT INTO orders (user_id, status, total) VALUES ($1, $2, $3) RETURNING id, user_id, status, total', + [user.id, 'pending', 0] + ); + const order = new Order(orderResult.rows[0].id, orderResult.rows[0].user_id, orderResult.rows[0].status, orderResult.rows[0].total); + + await client.query('COMMIT'); + return user; + } catch (error) { + await client.query('ROLLBACK'); + throw error; + } finally { + client.release(); + } +} + +// NoSQL подход (MongoDB) +// Схема пользователя +const userSchema = new mongoose.Schema({ + name: { type: String, required: true }, + email: { type: String, required: true, unique: true }, + orders: [{ + id: String, + status: String, + total: Number + }], + // Гибкая схема данных + additionalProperties: { type: Map, of: mongoose.Schema.Types.Mixed } +}); + +const MongoUser = mongoose.model('User', userSchema); + +// Простая работа с данными +async function createUserWithOrderMongo(request) { + const user = new MongoUser({ + name: request.name, + email: request.email, + orders: [{ + id: new mongoose.Types.ObjectId().toString(), + status: 'pending', + total: 0 + }], + additionalProperties: new Map() + }); + + return await user.save(); +} +``` + +**Python (SQLAlchemy + MongoDB)** + +```python +# SQL подход (PostgreSQL) +from sqlalchemy import create_engine, Column, Integer, String, Float, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, relationship +from sqlalchemy.exc import SQLAlchemyError + +Base = declarative_base() + +class User(Base): + __tablename__ = 'users' + + id = Column(Integer, primary_key=True) + name = Column(String(100), nullable=False) + email = Column(String(100), unique=True, nullable=False) + orders = relationship("Order", back_populates="user") + +class Order(Base): + __tablename__ = 'orders' + + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey('users.id')) + status = Column(String(50)) + total = Column(Float) + user = relationship("User", back_populates="orders") + +# ACID транзакции +def create_user_with_order_sql(request): + session = Session() + try: + session.begin() + + # Создаем пользователя + user = User(name=request.name, email=request.email) + session.add(user) + session.flush() # Получаем ID + + # Создаем заказ + order = Order(user_id=user.id, status='pending', total=0.0) + session.add(order) + + session.commit() + return user + except SQLAlchemyError as e: + session.rollback() + raise e + finally: + session.close() + +# NoSQL подход (MongoDB) +from pymongo import MongoClient +from bson import ObjectId + +client = MongoClient('mongodb://localhost:27017/') +db = client['ecommerce'] + +# Простая работа с данными +def create_user_with_order_mongo(request): + user = { + 'name': request.name, + 'email': request.email, + 'orders': [{ + 'id': str(ObjectId()), + 'status': 'pending', + 'total': 0 + }], + # Гибкая схема данных + 'additional_properties': {} + } + + result = db.users.insert_one(user) + user['_id'] = result.inserted_id + return user +``` + +### 3. REST vs GraphQL + +#### REST API + +**✅ Плюсы:** +- **Простота** — понятный HTTP интерфейс +- **Кэширование** — HTTP кэширование работает из коробки +- **Стандартизация** — REST принципы понятны всем +- **Инструменты** — множество инструментов для тестирования и документации +- **Статус коды** — HTTP статус коды для обработки ошибок +- **Безопасность** — можно использовать стандартные HTTP методы аутентификации + +**❌ Минусы:** +- **Over-fetching** — клиент получает больше данных чем нужно +- **Under-fetching** — нужно делать несколько запросов для получения связанных данных +- **Версионирование** — сложно версионировать API +- **N+1 проблема** — множественные запросы для связанных данных +- **Жесткость** — сложно адаптировать под потребности клиентов + +#### GraphQL + +**✅ Плюсы:** +- **Точные запросы** — клиент получает только нужные данные +- **Единая точка входа** — один endpoint для всех запросов +- **Типизация** — строгая типизация схемы +- **Интроспекция** — автоматическая документация API +- **Версионирование** — эволюция схемы без breaking changes +- **Реальные данные** — клиент запрашивает данные в нужном формате + +**❌ Минусы:** +- **Сложность** — более сложный для понимания и реализации +- **Кэширование** — сложнее кэшировать чем REST +- **Производительность** — может быть медленнее для простых запросов +- **Безопасность** — сложнее контролировать доступ к данным +- **Обучение** — команде нужно изучать GraphQL +- **Инструменты** — меньше инструментов чем для REST + +#### Примеры реализации + +**Java (Spring Boot)** + +```java +// REST API +@RestController +@RequestMapping("/api/users") +public class UserController { + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + User user = userService.findById(id); + return ResponseEntity.ok(user); + } + + @GetMapping("/{id}/orders") + public ResponseEntity> getUserOrders(@PathVariable Long id) { + List orders = orderService.findByUserId(id); + return ResponseEntity.ok(orders); + } +} + +// GraphQL +@Component +public class UserResolver implements GraphQLResolver { + public List orders(User user) { + return orderService.findByUserId(user.getId()); + } +} +``` + +**Rust (Actix Web + Juniper)** + +```rust +// REST API +async fn get_user(path: web::Path) -> Result, actix_web::Error> { + let user = user_service.find_by_id(path.into_inner()).await?; + Ok(web::Json(user)) +} + +// GraphQL +#[juniper::object] +impl User { + fn orders(&self) -> Vec { + order_service.find_by_user_id(self.id) + } +} +``` + +**Node.js (Fastify + Mercurius)** + +```javascript +// REST API +fastify.get('/api/users/:id', async (request, reply) => { + const user = await userService.findById(request.params.id); + return user; +}); + +// GraphQL +const resolvers = { + User: { + orders: async (user) => { + return await orderService.findByUserId(user.id); + } + } +}; +``` + +**Python (FastAPI + Strawberry)** + +```python +# REST API +@app.get("/api/users/{user_id}") +async def get_user(user_id: int): + user = await user_service.find_by_id(user_id) + return user + +# GraphQL +@strawberry.type +class User: + @strawberry.field + async def orders(self) -> List[Order]: + return await order_service.find_by_user_id(self.id) +``` + +### 4. Стратегии кэширования + +#### Cache-Aside (Lazy Loading) + +**Описание:** Приложение само управляет кэшем. При чтении сначала проверяется кэш, при записи обновляется и кэш, и основное хранилище. + +**Как работает:** +1. **Чтение**: Проверить кэш → если есть, вернуть данные → если нет, загрузить из БД и сохранить в кэш +2. **Запись**: Записать в БД → обновить кэш + +**✅ Плюсы:** +- **Простота реализации** — легко понять и реализовать +- **Гибкость** — можно кэшировать только нужные данные +- **Отказоустойчивость** — при сбое кэша система продолжает работать +- **Контроль** — полный контроль над тем, что кэшируется +- **Независимость** — кэш и БД работают независимо + +**❌ Минусы:** +- **Cache miss penalty** — при промахе кэша нужно обращаться к БД +- **Сложность логики** — нужно управлять кэшем в коде приложения +- **Дублирование кода** — логика кэширования может дублироваться +- **Race conditions** — возможны состояния гонки при параллельных запросах + +**Когда использовать:** +- Сложная логика кэширования +- Нужен полный контроль над кэшем +- Разные стратегии для разных типов данных + +#### Write-Through + +**Описание:** При записи данные сохраняются одновременно в кэш и в основное хранилище. При чтении данные всегда берутся из кэша. + +**Как работает:** +1. **Чтение**: Всегда читать из кэша +2. **Запись**: Записать в БД → записать в кэш + +**✅ Плюсы:** +- **Консистентность** — данные в кэше всегда актуальны +- **Простота чтения** — не нужно проверять кэш при чтении +- **Надежность** — данные сохраняются в БД сразу +- **Простота реализации** — логика записи простая + +**❌ Минусы:** +- **Медленная запись** — каждая запись идет через БД +- **Производительность** — может быть медленнее для write-heavy нагрузок +- **Сложность** — нужно синхронизировать кэш и БД +- **Задержка записи** — запись ждет подтверждения от БД + +**Когда использовать:** +- Критична консистентность данных +- Write-heavy нагрузка невысокая +- Простая логика кэширования + +#### Write-Behind (Write-Back) + +**Описание:** При записи данные сначала сохраняются в кэш, а затем асинхронно записываются в основное хранилище. При чтении данные берутся из кэша. + +**Как работает:** +1. **Чтение**: Всегда читать из кэша +2. **Запись**: Записать в кэш → асинхронно записать в БД + +**✅ Плюсы:** +- **Высокая производительность** — быстрая запись в кэш +- **Batch операции** — можно группировать записи в БД +- **Масштабируемость** — хорошо для write-heavy нагрузок +- **Отзывчивость** — пользователь не ждет записи в БД + +**❌ Минусы:** +- **Риск потери данных** — данные могут быть потеряны при сбое +- **Сложность** — сложнее реализовать и отладить +- **Консистентность** — возможны временные несоответствия +- **Сложность отладки** — асинхронные операции сложнее отслеживать + +**Когда использовать:** +- Высокая write-heavy нагрузка +- Можно допустить потерю небольшого количества данных +- Критична производительность записи + +#### Refresh-Ahead + +**Описание:** Кэш обновляется до истечения срока действия, чтобы избежать cache miss. Используется для данных с предсказуемым паттерном доступа. + +**Как работает:** +1. **Чтение**: Читать из кэша +2. **Обновление**: Асинхронно обновлять кэш до истечения TTL +3. **Прогнозирование**: Анализировать паттерны доступа для предсказания + +**✅ Плюсы:** +- **Минимум cache miss** — данные обновляются заранее +- **Высокая производительность** — пользователь всегда получает данные из кэша +- **Предсказуемость** — можно прогнозировать нагрузку + +**❌ Минусы:** +- **Сложность реализации** — нужна логика прогнозирования +- **Избыточные обновления** — могут обновляться неиспользуемые данные +- **Ресурсы** — тратит ресурсы на обновление данных + +**Когда использовать:** +- Предсказуемые паттерны доступа +- Критична производительность +- Можно предсказать, какие данные понадобятся + +#### Write-Around + +**Описание:** При записи данные записываются только в основное хранилище, минуя кэш. Кэш обновляется только при чтении. + +**Как работает:** +1. **Чтение**: Проверить кэш → если есть, вернуть → если нет, загрузить из БД и сохранить в кэш +2. **Запись**: Записать только в БД + +**✅ Плюсы:** +- **Простота записи** — не нужно обновлять кэш при записи +- **Экономия ресурсов** — не тратит ресурсы на обновление кэша +- **Простота реализации** — логика записи очень простая + +**❌ Минусы:** +- **Cache miss** — после записи данные не будут в кэше +- **Производительность чтения** — может быть много cache miss +- **Неэффективность** — не использует кэш для записи + +**Когда использовать:** +- Write-heavy нагрузка +- Данные редко читаются после записи +- Простая логика кэширования + +#### Cache-Only + +**Описание:** Все данные хранятся только в кэше, без основного хранилища. Подходит для временных данных или данных, которые можно восстановить. + +**Как работает:** +1. **Чтение**: Всегда читать из кэша +2. **Запись**: Записать только в кэш + +**✅ Плюсы:** +- **Максимальная производительность** — нет обращений к БД +- **Простота** — нет сложной логики синхронизации +- **Масштабируемость** — легко масштабировать кэш + +**❌ Минусы:** +- **Риск потери данных** — данные могут быть потеряны при сбое +- **Ограниченный объем** — ограничен размером кэша +- **Временность** — данные могут быть удалены + +**Когда использовать:** +- Временные данные +- Данные можно восстановить +- Критична производительность +- Ограниченный объем данных + +#### Multi-Level Caching + +**Описание:** Использование нескольких уровней кэширования (L1, L2, L3) с разными характеристиками производительности и объема. + +**Как работает:** +1. **L1 Cache** — быстрый, небольшой (CPU cache, in-memory) +2. **L2 Cache** — средний по скорости и объему (Redis, Memcached) +3. **L3 Cache** — медленный, большой (Database, File system) + +**✅ Плюсы:** +- **Оптимальная производительность** — баланс скорости и объема +- **Гибкость** — можно настроить под конкретные нужды +- **Масштабируемость** — можно добавлять уровни + +**❌ Минусы:** +- **Сложность** — сложно реализовать и поддерживать +- **Синхронизация** — нужно синхронизировать между уровнями +- **Отладка** — сложно отлаживать многоуровневую систему + +**Когда использовать:** +- Сложные системы с разными требованиями +- Нужен баланс производительности и объема +- Есть ресурсы на поддержку сложной системы + +#### Cache-As-Sidecar + +**Описание:** Кэш работает как отдельный сервис рядом с основным приложением, но в том же контейнере или процессе. + +**Как работает:** +1. **Основное приложение** — обрабатывает бизнес-логику +2. **Sidecar кэш** — обрабатывает кэширование +3. **Коммуникация** — через локальные вызовы + +**✅ Плюсы:** +- **Производительность** — локальные вызовы быстрее сетевых +- **Простота развертывания** — один контейнер +- **Изоляция** — кэш изолирован от основного приложения + +**❌ Минусы:** +- **Сложность** — нужно управлять двумя процессами +- **Ресурсы** — тратит ресурсы на кэш-сервис +- **Масштабирование** — сложно масштабировать независимо + +**Когда использовать:** +- Нужна высокая производительность кэша +- Простота развертывания важнее независимого масштабирования +- Ограниченные сетевые ресурсы + +#### Примеры реализации + +**Java (Spring Boot + Redis)** + +```java +// Cache-Aside Pattern +@Service +public class UserService { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private UserRepository userRepository; + + private static final String CACHE_KEY = "user:"; + private static final long CACHE_TTL = 3600; // 1 час + + public User findById(Long id) { + String cacheKey = CACHE_KEY + id; + + // 1. Проверяем кэш + User cachedUser = (User) redisTemplate.opsForValue().get(cacheKey); + if (cachedUser != null) { + return cachedUser; + } + + // 2. Загружаем из БД + User user = userRepository.findById(id); + if (user != null) { + // 3. Сохраняем в кэш + redisTemplate.opsForValue().set(cacheKey, user, CACHE_TTL, TimeUnit.SECONDS); + } + + return user; + } +} + +// Write-Through Pattern +@Service +public class UserServiceWriteThrough { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private UserRepository userRepository; + + public User save(User user) { + // 1. Сохраняем в БД + User savedUser = userRepository.save(user); + + // 2. Обновляем кэш + String cacheKey = CACHE_KEY + savedUser.getId(); + redisTemplate.opsForValue().set(cacheKey, savedUser, CACHE_TTL, TimeUnit.SECONDS); + + return savedUser; + } +} +``` + +**Rust (Redis + Tokio)** + +```rust +// Cache-Aside Pattern +use redis::Commands; +use tokio::time::{sleep, Duration}; + +pub struct UserService { + redis_client: redis::Client, + user_repository: UserRepository, +} + +impl UserService { + pub async fn find_by_id(&self, id: u64) -> Result, Box> { + let cache_key = format!("user:{}", id); + let mut conn = self.redis_client.get_async_connection().await?; + + // 1. Проверяем кэш + if let Ok(cached_user) = conn.get::(&cache_key).await { + if let Ok(user) = serde_json::from_str::(&cached_user) { + return Ok(Some(user)); + } + } + + // 2. Загружаем из БД + if let Some(user) = self.user_repository.find_by_id(id).await? { + // 3. Сохраняем в кэш + let user_json = serde_json::to_string(&user)?; + let _: () = conn.set_ex(&cache_key, user_json, 3600).await?; + return Ok(Some(user)); + } + + Ok(None) + } +} + +// Write-Behind Pattern +impl UserService { + pub async fn save_async(&self, user: User) -> Result> { + let cache_key = format!("user:{}", user.id); + let mut conn = self.redis_client.get_async_connection().await?; + + // 1. Обновляем кэш сразу + let user_json = serde_json::to_string(&user)?; + let _: () = conn.set_ex(&cache_key, user_json, 3600).await?; + + // 2. Сохраняем в БД асинхронно + let user_clone = user.clone(); + let repo = self.user_repository.clone(); + tokio::spawn(async move { + sleep(Duration::from_millis(100)).await; + let _ = repo.save(user_clone).await; + }); + + Ok(user) + } +} +``` + +**Node.js (Redis + ioredis)** + +```javascript +// Cache-Aside Pattern +const Redis = require('ioredis'); +const redis = new Redis(); + +class UserService { + constructor(userRepository) { + this.userRepository = userRepository; + this.CACHE_TTL = 3600; // 1 час + } + + async findById(id) { + const cacheKey = `user:${id}`; + + try { + // 1. Проверяем кэш + const cachedUser = await redis.get(cacheKey); + if (cachedUser) { + return JSON.parse(cachedUser); + } + + // 2. Загружаем из БД + const user = await this.userRepository.findById(id); + if (user) { + // 3. Сохраняем в кэш + await redis.setex(cacheKey, this.CACHE_TTL, JSON.stringify(user)); + } + + return user; + } catch (error) { + console.error('Cache error:', error); + // Fallback to database + return await this.userRepository.findById(id); + } + } +} + +// Write-Through Pattern +class UserServiceWriteThrough { + async save(user) { + // 1. Сохраняем в БД + const savedUser = await this.userRepository.save(user); + + // 2. Обновляем кэш + const cacheKey = `user:${savedUser.id}`; + await redis.setex(cacheKey, this.CACHE_TTL, JSON.stringify(savedUser)); + + return savedUser; + } +} + +// Write-Behind Pattern +class UserServiceWriteBehind { + async saveAsync(user) { + const cacheKey = `user:${user.id}`; + + // 1. Обновляем кэш сразу + await redis.setex(cacheKey, this.CACHE_TTL, JSON.stringify(user)); + + // 2. Сохраняем в БД асинхронно + setImmediate(async () => { + try { + await this.userRepository.save(user); + } catch (error) { + console.error('Async save error:', error); + } + }); + + return user; + } +} +``` + +**Python (Redis + aioredis)** + +```python +# Cache-Aside Pattern +import asyncio +import json +import aioredis + +class UserService: + def __init__(self, user_repository): + self.user_repository = user_repository + self.redis = None + self.CACHE_TTL = 3600 # 1 час + + async def get_redis(self): + if self.redis is None: + self.redis = await aioredis.from_url("redis://localhost") + return self.redis + + async def find_by_id(self, user_id): + cache_key = f"user:{user_id}" + redis = await self.get_redis() + + try: + # 1. Проверяем кэш + cached_user = await redis.get(cache_key) + if cached_user: + return json.loads(cached_user) + + # 2. Загружаем из БД + user = await self.user_repository.find_by_id(user_id) + if user: + # 3. Сохраняем в кэш + await redis.setex(cache_key, self.CACHE_TTL, json.dumps(user.to_dict())) + + return user + except Exception as e: + print(f"Cache error: {e}") + # Fallback to database + return await self.user_repository.find_by_id(user_id) + + # Write-Through Pattern + async def save(self, user): + # 1. Сохраняем в БД + saved_user = await self.user_repository.save(user) + + # 2. Обновляем кэш + cache_key = f"user:{saved_user.id}" + redis = await self.get_redis() + await redis.setex(cache_key, self.CACHE_TTL, json.dumps(saved_user.to_dict())) + + return saved_user + + # Write-Behind Pattern + async def save_async(self, user): + cache_key = f"user:{user.id}" + redis = await self.get_redis() + + # 1. Обновляем кэш сразу + await redis.setex(cache_key, self.CACHE_TTL, json.dumps(user.to_dict())) + + # 2. Сохраняем в БД асинхронно + asyncio.create_task(self._save_to_database(user)) + + return user + + async def _save_to_database(self, user): + try: + await asyncio.sleep(0.1) # Небольшая задержка + await self.user_repository.save(user) + except Exception as e: + print(f"Async save error: {e}") +``` + +## Рекомендации по выбору + +### Когда выбирать монолит: +- Команда до 10 человек +- Простой домен +- Быстрый MVP +- Нет опыта с микросервисами + +### Когда выбирать микросервисы: +- Большая команда (10+ человек) +- Сложный домен +- Разные технологии для разных сервисов +- Необходимость независимого масштабирования + +### Когда выбирать SQL: +- Сложные запросы и аналитика +- Критична консистентность данных +- Транзакции между сущностями +- Команда знает SQL + +### Когда выбирать NoSQL: +- Большие объемы данных +- Простые запросы +- Горизонтальное масштабирование +- Гибкая схема данных + +### Когда выбирать REST: +- Простые API +- HTTP кэширование важно +- Команда знает REST +- Много разных клиентов + +### Когда выбирать GraphQL: +- Сложные клиентские приложения +- Мобильные приложения +- Важна производительность сети +- Единый endpoint для всех данных + +### Когда выбирать стратегии кэширования: + +#### Cache-Aside (Lazy Loading): +- Сложная логика кэширования +- Нужен полный контроль над кэшем +- Разные стратегии для разных типов данных +- Можно допустить cache miss + +#### Write-Through: +- Критична консистентность данных +- Write-heavy нагрузка невысокая +- Простая логика кэширования +- Нужна надежность + +#### Write-Behind (Write-Back): +- Высокая write-heavy нагрузка +- Можно допустить потерю небольшого количества данных +- Критична производительность записи +- Асинхронная обработка допустима + +#### Refresh-Ahead: +- Предсказуемые паттерны доступа +- Критична производительность +- Можно предсказать, какие данные понадобятся +- Есть ресурсы на прогнозирование + +#### Write-Around: +- Write-heavy нагрузка +- Данные редко читаются после записи +- Простая логика кэширования +- Экономия ресурсов важна + +#### Cache-Only: +- Временные данные +- Данные можно восстановить +- Критична производительность +- Ограниченный объем данных + +#### Multi-Level Caching: +- Сложные системы с разными требованиями +- Нужен баланс производительности и объема +- Есть ресурсы на поддержку сложной системы +- Разные типы данных требуют разных подходов + +#### Cache-As-Sidecar: +- Нужна высокая производительность кэша +- Простота развертывания важнее независимого масштабирования +- Ограниченные сетевые ресурсы +- Локальная производительность критична + +## Заключение + +Архитектурные решения — это основа качественной системы. Каждое решение должно приниматься с пониманием: + +- **Последствий** — как решение повлияет на систему +- **Альтернатив** — какие есть другие варианты +- **Торговых компромиссов** — что мы получаем и что теряем +- **Контекста** — в каких условиях решение будет работать + +> **💡 Совет**: Документируйте архитектурные решения в Architecture Decision Records (ADR) для будущих поколений разработчиков. + diff --git a/modules/module1/sections/03-domain-driven-design.md b/modules/module1/sections/03-domain-driven-design.md new file mode 100644 index 0000000..4e4d41b --- /dev/null +++ b/modules/module1/sections/03-domain-driven-design.md @@ -0,0 +1,683 @@ +# Доменная модель и Domain-Driven Design + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +## Что такое домен? + +**Домен (Domain)** — это предметная область, в которой работает приложение. Это сфера знаний, правил и процессов, которые определяют, как работает бизнес. + +### Ключевые характеристики домена: + +1. **Бизнес-логика** — правила и процессы, специфичные для предметной области +2. **Терминология** — язык, используемый экспертами в данной области +3. **Ограничения** — правила и ограничения, которые должны соблюдаться +4. **Процессы** — последовательности действий, выполняемых в домене + +## Domain-Driven Design (DDD) + +**Domain-Driven Design** — подход к разработке программного обеспечения, который фокусируется на моделировании предметной области. + +### Основные принципы DDD: + +1. **Фокус на домене** — архитектура отражает структуру домена +2. **Единый язык** — общий язык между разработчиками и экспертами +3. **Модель-код соответствие** — код отражает доменную модель +4. **Итеративное уточнение** — постоянное улучшение понимания домена + +## Строительные блоки DDD + +### 1. Entity (Сущность) + +**Entity** — объект, который имеет уникальную идентичность и жизненный цикл. + +```java +// Пример Entity - Заказ +@Entity +public class Order { + @Id + private OrderId id; + private CustomerId customerId; + private OrderStatus status; + private Money totalAmount; + private List items; + + // Конструктор + public Order(OrderId id, CustomerId customerId) { + this.id = id; + this.customerId = customerId; + this.status = OrderStatus.DRAFT; + this.items = new ArrayList<>(); + } + + // Бизнес-методы + public void addItem(ProductId productId, Quantity quantity, Money unitPrice) { + if (status != OrderStatus.DRAFT) { + throw new IllegalStateException("Cannot modify confirmed order"); + } + + OrderItem item = new OrderItem(productId, quantity, unitPrice); + items.add(item); + recalculateTotal(); + } + + public void confirm() { + if (items.isEmpty()) { + throw new IllegalStateException("Cannot confirm empty order"); + } + + this.status = OrderStatus.CONFIRMED; + // Доменное событие + DomainEvents.raise(new OrderConfirmed(this.id)); + } + + private void recalculateTotal() { + this.totalAmount = items.stream() + .map(OrderItem::getSubtotal) + .reduce(Money.ZERO, Money::add); + } + + // Геттеры + public OrderId getId() { return id; } + public CustomerId getCustomerId() { return customerId; } + public OrderStatus getStatus() { return status; } + public Money getTotalAmount() { return totalAmount; } + public List getItems() { return Collections.unmodifiableList(items); } +} +``` + +### 2. Value Object (Объект-значение) + +**Value Object** — объект, который определяется только своими атрибутами, без идентичности. + +```java +// Пример Value Object - Деньги +public class Money { + private final BigDecimal amount; + private final Currency currency; + + public Money(BigDecimal amount, Currency currency) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Amount must be non-negative"); + } + if (currency == null) { + throw new IllegalArgumentException("Currency cannot be null"); + } + + this.amount = amount.setScale(2, RoundingMode.HALF_UP); + this.currency = currency; + } + + public Money add(Money other) { + if (!this.currency.equals(other.currency)) { + throw new IllegalArgumentException("Cannot add different currencies"); + } + return new Money(this.amount.add(other.amount), this.currency); + } + + public Money multiply(BigDecimal factor) { + return new Money(this.amount.multiply(factor), this.currency); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Money money = (Money) obj; + return Objects.equals(amount, money.amount) && + Objects.equals(currency, money.currency); + } + + @Override + public int hashCode() { + return Objects.hash(amount, currency); + } + + // Геттеры + public BigDecimal getAmount() { return amount; } + public Currency getCurrency() { return currency; } + + public static final Money ZERO = new Money(BigDecimal.ZERO, Currency.getInstance("USD")); +} +``` + +### 3. Aggregate (Агрегат) + +**Aggregate** — кластер связанных объектов, которые рассматриваются как единое целое. + +```java +// Пример Aggregate - Заказ +public class Order { + private OrderId id; + private CustomerId customerId; + private OrderStatus status; + private Money totalAmount; + private List items; + private List domainEvents; + + // Конструктор и методы... + + // Методы для работы с доменными событиями + public List getUncommittedEvents() { + return new ArrayList<>(domainEvents); + } + + public void markEventsAsCommitted() { + domainEvents.clear(); + } + + private void raiseEvent(DomainEvent event) { + domainEvents.add(event); + } +} + +// Корневой объект агрегата +public class OrderAggregate { + private Order order; + + public OrderAggregate(OrderId id, CustomerId customerId) { + this.order = new Order(id, customerId); + } + + public void addItem(ProductId productId, Quantity quantity, Money unitPrice) { + order.addItem(productId, quantity, unitPrice); + } + + public void confirm() { + order.confirm(); + } + + public Order getOrder() { + return order; + } +} +``` + +### 4. Domain Service (Доменный сервис) + +**Domain Service** — сервис, который содержит бизнес-логику, не принадлежащую ни одной сущности. + +```java +// Пример Domain Service - Расчет скидки +@Service +public class DiscountCalculationService { + + public Money calculateDiscount(Order order, Customer customer) { + Money discount = Money.ZERO; + + // Скидка за количество товаров + int totalItems = order.getItems().stream() + .mapToInt(item -> item.getQuantity().getValue()) + .sum(); + + if (totalItems >= 10) { + discount = discount.add(order.getTotalAmount().multiply(new BigDecimal("0.1"))); + } + + // Скидка для VIP клиентов + if (customer.isVip()) { + discount = discount.add(order.getTotalAmount().multiply(new BigDecimal("0.05"))); + } + + // Скидка за праздники + if (isHolidayPeriod()) { + discount = discount.add(order.getTotalAmount().multiply(new BigDecimal("0.15"))); + } + + return discount; + } + + private boolean isHolidayPeriod() { + LocalDate now = LocalDate.now(); + return now.getMonth() == Month.DECEMBER && now.getDayOfMonth() >= 20; + } +} +``` + +### 5. Repository (Репозиторий) + +**Repository** — интерфейс для доступа к агрегатам. + +```java +// Интерфейс репозитория +public interface OrderRepository { + void save(Order order); + Optional findById(OrderId id); + List findByCustomerId(CustomerId customerId); + List findByStatus(OrderStatus status); + void delete(OrderId id); +} + +// Реализация репозитория +@Repository +public class JpaOrderRepository implements OrderRepository { + + @PersistenceContext + private EntityManager entityManager; + + @Override + public void save(Order order) { + entityManager.persist(order); + } + + @Override + public Optional findById(OrderId id) { + Order order = entityManager.find(Order.class, id); + return Optional.ofNullable(order); + } + + @Override + public List findByCustomerId(CustomerId customerId) { + return entityManager.createQuery( + "SELECT o FROM Order o WHERE o.customerId = :customerId", Order.class) + .setParameter("customerId", customerId) + .getResultList(); + } + + @Override + public List findByStatus(OrderStatus status) { + return entityManager.createQuery( + "SELECT o FROM Order o WHERE o.status = :status", Order.class) + .setParameter("status", status) + .getResultList(); + } + + @Override + public void delete(OrderId id) { + Order order = entityManager.find(Order.class, id); + if (order != null) { + entityManager.remove(order); + } + } +} +``` + +## Bounded Context (Ограниченный контекст) + +**Bounded Context** — граница, в которой определенная доменная модель является валидной. + +### Примеры Bounded Context: + +1. **Order Management** — управление заказами +2. **Customer Management** — управление клиентами +3. **Inventory Management** — управление складом +4. **Payment Processing** — обработка платежей + +```java +// Bounded Context: Order Management +@BoundedContext("OrderManagement") +public class OrderContext { + + // Агрегаты + public class Order { /* ... */ } + public class OrderItem { /* ... */ } + + // Сервисы + public class OrderService { /* ... */ } + public class OrderValidationService { /* ... */ } + + // Репозитории + public interface OrderRepository { /* ... */ } +} + +// Bounded Context: Customer Management +@BoundedContext("CustomerManagement") +public class CustomerContext { + + // Агрегаты + public class Customer { /* ... */ } + public class CustomerProfile { /* ... */ } + + // Сервисы + public class CustomerService { /* ... */ } + public class CustomerRegistrationService { /* ... */ } + + // Репозитории + public interface CustomerRepository { /* ... */ } +} +``` + +## Ubiquitous Language (Единый язык) + +**Ubiquitous Language** — общий язык между разработчиками и экспертами предметной области. + +### Пример единого языка для e-commerce: + +```java +// Термины домена +public enum OrderStatus { + DRAFT("Черновик"), + CONFIRMED("Подтвержден"), + SHIPPED("Отправлен"), + DELIVERED("Доставлен"), + CANCELLED("Отменен"); + + private final String description; + + OrderStatus(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} + +// Доменные события +public class OrderConfirmed implements DomainEvent { + private final OrderId orderId; + private final LocalDateTime occurredOn; + + public OrderConfirmed(OrderId orderId) { + this.orderId = orderId; + this.occurredOn = LocalDateTime.now(); + } + + public OrderId getOrderId() { + return orderId; + } + + public LocalDateTime getOccurredOn() { + return occurredOn; + } +} +``` + +## Event Storming + +**Event Storming** — методология для изучения домена через события. + +### Процесс Event Storming: + +1. **Доменные события** — что происходит в системе +2. **Команды** — что вызывает события +3. **Агрегаты** — что обрабатывает команды +4. **Политики** — бизнес-правила +5. **Внешние системы** — интеграции + +```markdown +# Event Storming для E-commerce + +## Доменные события +- OrderCreated (Заказ создан) +- OrderConfirmed (Заказ подтвержден) +- PaymentProcessed (Платеж обработан) +- OrderShipped (Заказ отправлен) +- OrderDelivered (Заказ доставлен) + +## Команды +- CreateOrder (Создать заказ) +- ConfirmOrder (Подтвердить заказ) +- ProcessPayment (Обработать платеж) +- ShipOrder (Отправить заказ) + +## Агрегаты +- Order (Заказ) +- Customer (Клиент) +- Product (Товар) +- Payment (Платеж) + +## Политики +- Скидка за количество товаров +- VIP статус клиента +- Праздничные скидки +``` + +## Антипаттерны DDD + +### 1. Anemic Domain Model (Анемичная доменная модель) + +**Проблема:** Доменные объекты содержат только данные без поведения. + +```java +// Плохо: Анемичная модель +public class Order { + private Long id; + private String customerId; + private String status; + private BigDecimal totalAmount; + + // Только геттеры и сеттеры + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + // ... +} + +// Хорошо: Богатая доменная модель +public class Order { + private OrderId id; + private CustomerId customerId; + private OrderStatus status; + private Money totalAmount; + + // Бизнес-методы + public void addItem(ProductId productId, Quantity quantity, Money unitPrice) { + // Бизнес-логика + } + + public void confirm() { + // Бизнес-логика + } +} +``` + +### 2. God Object (Божественный объект) + +**Проблема:** Один объект содержит слишком много ответственности. + +```java +// Плохо: God Object +public class Order { + // Слишком много ответственности + public void addItem() { /* ... */ } + public void calculateTax() { /* ... */ } + public void sendEmail() { /* ... */ } + public void updateInventory() { /* ... */ } + public void processPayment() { /* ... */ } +} + +// Хорошо: Разделение ответственности +public class Order { + public void addItem() { /* ... */ } +} + +public class TaxCalculationService { + public Money calculateTax(Order order) { /* ... */ } +} + +public class EmailService { + public void sendOrderConfirmation(Order order) { /* ... */ } +} +``` + +## Интеграция DDD с архитектурой + +### 1. Layered Architecture + DDD + +```java +// Presentation Layer +@RestController +public class OrderController { + @Autowired + private OrderApplicationService orderService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + OrderId orderId = orderService.createOrder(request); + return ResponseEntity.ok(new OrderResponse(orderId)); + } +} + +// Application Layer +@Service +public class OrderApplicationService { + @Autowired + private OrderRepository orderRepository; + + @Autowired + private OrderDomainService orderDomainService; + + public OrderId createOrder(CreateOrderRequest request) { + Order order = new Order( + new OrderId(UUID.randomUUID()), + new CustomerId(request.getCustomerId()) + ); + + for (OrderItemRequest itemRequest : request.getItems()) { + order.addItem( + new ProductId(itemRequest.getProductId()), + new Quantity(itemRequest.getQuantity()), + new Money(itemRequest.getUnitPrice(), Currency.USD) + ); + } + + orderRepository.save(order); + return order.getId(); + } +} + +// Domain Layer +public class Order { + // Доменная логика +} + +// Infrastructure Layer +@Repository +public class JpaOrderRepository implements OrderRepository { + // Реализация репозитория +} +``` + +### 2. Hexagonal Architecture + DDD + +```java +// Domain Layer (внутренний круг) +public class Order { + // Доменная логика +} + +public interface OrderRepository { + void save(Order order); + Optional findById(OrderId id); +} + +// Application Layer +@Service +public class OrderService { + private final OrderRepository orderRepository; + + public OrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public void createOrder(Order order) { + // Применение доменных правил + orderRepository.save(order); + } +} + +// Infrastructure Layer (внешний круг) +@Repository +public class JpaOrderRepository implements OrderRepository { + // Реализация через JPA +} + +// Adapters +@RestController +public class OrderController { + // REST API +} + +@Component +public class OrderEventHandler { + // Обработка событий +} +``` + +## Практические рекомендации + +### 1. Начните с доменных событий + +```java +// Сначала определите события +public class OrderCreated implements DomainEvent { + private final OrderId orderId; + private final CustomerId customerId; + private final LocalDateTime occurredOn; + + // Конструктор, геттеры +} + +// Затем создайте агрегаты +public class Order { + public void confirm() { + // Бизнес-логика + DomainEvents.raise(new OrderConfirmed(this.id)); + } +} +``` + +### 2. Используйте Value Objects для примитивов + +```java +// Вместо String +public class CustomerId { + private final String value; + + public CustomerId(String value) { + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException("Customer ID cannot be empty"); + } + this.value = value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + CustomerId that = (CustomerId) obj; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} +``` + +### 3. Следите за границами агрегатов + +```java +// Правильно: Агрегат управляет только своими данными +public class Order { + private OrderId id; + private List items; + + public void addItem(ProductId productId, Quantity quantity, Money unitPrice) { + // Логика добавления товара в заказ + } +} + +// Неправильно: Агрегат управляет чужими данными +public class Order { + public void updateProductInventory(ProductId productId, Quantity quantity) { + // Это не должно быть в Order агрегате + } +} +``` + +## Заключение + +Domain-Driven Design помогает создавать архитектуру, которая: + +1. **Отражает бизнес-логику** — код соответствует домену +2. **Улучшает коммуникацию** — единый язык между командами +3. **Повышает качество** — четкие границы и ответственности +4. **Упрощает тестирование** — изолированная доменная логика +5. **Обеспечивает эволюцию** — гибкая архитектура для изменений + +DDD особенно эффективен для сложных бизнес-доменов, где важно точное понимание предметной области и создание архитектуры, которая может эволюционировать вместе с бизнесом. diff --git a/modules/module1/sections/04-design-principles.md b/modules/module1/sections/04-design-principles.md new file mode 100644 index 0000000..ce8733b --- /dev/null +++ b/modules/module1/sections/04-design-principles.md @@ -0,0 +1,1040 @@ +# Принципы проектирования + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +## SOLID принципы + +### 1. Single Responsibility Principle (SRP) +**Принцип единственной ответственности** + +> Класс должен иметь только одну причину для изменения + +**Пример нарушения:** +```java +// Нарушение SRP - класс делает слишком много +class UserManager { + private UserRepository userRepository; + private EmailService emailService; + private PasswordEncoder passwordEncoder; + private AuditService auditService; + + public User createUser(CreateUserRequest request) { + // 1. Валидация данных + if (request.getEmail() == null || !isValidEmail(request.getEmail())) { + throw new ValidationException("Invalid email"); + } + + // 2. Хеширование пароля + String hashedPassword = passwordEncoder.encode(request.getPassword()); + + // 3. Создание пользователя + User user = new User(request.getName(), request.getEmail(), hashedPassword); + user = userRepository.save(user); + + // 4. Отправка приветственного email + emailService.sendWelcomeEmail(user.getEmail()); + + // 5. Аудит + auditService.logUserCreation(user.getId()); + + return user; + } + + public void updateUser(Long userId, UpdateUserRequest request) { + // Аналогично много ответственностей... + } + + public void deleteUser(Long userId) { + // Аналогично много ответственностей... + } +} +``` + +**Правильная реализация:** +```java +// Разделение ответственностей +class User { + private Long id; + private String name; + private String email; + private String passwordHash; + + // Только данные пользователя и бизнес-логика + public void changePassword(String newPassword, PasswordEncoder encoder) { + this.passwordHash = encoder.encode(newPassword); + } + + public boolean isEmailValid() { + return email != null && email.contains("@"); + } +} + +class UserValidator { + public void validateCreateRequest(CreateUserRequest request) { + if (request.getName() == null || request.getName().trim().isEmpty()) { + throw new ValidationException("Name is required"); + } + if (request.getEmail() == null || !isValidEmail(request.getEmail())) { + throw new ValidationException("Invalid email"); + } + if (request.getPassword() == null || request.getPassword().length() < 8) { + throw new ValidationException("Password must be at least 8 characters"); + } + } + + private boolean isValidEmail(String email) { + return email.matches("^[A-Za-z0-9+_.-]+@(.+)$"); + } +} + +class UserRepository { + public User save(User user) { + // Только операции с БД + return userRepository.save(user); + } + + public Optional findById(Long id) { + return userRepository.findById(id); + } +} + +class UserNotificationService { + public void sendWelcomeEmail(String email) { + // Только отправка уведомлений + emailService.sendWelcomeEmail(email); + } +} + +class UserAuditService { + public void logUserCreation(Long userId) { + // Только аудит + auditService.logUserCreation(userId); + } +} + +// Координатор, который использует все сервисы +class UserService { + private UserValidator validator; + private UserRepository repository; + private UserNotificationService notificationService; + private UserAuditService auditService; + private PasswordEncoder passwordEncoder; + + public User createUser(CreateUserRequest request) { + // 1. Валидация + validator.validateCreateRequest(request); + + // 2. Создание пользователя + String hashedPassword = passwordEncoder.encode(request.getPassword()); + User user = new User(request.getName(), request.getEmail(), hashedPassword); + + // 3. Сохранение + user = repository.save(user); + + // 4. Уведомления + notificationService.sendWelcomeEmail(user.getEmail()); + + // 5. Аудит + auditService.logUserCreation(user.getId()); + + return user; + } +} +``` + +### 2. Open/Closed Principle (OCP) +**Принцип открытости/закрытости** + +> Программные сущности должны быть открыты для расширения, но закрыты для модификации + +**Пример нарушения:** +```java +// Нарушение OCP - нужно модифицировать класс для добавления новых типов +class OrderProcessor { + public void processOrder(Order order) { + switch (order.getPaymentType()) { + case CREDIT_CARD: + processCreditCardPayment(order); + break; + case PAYPAL: + processPayPalPayment(order); + break; + case BANK_TRANSFER: + processBankTransferPayment(order); + break; + // При добавлении нового типа платежа нужно модифицировать этот класс + } + } + + private void processCreditCardPayment(Order order) { + // логика обработки кредитной карты + } + + private void processPayPalPayment(Order order) { + // логика обработки PayPal + } + + private void processBankTransferPayment(Order order) { + // логика обработки банковского перевода + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение OCP - открыто для расширения, закрыто для модификации +interface PaymentProcessor { + boolean canProcess(PaymentType paymentType); + PaymentResult processPayment(PaymentRequest request); +} + +class CreditCardProcessor implements PaymentProcessor { + @Override + public boolean canProcess(PaymentType paymentType) { + return paymentType == PaymentType.CREDIT_CARD; + } + + @Override + public PaymentResult processPayment(PaymentRequest request) { + // логика обработки кредитной карты + return new PaymentResult(true, "Payment processed via credit card"); + } +} + +class PayPalProcessor implements PaymentProcessor { + @Override + public boolean canProcess(PaymentType paymentType) { + return paymentType == PaymentType.PAYPAL; + } + + @Override + public PaymentResult processPayment(PaymentRequest request) { + // логика обработки PayPal + return new PaymentResult(true, "Payment processed via PayPal"); + } +} + +class CryptocurrencyProcessor implements PaymentProcessor { + @Override + public boolean canProcess(PaymentType paymentType) { + return paymentType == PaymentType.CRYPTOCURRENCY; + } + + @Override + public PaymentResult processPayment(PaymentRequest request) { + // логика обработки криптовалюты + return new PaymentResult(true, "Payment processed via cryptocurrency"); + } +} + +// Основной класс не нужно модифицировать для добавления новых процессоров +class OrderProcessor { + private List processors; + + public OrderProcessor(List processors) { + this.processors = processors; + } + + public PaymentResult processOrder(Order order) { + PaymentProcessor processor = processors.stream() + .filter(p -> p.canProcess(order.getPaymentType())) + .findFirst() + .orElseThrow(() -> new UnsupportedPaymentTypeException( + "No processor found for payment type: " + order.getPaymentType())); + + PaymentRequest request = new PaymentRequest(order.getAmount(), order.getPaymentDetails()); + return processor.processPayment(request); + } +} + +// Конфигурация процессоров +@Configuration +public class PaymentProcessorConfig { + + @Bean + public List paymentProcessors() { + return Arrays.asList( + new CreditCardProcessor(), + new PayPalProcessor(), + new CryptocurrencyProcessor() // Новый процессор добавляется без модификации существующего кода + ); + } +} +``` + +### 3. Liskov Substitution Principle (LSP) +**Принцип подстановки Лисков** + +> Объекты производного класса должны быть заменяемы объектами базового класса + +**Пример нарушения:** +```java +// Нарушение LSP +class Rectangle { + protected int width; + protected int height; + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getArea() { + return width * height; + } +} + +class Square extends Rectangle { + @Override + public void setWidth(int width) { + this.width = width; + this.height = width; // Нарушение LSP - изменяет поведение + } + + @Override + public void setHeight(int height) { + this.height = height; + this.width = height; // Нарушение LSP - изменяет поведение + } +} + +// Проблема: код, работающий с Rectangle, может сломаться с Square +public void testRectangle(Rectangle rectangle) { + rectangle.setWidth(5); + rectangle.setHeight(4); + assert rectangle.getArea() == 20; // Для Square это будет 16! +} +``` + +**Правильная реализация:** +```java +// Соблюдение LSP +abstract class Shape { + public abstract int getArea(); +} + +class Rectangle extends Shape { + private int width; + private int height; + + public Rectangle(int width, int height) { + this.width = width; + this.height = height; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + @Override + public int getArea() { + return width * height; + } +} + +class Square extends Shape { + private int side; + + public Square(int side) { + this.side = side; + } + + public void setSide(int side) { + this.side = side; + } + + @Override + public int getArea() { + return side * side; + } +} + +// Теперь любой Shape можно заменить другим Shape +public void testShape(Shape shape) { + int area = shape.getArea(); + // Логика работает корректно для любого типа Shape +} +``` + +### 4. Interface Segregation Principle (ISP) +**Принцип разделения интерфейсов** + +> Клиенты не должны зависеть от интерфейсов, которые они не используют + +**Пример нарушения:** +```java +// Нарушение ISP - слишком большой интерфейс +interface Worker { + void work(); + void eat(); + void sleep(); + void code(); + void design(); + void test(); + void deploy(); +} + +class Developer implements Worker { + @Override + public void work() { /* разработка */ } + + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void code() { /* кодирование */ } + + @Override + public void design() { /* не используется */ } + + @Override + public void test() { /* не используется */ } + + @Override + public void deploy() { /* не используется */ } +} + +class Designer implements Worker { + @Override + public void work() { /* работа */ } + + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void code() { /* не используется */ } + + @Override + public void design() { /* дизайн */ } + + @Override + public void test() { /* не используется */ } + + @Override + public void deploy() { /* не используется */ } +} +``` + +**Правильная реализация:** +```java +// Соблюдение ISP - разделение интерфейсов +interface Human { + void eat(); + void sleep(); +} + +interface Worker extends Human { + void work(); +} + +interface Developer extends Worker { + void code(); +} + +interface Designer extends Worker { + void design(); +} + +interface Tester extends Worker { + void test(); +} + +interface DevOps extends Worker { + void deploy(); +} + +// Теперь каждый класс реализует только нужные ему интерфейсы +class JavaDeveloper implements Developer { + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void work() { /* работа */ } + + @Override + public void code() { /* Java кодирование */ } +} + +class UIDesigner implements Designer { + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void work() { /* работа */ } + + @Override + public void design() { /* UI дизайн */ } +} + +// Можно комбинировать интерфейсы +class FullStackDeveloper implements Developer, Designer { + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void work() { /* работа */ } + + @Override + public void code() { /* кодирование */ } + + @Override + public void design() { /* дизайн */ } +} +``` + +### 5. Dependency Inversion Principle (DIP) +**Принцип инверсии зависимостей** + +> Модули высокого уровня не должны зависеть от модулей низкого уровня + +**Пример нарушения:** +```java +// Нарушение DIP - высокоуровневый модуль зависит от низкоуровневого +class EmailService { + public void sendEmail(String to, String subject, String body) { + // Прямая зависимость от конкретной реализации + SMTPClient smtpClient = new SMTPClient(); + smtpClient.connect("smtp.gmail.com", 587); + smtpClient.send(to, subject, body); + smtpClient.disconnect(); + } +} + +class OrderService { + private EmailService emailService; + + public OrderService() { + this.emailService = new EmailService(); // Жесткая зависимость + } + + public void processOrder(Order order) { + // Обработка заказа + order.setStatus(OrderStatus.PROCESSED); + + // Отправка уведомления + emailService.sendEmail( + order.getCustomerEmail(), + "Order Confirmation", + "Your order has been processed" + ); + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение DIP - зависимость от абстракций +interface NotificationService { + void sendNotification(String recipient, String subject, String message); +} + +interface EmailProvider { + void sendEmail(String to, String subject, String body); +} + +class SMTPEmailProvider implements EmailProvider { + @Override + public void sendEmail(String to, String subject, String body) { + SMTPClient smtpClient = new SMTPClient(); + smtpClient.connect("smtp.gmail.com", 587); + smtpClient.send(to, subject, body); + smtpClient.disconnect(); + } +} + +class SendGridEmailProvider implements EmailProvider { + @Override + public void sendEmail(String to, String subject, String body) { + SendGridClient client = new SendGridClient(); + client.send(to, subject, body); + } +} + +class EmailNotificationService implements NotificationService { + private EmailProvider emailProvider; + + public EmailNotificationService(EmailProvider emailProvider) { + this.emailProvider = emailProvider; // Инъекция зависимости + } + + @Override + public void sendNotification(String recipient, String subject, String message) { + emailProvider.sendEmail(recipient, subject, message); + } +} + +class SMSNotificationService implements NotificationService { + private SMSProvider smsProvider; + + public SMSNotificationService(SMSProvider smsProvider) { + this.smsProvider = smsProvider; + } + + @Override + public void sendNotification(String recipient, String subject, String message) { + smsProvider.sendSMS(recipient, message); + } +} + +// Высокоуровневый модуль зависит от абстракции +class OrderService { + private NotificationService notificationService; + + public OrderService(NotificationService notificationService) { + this.notificationService = notificationService; // Инъекция зависимости + } + + public void processOrder(Order order) { + // Обработка заказа + order.setStatus(OrderStatus.PROCESSED); + + // Отправка уведомления через абстракцию + notificationService.sendNotification( + order.getCustomerEmail(), + "Order Confirmation", + "Your order has been processed" + ); + } +} + +// Конфигурация зависимостей +@Configuration +public class AppConfig { + + @Bean + public EmailProvider emailProvider() { + return new SMTPEmailProvider(); // Можно легко заменить на SendGridEmailProvider + } + + @Bean + public NotificationService notificationService(EmailProvider emailProvider) { + return new EmailNotificationService(emailProvider); + } + + @Bean + public OrderService orderService(NotificationService notificationService) { + return new OrderService(notificationService); + } +} +``` + +## Дополнительные принципы + +### DRY (Не повторяйся) +**Не повторяйся** + +- Избегайте дублирования кода +- Выносите общую логику в отдельные функции/классы +- Используйте абстракции для переиспользования + +**Пример нарушения:** +```java +// Нарушение DRY - дублирование кода +class UserService { + public void createUser(String email, String password) { + if (email == null || email.isEmpty()) { + throw new IllegalArgumentException("Email cannot be null or empty"); + } + if (!email.contains("@")) { + throw new IllegalArgumentException("Invalid email format"); + } + if (password == null || password.length() < 8) { + throw new IllegalArgumentException("Password must be at least 8 characters"); + } + // создание пользователя + } + + public void updateUser(String email, String password) { + if (email == null || email.isEmpty()) { + throw new IllegalArgumentException("Email cannot be null or empty"); + } + if (!email.contains("@")) { + throw new IllegalArgumentException("Invalid email format"); + } + if (password == null || password.length() < 8) { + throw new IllegalArgumentException("Password must be at least 8 characters"); + } + // обновление пользователя + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение DRY - выделение общей логики +class ValidationUtils { + public static void validateEmail(String email) { + if (email == null || email.isEmpty()) { + throw new IllegalArgumentException("Email cannot be null or empty"); + } + if (!email.contains("@")) { + throw new IllegalArgumentException("Invalid email format"); + } + } + + public static void validatePassword(String password) { + if (password == null || password.length() < 8) { + throw new IllegalArgumentException("Password must be at least 8 characters"); + } + } +} + +class UserService { + public void createUser(String email, String password) { + ValidationUtils.validateEmail(email); + ValidationUtils.validatePassword(password); + // создание пользователя + } + + public void updateUser(String email, String password) { + ValidationUtils.validateEmail(email); + ValidationUtils.validatePassword(password); + // обновление пользователя + } +} +``` + +### KISS (Будь проще) +**Делай проще** + +- Простые решения часто лучше сложных +- Избегайте преждевременной оптимизации +- Читаемость кода важнее "умности" + +**Пример нарушения:** +```java +// Нарушение KISS - излишняя сложность +class ComplexCalculator { + public double calculate(String expression) { + if (expression == null || expression.trim().isEmpty()) { + throw new IllegalArgumentException("Expression cannot be null or empty"); + } + + String trimmedExpression = expression.trim(); + if (!trimmedExpression.matches("^[0-9+\\-*/\\s()]+$")) { + throw new IllegalArgumentException("Invalid characters in expression"); + } + + // Сложная логика парсинга и вычисления... + return evaluateComplexExpression(trimmedExpression); + } + + private double evaluateComplexExpression(String expr) { + // Очень сложная реализация + return 0.0; + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение KISS - простое решение +class SimpleCalculator { + public double add(double a, double b) { + return a + b; + } + + public double subtract(double a, double b) { + return a - b; + } + + public double multiply(double a, double b) { + return a * b; + } + + public double divide(double a, double b) { + if (b == 0) { + throw new IllegalArgumentException("Division by zero"); + } + return a / b; + } +} + +// Использование +SimpleCalculator calc = new SimpleCalculator(); +double result = calc.add(calc.multiply(2, 3), calc.divide(10, 2)); // 2*3 + 10/2 = 11 +``` + +### YAGNI (Вам это не понадобится) +**Тебе это не понадобится** + +- Не реализуйте функциональность "на будущее" +- Добавляйте функции только когда они действительно нужны +- Избегайте over-engineering + +**Пример нарушения:** +```java +// Нарушение YAGNI - преждевременная оптимизация +class UserRepository { + private Map cache = new ConcurrentHashMap<>(); + private List users = new ArrayList<>(); + private UserIndexer indexer = new UserIndexer(); + private UserValidator validator = new UserValidator(); + private UserMetricsCollector metrics = new UserMetricsCollector(); + + public void saveUser(User user) { + // Валидация (может не понадобиться) + validator.validate(user); + + // Индексация (может не понадобиться) + indexer.index(user); + + // Кэширование (может не понадобиться) + cache.put(user.getId(), user); + + // Сохранение + users.add(user); + + // Метрики (может не понадобиться) + metrics.recordSave(user); + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение YAGNI - только необходимая функциональность +class UserRepository { + private List users = new ArrayList<>(); + + public void saveUser(User user) { + if (user == null) { + throw new IllegalArgumentException("User cannot be null"); + } + users.add(user); + } + + public User findById(String id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + + return users.stream() + .filter(u -> u.getId().equals(id)) + .findFirst() + .orElse(null); + } +} + +// Когда понадобится кэширование - добавим +// Когда понадобится валидация - добавим +// Когда понадобится индексация - добавим +``` + +## Принципы модульности + +### Слабая связанность (Loose Coupling) +- Компоненты минимально зависят друг от друга +- Изменения в одном компоненте не влияют на другие +- Легче тестировать и поддерживать + +**Пример нарушения:** +```java +// Нарушение - тесная связанность +class OrderService { + private MySQLDatabase database; + private SMTPEmailService emailService; + private FileLogger logger; + + public OrderService() { + this.database = new MySQLDatabase("localhost", "orders_db"); + this.emailService = new SMTPEmailService("smtp.gmail.com", 587); + this.logger = new FileLogger("/var/log/orders.log"); + } + + public void processOrder(Order order) { + // Прямая зависимость от конкретных реализаций + database.save(order); + emailService.sendEmail(order.getCustomerEmail(), "Order processed"); + logger.log("Order processed: " + order.getId()); + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение - слабая связанность +interface OrderRepository { + void save(Order order); +} + +interface NotificationService { + void sendNotification(String recipient, String message); +} + +interface LoggingService { + void log(String message); +} + +class OrderService { + private OrderRepository repository; + private NotificationService notificationService; + private LoggingService loggingService; + + public OrderService(OrderRepository repository, + NotificationService notificationService, + LoggingService loggingService) { + this.repository = repository; + this.notificationService = notificationService; + this.loggingService = loggingService; + } + + public void processOrder(Order order) { + repository.save(order); + notificationService.sendNotification( + order.getCustomerEmail(), + "Order processed" + ); + loggingService.log("Order processed: " + order.getId()); + } +} + +// Конкретные реализации +class MySQLOrderRepository implements OrderRepository { + @Override + public void save(Order order) { + // MySQL специфичная логика + } +} + +class EmailNotificationService implements NotificationService { + @Override + public void sendNotification(String recipient, String message) { + // Email специфичная логика + } +} + +class FileLoggingService implements LoggingService { + @Override + public void log(String message) { + // File logging специфичная логика + } +} +``` + +### Высокая сплоченность (High Cohesion) +- Элементы внутри модуля тесно связаны +- Модуль имеет четко определенную ответственность +- Все функции модуля работают для достижения одной цели + +**Пример нарушения:** +```java +// Нарушение - низкая сплоченность +class UtilityClass { + public void sendEmail(String to, String subject, String body) { + // Отправка email + } + + public void calculateTax(double amount, double rate) { + // Расчет налога + } + + public void validatePassword(String password) { + // Валидация пароля + } + + public void formatDate(Date date, String format) { + // Форматирование даты + } + + public void compressFile(String filePath) { + // Сжатие файла + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение - высокая сплоченность +class EmailService { + public void sendEmail(String to, String subject, String body) { + validateEmail(to); + String formattedBody = formatEmailBody(body); + deliverEmail(to, subject, formattedBody); + } + + private void validateEmail(String email) { + // Валидация email адреса + } + + private String formatEmailBody(String body) { + // Форматирование тела письма + return body; + } + + private void deliverEmail(String to, String subject, String body) { + // Доставка email + } +} + +class TaxCalculator { + public double calculateTax(double amount, double rate) { + validateAmount(amount); + validateRate(rate); + return amount * rate; + } + + private void validateAmount(double amount) { + if (amount < 0) { + throw new IllegalArgumentException("Amount cannot be negative"); + } + } + + private void validateRate(double rate) { + if (rate < 0 || rate > 1) { + throw new IllegalArgumentException("Rate must be between 0 and 1"); + } + } +} + +class PasswordValidator { + public boolean isValid(String password) { + return hasMinimumLength(password) && + containsUppercase(password) && + containsLowercase(password) && + containsDigit(password); + } + + private boolean hasMinimumLength(String password) { + return password.length() >= 8; + } + + private boolean containsUppercase(String password) { + return password.chars().anyMatch(Character::isUpperCase); + } + + private boolean containsLowercase(String password) { + return password.chars().anyMatch(Character::isLowerCase); + } + + private boolean containsDigit(String password) { + return password.chars().anyMatch(Character::isDigit); + } +} +``` diff --git a/modules/module1/sections/05-architectural-styles-patterns.md b/modules/module1/sections/05-architectural-styles-patterns.md new file mode 100644 index 0000000..4deeab4 --- /dev/null +++ b/modules/module1/sections/05-architectural-styles-patterns.md @@ -0,0 +1,552 @@ +# Архитектурные стили и паттерны + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +## Монолитная архитектура + +### Определение +**Монолитная архитектура** — это архитектурный стиль, при котором все компоненты приложения объединены в единое развертываемое приложение. + +### Характеристики +- **Единое развертывание** — все компоненты развертываются вместе +- **Общая база данных** — все модули используют одну БД +- **Единый технологический стек** — один язык, один фреймворк +- **Прямые вызовы** — компоненты вызывают друг друга напрямую + +### Пример реализации +```java +// Монолитное приложение e-commerce +@SpringBootApplication +public class EcommerceApplication { + public static void main(String[] args) { + SpringApplication.run(EcommerceApplication.class, args); + } +} + +// Все контроллеры в одном приложении +@RestController +@RequestMapping("/api") +public class EcommerceController { + + @Autowired + private UserService userService; + + @Autowired + private OrderService orderService; + + @Autowired + private PaymentService paymentService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + // Вся логика в одном месте + User user = userService.findById(request.getUserId()); + Order order = orderService.createOrder(request); + PaymentResult payment = paymentService.processPayment(order); + return ResponseEntity.ok(order); + } +} +``` + +### Преимущества +- **Простота разработки** — легко начать проект +- **Простота тестирования** — все в одном процессе +- **Простота развертывания** — один артефакт +- **Производительность** — нет сетевых вызовов + +### Недостатки +- **Сложность масштабирования** — нужно масштабировать все приложение +- **Технологические ограничения** — привязанность к одному стеку +- **Риск сбоев** — падение одного компонента влияет на все +- **Сложность команд** — команды работают над одним кодом + +## Микросервисная архитектура + +### Определение +**Микросервисная архитектура** — это архитектурный стиль, при котором приложение разбивается на множество небольших, независимых сервисов. + +### Характеристики +- **Независимые сервисы** — каждый сервис работает автономно +- **Отдельные базы данных** — каждый сервис имеет свою БД +- **Разные технологии** — сервисы могут использовать разные стеки +- **Сетевое взаимодействие** — сервисы общаются по сети + +### Пример реализации +```java +// User Service +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + return ResponseEntity.ok(userService.findById(id)); + } +} + +// Order Service +@RestController +@RequestMapping("/api/orders") +public class OrderController { + @Autowired + private OrderService orderService; + + @Autowired + private UserServiceClient userServiceClient; + + @PostMapping + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + // Вызов внешнего сервиса + User user = userServiceClient.getUser(request.getUserId()); + Order order = orderService.createOrder(request); + return ResponseEntity.ok(order); + } +} + +// Payment Service +@RestController +@RequestMapping("/api/payments") +public class PaymentController { + @Autowired + private PaymentService paymentService; + + @PostMapping + public ResponseEntity processPayment(@RequestBody PaymentRequest request) { + PaymentResult result = paymentService.processPayment(request); + return ResponseEntity.ok(result); + } +} +``` + +### Преимущества +- **Независимое масштабирование** — каждый сервис масштабируется отдельно +- **Технологическое разнообразие** — разные технологии для разных задач +- **Независимые команды** — команды работают над разными сервисами +- **Отказоустойчивость** — падение одного сервиса не влияет на другие + +### Недостатки +- **Сложность разработки** — нужно управлять множеством сервисов +- **Сетевая задержка** — вызовы между сервисами медленнее +- **Сложность тестирования** — нужно тестировать интеграции +- **Сложность развертывания** — нужно координировать развертывание + +## Event-Driven Architecture (EDA) + +### Определение +**Event-Driven Architecture** — это архитектурный стиль, основанный на производстве, обнаружении, потреблении и реакции на события. + +### Характеристики +- **События** — значимые изменения в системе +- **Асинхронность** — события обрабатываются асинхронно +- **Слабая связанность** — компоненты не знают друг о друге +- **Масштабируемость** — легко добавлять новых потребителей + +### Пример реализации +```java +// Доменное событие +public class OrderCreatedEvent { + private final String orderId; + private final String customerId; + private final BigDecimal amount; + private final LocalDateTime occurredOn; + + public OrderCreatedEvent(String orderId, String customerId, BigDecimal amount) { + this.orderId = orderId; + this.customerId = customerId; + this.amount = amount; + this.occurredOn = LocalDateTime.now(); + } + + // Геттеры +} + +// Публикация события +@Service +public class OrderService { + + @Autowired + private ApplicationEventPublisher eventPublisher; + + public Order createOrder(CreateOrderRequest request) { + Order order = new Order(request); + order = orderRepository.save(order); + + // Публикация события + eventPublisher.publishEvent(new OrderCreatedEvent( + order.getId(), + order.getCustomerId(), + order.getAmount() + )); + + return order; + } +} + +// Обработка события +@Component +public class OrderCreatedEventHandler { + + @Autowired + private InventoryService inventoryService; + + @Autowired + private NotificationService notificationService; + + @EventListener + public void handleOrderCreated(OrderCreatedEvent event) { + // Резервирование товара + inventoryService.reserveItems(event.getOrderId()); + + // Отправка уведомления + notificationService.sendOrderConfirmation(event.getCustomerId()); + } +} +``` + +## CQRS (Command Query Responsibility Segregation) + +### Определение +**CQRS** — это паттерн, который разделяет операции чтения и записи данных на разные модели. + +### Характеристики +- **Разделение команд и запросов** — разные модели для записи и чтения +- **Оптимизация** — модели оптимизированы для своих задач +- **Масштабируемость** — можно масштабировать чтение и запись отдельно +- **Сложность** — увеличивает сложность системы + +### Пример реализации +```java +// Command Model (Write) +@Entity +@Table(name = "orders") +public class Order { + @Id + private String id; + private String customerId; + private OrderStatus status; + private BigDecimal amount; + private LocalDateTime createdAt; + + // Методы для изменения состояния + public void confirm() { + this.status = OrderStatus.CONFIRMED; + } + + public void cancel() { + this.status = OrderStatus.CANCELLED; + } +} + +// Query Model (Read) +@Entity +@Table(name = "order_summaries") +public class OrderSummary { + @Id + private String id; + private String customerName; + private String customerEmail; + private OrderStatus status; + private BigDecimal amount; + private LocalDateTime createdAt; + private int itemCount; + + // Только геттеры для чтения +} + +// Command Handler +@Service +public class OrderCommandHandler { + + @Autowired + private OrderRepository orderRepository; + + @Autowired + private OrderSummaryRepository orderSummaryRepository; + + public void handle(CreateOrderCommand command) { + // Создание заказа + Order order = new Order(command); + orderRepository.save(order); + + // Обновление read model + OrderSummary summary = new OrderSummary(order); + orderSummaryRepository.save(summary); + } +} + +// Query Handler +@Service +public class OrderQueryHandler { + + @Autowired + private OrderSummaryRepository orderSummaryRepository; + + public List handle(GetOrdersQuery query) { + return orderSummaryRepository.findByCustomerId(query.getCustomerId()); + } +} +``` + +## Hexagonal Architecture (Ports and Adapters) + +### Определение +**Hexagonal Architecture** — это архитектурный паттерн, который изолирует бизнес-логику от внешних зависимостей. + +### Характеристики +- **Порты** — интерфейсы для взаимодействия с внешним миром +- **Адаптеры** — реализации портов для конкретных технологий +- **Изоляция** — бизнес-логика не зависит от внешних технологий +- **Тестируемость** — легко тестировать с mock-объектами + +### Пример реализации +```java +// Domain Layer (внутренний круг) +public class Order { + private String id; + private String customerId; + private OrderStatus status; + private BigDecimal amount; + + public void confirm() { + this.status = OrderStatus.CONFIRMED; + } +} + +// Ports (интерфейсы) +public interface OrderRepository { + void save(Order order); + Optional findById(String id); +} + +public interface PaymentService { + PaymentResult processPayment(PaymentRequest request); +} + +public interface NotificationService { + void sendNotification(String recipient, String message); +} + +// Application Layer +@Service +public class OrderService { + private final OrderRepository orderRepository; + private final PaymentService paymentService; + private final NotificationService notificationService; + + public OrderService(OrderRepository orderRepository, + PaymentService paymentService, + NotificationService notificationService) { + this.orderRepository = orderRepository; + this.paymentService = paymentService; + this.notificationService = notificationService; + } + + public void processOrder(Order order) { + // Бизнес-логика + order.confirm(); + orderRepository.save(order); + + // Внешние сервисы через порты + PaymentResult payment = paymentService.processPayment( + new PaymentRequest(order.getAmount()) + ); + + if (payment.isSuccess()) { + notificationService.sendNotification( + order.getCustomerId(), + "Order confirmed" + ); + } + } +} + +// Adapters (внешний круг) +@Repository +public class JpaOrderRepository implements OrderRepository { + @Override + public void save(Order order) { + // JPA реализация + } + + @Override + public Optional findById(String id) { + // JPA реализация + return Optional.empty(); + } +} + +@Component +public class StripePaymentService implements PaymentService { + @Override + public PaymentResult processPayment(PaymentRequest request) { + // Stripe API реализация + return new PaymentResult(true, "Payment processed"); + } +} + +@Component +public class EmailNotificationService implements NotificationService { + @Override + public void sendNotification(String recipient, String message) { + // Email реализация + } +} +``` + +## Layered Architecture + +### Определение +**Layered Architecture** — это архитектурный паттерн, который организует код в горизонтальные слои. + +### Слои +1. **Presentation Layer** — пользовательский интерфейс +2. **Application Layer** — бизнес-логика приложения +3. **Domain Layer** — доменная логика +4. **Infrastructure Layer** — внешние зависимости + +### Пример реализации +```java +// Presentation Layer +@RestController +@RequestMapping("/api/orders") +public class OrderController { + + @Autowired + private OrderApplicationService orderService; + + @PostMapping + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + OrderResponse response = orderService.createOrder(request); + return ResponseEntity.ok(response); + } +} + +// Application Layer +@Service +public class OrderApplicationService { + + @Autowired + private OrderRepository orderRepository; + + @Autowired + private PaymentService paymentService; + + public OrderResponse createOrder(CreateOrderRequest request) { + // Применение бизнес-правил + Order order = new Order(request); + + // Обработка платежа + PaymentResult payment = paymentService.processPayment(order); + + if (payment.isSuccess()) { + order.confirm(); + orderRepository.save(order); + } + + return OrderResponse.fromOrder(order); + } +} + +// Domain Layer +public class Order { + private String id; + private String customerId; + private OrderStatus status; + private BigDecimal amount; + + public void confirm() { + if (this.status != OrderStatus.DRAFT) { + throw new IllegalStateException("Cannot confirm non-draft order"); + } + this.status = OrderStatus.CONFIRMED; + } +} + +// Infrastructure Layer +@Repository +public class JpaOrderRepository implements OrderRepository { + + @PersistenceContext + private EntityManager entityManager; + + @Override + public void save(Order order) { + entityManager.persist(order); + } +} +``` + +## Serverless Architecture + +### Определение +**Serverless Architecture** — это архитектурный стиль, при котором приложение состоит из функций, которые выполняются в ответ на события. + +### Характеристики +- **FaaS** — Functions as a Service +- **Event-driven** — функции запускаются по событиям +- **Auto-scaling** — автоматическое масштабирование +- **Pay-per-use** — оплата только за использование + +### Пример реализации +```java +// AWS Lambda функция +public class OrderProcessor implements RequestHandler { + + @Override + public String handleRequest(OrderEvent event, Context context) { + // Обработка заказа + Order order = processOrder(event); + + // Отправка уведомления + sendNotification(order); + + return "Order processed successfully"; + } + + private Order processOrder(OrderEvent event) { + // Логика обработки заказа + return new Order(event); + } + + private void sendNotification(Order order) { + // Отправка уведомления + } +} + +// Azure Functions +@FunctionName("ProcessOrder") +public String processOrder( + @HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) + HttpRequestMessage> request, + ExecutionContext context) { + + String orderData = request.getBody().orElse(""); + + // Обработка заказа + Order order = processOrderData(orderData); + + return "Order processed: " + order.getId(); +} +``` + +## Выбор архитектурного стиля + +### Факторы выбора +1. **Размер команды** — маленькая команда → монолит +2. **Сложность домена** — сложный домен → микросервисы +3. **Требования к масштабированию** — высокие → микросервисы +4. **Технологическое разнообразие** — разные технологии → микросервисы +5. **Время разработки** — быстрое время → монолит + +### Рекомендации +- **Начните с монолита** — для большинства проектов +- **Переходите к микросервисам** — когда монолит становится проблемой +- **Используйте EDA** — для асинхронной обработки +- **Рассмотрите CQRS** — для сложных read/write требований +- **Применяйте Hexagonal** — для изоляции бизнес-логики diff --git a/modules/module1/sections/06-quality-attributes.md b/modules/module1/sections/06-quality-attributes.md new file mode 100644 index 0000000..183257f --- /dev/null +++ b/modules/module1/sections/06-quality-attributes.md @@ -0,0 +1,416 @@ +# Качественные атрибуты + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +## Определение качественных атрибутов + +**Качественные атрибуты** — это характеристики системы, которые определяют, насколько хорошо система выполняет свои функции с точки зрения пользователей и заинтересованных сторон. + +## Основные качественные атрибуты + +### 1. Производительность (Performance) + +**Определение:** Скорость и эффективность выполнения операций системой. + +#### Метрики производительности +- **Время отклика** — время между запросом и ответом +- **Пропускная способность** — количество операций в единицу времени +- **Использование ресурсов** — CPU, память, диск, сеть +- **Время загрузки** — время запуска приложения + +#### Примеры требований +```yaml +performance: + response_time: + average: "< 200ms" + p95: "< 500ms" + p99: "< 1000ms" + throughput: + requests_per_second: 1000 + concurrent_users: 10000 + resource_usage: + cpu: "< 70%" + memory: "< 2GB" + disk_io: "< 100MB/s" +``` + +#### Техники оптимизации +```java +// Кэширование +@Service +public class UserService { + + @Cacheable("users") + public User findById(Long id) { + return userRepository.findById(id); + } + + @CacheEvict("users") + public void updateUser(User user) { + userRepository.save(user); + } +} + +// Асинхронная обработка +@Service +public class OrderService { + + @Async + public CompletableFuture processOrderAsync(Order order) { + // Длительная обработка + return CompletableFuture.completedFuture(order); + } +} + +// Пакетная обработка +@Service +public class BatchProcessor { + + public void processBatch(List orders) { + // Обработка пакетами по 100 + Lists.partition(orders, 100) + .forEach(this::processBatch); + } +} +``` + +### 2. Масштабируемость (Scalability) + +**Определение:** Способность системы обрабатывать увеличение нагрузки. + +#### Типы масштабирования +- **Горизонтальное** — добавление новых серверов +- **Вертикальное** — увеличение мощности сервера +- **Автоматическое** — автоматическое масштабирование + +#### Примеры требований +```yaml +scalability: + horizontal_scaling: true + auto_scaling: + min_instances: 2 + max_instances: 10 + cpu_threshold: 70% + load_balancing: "round_robin" + database_sharding: true +``` + +#### Техники масштабирования +```java +// Горизонтальное масштабирование +@RestController +public class UserController { + + @Autowired + private UserService userService; + + @GetMapping("/users/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + // Load balancer распределяет нагрузку + User user = userService.findById(id); + return ResponseEntity.ok(user); + } +} + +// Шардирование базы данных +@Service +public class UserService { + + @Autowired + private List userRepositories; + + public User findById(Long id) { + // Определение шарда по ID + int shardIndex = (int) (id % userRepositories.size()); + return userRepositories.get(shardIndex).findById(id); + } +} +``` + +### 3. Надежность (Reliability) + +**Определение:** Способность системы выполнять требуемые функции в заданных условиях. + +#### Аспекты надежности +- **Отказоустойчивость** — работа при сбоях +- **Восстановление** — быстрое восстановление после сбоев +- **Предотвращение потери данных** — сохранность данных + +#### Примеры требований +```yaml +reliability: + availability: "99.9%" + mean_time_to_recovery: "< 5 minutes" + data_loss: "0%" + backup_frequency: "every 6 hours" + disaster_recovery: "RTO < 1 hour, RPO < 15 minutes" +``` + +#### Техники обеспечения надежности +```java +// Circuit Breaker +@Component +public class PaymentService { + + private final CircuitBreaker circuitBreaker; + + public PaymentService() { + this.circuitBreaker = CircuitBreaker.ofDefaults("payment"); + } + + public PaymentResult processPayment(PaymentRequest request) { + return circuitBreaker.executeSupplier(() -> { + // Вызов внешнего сервиса + return externalPaymentService.process(request); + }); + } +} + +// Retry механизм +@Service +public class OrderService { + + @Retryable(value = {Exception.class}, maxAttempts = 3) + public Order createOrder(OrderRequest request) { + return orderRepository.save(new Order(request)); + } + + @Recover + public Order recover(Exception ex, OrderRequest request) { + // Fallback логика + return new Order(request, OrderStatus.FAILED); + } +} +``` + +### 4. Безопасность (Security) + +**Определение:** Защита системы от угроз и несанкционированного доступа. + +#### Аспекты безопасности +- **Аутентификация** — проверка личности пользователя +- **Авторизация** — проверка прав доступа +- **Шифрование** — защита данных +- **Аудит** — отслеживание действий + +#### Примеры требований +```yaml +security: + authentication: "JWT tokens" + authorization: "RBAC" + encryption: + data_at_rest: "AES-256" + data_in_transit: "TLS 1.3" + password_policy: + min_length: 8 + complexity: "uppercase, lowercase, numbers, symbols" + session_timeout: "30 minutes" + audit_logging: true +``` + +#### Техники обеспечения безопасности +```java +// JWT аутентификация +@RestController +public class AuthController { + + @PostMapping("/login") + public ResponseEntity login(@RequestBody LoginRequest request) { + User user = userService.authenticate(request.getUsername(), request.getPassword()); + String token = jwtService.generateToken(user); + return ResponseEntity.ok(new AuthResponse(token)); + } +} + +// RBAC авторизация +@PreAuthorize("hasRole('ADMIN') or hasRole('USER')") +@GetMapping("/users/{id}") +public ResponseEntity getUser(@PathVariable Long id) { + User user = userService.findById(id); + return ResponseEntity.ok(user); +} + +// Шифрование данных +@Service +public class EncryptionService { + + public String encrypt(String data) { + // AES шифрование + return aesEncryptor.encrypt(data); + } + + public String decrypt(String encryptedData) { + // AES расшифровка + return aesEncryptor.decrypt(encryptedData); + } +} +``` + +### 5. Удобство использования (Usability) + +**Определение:** Легкость использования системы пользователями. + +#### Аспекты удобства +- **Интуитивность** — понятность интерфейса +- **Производительность** — скорость выполнения задач +- **Обучаемость** — легкость изучения +- **Доступность** — доступность для людей с ограниченными возможностями + +#### Примеры требований +```yaml +usability: + response_time: "< 2 seconds" + error_messages: "clear and helpful" + navigation: "intuitive" + accessibility: "WCAG 2.1 AA" + mobile_responsive: true + browser_support: "Chrome, Firefox, Safari, Edge" +``` + +### 6. Сопровождаемость (Maintainability) + +**Определение:** Легкость внесения изменений в систему. + +#### Аспекты сопровождаемости +- **Читаемость кода** — понятность кода +- **Модульность** — разделение на модули +- **Тестируемость** — легкость тестирования +- **Документированность** — наличие документации + +#### Примеры требований +```yaml +maintainability: + code_coverage: "> 80%" + documentation: "comprehensive" + modularity: "high" + test_automation: "> 90%" + code_review: "mandatory" + static_analysis: "enabled" +``` + +#### Техники обеспечения сопровождаемости +```java +// Читаемый код +public class OrderService { + + public Order createOrder(CreateOrderRequest request) { + validateRequest(request); + Order order = buildOrder(request); + Order savedOrder = saveOrder(order); + notifyCustomer(savedOrder); + return savedOrder; + } + + private void validateRequest(CreateOrderRequest request) { + if (request == null) { + throw new IllegalArgumentException("Request cannot be null"); + } + // Дополнительная валидация + } + + private Order buildOrder(CreateOrderRequest request) { + return Order.builder() + .customerId(request.getCustomerId()) + .items(request.getItems()) + .status(OrderStatus.DRAFT) + .build(); + } +} + +// Тестируемый код +@ExtendWith(MockitoExtension.class) +class OrderServiceTest { + + @Mock + private OrderRepository orderRepository; + + @InjectMocks + private OrderService orderService; + + @Test + void shouldCreateOrder() { + // Given + CreateOrderRequest request = new CreateOrderRequest(); + Order expectedOrder = new Order(); + when(orderRepository.save(any(Order.class))).thenReturn(expectedOrder); + + // When + Order result = orderService.createOrder(request); + + // Then + assertThat(result).isEqualTo(expectedOrder); + verify(orderRepository).save(any(Order.class)); + } +} +``` + +### 7. Переносимость (Portability) + +**Определение:** Способность системы работать в разных средах. + +#### Аспекты переносимости +- **Платформенная независимость** — работа на разных ОС +- **Облачная совместимость** — работа в разных облаках +- **Контейнеризация** — упаковка в контейнеры + +#### Примеры требований +```yaml +portability: + operating_systems: ["Linux", "Windows", "macOS"] + cloud_providers: ["AWS", "Azure", "GCP"] + containerization: "Docker" + orchestration: "Kubernetes" + database_agnostic: true +``` + +#### Техники обеспечения переносимости +```yaml +# Dockerfile +FROM openjdk:11-jre-slim +COPY target/app.jar app.jar +EXPOSE 8080 +ENTRYPOINT ["java", "-jar", "app.jar"] + +# docker-compose.yml +version: '3.8' +services: + app: + build: . + ports: + - "8080:8080" + environment: + - SPRING_PROFILES_ACTIVE=docker + depends_on: + - database + + database: + image: postgres:13 + environment: + POSTGRES_DB: myapp + POSTGRES_USER: user + POSTGRES_PASSWORD: password +``` + +## Приоритизация качественных атрибутов + +### Матрица приоритетов +| Атрибут | Критичность | Сложность | Приоритет | +|---------|-------------|-----------|-----------| +| Производительность | Высокая | Средняя | Высокий | +| Безопасность | Высокая | Высокая | Высокий | +| Надежность | Высокая | Высокая | Высокий | +| Масштабируемость | Средняя | Высокая | Средний | +| Удобство использования | Средняя | Средняя | Средний | +| Сопровождаемость | Средняя | Низкая | Средний | +| Переносимость | Низкая | Средняя | Низкий | + +### Рекомендации +1. **Начните с критичных атрибутов** — безопасность, надежность +2. **Учитывайте контекст** — разные требования для разных систем +3. **Планируйте заранее** — некоторые атрибуты сложно добавить позже +4. **Измеряйте прогресс** — используйте метрики для отслеживания +5. **Балансируйте** — не все атрибуты можно максимизировать одновременно diff --git a/modules/module1/sections/07-architect-role-in-team.md b/modules/module1/sections/07-architect-role-in-team.md new file mode 100644 index 0000000..77d4e5e --- /dev/null +++ b/modules/module1/sections/07-architect-role-in-team.md @@ -0,0 +1,517 @@ +# Роль архитектора в команде + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +## Введение + +**Solution Architect** играет ключевую роль в команде разработки, выступая как технический лидер, наставник и мост между бизнесом и разработкой. + +## Основные роли архитектора + +### 1. Технический лидер + +#### Обязанности +- **Принятие архитектурных решений** — выбор технологий и подходов +- **Техническое руководство** — направление команды в технических вопросах +- **Решение сложных проблем** — помощь в решении технических вызовов +- **Код-ревью** — контроль качества кода и архитектурных решений + +#### Примеры деятельности +```java +// Архитектурное решение - выбор паттерна +public class OrderService { + + // Архитектор решает использовать Dependency Injection + private final OrderRepository orderRepository; + private final PaymentService paymentService; + private final NotificationService notificationService; + + public OrderService(OrderRepository orderRepository, + PaymentService paymentService, + NotificationService notificationService) { + this.orderRepository = orderRepository; + this.paymentService = paymentService; + this.notificationService = notificationService; + } + + // Архитектор определяет бизнес-логику + public Order processOrder(OrderRequest request) { + // 1. Валидация + validateOrder(request); + + // 2. Создание заказа + Order order = createOrder(request); + + // 3. Обработка платежа + PaymentResult payment = paymentService.processPayment(order); + + // 4. Сохранение и уведомление + if (payment.isSuccess()) { + order = orderRepository.save(order); + notificationService.sendConfirmation(order); + } + + return order; + } +} +``` + +### 2. Наставник и ментор + +#### Обязанности +- **Обучение команды** — передача знаний и опыта +- **Развитие навыков** — помощь в профессиональном росте +- **Код-ревью** — обучение через обратную связь +- **Технические сессии** — проведение технических презентаций + +#### Примеры наставничества +```java +// Обучение через примеры +public class UserService { + + // Показываем правильный подход к обработке ошибок + public User findById(Long id) { + if (id == null) { + throw new IllegalArgumentException("User ID cannot be null"); + } + + return userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found with ID: " + id)); + } + + // Демонстрируем правильное использование Optional + public Optional findByEmail(String email) { + if (email == null || email.trim().isEmpty()) { + return Optional.empty(); + } + + return userRepository.findByEmail(email.trim().toLowerCase()); + } + + // Показываем правильную валидацию + public User createUser(CreateUserRequest request) { + validateCreateUserRequest(request); + + User user = User.builder() + .name(request.getName().trim()) + .email(request.getEmail().trim().toLowerCase()) + .password(encodePassword(request.getPassword())) + .build(); + + return userRepository.save(user); + } + + private void validateCreateUserRequest(CreateUserRequest request) { + if (request == null) { + throw new IllegalArgumentException("Request cannot be null"); + } + if (request.getName() == null || request.getName().trim().isEmpty()) { + throw new IllegalArgumentException("Name is required"); + } + if (request.getEmail() == null || !isValidEmail(request.getEmail())) { + throw new IllegalArgumentException("Invalid email format"); + } + if (request.getPassword() == null || request.getPassword().length() < 8) { + throw new IllegalArgumentException("Password must be at least 8 characters"); + } + } +} +``` + +### 3. Коммуникатор + +#### Обязанности +- **Взаимодействие с бизнесом** — перевод бизнес-требований в технические решения +- **Презентация решений** — объяснение архитектурных решений стейкхолдерам +- **Документирование** — создание архитектурной документации +- **Координация команд** — обеспечение взаимодействия между командами + +#### Примеры коммуникации +```markdown +# Архитектурное решение: Выбор базы данных + +## Проблема +Система должна обрабатывать 1M+ записей в день с требованиями к производительности < 100ms. + +## Варианты решения +1. **PostgreSQL** - реляционная БД с ACID +2. **MongoDB** - документная БД с горизонтальным масштабированием +3. **Redis** - in-memory БД для кэширования + +## Рекомендация +**PostgreSQL** + **Redis** для кэширования + +## Обоснование +- PostgreSQL обеспечивает ACID и сложные запросы +- Redis обеспечивает быстрый доступ к часто используемым данным +- Комбинация дает лучшее из двух миров + +## Влияние на команду +- Разработчики должны изучить Redis +- Нужно реализовать стратегию кэширования +- Мониторинг двух БД +``` + +### 4. Решатель проблем + +#### Обязанности +- **Диагностика проблем** — анализ и выявление корневых причин +- **Поиск решений** — исследование и оценка вариантов +- **Реализация решений** — помощь в реализации сложных решений +- **Предотвращение проблем** — проактивное выявление потенциальных проблем + +#### Примеры решения проблем +```java +// Проблема: Медленные запросы к БД +// Решение: Добавление индексов и оптимизация запросов + +@Entity +@Table(name = "orders", indexes = { + @Index(name = "idx_order_customer", columnList = "customer_id"), + @Index(name = "idx_order_status", columnList = "status"), + @Index(name = "idx_order_created", columnList = "created_at") +}) +public class Order { + @Id + private Long id; + + @Column(name = "customer_id") + private Long customerId; + + @Enumerated(EnumType.STRING) + private OrderStatus status; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + // Оптимизированный запрос + @Query("SELECT o FROM Order o WHERE o.customerId = :customerId AND o.status = :status ORDER BY o.createdAt DESC") + List findByCustomerIdAndStatus(@Param("customerId") Long customerId, + @Param("status") OrderStatus status); +} + +// Проблема: Блокировки при одновременном доступе +// Решение: Использование оптимистичных блокировок + +@Entity +public class Product { + @Id + private Long id; + + private String name; + private Integer quantity; + + @Version + private Long version; // Для оптимистичных блокировок + + public boolean decreaseQuantity(int amount) { + if (this.quantity >= amount) { + this.quantity -= amount; + return true; + } + return false; + } +} +``` + +## Взаимодействие с разными ролями + +### 1. С разработчиками + +#### Формы взаимодействия +- **Ежедневные стендапы** — обсуждение технических вопросов +- **Код-ревью** — контроль качества и обучение +- **Технические сессии** — обмен знаниями +- **Парное программирование** — совместная работа над сложными задачами + +#### Примеры взаимодействия +```java +// Код-ревью комментарии +public class UserService { + + // ❌ Плохо: Слишком много ответственности + public User createUser(String name, String email, String password) { + // валидация + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Name is required"); + } + // ... много валидации + + // создание пользователя + User user = new User(name, email, password); + + // отправка email + emailService.sendWelcomeEmail(email); + + // логирование + logger.info("User created: " + email); + + return userRepository.save(user); + } + + // ✅ Хорошо: Разделение ответственности + public User createUser(CreateUserRequest request) { + validateRequest(request); + User user = buildUser(request); + User savedUser = userRepository.save(user); + notifyUserCreated(savedUser); + return savedUser; + } + + private void validateRequest(CreateUserRequest request) { + userValidator.validate(request); + } + + private User buildUser(CreateUserRequest request) { + return User.builder() + .name(request.getName()) + .email(request.getEmail()) + .password(encodePassword(request.getPassword())) + .build(); + } + + private void notifyUserCreated(User user) { + eventPublisher.publishEvent(new UserCreatedEvent(user)); + } +} +``` + +### 2. С DevOps инженерами + +#### Формы взаимодействия +- **Планирование инфраструктуры** — определение требований к инфраструктуре +- **Автоматизация развертывания** — настройка CI/CD +- **Мониторинг и логирование** — настройка observability +- **Безопасность** — обеспечение безопасности инфраструктуры + +#### Примеры взаимодействия +```yaml +# docker-compose.yml - совместная работа с DevOps +version: '3.8' +services: + app: + build: . + ports: + - "8080:8080" + environment: + - SPRING_PROFILES_ACTIVE=docker + - DATABASE_URL=jdbc:postgresql://db:5432/myapp + - REDIS_URL=redis://redis:6379 + depends_on: + - db + - redis + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + + db: + image: postgres:13 + environment: + POSTGRES_DB: myapp + POSTGRES_USER: user + POSTGRES_PASSWORD: password + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U user -d myapp"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:6-alpine + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 + +volumes: + postgres_data: +``` + +### 3. С QA инженерами + +#### Формы взаимодействия +- **Планирование тестирования** — определение стратегии тестирования +- **Техническая поддержка** — помощь в понимании архитектуры +- **Автоматизация тестов** — помощь в создании тестов +- **Анализ багов** — помощь в диагностике проблем + +#### Примеры взаимодействия +```java +// Помощь в создании тестов +@SpringBootTest +@TestPropertySource(properties = { + "spring.datasource.url=jdbc:h2:mem:testdb", + "spring.jpa.hibernate.ddl-auto=create-drop" +}) +class OrderServiceIntegrationTest { + + @Autowired + private OrderService orderService; + + @Autowired + private OrderRepository orderRepository; + + @Test + @Transactional + void shouldCreateOrderSuccessfully() { + // Given + CreateOrderRequest request = CreateOrderRequest.builder() + .customerId(1L) + .items(Arrays.asList( + new OrderItemRequest(1L, 2), + new OrderItemRequest(2L, 1) + )) + .build(); + + // When + Order result = orderService.createOrder(request); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getCustomerId()).isEqualTo(1L); + assertThat(result.getItems()).hasSize(2); + assertThat(result.getStatus()).isEqualTo(OrderStatus.CONFIRMED); + + // Проверяем, что заказ сохранен в БД + Optional savedOrder = orderRepository.findById(result.getId()); + assertThat(savedOrder).isPresent(); + } +} +``` + +### 4. С Product Manager + +#### Формы взаимодействия +- **Планирование функций** — оценка технической сложности +- **Приоритизация** — техническая оценка приоритетов +- **Ограничения** — объяснение технических ограничений +- **Roadmap** — техническое планирование + +#### Примеры взаимодействия +```markdown +# Техническая оценка функции: "Уведомления в реальном времени" + +## Описание функции +Пользователи должны получать уведомления о статусе заказа в реальном времени. + +## Технические варианты +1. **WebSockets** - прямое соединение +2. **Server-Sent Events** - односторонняя связь +3. **Polling** - периодические запросы + +## Рекомендация +**WebSockets** + **Redis Pub/Sub** + +## Оценка сложности +- **Время разработки**: 2-3 недели +- **Сложность**: Средняя +- **Зависимости**: Redis, WebSocket поддержка + +## Технические требования +- Redis для pub/sub +- WebSocket endpoint +- Frontend WebSocket клиент +- Обработка переподключений + +## Риски +- Сложность отладки +- Проблемы с масштабированием +- Совместимость с браузерами + +## Альтернативы +- Server-Sent Events (проще, но менее функционально) +- Polling (проще, но менее эффективно) +``` + +## Управление конфликтами + +### Типы конфликтов +1. **Технические разногласия** — разные мнения о технологиях +2. **Приоритеты** — конфликт между функциональностью и качеством +3. **Ресурсы** — нехватка времени или людей +4. **Ответственность** — размытые границы ответственности + +### Стратегии разрешения +```java +// Пример: Разрешение технического конфликта +public class ConflictResolutionExample { + + // Конфликт: Выбор между синхронным и асинхронным подходом + + // Вариант 1: Синхронный (простой, но медленный) + public Order processOrderSync(OrderRequest request) { + Order order = createOrder(request); + PaymentResult payment = paymentService.processPayment(order); + if (payment.isSuccess()) { + order.confirm(); + notificationService.sendConfirmation(order); + } + return order; + } + + // Вариант 2: Асинхронный (сложный, но быстрый) + public Order processOrderAsync(OrderRequest request) { + Order order = createOrder(request); + orderRepository.save(order); + + // Асинхронная обработка + CompletableFuture.runAsync(() -> { + PaymentResult payment = paymentService.processPayment(order); + if (payment.isSuccess()) { + order.confirm(); + orderRepository.save(order); + notificationService.sendConfirmation(order); + } + }); + + return order; + } + + // Решение: Компромисс - гибридный подход + public Order processOrderHybrid(OrderRequest request) { + Order order = createOrder(request); + + // Быстрая валидация синхронно + if (order.getAmount().compareTo(BigDecimal.valueOf(1000)) < 0) { + return processOrderSync(request); + } else { + return processOrderAsync(request); + } + } +} +``` + +## Развитие команды + +### 1. Техническое развитие +- **Обучение новым технологиям** — проведение технических сессий +- **Code review** — обучение через обратную связь +- **Парное программирование** — передача опыта +- **Технические вызовы** — постановка интересных задач + +### 2. Процессное развитие +- **Внедрение лучших практик** — Agile, DevOps, CI/CD +- **Автоматизация** — автоматизация рутинных процессов +- **Метрики и мониторинг** — внедрение метрик качества +- **Документирование** — улучшение документации + +### 3. Культурное развитие +- **Техническая культура** — культура качества кода +- **Открытость** — открытость к новым идеям +- **Экспериментирование** — поощрение экспериментов +- **Обучение** — культура непрерывного обучения + +## Заключение + +Роль Solution Architect в команде многогранна и требует баланса между техническими навыками, лидерскими качествами и коммуникативными способностями. Успешный архитектор не только принимает правильные технические решения, но и помогает команде расти и развиваться, создавая культуру качества и инноваций. diff --git a/modules/module1/sections/08-practical-tasks.md b/modules/module1/sections/08-practical-tasks.md new file mode 100644 index 0000000..d3635d7 --- /dev/null +++ b/modules/module1/sections/08-practical-tasks.md @@ -0,0 +1,22 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +### Задание 1: Анализ архитектуры +Проанализируйте архитектуру известной системы (например, Twitter, Netflix, Uber) и определите: +1. Архитектурный стиль +2. Основные компоненты +3. Качественные атрибуты +4. Архитектурные решения + +### Задание 2: Применение SOLID принципов +Возьмите пример кода с нарушениями SOLID принципов и рефакторите его согласно принципам. + +### Задание 3: Trade-off анализ +Рассмотрите конкретный случай, где нужно выбрать между двумя качественными атрибутами, и обоснуйте выбор. + +--- \ No newline at end of file diff --git a/modules/module1/sections/09-architecture-examples.md b/modules/module1/sections/09-architecture-examples.md new file mode 100644 index 0000000..5f6e22c --- /dev/null +++ b/modules/module1/sections/09-architecture-examples.md @@ -0,0 +1,844 @@ +# Примеры архитектур известных систем + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 1](../README.md) + +--- + +## Twitter + +### Обзор системы +Twitter - социальная сеть для микроблогинга, где пользователи публикуют короткие сообщения (твиты) до 280 символов. + +### Архитектурный стиль +- **Микросервисная архитектура** с event-driven подходом +- **Гибридная архитектура**: комбинация микросервисов и монолитных компонентов +- **CQRS** (Command Query Responsibility Segregation) для разделения чтения и записи + +### Архитектурная диаграмма + +```mermaid +graph TB + subgraph "Twitter Architecture" + subgraph "Client Layer" + Web[Web App] + Mobile[Mobile App] + API_Client[API Clients] + end + + subgraph "API Gateway" + LB[Load Balancer] + Gateway[API Gateway] + end + + subgraph "Core Services" + Timeline[Timeline Service] + Tweet[Tweet Service] + User[User Service] + Search[Search Service] + Media[Media Service] + end + + subgraph "Data Layer" + MySQL[(MySQL)] + Redis[(Redis)] + ES[(Elasticsearch)] + S3[(AWS S3)] + end + + subgraph "Message Queue" + Kafka[Apache Kafka] + end + end + + Web --> LB + Mobile --> LB + API_Client --> LB + LB --> Gateway + + Gateway --> Timeline + Gateway --> Tweet + Gateway --> User + Gateway --> Search + Gateway --> Media + + Timeline --> Redis + Timeline --> MySQL + Tweet --> MySQL + Tweet --> Kafka + User --> MySQL + Search --> ES + Media --> S3 + + Kafka --> Timeline +``` + +### Основные компоненты + +#### 1. Timeline Service +- **Назначение**: Генерация персональных лент пользователей +- **Технологии**: Scala, Finagle +- **Особенности**: + - Реалтайм генерация лент + - Кэширование популярного контента + - Асинхронная обработка + +#### 2. Tweet Service +- **Назначение**: Создание, хранение и распространение твитов +- **Технологии**: Scala, MySQL, Redis +- **Особенности**: + - Fan-out паттерн для распространения + - Шардирование по user_id + - Event sourcing для аудита + +#### 3. User Service +- **Назначение**: Управление пользователями и профилями +- **Технологии**: Scala, MySQL +- **Особенности**: + - Аутентификация и авторизация + - Управление подписками + - Профильная информация + +#### 4. Search Service +- **Назначение**: Поиск по твитам и пользователям +- **Технологии**: Elasticsearch, Scala +- **Особенности**: + - Индексирование в реальном времени + - Полнотекстовый поиск + - Геопространственный поиск + +#### 5. Media Service +- **Назначение**: Обработка изображений и видео +- **Технологии**: Scala, AWS S3 +- **Особенности**: + - Загрузка и оптимизация медиа + - CDN для быстрой доставки + - Метаданные медиафайлов + +### Качественные атрибуты + +#### Производительность +- **Время отклика**: < 200ms для timeline +- **Пропускная способность**: 6,000 твитов/сек +- **Метрики**: + - 99.9% запросов < 1 секунды + - Кэш hit rate > 95% + +#### Масштабируемость +- **Горизонтальное масштабирование**: Все сервисы масштабируются независимо +- **Шардирование**: Данные разделены по user_id +- **Кэширование**: Многоуровневое кэширование (Redis, CDN) + +#### Надежность +- **Доступность**: 99.9% uptime +- **Отказоустойчивость**: Graceful degradation при сбоях +- **Резервирование**: Multi-region deployment + +#### Безопасность +- **Аутентификация**: OAuth 2.0 +- **Шифрование**: TLS для всех соединений +- **Аудит**: Логирование всех действий + +### Архитектурные решения + +#### 1. Fan-out Pattern +```scala +// Упрощенный пример fan-out для твитов +class TweetFanoutService { + def fanoutTweet(tweet: Tweet, followers: List[UserId]): Unit = { + followers.par.foreach { followerId => + timelineService.addToTimeline(followerId, tweet) + } + } +} +``` + +#### 2. Event Sourcing +```scala +// События для аудита твитов +sealed trait TweetEvent +case class TweetCreated(tweetId: String, userId: String, content: String) extends TweetEvent +case class TweetDeleted(tweetId: String, userId: String) extends TweetEvent +case class TweetLiked(tweetId: String, userId: String) extends TweetEvent +``` + +#### 3. CQRS Implementation +```scala +// Command side - запись +class TweetCommandHandler { + def createTweet(command: CreateTweetCommand): Unit = { + // Валидация и создание твита + val tweet = Tweet(command.content, command.userId) + tweetRepository.save(tweet) + eventBus.publish(TweetCreated(tweet.id, tweet.userId, tweet.content)) + } +} + +// Query side - чтение +class TimelineQueryService { + def getTimeline(userId: String): List[Tweet] = { + // Чтение из оптимизированного read store + timelineRepository.findByUserId(userId) + } +} +``` + +--- + +## Netflix + +### Обзор системы +Netflix - стриминговая платформа для видео контента с более чем 200 миллионами подписчиков по всему миру. + +### Архитектурный стиль +- **Микросервисная архитектура** с cloud-native подходом +- **Event-driven architecture** с Apache Kafka +- **Serverless** компоненты для обработки медиа + +### Архитектурная диаграмма + +```mermaid +graph TB + subgraph "Netflix Architecture" + subgraph "Client Layer" + Web[Web Browser] + Mobile[Mobile App] + TV[Smart TV] + Console[Gaming Console] + end + + subgraph "CDN Layer" + CloudFront[AWS CloudFront] + OpenConnect[Open Connect] + end + + subgraph "API Gateway" + Zuul[Zuul Gateway] + Eureka[Eureka Registry] + end + + subgraph "Microservices" + User[User Service] + Content[Content Service] + Recommendation[Recommendation Service] + Playback[Playback Service] + Billing[Billing Service] + end + + subgraph "Data Layer" + Cassandra[(Cassandra)] + Elasticsearch[(Elasticsearch)] + Redis[(Redis)] + S3[(AWS S3)] + end + + subgraph "ML Pipeline" + Spark[Apache Spark] + TensorFlow[TensorFlow] + Kafka[Apache Kafka] + end + end + + Web --> CloudFront + Mobile --> CloudFront + TV --> OpenConnect + Console --> CloudFront + + CloudFront --> Zuul + OpenConnect --> Zuul + + Zuul --> Eureka + Eureka --> User + Eureka --> Content + Eureka --> Recommendation + Eureka --> Playback + Eureka --> Billing + + User --> Cassandra + Content --> Elasticsearch + Recommendation --> Redis + Playback --> Redis + Billing --> Cassandra + + Content --> S3 + Playback --> S3 + + Recommendation --> Spark + Spark --> TensorFlow + Kafka --> Recommendation +``` + +### Основные компоненты + +#### 1. API Gateway +- **Назначение**: Единая точка входа для всех клиентов +- **Технологии**: Zuul, Spring Cloud Gateway +- **Особенности**: + - Маршрутизация запросов + - Аутентификация и авторизация + - Rate limiting и throttling + +#### 2. User Service +- **Назначение**: Управление пользователями и подписками +- **Технологии**: Java, Spring Boot, Cassandra +- **Особенности**: + - Профили пользователей + - Управление подписками + - Предпочтения контента + +#### 3. Content Service +- **Назначение**: Управление каталогом контента +- **Технологии**: Java, Spring Boot, Elasticsearch +- **Особенности**: + - Метаданные фильмов и сериалов + - Поиск и фильтрация + - Многоязычная поддержка + +#### 4. Recommendation Service +- **Назначение**: Персональные рекомендации +- **Технологии**: Python, TensorFlow, Apache Spark +- **Особенности**: + - Machine Learning модели + - A/B тестирование + - Real-time рекомендации + +#### 5. Playback Service +- **Назначение**: Воспроизведение видео +- **Технологии**: Java, Spring Boot, Redis +- **Особенности**: + - Управление сессиями просмотра + - Адаптивная потоковая передача + - Аналитика просмотров + +#### 6. CDN (Content Delivery Network) +- **Назначение**: Доставка видео контента +- **Технологии**: AWS CloudFront, Open Connect +- **Особенности**: + - Глобальная сеть серверов + - Кэширование популярного контента + - Оптимизация для мобильных устройств + +### Качественные атрибуты + +#### Производительность +- **Время отклика**: < 100ms для API +- **Пропускная способность**: Миллионы запросов в минуту +- **Видео качество**: Адаптивная потоковая передача + +#### Масштабируемость +- **Горизонтальное масштабирование**: Все сервисы масштабируются автоматически +- **Multi-region**: Развертывание в нескольких регионах AWS +- **Auto-scaling**: Автоматическое масштабирование на основе нагрузки + +#### Надежность +- **Доступность**: 99.99% uptime +- **Fault tolerance**: Circuit breaker pattern +- **Disaster recovery**: Multi-region backup + +#### Безопасность +- **DRM**: Защита контента от пиратства +- **Аутентификация**: OAuth 2.0 + JWT +- **Шифрование**: End-to-end шифрование + +### Архитектурные решения + +#### 1. Circuit Breaker Pattern +```java +@Component +public class RecommendationServiceClient { + + @CircuitBreaker(name = "recommendation-service") + @Retry(name = "recommendation-service") + public List getRecommendations(String userId) { + return recommendationService.getRecommendations(userId); + } +} +``` + +#### 2. Event-Driven Architecture +```java +// Событие просмотра фильма +@Component +public class PlaybackEventHandler { + + @EventListener + public void handleMovieWatched(MovieWatchedEvent event) { + // Обновление рекомендаций + recommendationService.updateUserPreferences( + event.getUserId(), + event.getMovieId() + ); + + // Аналитика + analyticsService.recordView(event); + } +} +``` + +#### 3. Caching Strategy +```java +@Service +public class ContentService { + + @Cacheable(value = "popular-movies", key = "#region") + public List getPopularMovies(String region) { + return contentRepository.findPopularByRegion(region); + } + + @CacheEvict(value = "popular-movies", allEntries = true) + public void updateMoviePopularity(String movieId) { + // Обновление популярности + } +} +``` + +--- + +## Uber + +### Обзор системы +Uber - платформа для заказа поездок, которая соединяет водителей и пассажиров в реальном времени. + +### Архитектурный стиль +- **Микросервисная архитектура** с domain-driven design +- **Event-driven architecture** для real-time обработки +- **CQRS** для разделения команд и запросов + +### Архитектурная диаграмма + +```mermaid +graph TB + subgraph "Uber Architecture" + subgraph "Client Layer" + Rider[Rider App] + Driver[Driver App] + Web[Web Dashboard] + end + + subgraph "API Gateway" + LB[Load Balancer] + Gateway[API Gateway] + end + + subgraph "Core Services" + Dispatch[Dispatch Service] + Trip[Trip Service] + Payment[Payment Service] + User[User Service] + Notification[Notification Service] + Map[Map Service] + end + + subgraph "Data Layer" + PostgreSQL[(PostgreSQL)] + MongoDB[(MongoDB)] + Redis[(Redis)] + PostGIS[(PostGIS)] + end + + subgraph "Message Queue" + Kafka[Apache Kafka] + end + + subgraph "External Services" + Maps[Google Maps API] + Payment_Gateway[Payment Gateway] + SMS[SMS Service] + Push[Push Notification] + end + end + + Rider --> LB + Driver --> LB + Web --> LB + LB --> Gateway + + Gateway --> Dispatch + Gateway --> Trip + Gateway --> Payment + Gateway --> User + Gateway --> Notification + Gateway --> Map + + Dispatch --> Redis + Dispatch --> Kafka + Trip --> PostgreSQL + Trip --> Kafka + Payment --> PostgreSQL + User --> MongoDB + Notification --> Kafka + Map --> PostGIS + Map --> Redis + + Map --> Maps + Payment --> Payment_Gateway + Notification --> SMS + Notification --> Push + + Kafka --> Notification + Kafka --> Trip +``` + +### Основные компоненты + +#### 1. Dispatch Service +- **Назначение**: Сопоставление водителей и пассажиров +- **Технологии**: Go, Redis, Kafka +- **Особенности**: + - Real-time геолокация + - Алгоритмы сопоставления + - Load balancing + +#### 2. Trip Service +- **Назначение**: Управление поездками +- **Технологии**: Java, Spring Boot, PostgreSQL +- **Особенности**: + - Жизненный цикл поездки + - Расчет стоимости + - Отслеживание статуса + +#### 3. Payment Service +- **Назначение**: Обработка платежей +- **Технологии**: Java, Spring Boot, PostgreSQL +- **Особенности**: + - Интеграция с платежными системами + - Расчет комиссий + - Обработка возвратов + +#### 4. User Service +- **Назначение**: Управление пользователями +- **Технологии**: Go, MongoDB +- **Особенности**: + - Профили водителей и пассажиров + - Аутентификация + - Рейтинги и отзывы + +#### 5. Notification Service +- **Назначение**: Отправка уведомлений +- **Технологии**: Go, Kafka, Firebase +- **Особенности**: + - Push уведомления + - SMS и email + - Персонализация + +#### 6. Map Service +- **Назначение**: Картографические данные +- **Технологии**: Go, Redis, PostGIS +- **Особенности**: + - Геокодирование + - Маршрутизация + - Трафик в реальном времени + +### Качественные атрибуты + +#### Производительность +- **Время отклика**: < 50ms для dispatch +- **Пропускная способность**: Миллионы запросов в минуту +- **Real-time**: Обновления каждые 3 секунды + +#### Масштабируемость +- **Горизонтальное масштабирование**: Все сервисы масштабируются независимо +- **Sharding**: Данные разделены по регионам +- **Caching**: Агрессивное кэширование геоданных + +#### Надежность +- **Доступность**: 99.9% uptime +- **Fault tolerance**: Graceful degradation +- **Data consistency**: Eventual consistency + +#### Безопасность +- **Аутентификация**: Multi-factor authentication +- **Геолокация**: Защита персональных данных +- **Платежи**: PCI DSS compliance + +### Архитектурные решения + +#### 1. Event Sourcing для поездок +```java +// События поездки +public class TripEvent { + private String tripId; + private String eventType; + private LocalDateTime timestamp; + private Map data; +} + +// Обработчик событий +@Component +public class TripEventHandler { + + @EventHandler + public void handleTripStarted(TripStartedEvent event) { + // Уведомление водителя и пассажира + notificationService.notifyTripStarted(event.getTripId()); + + // Обновление статуса + tripService.updateStatus(event.getTripId(), TripStatus.IN_PROGRESS); + } +} +``` + +#### 2. CQRS для геоданных +```java +// Command side +@Service +public class LocationCommandService { + + public void updateDriverLocation(String driverId, Location location) { + // Обновление в write store + locationRepository.updateDriverLocation(driverId, location); + + // Публикация события + eventBus.publish(new LocationUpdatedEvent(driverId, location)); + } +} + +// Query side +@Service +public class LocationQueryService { + + public List findNearbyDrivers(Location passengerLocation) { + // Чтение из оптимизированного read store + return locationReadRepository.findNearbyDrivers(passengerLocation); + } +} +``` + +#### 3. Saga Pattern для поездок +```java +@Component +public class TripSaga { + + @SagaOrchestrationStart + public void startTrip(TripRequestedEvent event) { + // 1. Найти водителя + dispatchService.findDriver(event.getPassengerId()) + .thenAccept(driver -> { + // 2. Создать поездку + tripService.createTrip(event.getPassengerId(), driver.getId()); + }); + } + + @SagaOrchestrationStep + public void handleDriverAccepted(DriverAcceptedEvent event) { + // 3. Подтвердить поездку + tripService.confirmTrip(event.getTripId()); + } +} +``` + +--- + +## Сравнительная диаграмма архитектурных паттернов + +```mermaid +graph TB + subgraph "Architectural Patterns Comparison" + subgraph "Twitter - Event-Driven + CQRS" + T1[Timeline Service] + T2[Tweet Service] + T3[User Service] + T4[Search Service] + T5[Media Service] + TK[Kafka Events] + TR[Redis Cache] + TM[MySQL] + end + + subgraph "Netflix - Cloud-Native + ML" + N1[User Service] + N2[Content Service] + N3[Recommendation Service] + N4[Playback Service] + N5[Billing Service] + NC[CDN] + NK[Kafka] + NCass[Cassandra] + end + + subgraph "Uber - DDD + Real-time" + U1[Dispatch Service] + U2[Trip Service] + U3[Payment Service] + U4[User Service] + U5[Notification Service] + U6[Map Service] + UK[Kafka] + UPG[PostgreSQL] + UMG[MongoDB] + end + end + + T1 -.-> TK + T2 -.-> TK + T3 -.-> TM + T4 -.-> TM + T5 -.-> TM + + N1 -.-> NK + N2 -.-> NCass + N3 -.-> NK + N4 -.-> NCass + N5 -.-> NCass + + U1 -.-> UK + U2 -.-> UK + U3 -.-> UPG + U4 -.-> UMG + U5 -.-> UK + U6 -.-> UPG +``` + +## Сравнительная таблица архитектур + +| Система | Архитектурный стиль | Основные технологии | Особенности | +|---------|-------------------|-------------------|-------------| +| **Twitter** | Микросервисы + CQRS | Scala, MySQL, Redis | Fan-out pattern, Event sourcing | +| **Netflix** | Микросервисы + Cloud-native | Java, Spring Boot, AWS | CDN, ML рекомендации | +| **Uber** | Микросервисы + DDD | Go, Java, PostgreSQL | Real-time геолокация, Saga pattern | + +--- + +## Диаграмма ключевых архитектурных паттернов + +```mermaid +graph TB + subgraph "Key Architectural Patterns" + subgraph "Microservices Pattern" + MS1[Service A] + MS2[Service B] + MS3[Service C] + MS4[Service D] + MSG[API Gateway] + MSL[Load Balancer] + end + + subgraph "Event-Driven Pattern" + ED1[Event Producer] + ED2[Event Bus/Kafka] + ED3[Event Consumer A] + ED4[Event Consumer B] + ED5[Event Consumer C] + end + + subgraph "CQRS Pattern" + CQRS_C[Command Side] + CQRS_Q[Query Side] + CQRS_WS[(Write Store)] + CQRS_RS[(Read Store)] + CQRS_E[Event Bus] + end + + subgraph "Caching Strategy" + CACHE_L1[L1 Cache
Application] + CACHE_L2[L2 Cache
Redis] + CACHE_L3[L3 Cache
CDN] + CACHE_DB[(Database)] + end + end + + MSL --> MSG + MSG --> MS1 + MSG --> MS2 + MSG --> MS3 + MSG --> MS4 + + ED1 --> ED2 + ED2 --> ED3 + ED2 --> ED4 + ED2 --> ED5 + + CQRS_C --> CQRS_WS + CQRS_C --> CQRS_E + CQRS_E --> CQRS_Q + CQRS_Q --> CQRS_RS + + CACHE_L1 --> CACHE_L2 + CACHE_L2 --> CACHE_L3 + CACHE_L3 --> CACHE_DB +``` + +## Ключевые паттерны и решения + +### 1. Микросервисная архитектура +- **Преимущества**: Независимое развертывание, масштабируемость +- **Недостатки**: Сложность, network latency +- **Применение**: Все три системы используют микросервисы + +### 2. Event-Driven Architecture +- **Преимущества**: Слабая связанность, масштабируемость +- **Недостатки**: Сложность отладки, eventual consistency +- **Применение**: Twitter (твиты), Netflix (просмотры), Uber (события поездок) + +### 3. CQRS (Command Query Responsibility Segregation) +- **Преимущества**: Оптимизация для чтения и записи +- **Недостатки**: Сложность, eventual consistency +- **Применение**: Twitter (timeline), Uber (геолокация) + +### 4. Caching Strategies +- **Redis**: Для быстрого доступа к данным +- **CDN**: Для статического контента +- **Application Cache**: Для часто используемых данных + +### 5. Database Patterns +- **Sharding**: Разделение данных по ключам +- **Read Replicas**: Для масштабирования чтения +- **Event Sourcing**: Для аудита и восстановления + +--- + +## Диаграмма процесса анализа архитектуры + +```mermaid +flowchart TD + A[Выбор системы] --> B[Изучение документации] + B --> C[Анализ технических блогов] + C --> D[Изучение open-source компонентов] + + D --> E[Определение доменов] + E --> F[Выделение сервисов] + F --> G[Анализ взаимодействий] + + G --> H[Оценка производительности] + H --> I[Анализ масштабируемости] + I --> J[Проверка надежности] + J --> K[Оценка безопасности] + + K --> L[Изучение паттернов] + L --> M[Анализ технологий] + M --> N[Выявление trade-offs] + + N --> O[Создание диаграммы] + O --> P[Документирование решений] + P --> Q[Финальный отчет] + + style A fill:#e1f5fe + style Q fill:#c8e6c9 + style O fill:#fff3e0 +``` + +## Рекомендации для анализа + +### 1. Выберите систему +- Изучите публичную информацию о архитектуре +- Проанализируйте технические блоги компании +- Изучите open-source компоненты + +### 2. Определите компоненты +- Выделите основные домены +- Определите границы сервисов +- Проанализируйте взаимодействие + +### 3. Оцените качественные атрибуты +- Производительность (метрики, нагрузки) +- Масштабируемость (как система растет) +- Надежность (обработка сбоев) +- Безопасность (защита данных) + +### 4. Изучите архитектурные решения +- Паттерны проектирования +- Технологические выборы +- Trade-offs и компромиссы + +--- diff --git a/modules/module1/theory.md b/modules/module1/theory.md new file mode 100644 index 0000000..bb7f668 --- /dev/null +++ b/modules/module1/theory.md @@ -0,0 +1,6365 @@ +# Модуль 1: Основы архитектуры программного обеспечения + +## Содержание модуля +1. [Роль и обязанности Solution Architect](#роль-и-обязанности-solution-architect) +2. [Введение в архитектуру ПО](#введение-в-архитектуру-по) +3. [Доменная модель и Domain-Driven Design](#доменная-модель-и-domain-driven-design) +4. [Принципы проектирования](#принципы-проектирования) +5. [Архитектурные стили и паттерны](#архитектурные-стили-и-паттерны) +6. [Качественные атрибуты](#качественные-атрибуты) +7. [Роль архитектора в команде](#роль-архитектора-в-команде) + +--- + +## Роль и обязанности Solution Architect + +### Кто такой Solution Architect? + +**Solution Architect** — это специалист, который проектирует комплексные технические решения для решения бизнес-задач. Он является мостом между бизнес-требованиями и технической реализацией. + +### Ключевые обязанности Solution Architect + +#### 1. Анализ и проектирование решений +- **Анализ бизнес-требований** и перевод их в технические решения +- **Проектирование архитектуры** с учетом всех ограничений и требований +- **Выбор технологий** и инструментов для реализации +- **Создание технических спецификаций** и архитектурной документации + +#### 2. Управление техническими решениями +- **Принятие архитектурных решений** (Architecture Decision Records - ADR) +- **Оценка рисков** и компромиссов технических решений +- **Управление техническим долгом** и эволюцией системы +- **Обеспечение соответствия** стандартам и лучшим практикам + +#### 3. Коммуникация и координация +- **Взаимодействие со стейкхолдерами** (бизнес, разработчики, DevOps, QA) +- **Презентация архитектурных решений** на разных уровнях +- **Техническое лидерство** и менторство команды +- **Координация между командами** разработки + +### Отличия от других типов архитекторов + +#### Solution Architect против Enterprise Architect + +| Аспект | Solution Architect | Enterprise Architect | +|--------|-------------------|---------------------| +| **Фокус** | Конкретное решение/продукт | Вся организация | +| **Масштаб** | Один проект/система | Множество систем | +| **Временной горизонт** | 6-18 месяцев | 2-5 лет | +| **Детализация** | Высокая техническая детализация | Высокий уровень абстракции | +| **Взаимодействие** | Команды разработки | C-level, IT-директора | + +**Пример различий:** + +```java +// Solution Architect - детальное техническое решение +@RestController +@RequestMapping("/api/orders") +public class OrderController { + + @Autowired + private OrderService orderService; + + @Autowired + private PaymentService paymentService; + + @PostMapping + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + // Детальная реализация бизнес-логики + Order order = orderService.createOrder(request); + PaymentResult payment = paymentService.processPayment(order); + + if (payment.isSuccess()) { + return ResponseEntity.ok(OrderResponse.fromOrder(order)); + } else { + throw new PaymentException("Payment failed"); + } + } +} + +// Enterprise Architect - высокоуровневая диаграмма +/* +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ E-commerce │ │ Payment │ │ Inventory │ +│ Platform │◄──►│ Gateway │◄──►│ Management │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Customer │ │ Order │ │ Fulfillment │ +│ Management │ │ Processing │ │ System │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +*/ +``` + +#### Solution Architect против Technical Architect + +| Аспект | Solution Architect | Technical Architect | +|--------|-------------------|-------------------| +| **Фокус** | Бизнес-решение | Техническая реализация | +| **Уровень** | Системный | Компонентный | +| **Взаимодействие** | Бизнес + Технические команды | Только технические команды | +| **Решения** | Архитектурные паттерны, технологии | Алгоритмы, структуры данных | + +### Жизненный цикл проектирования архитектуры + +#### 1. Анализ требований (Requirements Analysis) + +**Входные данные:** +- Бизнес-требования (Business Requirements) +- Функциональные требования (Functional Requirements) +- Нефункциональные требования (Non-Functional Requirements) +- Ограничения (Constraints) + +**Процесс анализа:** + +```java +// Пример анализа требований +public class RequirementsAnalyzer { + + public ArchitectureRequirements analyzeRequirements(BusinessRequirements businessReq) { + ArchitectureRequirements archReq = new ArchitectureRequirements(); + + // Анализ функциональных требований + analyzeFunctionalRequirements(businessReq, archReq); + + // Анализ нефункциональных требований + analyzeNonFunctionalRequirements(businessReq, archReq); + + // Анализ ограничений + analyzeConstraints(businessReq, archReq); + + return archReq; + } + + private void analyzeNonFunctionalRequirements(BusinessRequirements businessReq, + ArchitectureRequirements archReq) { + // Производительность + if (businessReq.getExpectedUsers() > 10000) { + archReq.addRequirement("HIGH_PERFORMANCE", "System must handle 10k+ concurrent users"); + } + + // Масштабируемость + if (businessReq.isGlobalDeployment()) { + archReq.addRequirement("SCALABILITY", "System must scale globally"); + } + + // Безопасность + if (businessReq.isFinancialData()) { + archReq.addRequirement("SECURITY", "Financial data must be encrypted"); + } + } +} +``` + +#### 2. Проектирование архитектуры (Architecture Design) + +**Этапы проектирования:** + +1. **Высокоуровневое проектирование** + - Определение основных компонентов + - Выбор архитектурного стиля + - Определение интерфейсов + +2. **Детальное проектирование** + - Проектирование компонентов + - Выбор технологий + - Определение паттернов взаимодействия + +3. **Валидация архитектуры** + - Проверка соответствия требованиям + - Анализ рисков + - Прототипирование критических компонентов + +#### 3. Документирование (Documentation) + +**Типы архитектурной документации:** + +```markdown +# Solution Architecture Document + +## 1. Executive Summary +- Краткое описание решения +- Ключевые преимущества +- Основные риски + +## 2. Business Context +- Бизнес-цели +- Пользователи системы +- Ключевые процессы + +## 3. Architecture Overview +- Высокоуровневая диаграмма +- Основные компоненты +- Технологический стек + +## 4. Detailed Design +- Детальные диаграммы компонентов +- API спецификации +- Схемы данных + +## 5. Non-Functional Requirements +- Производительность +- Безопасность +- Масштабируемость +- Надежность + +## 6. Implementation Plan +- Этапы реализации +- Ресурсы +- Временные рамки + +## 7. Risks and Mitigation +- Технические риски +- Планы по их устранению +``` + +#### 4. Реализация и контроль (Implementation & Governance) + +**Роль Solution Architect в реализации:** + +- **Техническое лидерство** команды разработки +- **Контроль качества** архитектурных решений +- **Управление изменениями** в архитектуре +- **Решение технических проблем** + +### Нефункциональные требования (NFR) в фокусе Solution Architect + +#### 1. Масштабируемость (Scalability) + +**Определение:** Способность системы обрабатывать увеличение нагрузки. + +**Метрики:** +- Количество одновременных пользователей +- Объем обрабатываемых данных +- Количество транзакций в секунду + +**Примеры требований:** +```yaml +scalability: + concurrent_users: 10000 + data_volume: "1TB per day" + transactions_per_second: 1000 + response_time: "< 200ms for 95% of requests" + availability: "99.9%" +``` + +#### 2. Надежность (Reliability) + +**Определение:** Способность системы выполнять требуемые функции в заданных условиях. + +**Аспекты:** +- Отказоустойчивость (Fault Tolerance) +- Восстановление после сбоев (Recovery) +- Предотвращение потери данных (Data Integrity) + +#### 3. Безопасность (Security) + +**Определение:** Защита системы от угроз и несанкционированного доступа. + +**Компоненты:** +- Аутентификация и авторизация +- Шифрование данных +- Защита от атак +- Соответствие стандартам + +#### 4. Производительность (Performance) + +**Определение:** Скорость и эффективность выполнения операций. + +**Метрики:** +- Время отклика (Response Time) +- Пропускная способность (Throughput) +- Использование ресурсов (Resource Utilization) + +### Ключевые навыки Solution Architect + +#### Технические навыки + +1. **Архитектурные паттерны и стили** + - Микросервисы, SOA, Event-driven + - CQRS, Event Sourcing, Saga + - API Gateway, Service Mesh + +2. **Облачные технологии** + - AWS, Azure, GCP + - Containerization (Docker, Kubernetes) + - Serverless архитектура + +3. **Интеграционные технологии** + - REST, GraphQL, gRPC + - Message Queues (RabbitMQ, Kafka) + - API Management + +#### Мягкие навыки + +1. **Коммуникация** + - Презентация технических решений + - Взаимодействие с бизнесом + - Техническое письмо + +2. **Лидерство** + - Техническое лидерство + - Менторство + - Управление конфликтами + +3. **Аналитическое мышление** + - Анализ требований + - Принятие решений + - Управление рисками + +### Карьерный путь Solution Architect + +#### Уровни развития + +1. **Junior Solution Architect** (2-3 года опыта) + - Помощь в проектировании решений + - Изучение архитектурных паттернов + - Работа под руководством Senior Architect + +2. **Solution Architect** (3-7 лет опыта) + - Независимое проектирование решений + - Взаимодействие с бизнесом + - Техническое лидерство команды + +3. **Senior Solution Architect** (7+ лет опыта) + - Сложные enterprise решения + - Архитектурная стратегия + - Менторство других архитекторов + +4. **Principal/Lead Solution Architect** (10+ лет опыта) + - Архитектурная стратегия компании + - Управление архитектурной командой + - Влияние на технологическую стратегию + +--- + +## Введение в архитектуру ПО + +### Определение архитектуры ПО + +**Архитектура программного обеспечения** — это структура системы, включающая: +- Компоненты системы и их взаимодействие +- Внешние интерфейсы +- Ограничения и требования +- Принципы проектирования + +### Ключевые характеристики архитектуры + +1. **Структурность** — архитектура определяет структуру системы +2. **Абстрактность** — скрывает детали реализации +3. **Системность** — рассматривает систему как целое +4. **Эволюционность** — может изменяться со временем + +### Зачем нужна архитектура? + +- **Управление сложностью** — разбиение сложной системы на понятные части +- **Коммуникация** — общий язык для обсуждения системы +- **Планирование** — основа для принятия технических решений +- **Переиспользование** — возможность использования компонентов в других проектах + +### Архитектурные решения + +**Архитектурное решение** — это выбор, который: +- Влияет на структуру системы +- Имеет долгосрочные последствия +- Трудно изменить в будущем +- Влияет на качественные атрибуты + +### Примеры архитектурных решений: + +#### 1. Выбор между монолитом и микросервисами +```java +// Монолитная архитектура +@SpringBootApplication +public class EcommerceApplication { + public static void main(String[] args) { + SpringApplication.run(EcommerceApplication.class, args); + } +} + +// Все сервисы в одном приложении +@RestController +@RequestMapping("/api") +public class EcommerceController { + + @Autowired + private UserService userService; + + @Autowired + private OrderService orderService; + + @Autowired + private PaymentService paymentService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + // Вся логика в одном месте + User user = userService.findById(request.getUserId()); + Order order = orderService.createOrder(request); + PaymentResult payment = paymentService.processPayment(order); + return ResponseEntity.ok(order); + } +} + +// Микросервисная архитектура +// User Service +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + return ResponseEntity.ok(userService.findById(id)); + } +} + +// Order Service +@RestController +@RequestMapping("/api/orders") +public class OrderController { + @Autowired + private OrderService orderService; + + @Autowired + private UserServiceClient userServiceClient; + + @PostMapping + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + // Вызов внешнего сервиса + User user = userServiceClient.getUser(request.getUserId()); + Order order = orderService.createOrder(request); + return ResponseEntity.ok(order); + } +} +``` + +#### 2. Выбор базы данных (SQL vs NoSQL) +```java +// SQL подход (PostgreSQL) +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "email", unique = true, nullable = false) + private String email; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + private List orders = new ArrayList<>(); + + // Сложные запросы с JOIN + @Query("SELECT u FROM User u JOIN u.orders o WHERE o.status = :status") + List findUsersWithOrdersByStatus(@Param("status") OrderStatus status); +} + +// NoSQL подход (MongoDB) +@Document(collection = "users") +public class User { + @Id + private String id; + + private String name; + private String email; + private List orders = new ArrayList<>(); + + // Гибкая схема данных + private Map additionalProperties = new HashMap<>(); +} + +// Пример использования +@Service +public class UserService { + + // SQL: ACID транзакции + @Transactional + public User createUserWithOrder(CreateUserRequest request) { + User user = new User(request.getName(), request.getEmail()); + user = userRepository.save(user); + + Order order = new Order(user.getId(), request.getOrderItems()); + order = orderRepository.save(order); + + return user; + } + + // NoSQL: Документная модель + public User createUserWithOrder(CreateUserRequest request) { + User user = new User(); + user.setName(request.getName()); + user.setEmail(request.getEmail()); + + // Встраивание заказа в документ пользователя + Order order = new Order(request.getOrderItems()); + user.getOrders().add(order); + + return userRepository.save(user); + } +} +``` + +#### 3. Выбор протокола коммуникации (REST vs GraphQL) +```java +// REST API +@RestController +@RequestMapping("/api/users") +public class UserRestController { + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + User user = userService.findById(id); + return ResponseEntity.ok(user); + } + + @GetMapping("/{id}/orders") + public ResponseEntity> getUserOrders(@PathVariable Long id) { + List orders = orderService.findByUserId(id); + return ResponseEntity.ok(orders); + } + + // Проблема: N+1 запросов + @GetMapping + public ResponseEntity> getAllUsers() { + List users = userService.findAll(); + // Для каждого пользователя нужен отдельный запрос для заказов + users.forEach(user -> { + List orders = orderService.findByUserId(user.getId()); + user.setOrders(orders); + }); + return ResponseEntity.ok(users); + } +} + +// GraphQL API +@Component +public class UserGraphQLResolver implements GraphQLQueryResolver { + + public User getUser(Long id) { + return userService.findById(id); + } + + public List getAllUsers() { + return userService.findAll(); + } +} + +@Component +public class UserResolver implements GraphQLResolver { + + @Autowired + private OrderService orderService; + + // Автоматическое разрешение заказов + public List orders(User user) { + return orderService.findByUserId(user.getId()); + } +} + +// GraphQL Schema +/* +type User { + id: ID! + name: String! + email: String! + orders: [Order!]! +} + +type Query { + user(id: ID!): User + users: [User!]! +} + +# Клиент может запросить только нужные поля +query { + users { + id + name + orders { + id + total + } + } +} +*/ +``` + +#### 4. Выбор стратегии кэширования +```java +// Cache-Aside Pattern +@Service +public class UserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + private static final String CACHE_KEY_PREFIX = "user:"; + private static final Duration CACHE_TTL = Duration.ofMinutes(30); + + public User findById(Long id) { + String cacheKey = CACHE_KEY_PREFIX + id; + + // 1. Проверяем кэш + User user = redisTemplate.opsForValue().get(cacheKey); + if (user != null) { + return user; + } + + // 2. Загружаем из БД + user = userRepository.findById(id); + if (user != null) { + // 3. Сохраняем в кэш + redisTemplate.opsForValue().set(cacheKey, user, CACHE_TTL); + } + + return user; + } +} + +// Write-Through Pattern +@Service +public class UserService { + + public User save(User user) { + // 1. Сохраняем в БД + user = userRepository.save(user); + + // 2. Обновляем кэш + String cacheKey = CACHE_KEY_PREFIX + user.getId(); + redisTemplate.opsForValue().set(cacheKey, user, CACHE_TTL); + + return user; + } +} + +// Write-Behind Pattern +@Service +public class UserService { + + @Async + public CompletableFuture saveAsync(User user) { + // 1. Обновляем кэш сразу + String cacheKey = CACHE_KEY_PREFIX + user.getId(); + redisTemplate.opsForValue().set(cacheKey, user, CACHE_TTL); + + // 2. Сохраняем в БД асинхронно + return CompletableFuture.supplyAsync(() -> { + return userRepository.save(user); + }); + } +} +``` + +--- + +## Доменная модель и Domain-Driven Design + +### Что такое домен? + +**Домен (Domain)** — это предметная область, в которой работает приложение. Это сфера знаний, правил и процессов, которые определяют, как работает бизнес. + +#### Ключевые характеристики домена: + +1. **Бизнес-логика** — правила и процессы, специфичные для предметной области +2. **Терминология** — язык, используемый экспертами в данной области +3. **Ограничения** — правила и ограничения, которые должны соблюдаться +4. **Процессы** — последовательности действий, выполняемых в домене + +### Domain-Driven Design (DDD) + +**Domain-Driven Design** — подход к разработке программного обеспечения, который фокусируется на моделировании предметной области. + +#### Основные принципы DDD: + +1. **Фокус на домене** — архитектура отражает структуру домена +2. **Единый язык** — общий язык между разработчиками и экспертами +3. **Модель-код соответствие** — код отражает доменную модель +4. **Итеративное уточнение** — постоянное улучшение понимания домена + +### Строительные блоки DDD + +#### 1. Entity (Сущность) + +**Entity** — объект, который имеет уникальную идентичность и жизненный цикл. + +```java +// Пример Entity - Заказ +@Entity +public class Order { + @Id + private OrderId id; + private CustomerId customerId; + private OrderStatus status; + private Money totalAmount; + private List items; + + // Конструктор + public Order(OrderId id, CustomerId customerId) { + this.id = id; + this.customerId = customerId; + this.status = OrderStatus.DRAFT; + this.items = new ArrayList<>(); + } + + // Бизнес-методы + public void addItem(ProductId productId, Quantity quantity, Money unitPrice) { + if (status != OrderStatus.DRAFT) { + throw new IllegalStateException("Cannot modify confirmed order"); + } + + OrderItem item = new OrderItem(productId, quantity, unitPrice); + items.add(item); + recalculateTotal(); + } + + public void confirm() { + if (items.isEmpty()) { + throw new IllegalStateException("Cannot confirm empty order"); + } + + this.status = OrderStatus.CONFIRMED; + // Доменное событие + DomainEvents.raise(new OrderConfirmed(this.id)); + } + + private void recalculateTotal() { + this.totalAmount = items.stream() + .map(OrderItem::getSubtotal) + .reduce(Money.ZERO, Money::add); + } + + // Геттеры + public OrderId getId() { return id; } + public CustomerId getCustomerId() { return customerId; } + public OrderStatus getStatus() { return status; } + public Money getTotalAmount() { return totalAmount; } + public List getItems() { return Collections.unmodifiableList(items); } +} +``` + +#### 2. Value Object (Объект-значение) + +**Value Object** — объект, который определяется только своими атрибутами, без идентичности. + +```java +// Пример Value Object - Деньги +public class Money { + private final BigDecimal amount; + private final Currency currency; + + public Money(BigDecimal amount, Currency currency) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Amount must be non-negative"); + } + if (currency == null) { + throw new IllegalArgumentException("Currency cannot be null"); + } + + this.amount = amount.setScale(2, RoundingMode.HALF_UP); + this.currency = currency; + } + + public Money add(Money other) { + if (!this.currency.equals(other.currency)) { + throw new IllegalArgumentException("Cannot add different currencies"); + } + return new Money(this.amount.add(other.amount), this.currency); + } + + public Money multiply(BigDecimal factor) { + return new Money(this.amount.multiply(factor), this.currency); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Money money = (Money) obj; + return Objects.equals(amount, money.amount) && + Objects.equals(currency, money.currency); + } + + @Override + public int hashCode() { + return Objects.hash(amount, currency); + } + + // Геттеры + public BigDecimal getAmount() { return amount; } + public Currency getCurrency() { return currency; } + + public static final Money ZERO = new Money(BigDecimal.ZERO, Currency.getInstance("USD")); +} +``` + +#### 3. Aggregate (Агрегат) + +**Aggregate** — кластер связанных объектов, которые рассматриваются как единое целое. + +```java +// Пример Aggregate - Заказ +public class Order { + private OrderId id; + private CustomerId customerId; + private OrderStatus status; + private Money totalAmount; + private List items; + private List domainEvents; + + // Конструктор и методы... + + // Методы для работы с доменными событиями + public List getUncommittedEvents() { + return new ArrayList<>(domainEvents); + } + + public void markEventsAsCommitted() { + domainEvents.clear(); + } + + private void raiseEvent(DomainEvent event) { + domainEvents.add(event); + } +} + +// Корневой объект агрегата +public class OrderAggregate { + private Order order; + + public OrderAggregate(OrderId id, CustomerId customerId) { + this.order = new Order(id, customerId); + } + + public void addItem(ProductId productId, Quantity quantity, Money unitPrice) { + order.addItem(productId, quantity, unitPrice); + } + + public void confirm() { + order.confirm(); + } + + public Order getOrder() { + return order; + } +} +``` + +#### 4. Domain Service (Доменный сервис) + +**Domain Service** — сервис, который содержит бизнес-логику, не принадлежащую ни одной сущности. + +```java +// Пример Domain Service - Расчет скидки +@Service +public class DiscountCalculationService { + + public Money calculateDiscount(Order order, Customer customer) { + Money discount = Money.ZERO; + + // Скидка за количество товаров + int totalItems = order.getItems().stream() + .mapToInt(item -> item.getQuantity().getValue()) + .sum(); + + if (totalItems >= 10) { + discount = discount.add(order.getTotalAmount().multiply(new BigDecimal("0.1"))); + } + + // Скидка для VIP клиентов + if (customer.isVip()) { + discount = discount.add(order.getTotalAmount().multiply(new BigDecimal("0.05"))); + } + + // Скидка за праздники + if (isHolidayPeriod()) { + discount = discount.add(order.getTotalAmount().multiply(new BigDecimal("0.15"))); + } + + return discount; + } + + private boolean isHolidayPeriod() { + LocalDate now = LocalDate.now(); + return now.getMonth() == Month.DECEMBER && now.getDayOfMonth() >= 20; + } +} +``` + +#### 5. Repository (Репозиторий) + +**Repository** — интерфейс для доступа к агрегатам. + +```java +// Интерфейс репозитория +public interface OrderRepository { + void save(Order order); + Optional findById(OrderId id); + List findByCustomerId(CustomerId customerId); + List findByStatus(OrderStatus status); + void delete(OrderId id); +} + +// Реализация репозитория +@Repository +public class JpaOrderRepository implements OrderRepository { + + @PersistenceContext + private EntityManager entityManager; + + @Override + public void save(Order order) { + entityManager.persist(order); + } + + @Override + public Optional findById(OrderId id) { + Order order = entityManager.find(Order.class, id); + return Optional.ofNullable(order); + } + + @Override + public List findByCustomerId(CustomerId customerId) { + return entityManager.createQuery( + "SELECT o FROM Order o WHERE o.customerId = :customerId", Order.class) + .setParameter("customerId", customerId) + .getResultList(); + } + + @Override + public List findByStatus(OrderStatus status) { + return entityManager.createQuery( + "SELECT o FROM Order o WHERE o.status = :status", Order.class) + .setParameter("status", status) + .getResultList(); + } + + @Override + public void delete(OrderId id) { + Order order = entityManager.find(Order.class, id); + if (order != null) { + entityManager.remove(order); + } + } +} +``` + +### Bounded Context (Ограниченный контекст) + +**Bounded Context** — граница, в которой определенная доменная модель является валидной. + +#### Примеры Bounded Context: + +1. **Order Management** — управление заказами +2. **Customer Management** — управление клиентами +3. **Inventory Management** — управление складом +4. **Payment Processing** — обработка платежей + +```java +// Bounded Context: Order Management +@BoundedContext("OrderManagement") +public class OrderContext { + + // Агрегаты + public class Order { /* ... */ } + public class OrderItem { /* ... */ } + + // Сервисы + public class OrderService { /* ... */ } + public class OrderValidationService { /* ... */ } + + // Репозитории + public interface OrderRepository { /* ... */ } +} + +// Bounded Context: Customer Management +@BoundedContext("CustomerManagement") +public class CustomerContext { + + // Агрегаты + public class Customer { /* ... */ } + public class CustomerProfile { /* ... */ } + + // Сервисы + public class CustomerService { /* ... */ } + public class CustomerRegistrationService { /* ... */ } + + // Репозитории + public interface CustomerRepository { /* ... */ } +} +``` + +### Ubiquitous Language (Единый язык) + +**Ubiquitous Language** — общий язык между разработчиками и экспертами предметной области. + +#### Пример единого языка для e-commerce: + +```java +// Термины домена +public enum OrderStatus { + DRAFT("Черновик"), + CONFIRMED("Подтвержден"), + SHIPPED("Отправлен"), + DELIVERED("Доставлен"), + CANCELLED("Отменен"); + + private final String description; + + OrderStatus(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} + +// Доменные события +public class OrderConfirmed implements DomainEvent { + private final OrderId orderId; + private final LocalDateTime occurredOn; + + public OrderConfirmed(OrderId orderId) { + this.orderId = orderId; + this.occurredOn = LocalDateTime.now(); + } + + public OrderId getOrderId() { + return orderId; + } + + public LocalDateTime getOccurredOn() { + return occurredOn; + } +} +``` + +### Event Storming + +**Event Storming** — методология для изучения домена через события. + +#### Процесс Event Storming: + +1. **Доменные события** — что происходит в системе +2. **Команды** — что вызывает события +3. **Агрегаты** — что обрабатывает команды +4. **Политики** — бизнес-правила +5. **Внешние системы** — интеграции + +```markdown +# Event Storming для E-commerce + +## Доменные события +- OrderCreated (Заказ создан) +- OrderConfirmed (Заказ подтвержден) +- PaymentProcessed (Платеж обработан) +- OrderShipped (Заказ отправлен) +- OrderDelivered (Заказ доставлен) + +## Команды +- CreateOrder (Создать заказ) +- ConfirmOrder (Подтвердить заказ) +- ProcessPayment (Обработать платеж) +- ShipOrder (Отправить заказ) + +## Агрегаты +- Order (Заказ) +- Customer (Клиент) +- Product (Товар) +- Payment (Платеж) + +## Политики +- Скидка за количество товаров +- VIP статус клиента +- Праздничные скидки +``` + +### Антипаттерны DDD + +#### 1. Anemic Domain Model (Анемичная доменная модель) + +**Проблема:** Доменные объекты содержат только данные без поведения. + +```java +// Плохо: Анемичная модель +public class Order { + private Long id; + private String customerId; + private String status; + private BigDecimal totalAmount; + + // Только геттеры и сеттеры + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + // ... +} + +// Хорошо: Богатая доменная модель +public class Order { + private OrderId id; + private CustomerId customerId; + private OrderStatus status; + private Money totalAmount; + + // Бизнес-методы + public void addItem(ProductId productId, Quantity quantity, Money unitPrice) { + // Бизнес-логика + } + + public void confirm() { + // Бизнес-логика + } +} +``` + +#### 2. God Object (Божественный объект) + +**Проблема:** Один объект содержит слишком много ответственности. + +```java +// Плохо: God Object +public class Order { + // Слишком много ответственности + public void addItem() { /* ... */ } + public void calculateTax() { /* ... */ } + public void sendEmail() { /* ... */ } + public void updateInventory() { /* ... */ } + public void processPayment() { /* ... */ } +} + +// Хорошо: Разделение ответственности +public class Order { + public void addItem() { /* ... */ } +} + +public class TaxCalculationService { + public Money calculateTax(Order order) { /* ... */ } +} + +public class EmailService { + public void sendOrderConfirmation(Order order) { /* ... */ } +} +``` + +### Интеграция DDD с архитектурой + +#### 1. Layered Architecture + DDD + +```java +// Presentation Layer +@RestController +public class OrderController { + @Autowired + private OrderApplicationService orderService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + OrderId orderId = orderService.createOrder(request); + return ResponseEntity.ok(new OrderResponse(orderId)); + } +} + +// Application Layer +@Service +public class OrderApplicationService { + @Autowired + private OrderRepository orderRepository; + + @Autowired + private OrderDomainService orderDomainService; + + public OrderId createOrder(CreateOrderRequest request) { + Order order = new Order( + new OrderId(UUID.randomUUID()), + new CustomerId(request.getCustomerId()) + ); + + for (OrderItemRequest itemRequest : request.getItems()) { + order.addItem( + new ProductId(itemRequest.getProductId()), + new Quantity(itemRequest.getQuantity()), + new Money(itemRequest.getUnitPrice(), Currency.USD) + ); + } + + orderRepository.save(order); + return order.getId(); + } +} + +// Domain Layer +public class Order { + // Доменная логика +} + +// Infrastructure Layer +@Repository +public class JpaOrderRepository implements OrderRepository { + // Реализация репозитория +} +``` + +#### 2. Hexagonal Architecture + DDD + +```java +// Domain Layer (внутренний круг) +public class Order { + // Доменная логика +} + +public interface OrderRepository { + void save(Order order); + Optional findById(OrderId id); +} + +// Application Layer +@Service +public class OrderService { + private final OrderRepository orderRepository; + + public OrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public void createOrder(Order order) { + // Применение доменных правил + orderRepository.save(order); + } +} + +// Infrastructure Layer (внешний круг) +@Repository +public class JpaOrderRepository implements OrderRepository { + // Реализация через JPA +} + +// Adapters +@RestController +public class OrderController { + // REST API +} + +@Component +public class OrderEventHandler { + // Обработка событий +} +``` + +### Практические рекомендации + +#### 1. Начните с доменных событий + +```java +// Сначала определите события +public class OrderCreated implements DomainEvent { + private final OrderId orderId; + private final CustomerId customerId; + private final LocalDateTime occurredOn; + + // Конструктор, геттеры +} + +// Затем создайте агрегаты +public class Order { + public void confirm() { + // Бизнес-логика + DomainEvents.raise(new OrderConfirmed(this.id)); + } +} +``` + +#### 2. Используйте Value Objects для примитивов + +```java +// Вместо String +public class CustomerId { + private final String value; + + public CustomerId(String value) { + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException("Customer ID cannot be empty"); + } + this.value = value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + CustomerId that = (CustomerId) obj; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} +``` + +#### 3. Следите за границами агрегатов + +```java +// Правильно: Агрегат управляет только своими данными +public class Order { + private OrderId id; + private List items; + + public void addItem(ProductId productId, Quantity quantity, Money unitPrice) { + // Логика добавления товара в заказ + } +} + +// Неправильно: Агрегат управляет чужими данными +public class Order { + public void updateProductInventory(ProductId productId, Quantity quantity) { + // Это не должно быть в Order агрегате + } +} +``` + +### Заключение + +Domain-Driven Design помогает создавать архитектуру, которая: + +1. **Отражает бизнес-логику** — код соответствует домену +2. **Улучшает коммуникацию** — единый язык между командами +3. **Повышает качество** — четкие границы и ответственности +4. **Упрощает тестирование** — изолированная доменная логика +5. **Обеспечивает эволюцию** — гибкая архитектура для изменений + +DDD особенно эффективен для сложных бизнес-доменов, где важно точное понимание предметной области и создание архитектуры, которая может эволюционировать вместе с бизнесом. + +--- + +## Принципы проектирования + +### SOLID принципы + +#### 1. Single Responsibility Principle (SRP) +**Принцип единственной ответственности** + +> Класс должен иметь только одну причину для изменения + +**Пример нарушения:** +```java +// Нарушение SRP - класс делает слишком много +class UserManager { + private UserRepository userRepository; + private EmailService emailService; + private PasswordEncoder passwordEncoder; + private AuditService auditService; + + public User createUser(CreateUserRequest request) { + // 1. Валидация данных + if (request.getEmail() == null || !isValidEmail(request.getEmail())) { + throw new ValidationException("Invalid email"); + } + + // 2. Хеширование пароля + String hashedPassword = passwordEncoder.encode(request.getPassword()); + + // 3. Создание пользователя + User user = new User(request.getName(), request.getEmail(), hashedPassword); + user = userRepository.save(user); + + // 4. Отправка приветственного email + emailService.sendWelcomeEmail(user.getEmail()); + + // 5. Аудит + auditService.logUserCreation(user.getId()); + + return user; + } + + public void updateUser(Long userId, UpdateUserRequest request) { + // Аналогично много ответственностей... + } + + public void deleteUser(Long userId) { + // Аналогично много ответственностей... + } +} +``` + +**Правильная реализация:** +```java +// Разделение ответственностей +class User { + private Long id; + private String name; + private String email; + private String passwordHash; + + // Только данные пользователя и бизнес-логика + public void changePassword(String newPassword, PasswordEncoder encoder) { + this.passwordHash = encoder.encode(newPassword); + } + + public boolean isEmailValid() { + return email != null && email.contains("@"); + } +} + +class UserValidator { + public void validateCreateRequest(CreateUserRequest request) { + if (request.getName() == null || request.getName().trim().isEmpty()) { + throw new ValidationException("Name is required"); + } + if (request.getEmail() == null || !isValidEmail(request.getEmail())) { + throw new ValidationException("Invalid email"); + } + if (request.getPassword() == null || request.getPassword().length() < 8) { + throw new ValidationException("Password must be at least 8 characters"); + } + } + + private boolean isValidEmail(String email) { + return email.matches("^[A-Za-z0-9+_.-]+@(.+)$"); + } +} + +class UserRepository { + public User save(User user) { + // Только операции с БД + return userRepository.save(user); + } + + public Optional findById(Long id) { + return userRepository.findById(id); + } +} + +class UserNotificationService { + public void sendWelcomeEmail(String email) { + // Только отправка уведомлений + emailService.sendWelcomeEmail(email); + } +} + +class UserAuditService { + public void logUserCreation(Long userId) { + // Только аудит + auditService.logUserCreation(userId); + } +} + +// Координатор, который использует все сервисы +class UserService { + private UserValidator validator; + private UserRepository repository; + private UserNotificationService notificationService; + private UserAuditService auditService; + private PasswordEncoder passwordEncoder; + + public User createUser(CreateUserRequest request) { + // 1. Валидация + validator.validateCreateRequest(request); + + // 2. Создание пользователя + String hashedPassword = passwordEncoder.encode(request.getPassword()); + User user = new User(request.getName(), request.getEmail(), hashedPassword); + + // 3. Сохранение + user = repository.save(user); + + // 4. Уведомления + notificationService.sendWelcomeEmail(user.getEmail()); + + // 5. Аудит + auditService.logUserCreation(user.getId()); + + return user; + } +} +``` + +#### 2. Open/Closed Principle (OCP) +**Принцип открытости/закрытости** + +> Программные сущности должны быть открыты для расширения, но закрыты для модификации + +**Пример нарушения:** +```java +// Нарушение OCP - нужно модифицировать класс для добавления новых типов +class OrderProcessor { + public void processOrder(Order order) { + switch (order.getPaymentType()) { + case CREDIT_CARD: + processCreditCardPayment(order); + break; + case PAYPAL: + processPayPalPayment(order); + break; + case BANK_TRANSFER: + processBankTransferPayment(order); + break; + // При добавлении нового типа платежа нужно модифицировать этот класс + } + } + + private void processCreditCardPayment(Order order) { + // логика обработки кредитной карты + } + + private void processPayPalPayment(Order order) { + // логика обработки PayPal + } + + private void processBankTransferPayment(Order order) { + // логика обработки банковского перевода + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение OCP - открыто для расширения, закрыто для модификации +interface PaymentProcessor { + boolean canProcess(PaymentType paymentType); + PaymentResult processPayment(PaymentRequest request); +} + +class CreditCardProcessor implements PaymentProcessor { + @Override + public boolean canProcess(PaymentType paymentType) { + return paymentType == PaymentType.CREDIT_CARD; + } + + @Override + public PaymentResult processPayment(PaymentRequest request) { + // логика обработки кредитной карты + return new PaymentResult(true, "Payment processed via credit card"); + } +} + +class PayPalProcessor implements PaymentProcessor { + @Override + public boolean canProcess(PaymentType paymentType) { + return paymentType == PaymentType.PAYPAL; + } + + @Override + public PaymentResult processPayment(PaymentRequest request) { + // логика обработки PayPal + return new PaymentResult(true, "Payment processed via PayPal"); + } +} + +class CryptocurrencyProcessor implements PaymentProcessor { + @Override + public boolean canProcess(PaymentType paymentType) { + return paymentType == PaymentType.CRYPTOCURRENCY; + } + + @Override + public PaymentResult processPayment(PaymentRequest request) { + // логика обработки криптовалюты + return new PaymentResult(true, "Payment processed via cryptocurrency"); + } +} + +// Основной класс не нужно модифицировать для добавления новых процессоров +class OrderProcessor { + private List processors; + + public OrderProcessor(List processors) { + this.processors = processors; + } + + public PaymentResult processOrder(Order order) { + PaymentProcessor processor = processors.stream() + .filter(p -> p.canProcess(order.getPaymentType())) + .findFirst() + .orElseThrow(() -> new UnsupportedPaymentTypeException( + "No processor found for payment type: " + order.getPaymentType())); + + PaymentRequest request = new PaymentRequest(order.getAmount(), order.getPaymentDetails()); + return processor.processPayment(request); + } +} + +// Конфигурация процессоров +@Configuration +public class PaymentProcessorConfig { + + @Bean + public List paymentProcessors() { + return Arrays.asList( + new CreditCardProcessor(), + new PayPalProcessor(), + new CryptocurrencyProcessor() // Новый процессор добавляется без модификации существующего кода + ); + } +} +``` + +#### 3. Liskov Substitution Principle (LSP) +**Принцип подстановки Лисков** + +> Объекты производного класса должны быть заменяемы объектами базового класса + +**Пример нарушения:** +```java +// Нарушение LSP +class Rectangle { + protected int width; + protected int height; + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getArea() { + return width * height; + } +} + +class Square extends Rectangle { + @Override + public void setWidth(int width) { + this.width = width; + this.height = width; // Нарушение LSP - изменяет поведение + } + + @Override + public void setHeight(int height) { + this.height = height; + this.width = height; // Нарушение LSP - изменяет поведение + } +} + +// Проблема: код, работающий с Rectangle, может сломаться с Square +public void testRectangle(Rectangle rectangle) { + rectangle.setWidth(5); + rectangle.setHeight(4); + assert rectangle.getArea() == 20; // Для Square это будет 16! +} +``` + +**Правильная реализация:** +```java +// Соблюдение LSP +abstract class Shape { + public abstract int getArea(); +} + +class Rectangle extends Shape { + private int width; + private int height; + + public Rectangle(int width, int height) { + this.width = width; + this.height = height; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + @Override + public int getArea() { + return width * height; + } +} + +class Square extends Shape { + private int side; + + public Square(int side) { + this.side = side; + } + + public void setSide(int side) { + this.side = side; + } + + @Override + public int getArea() { + return side * side; + } +} + +// Теперь любой Shape можно заменить другим Shape +public void testShape(Shape shape) { + int area = shape.getArea(); + // Логика работает корректно для любого типа Shape +} +``` + +#### 4. Interface Segregation Principle (ISP) +**Принцип разделения интерфейсов** + +> Клиенты не должны зависеть от интерфейсов, которые они не используют + +**Пример нарушения:** +```java +// Нарушение ISP - слишком большой интерфейс +interface Worker { + void work(); + void eat(); + void sleep(); + void code(); + void design(); + void test(); + void deploy(); +} + +class Developer implements Worker { + @Override + public void work() { /* разработка */ } + + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void code() { /* кодирование */ } + + @Override + public void design() { /* не используется */ } + + @Override + public void test() { /* не используется */ } + + @Override + public void deploy() { /* не используется */ } +} + +class Designer implements Worker { + @Override + public void work() { /* работа */ } + + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void code() { /* не используется */ } + + @Override + public void design() { /* дизайн */ } + + @Override + public void test() { /* не используется */ } + + @Override + public void deploy() { /* не используется */ } +} +``` + +**Правильная реализация:** +```java +// Соблюдение ISP - разделение интерфейсов +interface Human { + void eat(); + void sleep(); +} + +interface Worker extends Human { + void work(); +} + +interface Developer extends Worker { + void code(); +} + +interface Designer extends Worker { + void design(); +} + +interface Tester extends Worker { + void test(); +} + +interface DevOps extends Worker { + void deploy(); +} + +// Теперь каждый класс реализует только нужные ему интерфейсы +class JavaDeveloper implements Developer { + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void work() { /* работа */ } + + @Override + public void code() { /* Java кодирование */ } +} + +class UIDesigner implements Designer { + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void work() { /* работа */ } + + @Override + public void design() { /* UI дизайн */ } +} + +// Можно комбинировать интерфейсы +class FullStackDeveloper implements Developer, Designer { + @Override + public void eat() { /* еда */ } + + @Override + public void sleep() { /* сон */ } + + @Override + public void work() { /* работа */ } + + @Override + public void code() { /* кодирование */ } + + @Override + public void design() { /* дизайн */ } +} +``` + +#### 5. Dependency Inversion Principle (DIP) +**Принцип инверсии зависимостей** + +> Модули высокого уровня не должны зависеть от модулей низкого уровня + +**Пример нарушения:** +```java +// Нарушение DIP - высокоуровневый модуль зависит от низкоуровневого +class EmailService { + public void sendEmail(String to, String subject, String body) { + // Прямая зависимость от конкретной реализации + SMTPClient smtpClient = new SMTPClient(); + smtpClient.connect("smtp.gmail.com", 587); + smtpClient.send(to, subject, body); + smtpClient.disconnect(); + } +} + +class OrderService { + private EmailService emailService; + + public OrderService() { + this.emailService = new EmailService(); // Жесткая зависимость + } + + public void processOrder(Order order) { + // Обработка заказа + order.setStatus(OrderStatus.PROCESSED); + + // Отправка уведомления + emailService.sendEmail( + order.getCustomerEmail(), + "Order Confirmation", + "Your order has been processed" + ); + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение DIP - зависимость от абстракций +interface NotificationService { + void sendNotification(String recipient, String subject, String message); +} + +interface EmailProvider { + void sendEmail(String to, String subject, String body); +} + +class SMTPEmailProvider implements EmailProvider { + @Override + public void sendEmail(String to, String subject, String body) { + SMTPClient smtpClient = new SMTPClient(); + smtpClient.connect("smtp.gmail.com", 587); + smtpClient.send(to, subject, body); + smtpClient.disconnect(); + } +} + +class SendGridEmailProvider implements EmailProvider { + @Override + public void sendEmail(String to, String subject, String body) { + SendGridClient client = new SendGridClient(); + client.send(to, subject, body); + } +} + +class EmailNotificationService implements NotificationService { + private EmailProvider emailProvider; + + public EmailNotificationService(EmailProvider emailProvider) { + this.emailProvider = emailProvider; // Инъекция зависимости + } + + @Override + public void sendNotification(String recipient, String subject, String message) { + emailProvider.sendEmail(recipient, subject, message); + } +} + +class SMSNotificationService implements NotificationService { + private SMSProvider smsProvider; + + public SMSNotificationService(SMSProvider smsProvider) { + this.smsProvider = smsProvider; + } + + @Override + public void sendNotification(String recipient, String subject, String message) { + smsProvider.sendSMS(recipient, message); + } +} + +// Высокоуровневый модуль зависит от абстракции +class OrderService { + private NotificationService notificationService; + + public OrderService(NotificationService notificationService) { + this.notificationService = notificationService; // Инъекция зависимости + } + + public void processOrder(Order order) { + // Обработка заказа + order.setStatus(OrderStatus.PROCESSED); + + // Отправка уведомления через абстракцию + notificationService.sendNotification( + order.getCustomerEmail(), + "Order Confirmation", + "Your order has been processed" + ); + } +} + +// Конфигурация зависимостей +@Configuration +public class AppConfig { + + @Bean + public EmailProvider emailProvider() { + return new SMTPEmailProvider(); // Можно легко заменить на SendGridEmailProvider + } + + @Bean + public NotificationService notificationService(EmailProvider emailProvider) { + return new EmailNotificationService(emailProvider); + } + + @Bean + public OrderService orderService(NotificationService notificationService) { + return new OrderService(notificationService); + } +} +``` + +### Дополнительные принципы + +#### DRY (Не повторяйся) +**Не повторяйся** + +- Избегайте дублирования кода +- Выносите общую логику в отдельные функции/классы +- Используйте абстракции для переиспользования + +**Пример нарушения:** +```java +// Нарушение DRY - дублирование кода +class UserService { + public void createUser(String email, String password) { + if (email == null || email.isEmpty()) { + throw new IllegalArgumentException("Email cannot be null or empty"); + } + if (!email.contains("@")) { + throw new IllegalArgumentException("Invalid email format"); + } + if (password == null || password.length() < 8) { + throw new IllegalArgumentException("Password must be at least 8 characters"); + } + // создание пользователя + } + + public void updateUser(String email, String password) { + if (email == null || email.isEmpty()) { + throw new IllegalArgumentException("Email cannot be null or empty"); + } + if (!email.contains("@")) { + throw new IllegalArgumentException("Invalid email format"); + } + if (password == null || password.length() < 8) { + throw new IllegalArgumentException("Password must be at least 8 characters"); + } + // обновление пользователя + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение DRY - выделение общей логики +class ValidationUtils { + public static void validateEmail(String email) { + if (email == null || email.isEmpty()) { + throw new IllegalArgumentException("Email cannot be null or empty"); + } + if (!email.contains("@")) { + throw new IllegalArgumentException("Invalid email format"); + } + } + + public static void validatePassword(String password) { + if (password == null || password.length() < 8) { + throw new IllegalArgumentException("Password must be at least 8 characters"); + } + } +} + +class UserService { + public void createUser(String email, String password) { + ValidationUtils.validateEmail(email); + ValidationUtils.validatePassword(password); + // создание пользователя + } + + public void updateUser(String email, String password) { + ValidationUtils.validateEmail(email); + ValidationUtils.validatePassword(password); + // обновление пользователя + } +} +``` + +#### KISS (Будь проще) +**Делай проще** + +- Простые решения часто лучше сложных +- Избегайте преждевременной оптимизации +- Читаемость кода важнее "умности" + +**Пример нарушения:** +```java +// Нарушение KISS - излишняя сложность +class ComplexCalculator { + public double calculate(String expression) { + if (expression == null || expression.trim().isEmpty()) { + throw new IllegalArgumentException("Expression cannot be null or empty"); + } + + String trimmedExpression = expression.trim(); + if (!trimmedExpression.matches("^[0-9+\\-*/\\s()]+$")) { + throw new IllegalArgumentException("Invalid characters in expression"); + } + + // Сложная логика парсинга и вычисления... + return evaluateComplexExpression(trimmedExpression); + } + + private double evaluateComplexExpression(String expr) { + // Очень сложная реализация + return 0.0; + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение KISS - простое решение +class SimpleCalculator { + public double add(double a, double b) { + return a + b; + } + + public double subtract(double a, double b) { + return a - b; + } + + public double multiply(double a, double b) { + return a * b; + } + + public double divide(double a, double b) { + if (b == 0) { + throw new IllegalArgumentException("Division by zero"); + } + return a / b; + } +} + +// Использование +SimpleCalculator calc = new SimpleCalculator(); +double result = calc.add(calc.multiply(2, 3), calc.divide(10, 2)); // 2*3 + 10/2 = 11 +``` + +#### YAGNI (Вам это не понадобится) +**Тебе это не понадобится** + +- Не реализуйте функциональность "на будущее" +- Добавляйте функции только когда они действительно нужны +- Избегайте over-engineering + +**Пример нарушения:** +```java +// Нарушение YAGNI - преждевременная оптимизация +class UserRepository { + private Map cache = new ConcurrentHashMap<>(); + private List users = new ArrayList<>(); + private UserIndexer indexer = new UserIndexer(); + private UserValidator validator = new UserValidator(); + private UserMetricsCollector metrics = new UserMetricsCollector(); + + public void saveUser(User user) { + // Валидация (может не понадобиться) + validator.validate(user); + + // Индексация (может не понадобиться) + indexer.index(user); + + // Кэширование (может не понадобиться) + cache.put(user.getId(), user); + + // Сохранение + users.add(user); + + // Метрики (может не понадобиться) + metrics.recordSave(user); + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение YAGNI - только необходимая функциональность +class UserRepository { + private List users = new ArrayList<>(); + + public void saveUser(User user) { + if (user == null) { + throw new IllegalArgumentException("User cannot be null"); + } + users.add(user); + } + + public User findById(String id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + + return users.stream() + .filter(u -> u.getId().equals(id)) + .findFirst() + .orElse(null); + } +} + +// Когда понадобится кэширование - добавим +// Когда понадобится валидация - добавим +// Когда понадобится индексация - добавим +``` + +### Принципы модульности + +#### Слабая связанность (Loose Coupling) +- Компоненты минимально зависят друг от друга +- Изменения в одном компоненте не влияют на другие +- Легче тестировать и поддерживать + +**Пример нарушения:** +```java +// Нарушение - тесная связанность +class OrderService { + private MySQLDatabase database; + private SMTPEmailService emailService; + private FileLogger logger; + + public OrderService() { + this.database = new MySQLDatabase("localhost", "orders_db"); + this.emailService = new SMTPEmailService("smtp.gmail.com", 587); + this.logger = new FileLogger("/var/log/orders.log"); + } + + public void processOrder(Order order) { + // Прямая зависимость от конкретных реализаций + database.save(order); + emailService.sendEmail(order.getCustomerEmail(), "Order processed"); + logger.log("Order processed: " + order.getId()); + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение - слабая связанность +interface OrderRepository { + void save(Order order); +} + +interface NotificationService { + void sendNotification(String recipient, String message); +} + +interface LoggingService { + void log(String message); +} + +class OrderService { + private OrderRepository repository; + private NotificationService notificationService; + private LoggingService loggingService; + + public OrderService(OrderRepository repository, + NotificationService notificationService, + LoggingService loggingService) { + this.repository = repository; + this.notificationService = notificationService; + this.loggingService = loggingService; + } + + public void processOrder(Order order) { + repository.save(order); + notificationService.sendNotification( + order.getCustomerEmail(), + "Order processed" + ); + loggingService.log("Order processed: " + order.getId()); + } +} + +// Конкретные реализации +class MySQLOrderRepository implements OrderRepository { + @Override + public void save(Order order) { + // MySQL специфичная логика + } +} + +class EmailNotificationService implements NotificationService { + @Override + public void sendNotification(String recipient, String message) { + // Email специфичная логика + } +} + +class FileLoggingService implements LoggingService { + @Override + public void log(String message) { + // File logging специфичная логика + } +} +``` + +#### Высокая сплоченность (High Cohesion) +- Элементы внутри модуля тесно связаны +- Модуль имеет четко определенную ответственность +- Все функции модуля работают для достижения одной цели + +**Пример нарушения:** +```java +// Нарушение - низкая сплоченность +class UtilityClass { + public void sendEmail(String to, String subject, String body) { + // Отправка email + } + + public void calculateTax(double amount, double rate) { + // Расчет налога + } + + public void validatePassword(String password) { + // Валидация пароля + } + + public void formatDate(Date date, String format) { + // Форматирование даты + } + + public void compressFile(String filePath) { + // Сжатие файла + } +} +``` + +**Правильная реализация:** +```java +// Соблюдение - высокая сплоченность +class EmailService { + public void sendEmail(String to, String subject, String body) { + validateEmail(to); + String formattedBody = formatEmailBody(body); + deliverEmail(to, subject, formattedBody); + } + + private void validateEmail(String email) { + // Валидация email адреса + } + + private String formatEmailBody(String body) { + // Форматирование тела письма + return body; + } + + private void deliverEmail(String to, String subject, String body) { + // Доставка email + } +} + +class TaxCalculator { + public double calculateTax(double amount, double rate) { + validateAmount(amount); + validateRate(rate); + return amount * rate; + } + + private void validateAmount(double amount) { + if (amount < 0) { + throw new IllegalArgumentException("Amount cannot be negative"); + } + } + + private void validateRate(double rate) { + if (rate < 0 || rate > 1) { + throw new IllegalArgumentException("Rate must be between 0 and 1"); + } + } +} + +class PasswordValidator { + public boolean isValid(String password) { + return hasMinimumLength(password) && + containsUppercase(password) && + containsLowercase(password) && + containsDigit(password); + } + + private boolean hasMinimumLength(String password) { + return password.length() >= 8; + } + + private boolean containsUppercase(String password) { + return password.chars().anyMatch(Character::isUpperCase); + } + + private boolean containsLowercase(String password) { + return password.chars().anyMatch(Character::isLowerCase); + } + + private boolean containsDigit(String password) { + return password.chars().anyMatch(Character::isDigit); + } +} +``` + +--- + +## Архитектурные стили и паттерны + +### Архитектурные стили + +#### 1. Монолитная архитектура +**Определение:** Вся система реализована как единое приложение + +**Преимущества:** +- Простота разработки и развертывания +- Легкое тестирование +- Простая отладка +- Низкая сложность сети + +**Недостатки:** +- Сложность масштабирования +- Технологическая привязка +- Сложность обновлений +- Риск каскадных отказов + +**Когда использовать:** +- Небольшие команды +- Простые приложения +- Быстрое прототипирование + +**Технический пример:** +```java +// Монолитное приложение - все в одном проекте +@SpringBootApplication +public class ECommerceApplication { + public static void main(String[] args) { + SpringApplication.run(ECommerceApplication.class, args); + } +} + +// Все контроллеры в одном приложении +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @PostMapping + public ResponseEntity createUser(@RequestBody User user) { + return ResponseEntity.ok(userService.createUser(user)); + } + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + return ResponseEntity.ok(userService.getUser(id)); + } +} + +@RestController +@RequestMapping("/api/orders") +public class OrderController { + @Autowired + private OrderService orderService; + + @PostMapping + public ResponseEntity createOrder(@RequestBody Order order) { + return ResponseEntity.ok(orderService.createOrder(order)); + } + + @GetMapping("/{id}") + public ResponseEntity getOrder(@PathVariable Long id) { + return ResponseEntity.ok(orderService.getOrder(id)); + } +} + +@RestController +@RequestMapping("/api/products") +public class ProductController { + @Autowired + private ProductService productService; + + @PostMapping + public ResponseEntity createProduct(@RequestBody Product product) { + return ResponseEntity.ok(productService.createProduct(product)); + } + + @GetMapping("/{id}") + public ResponseEntity getProduct(@PathVariable Long id) { + return ResponseEntity.ok(productService.getProduct(id)); + } +} + +// Все сервисы в одном приложении +@Service +public class UserService { + @Autowired + private UserRepository userRepository; + + public User createUser(User user) { + return userRepository.save(user); + } + + public User getUser(Long id) { + return userRepository.findById(id).orElse(null); + } +} + +@Service +public class OrderService { + @Autowired + private OrderRepository orderRepository; + @Autowired + private UserService userService; // Прямая зависимость между сервисами + + public Order createOrder(Order order) { + User user = userService.getUser(order.getUserId()); + if (user == null) { + throw new IllegalArgumentException("User not found"); + } + return orderRepository.save(order); + } + + public Order getOrder(Long id) { + return orderRepository.findById(id).orElse(null); + } +} + +// Единая база данных +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private String email; + // геттеры и сеттеры +} + +@Entity +@Table(name = "orders") +public class Order { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private Long userId; + private BigDecimal amount; + // геттеры и сеттеры +} + +@Entity +@Table(name = "products") +public class Product { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private BigDecimal price; + // геттеры и сеттеры +} +``` + +#### 2. Микросервисная архитектура +**Определение:** Система состоит из множества небольших независимых сервисов + +**Преимущества:** +- Независимое развертывание +- Технологическое разнообразие +- Масштабируемость +- Отказоустойчивость + +**Недостатки:** +- Сложность сети +- Проблемы с данными +- Сложность мониторинга +- Overhead коммуникации + +**Когда использовать:** +- Большие команды +- Сложные домены + +#### 3. Слоистая архитектура (Layered Architecture) +**Определение:** Система организована в горизонтальные слои, где каждый слой может использовать только слои ниже себя. + +**Слои:** +- **Presentation Layer** — пользовательский интерфейс +- **Business Layer** — бизнес-логика +- **Data Access Layer** — работа с данными +- **Database Layer** — хранение данных + +**Преимущества:** +- Четкое разделение ответственности +- Легкость тестирования +- Переиспользование компонентов +- Простота понимания + +**Недостатки:** +- Производительность (много слоев) +- Сложность при изменении требований +- Потенциальные проблемы с производительностью + +**Технические примеры:** + +**Java (Spring Boot):** +```java +// Presentation Layer +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @PostMapping + public ResponseEntity createUser(@RequestBody CreateUserRequest request) { + UserDto user = userService.createUser(request); + return ResponseEntity.ok(user); + } +} + +// Business Layer +@Service +@Transactional +public class UserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private EmailService emailService; + + public UserDto createUser(CreateUserRequest request) { + // Бизнес-логика валидации + validateUserRequest(request); + + // Создание пользователя + User user = new User(); + user.setName(request.getName()); + user.setEmail(request.getEmail()); + user.setCreatedDate(LocalDateTime.now()); + + // Сохранение через Data Access Layer + User savedUser = userRepository.save(user); + + // Бизнес-логика уведомления + emailService.sendWelcomeEmail(savedUser.getEmail()); + + return mapToDto(savedUser); + } + + private void validateUserRequest(CreateUserRequest request) { + if (request.getName() == null || request.getName().trim().isEmpty()) { + throw new ValidationException("Name is required"); + } + if (request.getEmail() == null || !isValidEmail(request.getEmail())) { + throw new ValidationException("Valid email is required"); + } + } +} + +// Data Access Layer +@Repository +public interface UserRepository extends JpaRepository { + Optional findByEmail(String email); + List findByCreatedDateBetween(LocalDateTime start, LocalDateTime end); +} + +// Database Layer (Entity) +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String name; + + @Column(unique = true, nullable = false) + private String email; + + @CreationTimestamp + private LocalDateTime createdDate; + + // геттеры и сеттеры +} +``` + +**Rust (Actix Web + Diesel):** +```rust +// Presentation Layer +use actix_web::{web, HttpResponse, Result}; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize)] +pub struct CreateUserRequest { + pub name: String, + pub email: String, +} + +#[derive(Serialize)] +pub struct UserDto { + pub id: i32, + pub name: String, + pub email: String, + pub created_date: chrono::DateTime, +} + +// Business Layer +pub struct UserService { + user_repository: Box, + email_service: Box, +} + +impl UserService { + pub fn new(user_repository: Box, email_service: Box) -> Self { + Self { + user_repository, + email_service, + } + } + + pub fn create_user(&self, request: CreateUserRequest) -> Result { + // Бизнес-логика валидации + self.validate_user_request(&request)?; + + // Создание пользователя + let user = User { + id: 0, // Will be set by database + name: request.name, + email: request.email, + created_date: chrono::Utc::now(), + }; + + // Сохранение через Data Access Layer + let saved_user = self.user_repository.save(user)?; + + // Бизнес-логика уведомления + self.email_service.send_welcome_email(&saved_user.email)?; + + Ok(UserDto { + id: saved_user.id, + name: saved_user.name, + email: saved_user.email, + created_date: saved_user.created_date, + }) + } + + fn validate_user_request(&self, request: &CreateUserRequest) -> Result<(), UserError> { + if request.name.trim().is_empty() { + return Err(UserError::ValidationError("Name is required".to_string())); + } + + if !self.is_valid_email(&request.email) { + return Err(UserError::ValidationError("Valid email is required".to_string())); + } + + Ok(()) + } + + fn is_valid_email(&self, email: &str) -> bool { + email.contains('@') && email.contains('.') + } +} + +// Data Access Layer +pub trait UserRepository { + fn save(&self, user: User) -> Result; + fn find_by_email(&self, email: &str) -> Result, UserError>; + fn find_by_created_date_between(&self, start: chrono::DateTime, end: chrono::DateTime) -> Result, UserError>; +} + +pub struct DieselUserRepository { + connection: diesel::PgConnection, +} + +impl UserRepository for DieselUserRepository { + fn save(&self, user: User) -> Result { + use crate::schema::users::dsl::*; + use diesel::prelude::*; + + diesel::insert_into(users) + .values(&user) + .get_result(&self.connection) + .map_err(|e| UserError::DatabaseError(e.to_string())) + } + + fn find_by_email(&self, email: &str) -> Result, UserError> { + use crate::schema::users::dsl::*; + use diesel::prelude::*; + + users + .filter(email.eq(email)) + .first(&self.connection) + .optional() + .map_err(|e| UserError::DatabaseError(e.to_string())) + } + + fn find_by_created_date_between(&self, start: chrono::DateTime, end: chrono::DateTime) -> Result, UserError> { + use crate::schema::users::dsl::*; + use diesel::prelude::*; + + users + .filter(created_date.between(start, end)) + .load(&self.connection) + .map_err(|e| UserError::DatabaseError(e.to_string())) + } +} + +// Database Layer (Model) +use diesel::prelude::*; + +#[derive(Queryable, Insertable, Serialize, Deserialize)] +#[table_name = "users"] +pub struct User { + pub id: i32, + pub name: String, + pub email: String, + pub created_date: chrono::DateTime, +} + +// Error handling +#[derive(Debug)] +pub enum UserError { + ValidationError(String), + DatabaseError(String), + EmailError(String), +} + +impl std::fmt::Display for UserError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + UserError::ValidationError(msg) => write!(f, "Validation error: {}", msg), + UserError::DatabaseError(msg) => write!(f, "Database error: {}", msg), + UserError::EmailError(msg) => write!(f, "Email error: {}", msg), + } + } +} + +impl std::error::Error for UserError {} + +// Email Service trait +pub trait EmailService { + fn send_welcome_email(&self, email: &str) -> Result<(), UserError>; +} +``` + +**Python (FastAPI + SQLAlchemy):** +```python +# Presentation Layer +from fastapi import FastAPI, HTTPException, Depends +from pydantic import BaseModel, EmailStr +from typing import Optional +from datetime import datetime + +app = FastAPI() + +class CreateUserRequest(BaseModel): + name: str + email: EmailStr + +class UserDto(BaseModel): + id: int + name: str + email: str + created_date: datetime + +# Business Layer +class UserService: + def __init__(self, user_repository: 'UserRepository', email_service: 'EmailService'): + self.user_repository = user_repository + self.email_service = email_service + + def create_user(self, request: CreateUserRequest) -> UserDto: + # Бизнес-логика валидации + self.validate_user_request(request) + + # Создание пользователя + user = User( + name=request.name, + email=request.email, + created_date=datetime.utcnow() + ) + + # Сохранение через Data Access Layer + saved_user = self.user_repository.save(user) + + # Бизнес-логика уведомления + self.email_service.send_welcome_email(saved_user.email) + + return UserDto( + id=saved_user.id, + name=saved_user.name, + email=saved_user.email, + created_date=saved_user.created_date + ) + + def validate_user_request(self, request: CreateUserRequest) -> None: + if not request.name or not request.name.strip(): + raise ValueError("Name is required") + + if not self.is_valid_email(request.email): + raise ValueError("Valid email is required") + + def is_valid_email(self, email: str) -> bool: + return '@' in email and '.' in email + +# Data Access Layer +from abc import ABC, abstractmethod +from sqlalchemy import create_engine, Column, Integer, String, DateTime +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, Session + +Base = declarative_base() + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False) + email = Column(String, unique=True, nullable=False, index=True) + created_date = Column(DateTime, default=datetime.utcnow) + +class UserRepository(ABC): + @abstractmethod + def save(self, user: User) -> User: + pass + + @abstractmethod + def find_by_email(self, email: str) -> Optional[User]: + pass + + @abstractmethod + def find_by_created_date_between(self, start: datetime, end: datetime) -> list[User]: + pass + +class SQLAlchemyUserRepository(UserRepository): + def __init__(self, db: Session): + self.db = db + + def save(self, user: User) -> User: + self.db.add(user) + self.db.commit() + self.db.refresh(user) + return user + + def find_by_email(self, email: str) -> Optional[User]: + return self.db.query(User).filter(User.email == email).first() + + def find_by_created_date_between(self, start: datetime, end: datetime) -> list[User]: + return self.db.query(User).filter( + User.created_date.between(start, end) + ).all() + +# Email Service +class EmailService(ABC): + @abstractmethod + def send_welcome_email(self, email: str) -> None: + pass + +class SMTPEmailService(EmailService): + def __init__(self, smtp_server: str, smtp_port: int, username: str, password: str): + self.smtp_server = smtp_server + self.smtp_port = smtp_port + self.username = username + self.password = password + + def send_welcome_email(self, email: str) -> None: + # Implementation for sending email + print(f"Sending welcome email to {email}") + +# Dependency injection +def get_user_service(db: Session = Depends(get_db)) -> UserService: + user_repository = SQLAlchemyUserRepository(db) + email_service = SMTPEmailService("smtp.gmail.com", 587, "user", "password") + return UserService(user_repository, email_service) + +# API endpoints +@app.post("/api/users", response_model=UserDto) +def create_user(request: CreateUserRequest, user_service: UserService = Depends(get_user_service)): + try: + return user_service.create_user(request) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + raise HTTPException(status_code=500, detail="Internal server error") + +@app.get("/api/users/{user_id}", response_model=UserDto) +def get_user(user_id: int, user_service: UserService = Depends(get_user_service)): + user = user_service.get_user(user_id) + if not user: + raise HTTPException(status_code=404, detail="User not found") + return user +``` + +**Node.js (Express + TypeScript + Prisma):** +```typescript +import express, { Request, Response, NextFunction } from 'express'; +import { PrismaClient } from '@prisma/client'; +import { z } from 'zod'; +import nodemailer from 'nodemailer'; + +const app = express(); +const prisma = new PrismaClient(); + +// Presentation Layer +interface CreateUserRequest { + name: string; + email: string; +} + +interface UserDto { + id: number; + name: string; + email: string; + createdDate: Date; +} + +// Business Layer +class UserService { + constructor( + private userRepository: UserRepository, + private emailService: EmailService + ) {} + + async createUser(request: CreateUserRequest): Promise { + // Бизнес-логика валидации + this.validateUserRequest(request); + + // Создание пользователя + const user = { + name: request.name, + email: request.email, + createdDate: new Date() + }; + + // Сохранение через Data Access Layer + const savedUser = await this.userRepository.save(user); + + // Бизнес-логика уведомления + await this.emailService.sendWelcomeEmail(savedUser.email); + + return { + id: savedUser.id, + name: savedUser.name, + email: savedUser.email, + createdDate: savedUser.createdDate + }; + } + + private validateUserRequest(request: CreateUserRequest): void { + if (!request.name || !request.name.trim()) { + throw new Error('Name is required'); + } + + if (!this.isValidEmail(request.email)) { + throw new Error('Valid email is required'); + } + } + + private isValidEmail(email: string): boolean { + return email.includes('@') && email.includes('.'); + } +} + +// Data Access Layer +interface UserRepository { + save(user: Omit): Promise; + findByEmail(email: string): Promise; + findByCreatedDateBetween(start: Date, end: Date): Promise; +} + +class PrismaUserRepository implements UserRepository { + async save(user: Omit): Promise { + const savedUser = await prisma.user.create({ + data: { + name: user.name, + email: user.email, + createdDate: user.createdDate + } + }); + + return { + id: savedUser.id, + name: savedUser.name, + email: savedUser.email, + createdDate: savedUser.createdDate + }; + } + + async findByEmail(email: string): Promise { + const user = await prisma.user.findUnique({ + where: { email } + }); + + if (!user) return null; + + return { + id: user.id, + name: user.name, + email: user.email, + createdDate: user.createdDate + }; + } + + async findByCreatedDateBetween(start: Date, end: Date): Promise { + const users = await prisma.user.findMany({ + where: { + createdDate: { + gte: start, + lte: end + } + } + }); + + return users.map(user => ({ + id: user.id, + name: user.name, + email: user.email, + createdDate: user.createdDate + })); + } +} + +// Email Service +interface EmailService { + sendWelcomeEmail(email: string): Promise; +} + +class SMTPEmailService implements EmailService { + private transporter = nodemailer.createTransporter({ + host: 'smtp.gmail.com', + port: 587, + secure: false, + auth: { + user: 'user@gmail.com', + pass: 'password' + } + }); + + async sendWelcomeEmail(email: string): Promise { + const mailOptions = { + from: 'user@gmail.com', + to: email, + subject: 'Welcome!', + text: 'Welcome to our service!' + }; + + await this.transporter.sendMail(mailOptions); + } +} + +// Dependency injection +function getUserService(): UserService { + const userRepository = new PrismaUserRepository(); + const emailService = new SMTPEmailService(); + return new UserService(userRepository, emailService); +} + +// Validation schemas +const createUserSchema = z.object({ + name: z.string().min(1, 'Name is required'), + email: z.string().email('Valid email is required') +}); + +// API endpoints +app.post('/api/users', async (req: Request, res: Response, next: NextFunction) => { + try { + const userService = getUserService(); + const validatedData = createUserSchema.parse(req.body); + const user = await userService.createUser(validatedData); + res.json(user); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.get('/api/users/:userId', async (req: Request, res: Response, next: NextFunction) => { + try { + const userService = getUserService(); + const userId = parseInt(req.params.userId); + const user = await userService.getUser(userId); + + if (!user) { + res.status(404).json({ error: 'User not found' }); + return; + } + + res.json(user); + } catch (error) { + next(error); + } +}); + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +app.listen(3000, () => { + console.log('Server running on port 3000'); +}); +``` + +#### 4. Архитектура на основе событий (Event-Driven Architecture) +**Определение:** Компоненты системы взаимодействуют через события, что обеспечивает слабую связанность. + +**Компоненты:** +- **Event Producers** — генерируют события +- **Event Consumers** — обрабатывают события +- **Event Bus/Message Broker** — доставляет события +- **Event Store** — хранит события + +**Преимущества:** +- Слабая связанность +- Масштабируемость +- Отказоустойчивость +- Асинхронная обработка + +**Недостатки:** +- Сложность отладки +- Проблемы с порядком событий +- Сложность тестирования +- Потенциальная потеря событий + +**Технические примеры:** + +**Java (Spring Boot):** +```java +// Event Producer +@Service +public class OrderService { + @Autowired + private OrderRepository orderRepository; + + @Autowired + private ApplicationEventPublisher eventPublisher; + + public Order createOrder(CreateOrderRequest request) { + Order order = new Order(); + order.setUserId(request.getUserId()); + order.setAmount(request.getAmount()); + order.setStatus(OrderStatus.CREATED); + + Order savedOrder = orderRepository.save(order); + + // Публикация события + OrderCreatedEvent event = new OrderCreatedEvent(savedOrder.getId(), + savedOrder.getUserId(), savedOrder.getAmount()); + eventPublisher.publishEvent(event); + + return savedOrder; + } +} + +// Event Consumer +@Component +public class OrderEventHandler { + @Autowired + private InventoryService inventoryService; + + @Autowired + private NotificationService notificationService; + + @EventListener + public void handleOrderCreated(OrderCreatedEvent event) { + // Резервирование товара + inventoryService.reserveItems(event.getOrderId()); + + // Уведомление пользователя + notificationService.sendOrderConfirmation(event.getUserId(), event.getOrderId()); + } +} + +// Event Class +public class OrderCreatedEvent { + private final Long orderId; + private final Long userId; + private final BigDecimal amount; + private final LocalDateTime timestamp; + + public OrderCreatedEvent(Long orderId, Long userId, BigDecimal amount) { + this.orderId = orderId; + this.userId = userId; + this.amount = amount; + this.timestamp = LocalDateTime.now(); + } + + // геттеры +} +``` + +**Rust (Actix Web + Tokio):** +```rust +use serde::{Deserialize, Serialize}; +use tokio::sync::mpsc; +use std::sync::Arc; +use tokio::sync::Mutex; + +// Event types +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OrderCreatedEvent { + pub order_id: i32, + pub user_id: i32, + pub amount: f64, + pub timestamp: chrono::DateTime, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum DomainEvent { + OrderCreated(OrderCreatedEvent), + OrderPaid(OrderPaidEvent), + OrderCancelled(OrderCancelledEvent), +} + +// Event Bus +pub struct EventBus { + subscribers: Arc>>>, +} + +impl EventBus { + pub fn new() -> Self { + Self { + subscribers: Arc::new(Mutex::new(Vec::new())), + } + } + + pub async fn subscribe(&self) -> mpsc::UnboundedReceiver { + let (tx, rx) = mpsc::unbounded_channel(); + let mut subscribers = self.subscribers.lock().await; + subscribers.push(tx); + rx + } + + pub async fn publish(&self, event: DomainEvent) { + let subscribers = self.subscribers.lock().await; + for subscriber in subscribers.iter() { + let _ = subscriber.send(event.clone()); + } + } +} + +// Event Producer +pub struct OrderService { + order_repository: Box, + event_bus: Arc, +} + +impl OrderService { + pub fn new(order_repository: Box, event_bus: Arc) -> Self { + Self { + order_repository, + event_bus, + } + } + + pub async fn create_order(&self, request: CreateOrderRequest) -> Result { + let order = Order { + id: 0, // Will be set by database + user_id: request.user_id, + amount: request.amount, + status: OrderStatus::Created, + created_date: chrono::Utc::now(), + }; + + let saved_order = self.order_repository.save(order).await?; + + // Публикация события + let event = DomainEvent::OrderCreated(OrderCreatedEvent { + order_id: saved_order.id, + user_id: saved_order.user_id, + amount: saved_order.amount, + timestamp: saved_order.created_date, + }); + + self.event_bus.publish(event).await; + + Ok(saved_order) + } +} + +// Event Consumer +pub struct OrderEventHandler { + inventory_service: Arc, + notification_service: Arc, +} + +impl OrderEventHandler { + pub fn new( + inventory_service: Arc, + notification_service: Arc, + ) -> Self { + Self { + inventory_service, + notification_service, + } + } + + pub async fn start_listening(&self, mut event_receiver: mpsc::UnboundedReceiver) { + while let Some(event) = event_receiver.recv().await { + match event { + DomainEvent::OrderCreated(order_event) => { + self.handle_order_created(order_event).await; + } + DomainEvent::OrderPaid(payment_event) => { + self.handle_order_paid(payment_event).await; + } + DomainEvent::OrderCancelled(cancel_event) => { + self.handle_order_cancelled(cancel_event).await; + } + } + } + } + + async fn handle_order_created(&self, event: OrderCreatedEvent) { + // Резервирование товара + if let Err(e) = self.inventory_service.reserve_items(event.order_id).await { + eprintln!("Failed to reserve items for order {}: {}", event.order_id, e); + } + + // Уведомление пользователя + if let Err(e) = self.notification_service.send_order_confirmation(event.user_id, event.order_id).await { + eprintln!("Failed to send notification for order {}: {}", event.order_id, e); + } + } + + async fn handle_order_paid(&self, event: OrderPaidEvent) { + // Логика обработки оплаты + println!("Order {} paid: {}", event.order_id, event.amount); + } + + async fn handle_order_cancelled(&self, event: OrderCancelledEvent) { + // Логика обработки отмены + println!("Order {} cancelled: {}", event.order_id, event.reason); + } +} + +// Event Store +pub struct EventStore { + events: Arc>>, +} + +#[derive(Debug, Clone)] +pub struct StoredEvent { + pub id: i32, + pub event_type: String, + pub event_data: String, + pub timestamp: chrono::DateTime, +} + +impl EventStore { + pub fn new() -> Self { + Self { + events: Arc::new(Mutex::new(Vec::new())), + } + } + + pub async fn store_event(&self, event: DomainEvent) -> Result<(), EventStoreError> { + let stored_event = StoredEvent { + id: self.get_next_id().await, + event_type: self.get_event_type(&event), + event_data: serde_json::to_string(&event) + .map_err(|e| EventStoreError::SerializationError(e.to_string()))?, + timestamp: chrono::Utc::now(), + }; + + let mut events = self.events.lock().await; + events.push(stored_event); + + Ok(()) + } + + async fn get_next_id(&self) -> i32 { + let events = self.events.lock().await; + events.len() as i32 + 1 + } + + fn get_event_type(&self, event: &DomainEvent) -> String { + match event { + DomainEvent::OrderCreated(_) => "OrderCreated".to_string(), + DomainEvent::OrderPaid(_) => "OrderPaid".to_string(), + DomainEvent::OrderCancelled(_) => "OrderCancelled".to_string(), + } + } +} +``` + +**Python (FastAPI + Celery):** +```python +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel +from datetime import datetime +from typing import Optional +import asyncio +from celery import Celery +from dataclasses import dataclass +import json + +app = FastAPI() + +# Celery configuration +celery_app = Celery('order_service', broker='redis://localhost:6379/0') + +# Event types +@dataclass +class OrderCreatedEvent: + order_id: int + user_id: int + amount: float + timestamp: datetime + +@dataclass +class OrderPaidEvent: + order_id: int + user_id: int + amount: float + payment_id: str + timestamp: datetime + +@dataclass +class OrderCancelledEvent: + order_id: int + user_id: int + reason: str + timestamp: datetime + +# Event Bus +class EventBus: + def __init__(self): + self.subscribers = {} + + def subscribe(self, event_type: str, handler): + if event_type not in self.subscribers: + self.subscribers[event_type] = [] + self.subscribers[event_type].append(handler) + + async def publish(self, event): + event_type = type(event).__name__ + if event_type in self.subscribers: + for handler in self.subscribers[event_type]: + try: + await handler(event) + except Exception as e: + print(f"Error handling event {event_type}: {e}") + +# Global event bus +event_bus = EventBus() + +# Event Producer +class OrderService: + def __init__(self, order_repository, event_bus): + self.order_repository = order_repository + self.event_bus = event_bus + + async def create_order(self, request: CreateOrderRequest) -> Order: + order = Order( + user_id=request.user_id, + amount=request.amount, + status=OrderStatus.CREATED, + created_date=datetime.utcnow() + ) + + saved_order = await self.order_repository.save(order) + + # Публикация события + event = OrderCreatedEvent( + order_id=saved_order.id, + user_id=saved_order.user_id, + amount=saved_order.amount, + timestamp=saved_order.created_date + ) + + await self.event_bus.publish(event) + + return saved_order + +# Event Consumers +class OrderEventHandler: + def __init__(self, inventory_service, notification_service): + self.inventory_service = inventory_service + self.notification_service = notification_service + + async def handle_order_created(self, event: OrderCreatedEvent): + # Резервирование товара + try: + await self.inventory_service.reserve_items(event.order_id) + except Exception as e: + print(f"Failed to reserve items for order {event.order_id}: {e}") + + # Уведомление пользователя + try: + await self.notification_service.send_order_confirmation(event.user_id, event.order_id) + except Exception as e: + print(f"Failed to send notification for order {event.order_id}: {e}") + + async def handle_order_paid(self, event: OrderPaidEvent): + print(f"Order {event.order_id} paid: {event.amount}") + + async def handle_order_cancelled(self, event: OrderCancelledEvent): + print(f"Order {event.order_id} cancelled: {event.reason}") + +# Celery tasks for async processing +@celery_app.task +def process_order_created_async(event_data: dict): + """Process order created event asynchronously""" + event = OrderCreatedEvent(**event_data) + + # Heavy processing that doesn't need to be synchronous + print(f"Processing order {event.order_id} asynchronously") + + # Send to external systems + # Update analytics + # Generate reports + pass + +@celery_app.task +def send_email_notification_async(user_id: int, order_id: int): + """Send email notification asynchronously""" + print(f"Sending email notification to user {user_id} for order {order_id}") + # Actual email sending logic here + pass + +# Event Store +class EventStore: + def __init__(self): + self.events = [] + + async def store_event(self, event): + stored_event = { + 'id': len(self.events) + 1, + 'event_type': type(event).__name__, + 'event_data': event.__dict__, + 'timestamp': datetime.utcnow() + } + self.events.append(stored_event) + return stored_event + + async def get_events_by_type(self, event_type: str): + return [event for event in self.events if event['event_type'] == event_type] + + async def get_events_by_order_id(self, order_id: int): + return [event for event in self.events + if event['event_data'].get('order_id') == order_id] + +# Event Replay functionality +class EventReplayer: + def __init__(self, event_store, event_bus): + self.event_store = event_store + self.event_bus = event_bus + + async def replay_events_for_order(self, order_id: int): + """Replay all events for a specific order""" + events = await self.event_store.get_events_by_order_id(order_id) + + for event_data in events: + event_type = event_data['event_type'] + event_class = globals()[event_type] + event = event_class(**event_data['event_data']) + + await self.event_bus.publish(event) + + async def replay_events_by_type(self, event_type: str): + """Replay all events of a specific type""" + events = await self.event_store.get_events_by_type(event_type) + + for event_data in events: + event_class = globals()[event_type] + event = event_class(**event_data['event_data']) + + await self.event_bus.publish(event) + +# Setup event handlers +async def setup_event_handlers(): + inventory_service = InventoryService() + notification_service = NotificationService() + event_handler = OrderEventHandler(inventory_service, notification_service) + + # Subscribe to events + event_bus.subscribe('OrderCreatedEvent', event_handler.handle_order_created) + event_bus.subscribe('OrderPaidEvent', event_handler.handle_order_paid) + event_bus.subscribe('OrderCancelledEvent', event_handler.handle_order_cancelled) + +# API endpoints +@app.post("/api/orders") +async def create_order(request: CreateOrderRequest): + order_repository = OrderRepository() + order_service = OrderService(order_repository, event_bus) + + try: + order = await order_service.create_order(request) + return order + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.post("/api/events/replay/{order_id}") +async def replay_order_events(order_id: int): + event_store = EventStore() + replayer = EventReplayer(event_store, event_bus) + + try: + await replayer.replay_events_for_order(order_id) + return {"message": f"Events replayed for order {order_id}"} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +# Startup event +@app.on_event("startup") +async def startup_event(): + await setup_event_handlers() +``` + +**Node.js (Express + TypeScript + EventEmitter):** +```typescript +import express, { Request, Response, NextFunction } from 'express'; +import { EventEmitter } from 'events'; +import { PrismaClient } from '@prisma/client'; +import { z } from 'zod'; +import Redis from 'ioredis'; + +const app = express(); +const prisma = new PrismaClient(); +const redis = new Redis(); + +// Event types +interface OrderCreatedEvent { + orderId: number; + userId: number; + amount: number; + timestamp: Date; +} + +interface OrderPaidEvent { + orderId: number; + userId: number; + amount: number; + paymentId: string; + timestamp: Date; +} + +interface OrderCancelledEvent { + orderId: number; + userId: number; + reason: string; + timestamp: Date; +} + +// Event Bus +class EventBus extends EventEmitter { + private static instance: EventBus; + + private constructor() { + super(); + this.setMaxListeners(100); + } + + static getInstance(): EventBus { + if (!EventBus.instance) { + EventBus.instance = new EventBus(); + } + return EventBus.instance; + } + + async publish(event: string, data: any): Promise { + // Store event in Redis for persistence + await redis.lpush('events', JSON.stringify({ + type: event, + data, + timestamp: new Date() + })); + + // Emit event to local listeners + this.emit(event, data); + } + + subscribe(event: string, handler: (data: any) => Promise): void { + this.on(event, async (data) => { + try { + await handler(data); + } catch (error) { + console.error(`Error handling event ${event}:`, error); + } + }); + } +} + +// Event Producer +class OrderService { + constructor( + private orderRepository: OrderRepository, + private eventBus: EventBus + ) {} + + async createOrder(request: CreateOrderRequest): Promise { + const order = { + userId: request.userId, + amount: request.amount, + status: 'CREATED', + createdDate: new Date() + }; + + const savedOrder = await this.orderRepository.save(order); + + // Публикация события + const event: OrderCreatedEvent = { + orderId: savedOrder.id, + userId: savedOrder.userId, + amount: savedOrder.amount, + timestamp: savedOrder.createdDate + }; + + await this.eventBus.publish('order.created', event); + + return savedOrder; + } + + async payOrder(orderId: number, paymentId: string): Promise { + const order = await this.orderRepository.findById(orderId); + if (!order) { + throw new Error('Order not found'); + } + + await this.orderRepository.updateStatus(orderId, 'PAID'); + + const event: OrderPaidEvent = { + orderId: order.id, + userId: order.userId, + amount: order.amount, + paymentId, + timestamp: new Date() + }; + + await this.eventBus.publish('order.paid', event); + } + + async cancelOrder(orderId: number, reason: string): Promise { + const order = await this.orderRepository.findById(orderId); + if (!order) { + throw new Error('Order not found'); + } + + await this.orderRepository.updateStatus(orderId, 'CANCELLED'); + + const event: OrderCancelledEvent = { + orderId: order.id, + userId: order.userId, + reason, + timestamp: new Date() + }; + + await this.eventBus.publish('order.cancelled', event); + } +} + +// Event Consumers +class OrderEventHandler { + constructor( + private inventoryService: InventoryService, + private notificationService: NotificationService, + private analyticsService: AnalyticsService + ) {} + + async handleOrderCreated(event: OrderCreatedEvent): Promise { + console.log(`Processing order created: ${event.orderId}`); + + // Резервирование товара + try { + await this.inventoryService.reserveItems(event.orderId); + } catch (error) { + console.error(`Failed to reserve items for order ${event.orderId}:`, error); + } + + // Уведомление пользователя + try { + await this.notificationService.sendOrderConfirmation(event.userId, event.orderId); + } catch (error) { + console.error(`Failed to send notification for order ${event.orderId}:`, error); + } + + // Аналитика + try { + await this.analyticsService.trackOrderCreated(event); + } catch (error) { + console.error(`Failed to track order ${event.orderId}:`, error); + } + } + + async handleOrderPaid(event: OrderPaidEvent): Promise { + console.log(`Order ${event.orderId} paid: ${event.amount}`); + + // Обновление инвентаря + await this.inventoryService.confirmReservation(event.orderId); + + // Отправка чека + await this.notificationService.sendReceipt(event.userId, event.orderId); + + // Аналитика + await this.analyticsService.trackOrderPaid(event); + } + + async handleOrderCancelled(event: OrderCancelledEvent): Promise { + console.log(`Order ${event.orderId} cancelled: ${event.reason}`); + + // Освобождение резерва + await this.inventoryService.releaseReservation(event.orderId); + + // Уведомление об отмене + await this.notificationService.sendCancellationNotice(event.userId, event.orderId); + + // Аналитика + await this.analyticsService.trackOrderCancelled(event); + } +} + +// Event Store +class EventStore { + async storeEvent(event: any): Promise { + await redis.lpush('event_store', JSON.stringify({ + ...event, + storedAt: new Date() + })); + } + + async getEventsByOrderId(orderId: number): Promise { + const events = await redis.lrange('event_store', 0, -1); + return events + .map(event => JSON.parse(event)) + .filter(event => event.data.orderId === orderId); + } + + async getEventsByType(eventType: string): Promise { + const events = await redis.lrange('event_store', 0, -1); + return events + .map(event => JSON.parse(event)) + .filter(event => event.type === eventType); + } +} + +// Event Replay functionality +class EventReplayer { + constructor( + private eventStore: EventStore, + private eventBus: EventBus + ) {} + + async replayEventsForOrder(orderId: number): Promise { + const events = await this.eventStore.getEventsByOrderId(orderId); + + for (const event of events) { + await this.eventBus.publish(event.type, event.data); + } + } + + async replayEventsByType(eventType: string): Promise { + const events = await this.eventStore.getEventsByType(eventType); + + for (const event of events) { + await this.eventBus.publish(event.type, event.data); + } + } +} + +// Services implementations +class InventoryService { + async reserveItems(orderId: number): Promise { + console.log(`Reserving items for order ${orderId}`); + // Implementation for inventory reservation + } + + async confirmReservation(orderId: number): Promise { + console.log(`Confirming reservation for order ${orderId}`); + // Implementation for reservation confirmation + } + + async releaseReservation(orderId: number): Promise { + console.log(`Releasing reservation for order ${orderId}`); + // Implementation for reservation release + } +} + +class NotificationService { + async sendOrderConfirmation(userId: number, orderId: number): Promise { + console.log(`Sending order confirmation to user ${userId} for order ${orderId}`); + // Implementation for sending confirmation email/SMS + } + + async sendReceipt(userId: number, orderId: number): Promise { + console.log(`Sending receipt to user ${userId} for order ${orderId}`); + // Implementation for sending receipt + } + + async sendCancellationNotice(userId: number, orderId: number): Promise { + console.log(`Sending cancellation notice to user ${userId} for order ${orderId}`); + // Implementation for sending cancellation notice + } +} + +class AnalyticsService { + async trackOrderCreated(event: OrderCreatedEvent): Promise { + console.log(`Tracking order created: ${event.orderId}`); + // Implementation for analytics tracking + } + + async trackOrderPaid(event: OrderPaidEvent): Promise { + console.log(`Tracking order paid: ${event.orderId}`); + // Implementation for analytics tracking + } + + async trackOrderCancelled(event: OrderCancelledEvent): Promise { + console.log(`Tracking order cancelled: ${event.orderId}`); + // Implementation for analytics tracking + } +} + +// Repository +interface OrderRepository { + save(order: Omit): Promise; + findById(id: number): Promise; + updateStatus(id: number, status: string): Promise; +} + +class PrismaOrderRepository implements OrderRepository { + async save(order: Omit): Promise { + return await prisma.order.create({ data: order }); + } + + async findById(id: number): Promise { + return await prisma.order.findUnique({ where: { id } }); + } + + async updateStatus(id: number, status: string): Promise { + await prisma.order.update({ + where: { id }, + data: { status } + }); + } +} + +// Setup event handlers +function setupEventHandlers(): void { + const eventBus = EventBus.getInstance(); + const inventoryService = new InventoryService(); + const notificationService = new NotificationService(); + const analyticsService = new AnalyticsService(); + const eventHandler = new OrderEventHandler( + inventoryService, + notificationService, + analyticsService + ); + + // Subscribe to events + eventBus.subscribe('order.created', eventHandler.handleOrderCreated.bind(eventHandler)); + eventBus.subscribe('order.paid', eventHandler.handleOrderPaid.bind(eventHandler)); + eventBus.subscribe('order.cancelled', eventHandler.handleOrderCancelled.bind(eventHandler)); +} + +// Validation schemas +const createOrderSchema = z.object({ + userId: z.number().positive(), + amount: z.number().positive() +}); + +// API endpoints +app.post('/api/orders', async (req: Request, res: Response, next: NextFunction) => { + try { + const eventBus = EventBus.getInstance(); + const orderRepository = new PrismaOrderRepository(); + const orderService = new OrderService(orderRepository, eventBus); + + const validatedData = createOrderSchema.parse(req.body); + const order = await orderService.createOrder(validatedData); + + res.json(order); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.post('/api/orders/:orderId/pay', async (req: Request, res: Response, next: NextFunction) => { + try { + const eventBus = EventBus.getInstance(); + const orderRepository = new PrismaOrderRepository(); + const orderService = new OrderService(orderRepository, eventBus); + + const orderId = parseInt(req.params.orderId); + const paymentId = req.body.paymentId; + + await orderService.payOrder(orderId, paymentId); + + res.json({ message: 'Order paid successfully' }); + } catch (error) { + next(error); + } +}); + +app.post('/api/orders/:orderId/cancel', async (req: Request, res: Response, next: NextFunction) => { + try { + const eventBus = EventBus.getInstance(); + const orderRepository = new PrismaOrderRepository(); + const orderService = new OrderService(orderRepository, eventBus); + + const orderId = parseInt(req.params.orderId); + const reason = req.body.reason; + + await orderService.cancelOrder(orderId, reason); + + res.json({ message: 'Order cancelled successfully' }); + } catch (error) { + next(error); + } +}); + +app.post('/api/events/replay/:orderId', async (req: Request, res: Response, next: NextFunction) => { + try { + const eventStore = new EventStore(); + const eventBus = EventBus.getInstance(); + const replayer = new EventReplayer(eventStore, eventBus); + + const orderId = parseInt(req.params.orderId); + await replayer.replayEventsForOrder(orderId); + + res.json({ message: `Events replayed for order ${orderId}` }); + } catch (error) { + next(error); + } +}); + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +// Initialize event handlers +setupEventHandlers(); + +app.listen(3000, () => { + console.log('Server running on port 3000'); +}); +``` + +#### 5. Архитектура на основе микросервисов с CQRS (Command Query Responsibility Segregation) +**Определение:** Разделение операций чтения и записи на отдельные модели. + +**Компоненты:** +- **Command Side** — обработка команд (запись) +- **Query Side** — обработка запросов (чтение) +- **Event Store** — хранилище событий +- **Projection** — представления для чтения + +**Преимущества:** +- Оптимизация для чтения и записи +- Масштабируемость +- Независимость моделей +- Аудит изменений + +**Недостатки:** +- Сложность реализации +- Потенциальная несогласованность +- Overhead синхронизации +- Сложность отладки + +**Технические примеры:** + +**Java (Spring Boot):** +```java +// Command Side +@Service +public class OrderCommandService { + @Autowired + private OrderRepository orderRepository; + + @Autowired + private EventStore eventStore; + + public void createOrder(CreateOrderCommand command) { + // Валидация команды + validateCommand(command); + + // Создание агрегата + OrderAggregate order = new OrderAggregate(); + order.createOrder(command.getUserId(), command.getItems()); + + // Сохранение событий + eventStore.saveEvents(order.getUncommittedEvents()); + + // Публикация событий + publishEvents(order.getUncommittedEvents()); + } +} + +// Query Side +@Service +public class OrderQueryService { + @Autowired + private OrderReadRepository orderReadRepository; + + public OrderView getOrder(Long orderId) { + return orderReadRepository.findById(orderId); + } + + public List getUserOrders(Long userId) { + return orderReadRepository.findByUserId(userId); + } +} + +// Read Model +@Entity +@Table(name = "order_views") +public class OrderView { + @Id + private Long orderId; + private Long userId; + private String status; + private BigDecimal totalAmount; + private LocalDateTime createdDate; + + // геттеры и сеттеры +} + +// Event Handler для синхронизации +@Component +public class OrderProjectionHandler { + @Autowired + private OrderReadRepository orderReadRepository; + + @EventHandler + public void handle(OrderCreatedEvent event) { + OrderView view = new OrderView(); + view.setOrderId(event.getOrderId()); + view.setUserId(event.getUserId()); + view.setStatus("CREATED"); + view.setTotalAmount(event.getAmount()); + view.setCreatedDate(event.getTimestamp()); + + orderReadRepository.save(view); + } +} +``` + +**Rust (Actix Web + Diesel):** +```rust +use serde::{Deserialize, Serialize}; +use diesel::prelude::*; +use std::sync::Arc; +use tokio::sync::Mutex; + +// Command Side +#[derive(Debug, Deserialize)] +pub struct CreateOrderCommand { + pub user_id: i32, + pub items: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct OrderItemCommand { + pub product_id: i32, + pub quantity: i32, + pub price: f64, +} + +pub struct OrderCommandService { + event_store: Arc, + command_repository: Arc, +} + +impl OrderCommandService { + pub fn new(event_store: Arc, command_repository: Arc) -> Self { + Self { + event_store, + command_repository, + } + } + + pub async fn create_order(&self, command: CreateOrderCommand) -> Result<(), OrderError> { + // Валидация команды + self.validate_command(&command)?; + + // Создание агрегата + let mut order_aggregate = OrderAggregate::new(); + order_aggregate.create_order(command.user_id, command.items); + + // Сохранение событий + let events = order_aggregate.get_uncommitted_events(); + self.event_store.save_events(&events).await?; + + // Публикация событий + self.publish_events(&events).await?; + + Ok(()) + } + + fn validate_command(&self, command: &CreateOrderCommand) -> Result<(), OrderError> { + if command.user_id <= 0 { + return Err(OrderError::ValidationError("Invalid user ID".to_string())); + } + + if command.items.is_empty() { + return Err(OrderError::ValidationError("Order must have items".to_string())); + } + + Ok(()) + } + + async fn publish_events(&self, events: &[DomainEvent]) -> Result<(), OrderError> { + for event in events { + self.event_store.publish_event(event).await?; + } + Ok(()) + } +} + +// Query Side +pub struct OrderQueryService { + read_repository: Arc, +} + +impl OrderQueryService { + pub fn new(read_repository: Arc) -> Self { + Self { read_repository } + } + + pub async fn get_order(&self, order_id: i32) -> Result, OrderError> { + self.read_repository.find_by_id(order_id).await + } + + pub async fn get_user_orders(&self, user_id: i32) -> Result, OrderError> { + self.read_repository.find_by_user_id(user_id).await + } + + pub async fn get_orders_by_status(&self, status: OrderStatus) -> Result, OrderError> { + self.read_repository.find_by_status(status).await + } +} + +// Read Model +#[derive(Debug, Clone, Serialize, Deserialize, Queryable)] +pub struct OrderView { + pub order_id: i32, + pub user_id: i32, + pub status: String, + pub total_amount: f64, + pub created_date: chrono::DateTime, + pub updated_date: chrono::DateTime, +} + +// Read Repository +#[async_trait::async_trait] +pub trait ReadRepository: Send + Sync { + async fn find_by_id(&self, order_id: i32) -> Result, OrderError>; + async fn find_by_user_id(&self, user_id: i32) -> Result, OrderError>; + async fn find_by_status(&self, status: OrderStatus) -> Result, OrderError>; + async fn save(&self, view: OrderView) -> Result; + async fn update(&self, view: OrderView) -> Result; +} + +pub struct DieselReadRepository { + connection: Arc>, +} + +#[async_trait::async_trait] +impl ReadRepository for DieselReadRepository { + async fn find_by_id(&self, order_id: i32) -> Result, OrderError> { + use crate::schema::order_views::dsl::*; + + let connection = self.connection.lock().await; + order_views + .filter(order_id.eq(order_id)) + .first(&*connection) + .optional() + .map_err(|e| OrderError::DatabaseError(e.to_string())) + } + + async fn find_by_user_id(&self, user_id: i32) -> Result, OrderError> { + use crate::schema::order_views::dsl::*; + + let connection = self.connection.lock().await; + order_views + .filter(user_id.eq(user_id)) + .load(&*connection) + .map_err(|e| OrderError::DatabaseError(e.to_string())) + } + + async fn find_by_status(&self, status: OrderStatus) -> Result, OrderError> { + use crate::schema::order_views::dsl::*; + + let connection = self.connection.lock().await; + order_views + .filter(status.eq(status.to_string())) + .load(&*connection) + .map_err(|e| OrderError::DatabaseError(e.to_string())) + } + + async fn save(&self, view: OrderView) -> Result { + use crate::schema::order_views::dsl::*; + + let connection = self.connection.lock().await; + diesel::insert_into(order_views) + .values(&view) + .get_result(&*connection) + .map_err(|e| OrderError::DatabaseError(e.to_string())) + } + + async fn update(&self, view: OrderView) -> Result { + use crate::schema::order_views::dsl::*; + + let connection = self.connection.lock().await; + diesel::update(order_views.filter(order_id.eq(view.order_id))) + .set(&view) + .get_result(&*connection) + .map_err(|e| OrderError::DatabaseError(e.to_string())) + } +} + +// Event Handler для синхронизации +pub struct OrderProjectionHandler { + read_repository: Arc, +} + +impl OrderProjectionHandler { + pub fn new(read_repository: Arc) -> Self { + Self { read_repository } + } + + pub async fn handle_order_created(&self, event: OrderCreatedEvent) -> Result<(), OrderError> { + let view = OrderView { + order_id: event.order_id, + user_id: event.user_id, + status: "CREATED".to_string(), + total_amount: event.amount, + created_date: event.timestamp, + updated_date: chrono::Utc::now(), + }; + + self.read_repository.save(view).await?; + Ok(()) + } + + pub async fn handle_order_paid(&self, event: OrderPaidEvent) -> Result<(), OrderError> { + if let Some(mut view) = self.read_repository.find_by_id(event.order_id).await? { + view.status = "PAID".to_string(); + view.updated_date = chrono::Utc::now(); + self.read_repository.update(view).await?; + } + Ok(()) + } + + pub async fn handle_order_cancelled(&self, event: OrderCancelledEvent) -> Result<(), OrderError> { + if let Some(mut view) = self.read_repository.find_by_id(event.order_id).await? { + view.status = "CANCELLED".to_string(); + view.updated_date = chrono::Utc::now(); + self.read_repository.update(view).await?; + } + Ok(()) + } +} + +// Event Store +pub struct EventStore { + events: Arc>>, +} + +impl EventStore { + pub fn new() -> Self { + Self { + events: Arc::new(Mutex::new(Vec::new())), + } + } + + pub async fn save_events(&self, events: &[DomainEvent]) -> Result<(), OrderError> { + let mut stored_events = self.events.lock().await; + + for event in events { + let stored_event = StoredEvent { + id: stored_events.len() as i32 + 1, + event_type: self.get_event_type(event), + event_data: serde_json::to_string(event) + .map_err(|e| OrderError::SerializationError(e.to_string()))?, + timestamp: chrono::Utc::now(), + }; + + stored_events.push(stored_event); + } + + Ok(()) + } + + pub async fn publish_event(&self, event: &DomainEvent) -> Result<(), OrderError> { + // Здесь можно добавить логику публикации в message broker + println!("Publishing event: {:?}", event); + Ok(()) + } + + fn get_event_type(&self, event: &DomainEvent) -> String { + match event { + DomainEvent::OrderCreated(_) => "OrderCreated".to_string(), + DomainEvent::OrderPaid(_) => "OrderPaid".to_string(), + DomainEvent::OrderCancelled(_) => "OrderCancelled".to_string(), + } + } +} +``` + +**Python (FastAPI + SQLAlchemy):** +```python +from fastapi import FastAPI, HTTPException, Depends +from pydantic import BaseModel +from datetime import datetime +from typing import List, Optional +from abc import ABC, abstractmethod +from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, Session +import asyncio + +app = FastAPI() +Base = declarative_base() + +# Command Side +class CreateOrderCommand(BaseModel): + user_id: int + items: List['OrderItemCommand'] + +class OrderItemCommand(BaseModel): + product_id: int + quantity: int + price: float + +class OrderCommandService: + def __init__(self, event_store: 'EventStore', command_repository: 'CommandRepository'): + self.event_store = event_store + self.command_repository = command_repository + + async def create_order(self, command: CreateOrderCommand) -> None: + # Валидация команды + self.validate_command(command) + + # Создание агрегата + order_aggregate = OrderAggregate() + order_aggregate.create_order(command.user_id, command.items) + + # Сохранение событий + events = order_aggregate.get_uncommitted_events() + await self.event_store.save_events(events) + + # Публикация событий + await self.publish_events(events) + + def validate_command(self, command: CreateOrderCommand) -> None: + if command.user_id <= 0: + raise ValueError("Invalid user ID") + + if not command.items: + raise ValueError("Order must have items") + + for item in command.items: + if item.quantity <= 0: + raise ValueError("Item quantity must be positive") + if item.price <= 0: + raise ValueError("Item price must be positive") + + async def publish_events(self, events: List['DomainEvent']) -> None: + for event in events: + await self.event_store.publish_event(event) + +# Query Side +class OrderQueryService: + def __init__(self, read_repository: 'ReadRepository'): + self.read_repository = read_repository + + async def get_order(self, order_id: int) -> Optional['OrderView']: + return await self.read_repository.find_by_id(order_id) + + async def get_user_orders(self, user_id: int) -> List['OrderView']: + return await self.read_repository.find_by_user_id(user_id) + + async def get_orders_by_status(self, status: str) -> List['OrderView']: + return await self.read_repository.find_by_status(status) + +# Read Model +class OrderView(Base): + __tablename__ = "order_views" + + order_id = Column(Integer, primary_key=True, index=True) + user_id = Column(Integer, nullable=False, index=True) + status = Column(String, nullable=False, index=True) + total_amount = Column(Float, nullable=False) + created_date = Column(DateTime, nullable=False) + updated_date = Column(DateTime, nullable=False, default=datetime.utcnow) + +# Read Repository +class ReadRepository(ABC): + @abstractmethod + async def find_by_id(self, order_id: int) -> Optional[OrderView]: + pass + + @abstractmethod + async def find_by_user_id(self, user_id: int) -> List[OrderView]: + pass + + @abstractmethod + async def find_by_status(self, status: str) -> List[OrderView]: + pass + + @abstractmethod + async def save(self, view: OrderView) -> OrderView: + pass + + @abstractmethod + async def update(self, view: OrderView) -> OrderView: + pass + +class SQLAlchemyReadRepository(ReadRepository): + def __init__(self, db: Session): + self.db = db + + async def find_by_id(self, order_id: int) -> Optional[OrderView]: + return self.db.query(OrderView).filter(OrderView.order_id == order_id).first() + + async def find_by_user_id(self, user_id: int) -> List[OrderView]: + return self.db.query(OrderView).filter(OrderView.user_id == user_id).all() + + async def find_by_status(self, status: str) -> List[OrderView]: + return self.db.query(OrderView).filter(OrderView.status == status).all() + + async def save(self, view: OrderView) -> OrderView: + self.db.add(view) + self.db.commit() + self.db.refresh(view) + return view + + async def update(self, view: OrderView) -> OrderView: + self.db.commit() + self.db.refresh(view) + return view + +# Event Handler для синхронизации +class OrderProjectionHandler: + def __init__(self, read_repository: ReadRepository): + self.read_repository = read_repository + + async def handle_order_created(self, event: 'OrderCreatedEvent') -> None: + view = OrderView( + order_id=event.order_id, + user_id=event.user_id, + status="CREATED", + total_amount=event.amount, + created_date=event.timestamp, + updated_date=datetime.utcnow() + ) + + await self.read_repository.save(view) + + async def handle_order_paid(self, event: 'OrderPaidEvent') -> None: + view = await self.read_repository.find_by_id(event.order_id) + if view: + view.status = "PAID" + view.updated_date = datetime.utcnow() + await self.read_repository.update(view) + + async def handle_order_cancelled(self, event: 'OrderCancelledEvent') -> None: + view = await self.read_repository.find_by_id(event.order_id) + if view: + view.status = "CANCELLED" + view.updated_date = datetime.utcnow() + await self.read_repository.update(view) + +# Event Store +class EventStore: + def __init__(self): + self.events = [] + + async def save_events(self, events: List['DomainEvent']) -> None: + for event in events: + stored_event = { + 'id': len(self.events) + 1, + 'event_type': type(event).__name__, + 'event_data': event.__dict__, + 'timestamp': datetime.utcnow() + } + self.events.append(stored_event) + + async def publish_event(self, event: 'DomainEvent') -> None: + # Здесь можно добавить логику публикации в message broker + print(f"Publishing event: {event}") + + async def get_events_by_order_id(self, order_id: int) -> List[dict]: + return [event for event in self.events + if event['event_data'].get('order_id') == order_id] + +# Domain Events +@dataclass +class OrderCreatedEvent: + order_id: int + user_id: int + amount: float + timestamp: datetime + +@dataclass +class OrderPaidEvent: + order_id: int + user_id: int + amount: float + payment_id: str + timestamp: datetime + +@dataclass +class OrderCancelledEvent: + order_id: int + user_id: int + reason: str + timestamp: datetime + +# Order Aggregate +class OrderAggregate: + def __init__(self): + self.uncommitted_events = [] + + def create_order(self, user_id: int, items: List[OrderItemCommand]) -> None: + total_amount = sum(item.price * item.quantity for item in items) + + event = OrderCreatedEvent( + order_id=0, # Will be set by database + user_id=user_id, + amount=total_amount, + timestamp=datetime.utcnow() + ) + + self.uncommitted_events.append(event) + + def get_uncommitted_events(self) -> List['DomainEvent']: + return self.uncommitted_events.copy() + + def mark_events_as_committed(self) -> None: + self.uncommitted_events.clear() + +# API endpoints +@app.post("/api/orders") +async def create_order(command: CreateOrderCommand): + event_store = EventStore() + command_repository = CommandRepository() # Implementation needed + command_service = OrderCommandService(event_store, command_repository) + + try: + await command_service.create_order(command) + return {"message": "Order created successfully"} + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + raise HTTPException(status_code=500, detail="Internal server error") + +@app.get("/api/orders/{order_id}") +async def get_order(order_id: int): + read_repository = SQLAlchemyReadRepository(get_db()) + query_service = OrderQueryService(read_repository) + + order = await query_service.get_order(order_id) + if not order: + raise HTTPException(status_code=404, detail="Order not found") + + return order + +@app.get("/api/users/{user_id}/orders") +async def get_user_orders(user_id: int): + read_repository = SQLAlchemyReadRepository(get_db()) + query_service = OrderQueryService(read_repository) + + orders = await query_service.get_user_orders(user_id) + return orders + +@app.get("/api/orders/status/{status}") +async def get_orders_by_status(status: str): + read_repository = SQLAlchemyReadRepository(get_db()) + query_service = OrderQueryService(read_repository) + + orders = await query_service.get_orders_by_status(status) + return orders +``` + +**Node.js (Express + TypeScript + Prisma):** +```typescript +import express, { Request, Response, NextFunction } from 'express'; +import { PrismaClient } from '@prisma/client'; +import { z } from 'zod'; +import { EventEmitter } from 'events'; + +const app = express(); +const prisma = new PrismaClient(); + +// Command Side +interface CreateOrderCommand { + userId: number; + items: OrderItemCommand[]; +} + +interface OrderItemCommand { + productId: number; + quantity: number; + price: number; +} + +class OrderCommandService { + constructor( + private eventStore: EventStore, + private commandRepository: CommandRepository + ) {} + + async createOrder(command: CreateOrderCommand): Promise { + // Валидация команды + this.validateCommand(command); + + // Создание агрегата + const orderAggregate = new OrderAggregate(); + orderAggregate.createOrder(command.userId, command.items); + + // Сохранение событий + const events = orderAggregate.getUncommittedEvents(); + await this.eventStore.saveEvents(events); + + // Публикация событий + await this.publishEvents(events); + } + + private validateCommand(command: CreateOrderCommand): void { + if (command.userId <= 0) { + throw new Error('Invalid user ID'); + } + + if (!command.items || command.items.length === 0) { + throw new Error('Order must have items'); + } + + for (const item of command.items) { + if (item.quantity <= 0) { + throw new Error('Item quantity must be positive'); + } + if (item.price <= 0) { + throw new Error('Item price must be positive'); + } + } + } + + private async publishEvents(events: DomainEvent[]): Promise { + for (const event of events) { + await this.eventStore.publishEvent(event); + } + } +} + +// Query Side +class OrderQueryService { + constructor(private readRepository: ReadRepository) {} + + async getOrder(orderId: number): Promise { + return await this.readRepository.findById(orderId); + } + + async getUserOrders(userId: number): Promise { + return await this.readRepository.findByUserId(userId); + } + + async getOrdersByStatus(status: string): Promise { + return await this.readRepository.findByStatus(status); + } +} + +// Read Model +interface OrderView { + orderId: number; + userId: number; + status: string; + totalAmount: number; + createdDate: Date; + updatedDate: Date; +} + +// Read Repository +interface ReadRepository { + findById(orderId: number): Promise; + findByUserId(userId: number): Promise; + findByStatus(status: string): Promise; + save(view: OrderView): Promise; + update(view: OrderView): Promise; +} + +class PrismaReadRepository implements ReadRepository { + async findById(orderId: number): Promise { + const order = await prisma.orderView.findUnique({ + where: { orderId } + }); + + if (!order) return null; + + return { + orderId: order.orderId, + userId: order.userId, + status: order.status, + totalAmount: order.totalAmount, + createdDate: order.createdDate, + updatedDate: order.updatedDate + }; + } + + async findByUserId(userId: number): Promise { + const orders = await prisma.orderView.findMany({ + where: { userId } + }); + + return orders.map(order => ({ + orderId: order.orderId, + userId: order.userId, + status: order.status, + totalAmount: order.totalAmount, + createdDate: order.createdDate, + updatedDate: order.updatedDate + })); + } + + async findByStatus(status: string): Promise { + const orders = await prisma.orderView.findMany({ + where: { status } + }); + + return orders.map(order => ({ + orderId: order.orderId, + userId: order.userId, + status: order.status, + totalAmount: order.totalAmount, + createdDate: order.createdDate, + updatedDate: order.updatedDate + })); + } + + async save(view: OrderView): Promise { + const savedOrder = await prisma.orderView.create({ + data: { + orderId: view.orderId, + userId: view.userId, + status: view.status, + totalAmount: view.totalAmount, + createdDate: view.createdDate, + updatedDate: view.updatedDate + } + }); + + return { + orderId: savedOrder.orderId, + userId: savedOrder.userId, + status: savedOrder.status, + totalAmount: savedOrder.totalAmount, + createdDate: savedOrder.createdDate, + updatedDate: savedOrder.updatedDate + }; + } + + async update(view: OrderView): Promise { + const updatedOrder = await prisma.orderView.update({ + where: { orderId: view.orderId }, + data: { + status: view.status, + totalAmount: view.totalAmount, + updatedDate: view.updatedDate + } + }); + + return { + orderId: updatedOrder.orderId, + userId: updatedOrder.userId, + status: updatedOrder.status, + totalAmount: updatedOrder.totalAmount, + createdDate: updatedOrder.createdDate, + updatedDate: updatedOrder.updatedDate + }; + } +} + +// Event Handler для синхронизации +class OrderProjectionHandler { + constructor(private readRepository: ReadRepository) {} + + async handleOrderCreated(event: OrderCreatedEvent): Promise { + const view: OrderView = { + orderId: event.orderId, + userId: event.userId, + status: 'CREATED', + totalAmount: event.amount, + createdDate: event.timestamp, + updatedDate: new Date() + }; + + await this.readRepository.save(view); + } + + async handleOrderPaid(event: OrderPaidEvent): Promise { + const existingView = await this.readRepository.findById(event.orderId); + if (existingView) { + existingView.status = 'PAID'; + existingView.updatedDate = new Date(); + await this.readRepository.update(existingView); + } + } + + async handleOrderCancelled(event: OrderCancelledEvent): Promise { + const existingView = await this.readRepository.findById(event.orderId); + if (existingView) { + existingView.status = 'CANCELLED'; + existingView.updatedDate = new Date(); + await this.readRepository.update(existingView); + } + } +} + +// Event Store +class EventStore { + private events: any[] = []; + + async saveEvents(events: DomainEvent[]): Promise { + for (const event of events) { + const storedEvent = { + id: this.events.length + 1, + eventType: this.getEventType(event), + eventData: event, + timestamp: new Date() + }; + this.events.push(storedEvent); + } + } + + async publishEvent(event: DomainEvent): Promise { + console.log(`Publishing event: ${JSON.stringify(event)}`); + // Здесь можно добавить логику публикации в message broker + } + + async getEventsByOrderId(orderId: number): Promise { + return this.events.filter(event => + event.eventData.orderId === orderId + ); + } + + private getEventType(event: DomainEvent): string { + if ('orderId' in event && 'userId' in event && 'amount' in event) { + return 'OrderCreated'; + } + if ('orderId' in event && 'paymentId' in event) { + return 'OrderPaid'; + } + if ('orderId' in event && 'reason' in event) { + return 'OrderCancelled'; + } + return 'Unknown'; + } +} + +// Domain Events +interface OrderCreatedEvent { + orderId: number; + userId: number; + amount: number; + timestamp: Date; +} + +interface OrderPaidEvent { + orderId: number; + userId: number; + amount: number; + paymentId: string; + timestamp: Date; +} + +interface OrderCancelledEvent { + orderId: number; + userId: number; + reason: string; + timestamp: Date; +} + +type DomainEvent = OrderCreatedEvent | OrderPaidEvent | OrderCancelledEvent; + +// Order Aggregate +class OrderAggregate { + private uncommittedEvents: DomainEvent[] = []; + + createOrder(userId: number, items: OrderItemCommand[]): void { + const totalAmount = items.reduce((sum, item) => sum + (item.price * item.quantity), 0); + + const event: OrderCreatedEvent = { + orderId: 0, // Will be set by database + userId, + amount: totalAmount, + timestamp: new Date() + }; + + this.uncommittedEvents.push(event); + } + + getUncommittedEvents(): DomainEvent[] { + return [...this.uncommittedEvents]; + } + + markEventsAsCommitted(): void { + this.uncommittedEvents = []; + } +} + +// Command Repository +interface CommandRepository { + save(command: any): Promise; +} + +class PrismaCommandRepository implements CommandRepository { + async save(command: any): Promise { + // Implementation for saving commands + return command; + } +} + +// Validation schemas +const createOrderSchema = z.object({ + userId: z.number().positive(), + items: z.array(z.object({ + productId: z.number().positive(), + quantity: z.number().positive(), + price: z.number().positive() + })) +}); + +// API endpoints +app.post('/api/orders', async (req: Request, res: Response, next: NextFunction) => { + try { + const eventStore = new EventStore(); + const commandRepository = new PrismaCommandRepository(); + const commandService = new OrderCommandService(eventStore, commandRepository); + + const validatedData = createOrderSchema.parse(req.body); + await commandService.createOrder(validatedData); + + res.json({ message: 'Order created successfully' }); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.get('/api/orders/:orderId', async (req: Request, res: Response, next: NextFunction) => { + try { + const readRepository = new PrismaReadRepository(); + const queryService = new OrderQueryService(readRepository); + + const orderId = parseInt(req.params.orderId); + const order = await queryService.getOrder(orderId); + + if (!order) { + res.status(404).json({ error: 'Order not found' }); + return; + } + + res.json(order); + } catch (error) { + next(error); + } +}); + +app.get('/api/users/:userId/orders', async (req: Request, res: Response, next: NextFunction) => { + try { + const readRepository = new PrismaReadRepository(); + const queryService = new OrderQueryService(readRepository); + + const userId = parseInt(req.params.userId); + const orders = await queryService.getUserOrders(userId); + + res.json(orders); + } catch (error) { + next(error); + } +}); + +app.get('/api/orders/status/:status', async (req: Request, res: Response, next: NextFunction) => { + try { + const readRepository = new PrismaReadRepository(); + const queryService = new OrderQueryService(readRepository); + + const status = req.params.status; + const orders = await queryService.getOrdersByStatus(status); + + res.json(orders); + } catch (error) { + next(error); + } +}); + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +app.listen(3000, () => { + console.log('Server running on port 3000'); +}); +``` + +#### 6. Гексагональная архитектура (Hexagonal Architecture) +**Определение:** Архитектура, где бизнес-логика изолирована от внешних зависимостей через порты и адаптеры. + +**Компоненты:** +- **Core (Domain)** — бизнес-логика +- **Ports** — интерфейсы для взаимодействия +- **Adapters** — реализации портов +- **Infrastructure** — внешние системы + +**Преимущества:** +- Независимость от фреймворков +- Легкость тестирования +- Гибкость в выборе технологий +- Четкое разделение ответственности + +**Недостатки:** +- Дополнительная сложность +- Потенциальный over-engineering +- Кривая обучения +- Больше кода + +**Технический пример:** +```java +// Domain Core +public class Order { + private Long id; + private Long userId; + private List items; + private OrderStatus status; + + public void addItem(Product product, int quantity) { + if (status != OrderStatus.DRAFT) { + throw new IllegalStateException("Cannot add items to non-draft order"); + } + + OrderItem item = new OrderItem(product, quantity); + items.add(item); + } + + public void confirm() { + if (items.isEmpty()) { + throw new IllegalStateException("Cannot confirm empty order"); + } + + this.status = OrderStatus.CONFIRMED; + } +} + +// Ports (Interfaces) +public interface OrderRepository { + Order save(Order order); + Optional findById(Long id); + List findByUserId(Long userId); +} + +public interface PaymentService { + PaymentResult processPayment(PaymentRequest request); +} + +public interface NotificationService { + void sendOrderConfirmation(Long userId, Long orderId); +} + +// Application Service (Use Cases) +@Service +public class OrderApplicationService { + private final OrderRepository orderRepository; + private final PaymentService paymentService; + private final NotificationService notificationService; + + public OrderApplicationService(OrderRepository orderRepository, + PaymentService paymentService, + NotificationService notificationService) { + this.orderRepository = orderRepository; + this.paymentService = paymentService; + this.notificationService = notificationService; + } + + public Order createOrder(CreateOrderRequest request) { + Order order = new Order(); + order.setUserId(request.getUserId()); + + for (OrderItemRequest itemRequest : request.getItems()) { + order.addItem(itemRequest.getProduct(), itemRequest.getQuantity()); + } + + order.confirm(); + + return orderRepository.save(order); + } + + public void processPayment(Long orderId, PaymentRequest paymentRequest) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new OrderNotFoundException("Order not found")); + + PaymentResult result = paymentService.processPayment(paymentRequest); + + if (result.isSuccess()) { + order.markAsPaid(); + orderRepository.save(order); + notificationService.sendOrderConfirmation(order.getUserId(), orderId); + } + } +} + +// Adapters (Infrastructure) +@Repository +public class JpaOrderRepository implements OrderRepository { + @Autowired + private OrderJpaRepository jpaRepository; + + @Override + public Order save(Order order) { + OrderEntity entity = mapToEntity(order); + OrderEntity saved = jpaRepository.save(entity); + return mapToDomain(saved); + } + + @Override + public Optional findById(Long id) { + return jpaRepository.findById(id) + .map(this::mapToDomain); + } +} + +@Component +public class StripePaymentService implements PaymentService { + @Autowired + private StripeClient stripeClient; + + @Override + public PaymentResult processPayment(PaymentRequest request) { + try { + Charge charge = stripeClient.charges().create( + ChargeCreateParams.builder() + .setAmount(request.getAmount().multiply(BigDecimal.valueOf(100)).longValue()) + .setCurrency("usd") + .setSource(request.getToken()) + .build() + ); + + return PaymentResult.success(charge.getId()); + } catch (StripeException e) { + return PaymentResult.failure(e.getMessage()); + } + } +} + +@Component +public class EmailNotificationService implements NotificationService { + @Autowired + private EmailService emailService; + + @Override + public void sendOrderConfirmation(Long userId, Long orderId) { + String email = getUserEmail(userId); + String subject = "Order Confirmation"; + String body = "Your order #" + orderId + " has been confirmed."; + + emailService.sendEmail(email, subject, body); + } +} +``` + +#### 7. Архитектура на основе доменов (Domain-Driven Design) +**Определение:** Архитектура, ориентированная на бизнес-домен и его модели. + +**Компоненты:** +- **Domain Model** — бизнес-модели +- **Aggregates** — консистентные группы объектов +- **Value Objects** — неизменяемые объекты +- **Domain Services** — бизнес-логика +- **Repositories** — доступ к данным +- **Bounded Contexts** — границы доменов + +**Преимущества:** +- Фокус на бизнес-логике +- Четкие границы доменов +- Переиспользование моделей +- Легкость тестирования + +**Недостатки:** +- Сложность для простых систем +- Кривая обучения +- Потенциальный over-engineering +- Сложность интеграции + +**Технический пример:** +```java +// Domain Model +public class Order { + private OrderId id; + private CustomerId customerId; + private List items; + private OrderStatus status; + private Money totalAmount; + + public static Order create(CustomerId customerId) { + Order order = new Order(); + order.id = OrderId.generate(); + order.customerId = customerId; + order.status = OrderStatus.DRAFT; + order.items = new ArrayList<>(); + order.totalAmount = Money.zero(); + + return order; + } + + public void addItem(Product product, Quantity quantity) { + if (status != OrderStatus.DRAFT) { + throw new OrderDomainException("Cannot add items to non-draft order"); + } + + OrderItem item = OrderItem.create(product, quantity); + items.add(item); + + recalculateTotal(); + } + + public void confirm() { + if (items.isEmpty()) { + throw new OrderDomainException("Cannot confirm empty order"); + } + + if (totalAmount.isZero()) { + throw new OrderDomainException("Cannot confirm order with zero amount"); + } + + this.status = OrderStatus.CONFIRMED; + + // Domain Event + DomainEvents.raise(new OrderConfirmedEvent(this.id, this.customerId)); + } + + private void recalculateTotal() { + this.totalAmount = items.stream() + .map(OrderItem::getSubtotal) + .reduce(Money.zero(), Money::add); + } +} + +// Value Objects +public class Money { + private final BigDecimal amount; + private final Currency currency; + + public Money(BigDecimal amount, Currency currency) { + if (amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Amount cannot be negative"); + } + this.amount = amount; + this.currency = currency; + } + + public Money add(Money other) { + if (!this.currency.equals(other.currency)) { + throw new IllegalArgumentException("Cannot add different currencies"); + } + return new Money(this.amount.add(other.amount), this.currency); + } + + public boolean isZero() { + return amount.compareTo(BigDecimal.ZERO) == 0; + } +} + +public class OrderId { + private final String value; + + private OrderId(String value) { + this.value = value; + } + + public static OrderId generate() { + return new OrderId(UUID.randomUUID().toString()); + } + + public static OrderId of(String value) { + return new OrderId(value); + } +} + +// Aggregate Root +@Entity +@Table(name = "orders") +public class OrderAggregate { + @Id + private String id; + + @Embedded + private CustomerId customerId; + + @Enumerated(EnumType.STRING) + private OrderStatus status; + + @Embedded + private Money totalAmount; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List items; + + @Transient + private List uncommittedEvents = new ArrayList<>(); + + // Domain methods + public void addItem(Product product, Quantity quantity) { + OrderItem item = OrderItem.create(product, quantity); + this.items.add(new OrderItemEntity(item)); + recalculateTotal(); + } + + public void confirm() { + if (items.isEmpty()) { + throw new OrderDomainException("Cannot confirm empty order"); + } + + this.status = OrderStatus.CONFIRMED; + + // Raise domain event + uncommittedEvents.add(new OrderConfirmedEvent( + OrderId.of(this.id), + this.customerId + )); + } + + public List getUncommittedEvents() { + return uncommittedEvents; + } + + public void markEventsAsCommitted() { + uncommittedEvents.clear(); + } +} + +// Domain Service +@Service +public class OrderDomainService { + public boolean canAddItem(Order order, Product product) { + // Бизнес-правило: нельзя добавлять товары из разных категорий + if (order.hasItems()) { + String existingCategory = order.getFirstItemCategory(); + return product.getCategory().equals(existingCategory); + } + return true; + } + + public Money calculateDiscount(Order order, Customer customer) { + // Бизнес-правило: скидка для VIP клиентов + if (customer.isVip()) { + return order.getTotalAmount().multiply(0.1); // 10% скидка + } + return Money.zero(); + } +} + +// Repository Interface +public interface OrderRepository { + void save(OrderAggregate order); + Optional findById(OrderId orderId); + List findByCustomerId(CustomerId customerId); +} + +// Application Service +@Service +@Transactional +public class OrderApplicationService { + private final OrderRepository orderRepository; + private final OrderDomainService orderDomainService; + private final EventPublisher eventPublisher; + + public void addItemToOrder(OrderId orderId, ProductId productId, Quantity quantity) { + OrderAggregate order = orderRepository.findById(orderId) + .orElseThrow(() -> new OrderNotFoundException("Order not found")); + + Product product = productService.getProduct(productId); + + if (!orderDomainService.canAddItem(order, product)) { + throw new OrderDomainException("Cannot add item from different category"); + } + + order.addItem(product, quantity); + orderRepository.save(order); + + // Publish domain events + order.getUncommittedEvents().forEach(eventPublisher::publish); + order.markEventsAsCommitted(); + } +} +``` + +#### 8. Архитектура на основе компонентов (Component-Based Architecture) +**Определение:** Система строится из переиспользуемых компонентов с четко определенными интерфейсами. + +**Компоненты:** +- **Components** — переиспользуемые модули +- **Interfaces** — контракты взаимодействия +- **Connectors** — механизмы связи +- **Configuration** — настройка компонентов + +**Преимущества:** +- Переиспользование кода +- Модульность +- Легкость тестирования +- Независимая разработка + +**Недостатки:** +- Сложность проектирования интерфейсов +- Потенциальные проблемы с производительностью +- Сложность управления зависимостями +- Риск over-engineering + +**Технический пример:** +```java +// Component Interface +public interface PaymentComponent { + PaymentResult processPayment(PaymentRequest request); + RefundResult processRefund(RefundRequest request); + PaymentStatus getPaymentStatus(String paymentId); +} + +// Component Implementation +@Component("stripePaymentComponent") +public class StripePaymentComponent implements PaymentComponent { + @Autowired + private StripeClient stripeClient; + + @Override + public PaymentResult processPayment(PaymentRequest request) { + try { + Charge charge = stripeClient.charges().create( + ChargeCreateParams.builder() + .setAmount(request.getAmount().multiply(BigDecimal.valueOf(100)).longValue()) + .setCurrency("usd") + .setSource(request.getToken()) + .build() + ); + + return PaymentResult.success(charge.getId()); + } catch (StripeException e) { + return PaymentResult.failure(e.getMessage()); + } + } + + @Override + public RefundResult processRefund(RefundRequest request) { + try { + Refund refund = stripeClient.refunds().create( + RefundCreateParams.builder() + .setCharge(request.getChargeId()) + .setAmount(request.getAmount().multiply(BigDecimal.valueOf(100)).longValue()) + .build() + ); + + return RefundResult.success(refund.getId()); + } catch (StripeException e) { + return RefundResult.failure(e.getMessage()); + } + } + + @Override + public PaymentStatus getPaymentStatus(String paymentId) { + try { + Charge charge = stripeClient.charges().retrieve(paymentId); + return PaymentStatus.valueOf(charge.getStatus().toUpperCase()); + } catch (StripeException e) { + return PaymentStatus.UNKNOWN; + } + } +} + +// Component Registry +@Service +public class ComponentRegistry { + private final Map paymentComponents = new HashMap<>(); + + @PostConstruct + public void initializeComponents() { + // Регистрация компонентов + paymentComponents.put("stripe", stripePaymentComponent); + paymentComponents.put("paypal", paypalPaymentComponent); + paymentComponents.put("square", squarePaymentComponent); + } + + public PaymentComponent getPaymentComponent(String provider) { + return paymentComponents.get(provider); + } +} + +// Component Manager +@Service +public class PaymentComponentManager { + @Autowired + private ComponentRegistry componentRegistry; + + public PaymentResult processPayment(String provider, PaymentRequest request) { + PaymentComponent component = componentRegistry.getPaymentComponent(provider); + if (component == null) { + throw new UnsupportedPaymentProviderException("Provider not supported: " + provider); + } + + return component.processPayment(request); + } +} +``` + +### Сравнение архитектурных стилей + +| Стиль | Сложность | Производительность | Масштабируемость | Тестируемость | Гибкость | +|-------|------------|-------------------|------------------|---------------|----------| +| Монолитная | Низкая | Высокая | Низкая | Высокая | Низкая | +| Микросервисы | Высокая | Средняя | Высокая | Средняя | Высокая | +| Слоистая | Средняя | Средняя | Средняя | Высокая | Средняя | +| Event-Driven | Высокая | Средняя | Высокая | Низкая | Высокая | +| CQRS | Очень высокая | Высокая | Высокая | Средняя | Высокая | +| Гексагональная | Высокая | Средняя | Средняя | Высокая | Высокая | +| DDD | Высокая | Средняя | Средняя | Высокая | Высокая | +| Component-Based | Средняя | Средняя | Средняя | Высокая | Высокая | + +### Выбор архитектурного стиля + +**Факторы для выбора:** + +1. **Размер команды** — большие команды лучше подходят для микросервисов +2. **Сложность домена** — сложные домены требуют DDD или гексагональной архитектуры +3. **Требования к производительности** — критичная производительность может потребовать монолита +4. **Необходимость масштабирования** — микросервисы лучше для горизонтального масштабирования +5. **Технологическое разнообразие** — микросервисы позволяют использовать разные технологии +6. **Время разработки** — монолит быстрее в разработке для простых систем +7. **Сложность тестирования** — слоистая архитектура проще для тестирования +8. **Необходимость аудита** — CQRS лучше для аудита изменений +- Высокие требования к масштабируемости + +**Технический пример:** + +**User Service (отдельное приложение):** +```java +// User Service - отдельный микросервис +@SpringBootApplication +public class UserServiceApplication { + public static void main(String[] args) { + SpringApplication.run(UserServiceApplication.class, args); + } +} + +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @PostMapping + public ResponseEntity createUser(@RequestBody User user) { + return ResponseEntity.ok(userService.createUser(user)); + } + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + return ResponseEntity.ok(userService.getUser(id)); + } +} + +@Service +public class UserService { + @Autowired + private UserRepository userRepository; + + public User createUser(User user) { + return userRepository.save(user); + } + + public User getUser(Long id) { + return userRepository.findById(id).orElse(null); + } +} + +// Собственная база данных для User Service +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private String email; + // геттеры и сеттеры +} +``` + +**Order Service (отдельное приложение):** +```java +// Order Service - отдельный микросервис +@SpringBootApplication +public class OrderServiceApplication { + public static void main(String[] args) { + SpringApplication.run(OrderServiceApplication.class, args); + } +} + +@RestController +@RequestMapping("/api/orders") +public class OrderController { + @Autowired + private OrderService orderService; + + @PostMapping + public ResponseEntity createOrder(@RequestBody Order order) { + return ResponseEntity.ok(orderService.createOrder(order)); + } + + @GetMapping("/{id}") + public ResponseEntity getOrder(@PathVariable Long id) { + return ResponseEntity.ok(orderService.getOrder(id)); + } +} + +@Service +public class OrderService { + @Autowired + private OrderRepository orderRepository; + @Autowired + private UserServiceClient userServiceClient; // HTTP клиент для User Service + + public Order createOrder(Order order) { + // Проверяем пользователя через HTTP вызов + User user = userServiceClient.getUser(order.getUserId()); + if (user == null) { + throw new IllegalArgumentException("User not found"); + } + return orderRepository.save(order); + } + + public Order getOrder(Long id) { + return orderRepository.findById(id).orElse(null); + } +} + +// HTTP клиент для взаимодействия с User Service +@Component +public class UserServiceClient { + @Autowired + private RestTemplate restTemplate; + + private static final String USER_SERVICE_URL = "http://user-service:8080"; + + public User getUser(Long userId) { + try { + ResponseEntity response = restTemplate.getForEntity( + USER_SERVICE_URL + "/api/users/" + userId, + User.class + ); + return response.getBody(); + } catch (Exception e) { + // Обработка ошибок сети + return null; + } + } +} + +// Собственная база данных для Order Service +@Entity +@Table(name = "orders") +public class Order { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private Long userId; // Ссылка на пользователя в другом сервисе + private BigDecimal amount; + // геттеры и сеттеры +} +``` + +**API Gateway (для маршрутизации):** +```java +// API Gateway - точка входа для всех микросервисов +@SpringBootApplication +@EnableZuulProxy +public class ApiGatewayApplication { + public static void main(String[] args) { + SpringApplication.run(ApiGatewayApplication.class, args); + } +} + +// Конфигурация маршрутов +@Configuration +public class GatewayConfig { + @Bean + public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("user-service", r -> r.path("/api/users/**") + .uri("http://user-service:8080")) + .route("order-service", r -> r.path("/api/orders/**") + .uri("http://order-service:8080")) + .route("product-service", r -> r.path("/api/products/**") + .uri("http://product-service:8080")) + .build(); + } +} +``` + +**Docker Compose для развертывания:** +```yaml +version: '3.8' +services: + user-service: + build: ./user-service + ports: + - "8081:8080" + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://user-db:5432/userdb + depends_on: + - user-db + + order-service: + build: ./order-service + ports: + - "8082:8080" + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://order-db:5432/orderdb + - USER_SERVICE_URL=http://user-service:8080 + depends_on: + - order-db + + product-service: + build: ./product-service + ports: + - "8083:8080" + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://product-db:5432/productdb + depends_on: + - product-db + + api-gateway: + build: ./api-gateway + ports: + - "8080:8080" + depends_on: + - user-service + - order-service + - product-service + + user-db: + image: postgres:13 + environment: + - POSTGRES_DB=userdb + - POSTGRES_USER=user + - POSTGRES_PASSWORD=password + + order-db: + image: postgres:13 + environment: + - POSTGRES_DB=orderdb + - POSTGRES_USER=order + - POSTGRES_PASSWORD=password + + product-db: + image: postgres:13 + environment: + - POSTGRES_DB=productdb + - POSTGRES_USER=product + - POSTGRES_PASSWORD=password +``` + +#### 3. Service-Oriented Architecture (SOA) +**Определение:** Архитектура, основанная на сервисах + +**Характеристики:** +- Сервисы как основные компоненты +- Стандартизированные интерфейсы +- Переиспользование сервисов +- Слабая связанность + +#### 4. Event-Driven Architecture +**Определение:** Архитектура, основанная на событиях + +**Компоненты:** +- Event Producers (производители событий) +- Event Consumers (потребители событий) +- Event Bus (шина событий) + +**Преимущества:** +- Асинхронная обработка +- Слабая связанность +- Масштабируемость +- Отказоустойчивость + +### Архитектурные паттерны + +#### 1. Layered Architecture (Слоистая архитектура) +``` +┌─────────────────┐ +│ Presentation │ ← UI Layer +├─────────────────┤ +│ Business │ ← Business Logic +├─────────────────┤ +│ Persistence │ ← Data Access +└─────────────────┘ +``` + +**Принципы:** +- Каждый слой зависит только от нижележащего +- Запрет на обход слоев +- Четкое разделение ответственности + +#### 2. Hexagonal Architecture (Порты и адаптеры) +``` + ┌─────────────┐ + │ Adapters │ ← Внешние интерфейсы + └─────────────┘ + │ + ┌─────────────┐ + │ Ports │ ← Интерфейсы + └─────────────┘ + │ + ┌─────────────┐ + │ Core │ ← Бизнес-логика + └─────────────┘ +``` + +**Преимущества:** +- Независимость от внешних систем +- Легкое тестирование +- Гибкость в выборе технологий + +#### 3. CQRS (Command Query Responsibility Segregation) +**Разделение команд и запросов** + +``` +Команды → Модель записи → Хранилище событий +Запросы → Модель чтения → База данных чтения +``` + +**Когда использовать:** +- Разные требования к чтению и записи +- Сложные запросы +- Высокая нагрузка на чтение + +#### 4. Event Sourcing +**Хранение событий вместо состояния** + +``` +События → Хранилище событий → Проекции → Модели чтения +``` + +**Преимущества:** +- Полная история изменений +- Возможность воспроизведения состояния +- Аудит и отладка + +--- + +## Качественные атрибуты + +### Определение +**Качественные атрибуты** — это свойства системы, которые определяют её качество с точки зрения пользователей и стейкхолдеров. + +### Классификация качественных атрибутов + +#### 1. Функциональные требования +- **Что** система должна делать +- Описывают функциональность +- Примеры: аутентификация, обработка платежей + +#### 2. Нефункциональные требования (NFR) +- **Как** система должна работать +- Описывают качество системы +- Влияют на архитектурные решения + +### Основные качественные атрибуты + +#### Производительность (Performance) +**Определение:** Скорость и эффективность выполнения операций + +**Метрики:** +- Время отклика (Response Time) +- Пропускная способность (Throughput) +- Использование ресурсов (Resource Utilization) + +**Архитектурные тактики:** +- Кэширование +- Пулы соединений +- Асинхронная обработка +- Оптимизация запросов + +#### Масштабируемость (Scalability) +**Определение:** Способность системы обрабатывать увеличение нагрузки + +**Типы масштабирования:** +- **Горизонтальное** (Scale Out) — добавление серверов +- **Вертикальное** (Scale Up) — увеличение мощности сервера + +**Паттерны:** +- Load Balancing +- Database Sharding +- Microservices +- CDN + +#### Надежность (Reliability) +**Определение:** Способность системы работать без сбоев + +**Метрики:** +- MTBF (Mean Time Between Failures) +- MTTR (Mean Time To Repair) +- Availability (Доступность) + +**Тактики:** +- Redundancy (Избыточность) +- Fault Tolerance (Отказоустойчивость) +- Health Checks +- Circuit Breaker + +#### Безопасность (Security) +**Определение:** Защита системы от угроз + +**Аспекты:** +- Аутентификация +- Авторизация +- Шифрование +- Аудит + +**Принципы:** +- Defense in Depth +- Least Privilege +- Security by Design + +#### Удобство использования (Usability) +**Определение:** Простота использования системы + +**Факторы:** +- Интуитивность интерфейса +- Производительность пользователя +- Обучаемость +- Доступность + +#### Совместимость (Compatibility) +**Определение:** Способность работать с другими системами + +**Типы:** +- Обратная совместимость +- Форвардная совместимость +- Межплатформенная совместимость + +#### Переносимость (Portability) +**Определение:** Легкость переноса системы на другие платформы + +**Факторы:** +- Независимость от ОС +- Стандартизированные интерфейсы +- Абстракция от аппаратуры + +#### Тестируемость (Testability) +**Определение:** Легкость тестирования системы + +**Принципы:** +- Dependency Injection +- Mocking +- Unit Testing +- Integration Testing + +#### Поддерживаемость (Maintainability) +**Определение:** Легкость внесения изменений + +**Аспекты:** +- Читаемость кода +- Документирование +- Модульность +- Рефакторинг + +### Trade-offs между атрибутами + +Часто улучшение одного атрибута ухудшает другой: + +- **Производительность vs Безопасность** — шифрование замедляет работу +- **Масштабируемость vs Простота** — распределенные системы сложнее +- **Надежность vs Производительность** — репликация добавляет overhead +- **Безопасность vs Удобство** — сложные пароли неудобны + +### Архитектурные тактики + +**Тактика** — это архитектурное решение для достижения качественного атрибута. + +#### Тактики для производительности: +1. **Управление ресурсами** + - Увеличение ресурсов + - Уменьшение накладных расходов + - Управление ресурсами + +2. **Управление запросами** + - Приоритизация запросов + - Уменьшение времени обработки + - Управление событиями + +#### Тактики для надежности: +1. **Предотвращение ошибок** + - Удаление точек отказа + - Транзакции + - Валидация + +2. **Обнаружение ошибок** + - Ping/Echo + - Heartbeat + - Exception handling + +3. **Восстановление после ошибок** + - Voting + - Active redundancy + - Passive redundancy + +--- + +## Роль архитектора в команде + +### Определение роли архитектора + +**Архитектор ПО** — это специалист, который: +- Проектирует структуру системы +- Принимает ключевые технические решения +- Обеспечивает соответствие архитектуры требованиям +- Руководит техническим развитием проекта + +### Обязанности архитектора + +#### 1. Проектирование архитектуры +- Анализ требований +- Выбор архитектурных решений +- Проектирование компонентов +- Определение интерфейсов + +#### 2. Техническое лидерство +- Наставничество разработчиков +- Code review +- Техническое планирование +- Управление техническим долгом + +#### 3. Коммуникация +- Взаимодействие с бизнесом +- Объяснение технических решений +- Презентация архитектуры +- Документирование решений + +#### 4. Контроль качества +- Архитектурные ревью +- Соответствие стандартам +- Мониторинг метрик +- Управление рисками + +### Навыки архитектора + +#### Технические навыки +- Глубокое понимание технологий +- Знание архитектурных паттернов +- Опыт работы с различными платформами +- Понимание качественных атрибутов + +#### Мягкие навыки +- Лидерские качества +- Коммуникативные навыки +- Аналитическое мышление +- Принятие решений + +#### Бизнес-навыки +- Понимание бизнес-процессов +- Анализ требований +- Управление проектами +- Работа со стейкхолдерами + +### Типы архитекторов + +#### 1. Solution Architect +- Проектирует решения для конкретных бизнес-задач +- Работает с бизнес-требованиями +- Фокусируется на интеграции систем + +#### 2. Enterprise Architect +- Проектирует архитектуру на уровне предприятия +- Стратегическое планирование +- Управление технологическим портфелем + +#### 3. Technical Architect +- Фокусируется на технических аспектах +- Выбор технологий +- Проектирование компонентов + +#### 4. Cloud Architect +- Специализируется на облачных решениях +- Проектирование cloud-native приложений +- Оптимизация облачных ресурсов + +### Закон Конвея + +> "Организации проектируют системы, которые копируют структуру коммуникации в организации" + +**Следствия:** +- Структура команды влияет на архитектуру +- Необходимо учитывать организационную структуру +- Команды должны быть организованы по доменам + +**Примеры:** +- Команда по фронтенду → отдельный фронтенд-сервис +- Команда по платежам → отдельный платежный сервис +- Команда по аналитике → отдельный аналитический сервис + +### Архитектурные ревью + +**Цель:** Оценка соответствия архитектуры требованиям + +**Типы ревью:** +1. **Архитектурный анализ** — оценка текущей архитектуры +2. **Архитектурная оценка** — оценка предлагаемых изменений +3. **Архитектурный аудит** — проверка соответствия стандартам + +**Процесс ревью:** +1. Подготовка материалов +2. Презентация архитектуры +3. Обсуждение и вопросы +4. Выработка рекомендаций +5. Документирование результатов + +### Управление техническим долгом + +**Технический долг** — это компромиссы в качестве кода, которые делаются для ускорения разработки. + +**Типы технического долга:** +- Устаревшие технологии +- Неоптимальная архитектура +- Отсутствие тестов +- Плохая документация + +**Стратегии управления:** +- Регулярный рефакторинг +- Планирование технических спринтов +- Мониторинг метрик качества +- Обучение команды + +--- + +## Практические задания + +### Задание 1: Анализ архитектуры +Проанализируйте архитектуру известной системы (например, Twitter, Netflix, Uber) и определите: +1. Архитектурный стиль +2. Основные компоненты +3. Качественные атрибуты +4. Архитектурные решения + +### Задание 2: Применение SOLID принципов +Возьмите пример кода с нарушениями SOLID принципов и рефакторите его согласно принципам. + +### Задание 3: Trade-off анализ +Рассмотрите конкретный случай, где нужно выбрать между двумя качественными атрибутами, и обоснуйте выбор. + +--- + +## Дополнительные ресурсы + +### Книги +- "Software Architecture in Practice" by Len Bass, Paul Clements, Rick Kazman +- "Clean Architecture" by Robert C. Martin +- "Building Microservices" by Sam Newman +- "Domain-Driven Design" by Eric Evans + +### Стандарты +- IEEE 1471-2000: Recommended Practice for Architectural Description +- ISO/IEC 42010: Systems and software engineering — Architecture description + +### Онлайн ресурсы +- [Software Architecture Guide](https://martinfowler.com/architecture/) +- [Architecture Decision Records](https://adr.github.io/) +- [C4 Model](https://c4model.com/) + +--- + +## Контрольные вопросы + +1. Что такое архитектура ПО и зачем она нужна? +2. Объясните принципы SOLID с примерами. +3. Какие архитектурные стили вы знаете? В чем их различия? +4. Что такое качественные атрибуты? Приведите примеры. +5. Какова роль архитектора в команде? +6. Что такое Закон Конвея и как он влияет на архитектуру? +7. Как управлять техническим долгом? +8. Какие trade-offs существуют между качественными атрибутами? diff --git a/modules/module10/README.md b/modules/module10/README.md new file mode 100644 index 0000000..51ae09d --- /dev/null +++ b/modules/module10/README.md @@ -0,0 +1,80 @@ +# Модуль 10: Производительность и оптимизация + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля +Этот модуль посвящен изучению принципов производительности и оптимизации систем. Студенты научатся анализировать производительность, выявлять узкие места, применять различные стратегии оптимизации и планировать ресурсы. + +## Содержание модуля + +### 1. [Основы производительности](sections/01-performance-fundamentals.md) +- Метрики производительности (Throughput, Latency, Resource Utilization) +- Производительность по уровням (Application, Database, Infrastructure) +- Мониторинг и измерение производительности + +### 2. [Профилирование и анализ](sections/02-profiling-analysis.md) +- Инструменты профилирования (JProfiler, VisualVM, APM) +- Анализ производительности и узких мест +- Анализ памяти и утечек + +### 3. [Оптимизация базы данных](sections/03-database-optimization.md) +- Оптимизация запросов и индексов +- Connection pooling и batch операции +- Анализ планов выполнения запросов + +### 4. [Кэширование](sections/04-caching.md) +- Стратегии кэширования (Cache-Aside, Write-Through, Write-Behind) +- Многоуровневое кэширование +- Управление кэшем и метрики + +### 5. [Оптимизация кода и минификация](sections/05-code-optimization-minification.md) +- Минификация JavaScript/TypeScript, Java, Rust, Python +- Оптимизация изображений и CSS +- Оптимизация загрузки ресурсов + +### 6. [Оптимизация сети](sections/06-network-optimization.md) +- Connection pooling +- Сжатие HTTP ответов +- Интеграция с CDN + +### 7. [Capacity Planning](sections/07-capacity-planning.md) +- Анализ текущей производительности +- Прогнозирование нагрузки +- Планирование ресурсов + +### 8. [Performance Testing](sections/08-performance-testing.md) +- Типы тестов производительности (Load, Stress, Endurance) +- Автоматизация тестов производительности +- Анализ результатов тестирования + +### 9. [Практические задания](sections/09-practical-tasks.md) +- Задания по профилированию +- Оптимизация базы данных +- Performance testing + +## Ключевые компетенции +- Понимание принципов производительности систем +- Умение анализировать и измерять производительность +- Навыки профилирования и выявления узких мест +- Знание стратегий оптимизации на разных уровнях +- Умение планировать ресурсы и capacity planning +- Навыки проведения тестов производительности + +## Практические задачи +1. **Профилирование приложения** - настройка инструментов профилирования и анализ производительности +2. **Оптимизация базы данных** - анализ и оптимизация медленных запросов +3. **Performance Testing** - создание и проведение тестов производительности + +## Дополнительные ресурсы +- **Инструменты**: JProfiler, VisualVM, New Relic, JMeter, Gatling +- **Книги**: "Java Performance", "High Performance Browser Networking" +- **Документация**: Spring Boot Performance, JVM Tuning Guide + +## Критерии оценки +- Понимание метрик производительности (25%) +- Умение использовать инструменты профилирования (25%) +- Навыки оптимизации кода и базы данных (25%) +- Способность планировать ресурсы и проводить тестирование (25%) diff --git a/modules/module10/sections/01-performance-fundamentals.md b/modules/module10/sections/01-performance-fundamentals.md new file mode 100644 index 0000000..d39ba80 --- /dev/null +++ b/modules/module10/sections/01-performance-fundamentals.md @@ -0,0 +1,363 @@ +# Основы производительности + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Что такое производительность + +**Производительность** — это способность системы выполнять задачи за определенное время с использованием доступных ресурсов. + +## Метрики производительности + +### 1. Throughput (Пропускная способность) +**Определение:** Количество операций, выполняемых за единицу времени. + +**Технический пример - Advanced Performance Monitoring System:** + +```java +// Advanced Performance Metrics Collector +@Component +public class AdvancedPerformanceMetrics { + + private final MeterRegistry meterRegistry; + private final Counter requestCounter; + private final Timer requestTimer; + private final Gauge activeConnections; + private final DistributionSummary responseSize; + private final Counter errorCounter; + + public AdvancedPerformanceMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + + // Request metrics + this.requestCounter = Counter.builder("requests.total") + .description("Total number of requests") + .tag("service", "user-service") + .register(meterRegistry); + + this.requestTimer = Timer.builder("request.duration") + .description("Request processing time") + .publishPercentiles(0.5, 0.95, 0.99) + .publishPercentileHistogram() + .register(meterRegistry); + + // Connection metrics + this.activeConnections = Gauge.builder("connections.active") + .description("Number of active connections") + .register(meterRegistry, this, AdvancedPerformanceMetrics::getActiveConnections); + + // Response size metrics + this.responseSize = DistributionSummary.builder("response.size") + .description("Response size in bytes") + .publishPercentiles(0.5, 0.95, 0.99) + .register(meterRegistry); + + // Error metrics + this.errorCounter = Counter.builder("errors.total") + .description("Total number of errors") + .register(meterRegistry); + } + + public void recordRequest(Duration duration, int responseSizeBytes, boolean isError) { + requestCounter.increment(); + requestTimer.record(duration); + this.responseSize.record(responseSizeBytes); + + if (isError) { + errorCounter.increment(); + } + } + + public PerformanceReport generateReport() { + return PerformanceReport.builder() + .throughput(getThroughput()) + .averageLatency(getAverageLatency()) + .p95Latency(getP95Latency()) + .p99Latency(getP99Latency()) + .errorRate(getErrorRate()) + .activeConnections(getActiveConnections()) + .averageResponseSize(getAverageResponseSize()) + .timestamp(LocalDateTime.now()) + .build(); + } + + public double getThroughput() { + double totalTime = requestTimer.totalTime(TimeUnit.SECONDS); + return totalTime > 0 ? requestCounter.count() / totalTime : 0; + } + + public double getAverageLatency() { + return requestTimer.mean(TimeUnit.MILLISECONDS); + } + + public double getP95Latency() { + return requestTimer.percentile(0.95, TimeUnit.MILLISECONDS); + } + + public double getP99Latency() { + return requestTimer.percentile(0.99, TimeUnit.MILLISECONDS); + } + + public double getErrorRate() { + double totalRequests = requestCounter.count(); + return totalRequests > 0 ? (errorCounter.count() / totalRequests) * 100 : 0; + } + + private double getActiveConnections() { + return 0; // Placeholder + } + + public double getAverageResponseSize() { + return responseSize.mean(); + } +} + +// Performance Optimization Service +@Service +public class PerformanceOptimizationService { + + @Autowired + private AdvancedPerformanceMetrics metrics; + + @Autowired + private DatabaseOptimizationService dbOptimizationService; + + // Automatic performance optimization + @Scheduled(fixedRate = 60000) // Every minute + public void optimizePerformance() { + PerformanceReport report = metrics.generateReport(); + + if (needsOptimization(report)) { + log.warn("Performance optimization triggered: {}", report); + + if (report.getAverageLatency() > 1000) { + optimizeLatency(); + } + + if (report.getErrorRate() > 5) { + optimizeErrorHandling(); + } + + if (report.getThroughput() < 100) { + optimizeThroughput(); + } + } + } + + private boolean needsOptimization(PerformanceReport report) { + return report.getAverageLatency() > 1000 || + report.getErrorRate() > 5 || + report.getThroughput() < 100 || + report.getP95Latency() > 2000; + } + + private void optimizeLatency() { + log.info("Optimizing latency..."); + increaseConnectionPoolSize(); + enableCompression(); + optimizeJVMSettings(); + } + + private void optimizeErrorHandling() { + log.info("Optimizing error handling..."); + enableCircuitBreaker(); + enableRetryLogic(); + improveErrorLogging(); + } + + private void optimizeThroughput() { + log.info("Optimizing throughput..."); + increaseThreadPoolSize(); + enableAsyncProcessing(); + optimizeBatchProcessing(); + } + + private void increaseConnectionPoolSize() { + log.info("Increasing connection pool size"); + } + + private void enableCompression() { + log.info("Enabling compression"); + } + + private void optimizeJVMSettings() { + log.info("Optimizing JVM settings"); + } + + private void enableCircuitBreaker() { + log.info("Enabling circuit breaker"); + } + + private void enableRetryLogic() { + log.info("Enabling retry logic"); + } + + private void improveErrorLogging() { + log.info("Improving error logging"); + } + + private void increaseThreadPoolSize() { + log.info("Increasing thread pool size"); + } + + private void enableAsyncProcessing() { + log.info("Enabling async processing"); + } + + private void optimizeBatchProcessing() { + log.info("Optimizing batch processing"); + } +} +``` + +### 2. Latency (Задержка) +**Определение:** Время выполнения одной операции. + +```java +// Измерение latency +@Service +public class UserService { + private final Timer requestTimer; + + public UserService(MeterRegistry meterRegistry) { + this.requestTimer = Timer.builder("user.service.duration") + .register(meterRegistry); + } + + public User getUser(Long id) { + return requestTimer.recordCallable(() -> { + // бизнес-логика + return userRepository.findById(id); + }); + } +} +``` + +### 3. Resource Utilization (Использование ресурсов) +**Определение:** Процент использования доступных ресурсов. + +```java +// Мониторинг ресурсов +@Component +public class ResourceMonitor { + private final Gauge cpuUsage; + private final Gauge memoryUsage; + private final Gauge diskUsage; + + public ResourceMonitor(MeterRegistry meterRegistry) { + this.cpuUsage = Gauge.builder("system.cpu.usage") + .register(meterRegistry, this, ResourceMonitor::getCpuUsage); + this.memoryUsage = Gauge.builder("system.memory.usage") + .register(meterRegistry, this, ResourceMonitor::getMemoryUsage); + this.diskUsage = Gauge.builder("system.disk.usage") + .register(meterRegistry, this, ResourceMonitor::getDiskUsage); + } + + private double getCpuUsage() { + OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); + return osBean.getProcessCpuLoad(); + } + + private double getMemoryUsage() { + MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); + return (double) heapUsage.getUsed() / heapUsage.getMax(); + } + + private double getDiskUsage() { + File root = new File("/"); + long totalSpace = root.getTotalSpace(); + long freeSpace = root.getFreeSpace(); + return (double) (totalSpace - freeSpace) / totalSpace; + } +} +``` + +## Производительность по уровням + +### 1. Application Level +```java +// Оптимизация на уровне приложения +@Service +public class OptimizedUserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private UserCache userCache; + + // Оптимизация: кэширование + @Cacheable("users") + public User getUser(Long id) { + return userRepository.findById(id); + } + + // Оптимизация: batch операции + public List getUsers(List ids) { + return userRepository.findByIdIn(ids); + } + + // Оптимизация: асинхронная обработка + @Async + public CompletableFuture processUserAsync(User user) { + // асинхронная обработка + return CompletableFuture.completedFuture(null); + } +} +``` + +### 2. Database Level +```java +// Оптимизация на уровне базы данных +@Repository +public class OptimizedUserRepository { + + // Оптимизация: использование индексов + @Query("SELECT u FROM User u WHERE u.email = :email") + User findByEmail(@Param("email") String email); + + // Оптимизация: пагинация + @Query("SELECT u FROM User u") + Page findAllUsers(Pageable pageable); + + // Оптимизация: batch операции + @Modifying + @Query("UPDATE User u SET u.lastLogin = :lastLogin WHERE u.id IN :ids") + int updateLastLogin(@Param("ids") List ids, @Param("lastLogin") LocalDateTime lastLogin); +} +``` + +### 3. Infrastructure Level +```yaml +# Оптимизация на уровне инфраструктуры +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service +spec: + replicas: 3 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + spec: + containers: + - name: user-service + image: user-service:latest + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + env: + - name: JAVA_OPTS + value: "-Xms512m -Xmx1g -XX:+UseG1GC" +``` diff --git a/modules/module10/sections/02-profiling-analysis.md b/modules/module10/sections/02-profiling-analysis.md new file mode 100644 index 0000000..2196edf --- /dev/null +++ b/modules/module10/sections/02-profiling-analysis.md @@ -0,0 +1,169 @@ +# Профилирование и анализ + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Инструменты профилирования + +### 1. JProfiler +```java +// Профилирование с JProfiler +@Service +public class ProfiledUserService { + @Autowired + private UserRepository userRepository; + + public User getUser(Long id) { + // JProfiler покажет время выполнения этого метода + return userRepository.findById(id); + } + + public List searchUsers(String query) { + // Профилирование покажет узкие места + List users = userRepository.findByNameContaining(query); + + // Профилирование покажет время обработки + return users.stream() + .filter(user -> user.isActive()) + .collect(Collectors.toList()); + } +} +``` + +### 2. VisualVM +```java +// Мониторинг с VisualVM +@Component +public class PerformanceMonitor { + private final MemoryMXBean memoryBean; + private final ThreadMXBean threadBean; + + public PerformanceMonitor() { + this.memoryBean = ManagementFactory.getMemoryMXBean(); + this.threadBean = ManagementFactory.getThreadMXBean(); + } + + @Scheduled(fixedRate = 5000) + public void logPerformanceMetrics() { + // Мониторинг памяти + MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); + log.info("Heap usage: {} / {}", heapUsage.getUsed(), heapUsage.getMax()); + + // Мониторинг потоков + int threadCount = threadBean.getThreadCount(); + log.info("Thread count: {}", threadCount); + + // Мониторинг CPU + OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); + double cpuUsage = osBean.getProcessCpuLoad(); + log.info("CPU usage: {}%", cpuUsage * 100); + } +} +``` + +### 3. Application Performance Monitoring (APM) +```java +// APM интеграция +@Service +public class APMUserService { + @Autowired + private NewRelic newRelic; + + public User getUser(Long id) { + newRelic.addCustomParameter("user.id", id); + + try { + User user = userRepository.findById(id); + newRelic.recordMetric("User/Found", 1); + return user; + } catch (Exception e) { + newRelic.recordMetric("User/Error", 1); + newRelic.recordException(e); + throw e; + } + } + + public List searchUsers(String query) { + newRelic.addCustomParameter("search.query", query); + + List users = userRepository.findByNameContaining(query); + newRelic.recordMetric("User/SearchResults", users.size()); + + return users; + } +} +``` + +## Анализ производительности + +### 1. Bottleneck Analysis +```java +// Анализ узких мест +@Service +public class BottleneckAnalyzer { + private final Timer databaseTimer; + private final Timer cacheTimer; + private final Timer networkTimer; + + public BottleneckAnalyzer(MeterRegistry meterRegistry) { + this.databaseTimer = Timer.builder("database.operation.duration") + .register(meterRegistry); + this.cacheTimer = Timer.builder("cache.operation.duration") + .register(meterRegistry); + this.networkTimer = Timer.builder("network.operation.duration") + .register(meterRegistry); + } + + public User getUser(Long id) { + // Анализ времени выполнения каждой операции + User user = databaseTimer.recordCallable(() -> { + return userRepository.findById(id); + }); + + if (user != null) { + cacheTimer.recordCallable(() -> { + userCache.put(id, user); + }); + } + + return user; + } +} +``` + +### 2. Memory Analysis +```java +// Анализ памяти +@Component +public class MemoryAnalyzer { + private final MemoryMXBean memoryBean; + + public MemoryAnalyzer() { + this.memoryBean = ManagementFactory.getMemoryMXBean(); + } + + @Scheduled(fixedRate = 10000) + public void analyzeMemory() { + MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); + MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage(); + + log.info("Heap: {} / {} ({}%)", + heapUsage.getUsed(), + heapUsage.getMax(), + (heapUsage.getUsed() * 100) / heapUsage.getMax()); + + log.info("Non-Heap: {} / {} ({}%)", + nonHeapUsage.getUsed(), + nonHeapUsage.getMax(), + (nonHeapUsage.getUsed() * 100) / nonHeapUsage.getMax()); + + // Анализ утечек памяти + if (heapUsage.getUsed() > heapUsage.getMax() * 0.8) { + log.warn("High memory usage detected!"); + } + } +} +``` diff --git a/modules/module10/sections/03-database-optimization.md b/modules/module10/sections/03-database-optimization.md new file mode 100644 index 0000000..78725e1 --- /dev/null +++ b/modules/module10/sections/03-database-optimization.md @@ -0,0 +1,145 @@ +# Оптимизация базы данных + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Оптимизация запросов + +### 1. Query Optimization +```sql +-- Оптимизация запросов +-- Плохо: медленный запрос +SELECT * FROM users u +JOIN orders o ON u.id = o.user_id +WHERE u.created_at > '2023-01-01' +AND o.status = 'COMPLETED'; + +-- Хорошо: оптимизированный запрос +SELECT u.id, u.name, u.email, COUNT(o.id) as order_count +FROM users u +JOIN orders o ON u.id = o.user_id +WHERE u.created_at > '2023-01-01' +AND o.status = 'COMPLETED' +GROUP BY u.id, u.name, u.email; + +-- Хорошо: использование индексов +CREATE INDEX idx_user_created_at ON users(created_at); +CREATE INDEX idx_order_status ON orders(status); +CREATE INDEX idx_order_user_status ON orders(user_id, status); +``` + +### 2. Connection Pooling +```java +// Настройка пула соединений +@Configuration +public class DatabaseConfig { + @Bean + public DataSource dataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb"); + config.setUsername("user"); + config.setPassword("password"); + + // Оптимизация пула соединений + config.setMaximumPoolSize(20); + config.setMinimumIdle(5); + config.setConnectionTimeout(30000); + config.setIdleTimeout(600000); + config.setMaxLifetime(1800000); + config.setLeakDetectionThreshold(60000); + + return new HikariDataSource(config); + } +} +``` + +### 3. Batch Operations +```java +// Batch операции +@Repository +public class OptimizedUserRepository { + @Autowired + private JdbcTemplate jdbcTemplate; + + // Batch вставка + public void saveUsers(List users) { + String sql = "INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)"; + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + User user = users.get(i); + ps.setString(1, user.getName()); + ps.setString(2, user.getEmail()); + ps.setTimestamp(3, Timestamp.valueOf(user.getCreatedAt())); + } + + @Override + public int getBatchSize() { + return users.size(); + } + }); + } + + // Batch обновление + public void updateUsers(List users) { + String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?"; + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + User user = users.get(i); + ps.setString(1, user.getName()); + ps.setString(2, user.getEmail()); + ps.setLong(3, user.getId()); + } + + @Override + public int getBatchSize() { + return users.size(); + } + }); + } +} +``` + +## Настройка базы данных + +### 1. Index Optimization +```sql +-- Оптимизация индексов +-- Анализ использования индексов +EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'user@example.com'; + +-- Создание составных индексов +CREATE INDEX idx_user_email_status ON users(email, status); + +-- Создание частичных индексов +CREATE INDEX idx_active_users ON users(email) WHERE status = 'ACTIVE'; + +-- Создание функциональных индексов +CREATE INDEX idx_user_lower_email ON users(LOWER(email)); +``` + +### 2. Query Plan Analysis +```sql +-- Анализ планов выполнения +EXPLAIN (ANALYZE, BUFFERS) +SELECT u.name, COUNT(o.id) as order_count +FROM users u +LEFT JOIN orders o ON u.id = o.user_id +WHERE u.created_at > '2023-01-01' +GROUP BY u.id, u.name; + +-- Анализ статистики +ANALYZE users; +ANALYZE orders; + +-- Обновление статистики +UPDATE pg_stat_user_tables +SET n_tup_ins = 0, n_tup_upd = 0, n_tup_del = 0 +WHERE relname = 'users'; +``` diff --git a/modules/module10/sections/04-caching.md b/modules/module10/sections/04-caching.md new file mode 100644 index 0000000..3a630ab --- /dev/null +++ b/modules/module10/sections/04-caching.md @@ -0,0 +1,310 @@ +# Кэширование + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Стратегии кэширования + +### 1. Cache-Aside Pattern +**Технический пример - Advanced Multi-Level Caching System:** + +```java +// Advanced Cache Management Service +@Service +public class AdvancedCacheService { + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private CacheMetricsService cacheMetricsService; + + // Multi-level caching + private final Map localCache = new ConcurrentHashMap<>(); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); + + public T get(String cacheKey, Class type, Supplier dataLoader) { + // Level 1: Local cache + T value = getFromLocalCache(cacheKey, type); + if (value != null) { + cacheMetricsService.recordCacheHit(cacheKey, CacheLevel.LOCAL); + return value; + } + + // Level 2: Redis cache + value = getFromRedisCache(cacheKey, type); + if (value != null) { + putToLocalCache(cacheKey, value, Duration.ofMinutes(5)); + cacheMetricsService.recordCacheHit(cacheKey, CacheLevel.REDIS); + return value; + } + + // Level 3: Data source + cacheMetricsService.recordCacheMiss(cacheKey); + value = dataLoader.get(); + + if (value != null) { + putToLocalCache(cacheKey, value, Duration.ofMinutes(5)); + putToRedisCache(cacheKey, value, Duration.ofMinutes(30)); + } + + return value; + } + + public CompletableFuture getAsync(String cacheKey, Class type, Supplier dataLoader) { + return CompletableFuture.supplyAsync(() -> get(cacheKey, type, dataLoader), scheduler); + } + + public List getBatch(List cacheKeys, Class type, Function, List> dataLoader) { + List results = new ArrayList<>(); + List missingKeys = new ArrayList<>(); + + // Check local cache first + for (String key : cacheKeys) { + T value = getFromLocalCache(key, type); + if (value != null) { + results.add(value); + cacheMetricsService.recordCacheHit(key, CacheLevel.LOCAL); + } else { + missingKeys.add(key); + } + } + + if (missingKeys.isEmpty()) { + return results; + } + + // Check Redis cache + List redisValues = getFromRedisCacheBatch(missingKeys, type); + List stillMissingKeys = new ArrayList<>(); + + for (int i = 0; i < missingKeys.size(); i++) { + String key = missingKeys.get(i); + T value = redisValues.get(i); + + if (value != null) { + results.add(value); + putToLocalCache(key, value, Duration.ofMinutes(5)); + cacheMetricsService.recordCacheHit(key, CacheLevel.REDIS); + } else { + stillMissingKeys.add(key); + } + } + + // Load missing data + if (!stillMissingKeys.isEmpty()) { + List loadedData = dataLoader.apply(stillMissingKeys); + + for (int i = 0; i < stillMissingKeys.size(); i++) { + String key = stillMissingKeys.get(i); + T value = loadedData.get(i); + + if (value != null) { + results.add(value); + putToLocalCache(key, value, Duration.ofMinutes(5)); + putToRedisCache(key, value, Duration.ofMinutes(30)); + } + + cacheMetricsService.recordCacheMiss(key); + } + } + + return results; + } + + public void put(String cacheKey, Object value) { + putToLocalCache(cacheKey, value, Duration.ofMinutes(5)); + putToRedisCache(cacheKey, value, Duration.ofMinutes(30)); + cacheMetricsService.recordCachePut(cacheKey); + } + + public void evict(String cacheKey) { + localCache.remove(cacheKey); + redisTemplate.delete(cacheKey); + cacheMetricsService.recordCacheEvict(cacheKey); + } + + public void evictPattern(String pattern) { + localCache.entrySet().removeIf(entry -> entry.getKey().matches(pattern)); + + Set keys = redisTemplate.keys(pattern); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + } + + cacheMetricsService.recordCacheEvictPattern(pattern); + } + + // Cache warming + public void warmCache(List keys, Supplier> dataLoader) { + log.info("Warming cache with {} keys", keys.size()); + + List data = dataLoader.get(); + + for (int i = 0; i < keys.size() && i < data.size(); i++) { + String key = keys.get(i); + Object value = data.get(i); + + if (value != null) { + putToLocalCache(key, value, Duration.ofMinutes(5)); + putToRedisCache(key, value, Duration.ofMinutes(30)); + } + } + + log.info("Cache warming completed"); + } + + private T getFromLocalCache(String key, Class type) { + Object value = localCache.get(key); + return type.cast(value); + } + + private T getFromRedisCache(String key, Class type) { + try { + Object value = redisTemplate.opsForValue().get(key); + return type.cast(value); + } catch (Exception e) { + log.warn("Error getting from Redis cache: {}", e.getMessage()); + return null; + } + } + + private List getFromRedisCacheBatch(List keys, Class type) { + try { + List values = redisTemplate.opsForValue().multiGet(keys); + return values.stream() + .map(type::cast) + .collect(Collectors.toList()); + } catch (Exception e) { + log.warn("Error getting batch from Redis cache: {}", e.getMessage()); + return keys.stream().map(k -> (T) null).collect(Collectors.toList()); + } + } + + private void putToLocalCache(String key, Object value, Duration ttl) { + localCache.put(key, value); + + scheduler.schedule(() -> localCache.remove(key), ttl.toMillis(), TimeUnit.MILLISECONDS); + } + + private void putToRedisCache(String key, Object value, Duration ttl) { + try { + redisTemplate.opsForValue().set(key, value, ttl); + } catch (Exception e) { + log.warn("Error putting to Redis cache: {}", e.getMessage()); + } + } +} +``` + +### 2. Write-Through Pattern +```java +// Write-Through реализация +@Service +public class WriteThroughUserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + public void createUser(User user) { + // Сохраняем в БД + user = userRepository.save(user); + + // Сохраняем в кэш + String cacheKey = "user:" + user.getId(); + redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); + } + + public void updateUser(User user) { + // Обновляем БД + user = userRepository.save(user); + + // Обновляем кэш + String cacheKey = "user:" + user.getId(); + redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); + } +} +``` + +### 3. Write-Behind Pattern +```java +// Write-Behind реализация +@Service +public class WriteBehindUserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + @Async + public void createUser(User user) { + // Сохраняем в кэш сразу + String cacheKey = "user:" + user.getId(); + redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); + + // Сохраняем в БД асинхронно + CompletableFuture.runAsync(() -> { + userRepository.save(user); + }); + } +} +``` + +## Многоуровневое кэширование + +### 1. L1 Cache (Application Level) +```java +// L1 кэш (в памяти приложения) +@Service +public class L1CacheUserService { + private final Map localCache = new ConcurrentHashMap<>(); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + @Autowired + private UserRepository userRepository; + + public User getUser(Long id) { + return localCache.computeIfAbsent(id, key -> { + User user = userRepository.findById(key); + if (user != null) { + // Автоматическое удаление через 5 минут + scheduler.schedule(() -> localCache.remove(key), 5, TimeUnit.MINUTES); + } + return user; + }); + } +} +``` + +### 2. L2 Cache (Distributed Cache) +```java +// L2 кэш (распределенный) +@Service +public class L2CacheUserService { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private UserRepository userRepository; + + public User getUser(Long id) { + String cacheKey = "user:" + id; + + User user = redisTemplate.opsForValue().get(cacheKey); + if (user == null) { + user = userRepository.findById(id); + if (user != null) { + redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); + } + } + + return user; + } +} +``` diff --git a/modules/module10/sections/05-code-optimization-minification.md b/modules/module10/sections/05-code-optimization-minification.md new file mode 100644 index 0000000..7c8c753 --- /dev/null +++ b/modules/module10/sections/05-code-optimization-minification.md @@ -0,0 +1,357 @@ +# Оптимизация кода и минификация + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Принципы оптимизации кода + +### 1. Минификация JavaScript/TypeScript + +**Цели минификации:** +- Уменьшение размера файлов +- Ускорение загрузки страниц +- Снижение трафика +- Улучшение производительности + +**Инструменты минификации:** + +```javascript +// webpack.config.js - Конфигурация минификации +const TerserPlugin = require('terser-webpack-plugin'); +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); + +module.exports = { + mode: 'production', + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: { + drop_console: true, // Удаление console.log + drop_debugger: true, // Удаление debugger + pure_funcs: ['console.log'], // Удаление чистых функций + dead_code: true, // Удаление мертвого кода + unused: true, // Удаление неиспользуемых переменных + }, + mangle: { + toplevel: true, // Обфускация глобальных переменных + properties: { + regex: /^_/ // Обфускация приватных свойств + } + }, + format: { + comments: false // Удаление комментариев + } + }, + extractComments: false + }), + new CssMinimizerPlugin({ + minimizerOptions: { + preset: [ + 'default', + { + discardComments: { removeAll: true }, + normalizeWhitespace: true, + colormin: true, + minifySelectors: true + } + ] + } + }) + ], + splitChunks: { + chunks: 'all', + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + chunks: 'all', + }, + common: { + name: 'common', + minChunks: 2, + chunks: 'all', + enforce: true + } + } + } + } +}; +``` + +**Java примеры - Оптимизация сборки:** + +```gradle +// build.gradle - Оптимизация Java приложения +plugins { + id 'java' + id 'org.springframework.boot' version '3.0.0' +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType(JavaCompile) { + options.compilerArgs += [ + '-parameters', // Сохранение имен параметров + '-Xlint:unchecked', // Предупреждения о небезопасных операциях + '-Xlint:deprecation' // Предупреждения об устаревших API + ] +} + +// Оптимизация JAR файла +jar { + enabled = false + archiveClassifier = '' +} + +bootJar { + enabled = true + archiveClassifier = '' + + // Исключение ненужных файлов + exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA' + + // Оптимизация манифеста + manifest { + attributes( + 'Main-Class': 'com.example.Application', + 'Implementation-Version': project.version, + 'Built-By': System.getProperty('user.name'), + 'Built-Date': new Date().toString() + ) + } +} +``` + +**Rust примеры - Оптимизация компиляции:** + +```toml +# Cargo.toml - Оптимизация Rust приложения +[package] +name = "my-app" +version = "0.1.0" +edition = "2021" + +[profile.release] +# Максимальная оптимизация +opt-level = 3 +# Оптимизация размера +lto = true +# Удаление отладочной информации +debug = false +# Оптимизация кода +codegen-units = 1 +# Оптимизация паники +panic = "abort" +# Оптимизация размера +strip = true + +[profile.release.package."*"] +# Оптимизация всех зависимостей +opt-level = 3 + +# Дополнительные флаги компиляции +[build] +rustflags = [ + "-C", "target-cpu=native", # Оптимизация под текущий CPU + "-C", "link-arg=-s", # Удаление символов + "-C", "opt-level=3", # Максимальная оптимизация + "-C", "lto=fat", # Link Time Optimization +] +``` + +**Python примеры - Оптимизация:** + +```python +# Оптимизация Python приложения +import sys +from typing import List, Dict, Optional +from dataclasses import dataclass +from functools import lru_cache +import asyncio +import aiohttp + +# Оптимизированная структура данных +@dataclass(frozen=True, slots=True) # slots для экономии памяти +class OptimizedUser: + id: int + name: str + email: str + status: int # 1 = active, 0 = inactive + + def is_active(self) -> bool: + return self.status == 1 + +# Кэширование для оптимизации +class OptimizedUserService: + def __init__(self): + self._users_cache: Dict[int, OptimizedUser] = {} + self._session: Optional[aiohttp.ClientSession] = None + + async def __aenter__(self): + self._session = aiohttp.ClientSession( + connector=aiohttp.TCPConnector( + limit=100, # Пул соединений + limit_per_host=30, + ttl_dns_cache=300, + use_dns_cache=True, + ), + timeout=aiohttp.ClientTimeout(total=30) + ) + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self._session: + await self._session.close() + + # LRU кэш для часто используемых методов + @lru_cache(maxsize=1000) + def get_user_by_id_cached(self, user_id: int) -> Optional[OptimizedUser]: + return self._users_cache.get(user_id) + + # Batch операции + async def get_users_batch(self, user_ids: List[int]) -> List[OptimizedUser]: + tasks = [self._fetch_user_async(user_id) for user_id in user_ids] + results = await asyncio.gather(*tasks, return_exceptions=True) + + users = [] + for result in results: + if isinstance(result, OptimizedUser): + users.append(result) + + return users +``` + +### 2. Оптимизация изображений + +```javascript +// webpack.config.js - Оптимизация изображений +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin'); + +module.exports = { + module: { + rules: [ + { + test: /\.(jpe?g|png|gif|svg)$/i, + type: 'asset', + parser: { + dataUrlCondition: { + maxSize: 8 * 1024 // 8kb + } + }, + generator: { + filename: 'images/[name].[hash:8][ext]' + } + } + ] + }, + optimization: { + minimizer: [ + new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + ['gifsicle', { interlaced: true }], + ['jpegtran', { progressive: true }], + ['optipng', { optimizationLevel: 5 }], + [ + 'svgo', + { + plugins: [ + { + name: 'preset-default', + params: { + overrides: { + removeViewBox: false, + }, + }, + }, + ], + }, + ], + ], + }, + }, + }), + ], + }, +}; +``` + +### 3. Оптимизация CSS + +```css +/* Оптимизированный CSS */ +/* Удаление неиспользуемых стилей */ +/* Минификация селекторов */ +/* Объединение правил */ + +/* До оптимизации */ +.header { + background-color: #ffffff; + padding: 20px; + margin: 0; + border: 1px solid #cccccc; +} + +.header .title { + font-size: 24px; + font-weight: bold; + color: #333333; +} + +/* После оптимизации */ +.header{background:#fff;padding:20px;margin:0;border:1px solid #ccc} +.header .title{font:bold 24px/1 sans-serif;color:#333} +``` + +### 4. Оптимизация загрузки ресурсов + +```html + +Описание + + + + + + + + + + + + +``` + +### 5. Оптимизация базы данных + +```sql +-- Оптимизация запросов +-- Использование индексов +CREATE INDEX idx_user_email ON users(email); +CREATE INDEX idx_order_date ON orders(created_at); + +-- Оптимизация JOIN +SELECT u.id, u.name, COUNT(o.id) as order_count +FROM users u +LEFT JOIN orders o ON u.id = o.user_id +WHERE u.created_at > '2023-01-01' +GROUP BY u.id, u.name; + +-- Партиционирование таблиц +CREATE TABLE orders_2023 PARTITION OF orders +FOR VALUES FROM ('2023-01-01') TO ('2024-01-01'); +``` diff --git a/modules/module10/sections/06-network-optimization.md b/modules/module10/sections/06-network-optimization.md new file mode 100644 index 0000000..8961a13 --- /dev/null +++ b/modules/module10/sections/06-network-optimization.md @@ -0,0 +1,102 @@ +# Оптимизация сети + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Оптимизация сети + +### 1. Connection Pooling +```java +// Оптимизация HTTP соединений +@Configuration +public class HttpClientConfig { + @Bean + public RestTemplate restTemplate() { + HttpComponentsClientHttpRequestFactory factory = + new HttpComponentsClientHttpRequestFactory(); + + // Настройка пула соединений + PoolingHttpClientConnectionManager connectionManager = + new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(100); + connectionManager.setDefaultMaxPerRoute(20); + + CloseableHttpClient httpClient = HttpClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(RequestConfig.custom() + .setConnectTimeout(5000) + .setSocketTimeout(10000) + .build()) + .build(); + + factory.setHttpClient(httpClient); + return new RestTemplate(factory); + } +} +``` + +### 2. Compression +```java +// Сжатие HTTP ответов +@Configuration +public class CompressionConfig { + @Bean + public FilterRegistrationBean compressionFilter() { + FilterRegistrationBean registration = + new FilterRegistrationBean<>(); + registration.setFilter(new CompressionFilter()); + registration.addUrlPatterns("/*"); + registration.setName("compressionFilter"); + registration.setOrder(1); + return registration; + } +} + +@Component +public class CompressionFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + String acceptEncoding = httpRequest.getHeader("Accept-Encoding"); + if (acceptEncoding != null && acceptEncoding.contains("gzip")) { + httpResponse.setHeader("Content-Encoding", "gzip"); + GzipResponseWrapper gzipResponse = new GzipResponseWrapper(httpResponse); + chain.doFilter(request, gzipResponse); + gzipResponse.finish(); + } else { + chain.doFilter(request, response); + } + } +} +``` + +### 3. CDN Integration +```java +// Интеграция с CDN +@Service +public class CDNService { + @Value("${cdn.base-url}") + private String cdnBaseUrl; + + public String getCDNUrl(String path) { + return cdnBaseUrl + "/" + path; + } + + public void uploadToCDN(String path, byte[] content) { + // Загрузка контента в CDN + // Реализация зависит от выбранного CDN провайдера + } + + public void invalidateCDNCache(String path) { + // Инвалидация кэша CDN + // Реализация зависит от выбранного CDN провайдера + } +} +``` diff --git a/modules/module10/sections/07-capacity-planning.md b/modules/module10/sections/07-capacity-planning.md new file mode 100644 index 0000000..77c2c38 --- /dev/null +++ b/modules/module10/sections/07-capacity-planning.md @@ -0,0 +1,116 @@ +# Capacity Planning + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Процесс планирования мощности + +### 1. Current Capacity Analysis +```java +// Анализ текущей производительности +@Component +public class CapacityAnalyzer { + private final MeterRegistry meterRegistry; + + public CapacityAnalyzer(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @Scheduled(fixedRate = 60000) // каждую минуту + public void analyzeCapacity() { + // Анализ CPU + double cpuUsage = getCpuUsage(); + log.info("CPU usage: {}%", cpuUsage * 100); + + // Анализ памяти + double memoryUsage = getMemoryUsage(); + log.info("Memory usage: {}%", memoryUsage * 100); + + // Анализ пропускной способности + double throughput = getThroughput(); + log.info("Throughput: {} RPS", throughput); + + // Анализ задержки + double latency = getLatency(); + log.info("Latency: {} ms", latency); + + // Предупреждения + if (cpuUsage > 0.8) { + log.warn("High CPU usage detected!"); + } + if (memoryUsage > 0.8) { + log.warn("High memory usage detected!"); + } + } +} +``` + +### 2. Load Forecasting +```java +// Прогнозирование нагрузки +@Service +public class LoadForecaster { + @Autowired + private MetricsRepository metricsRepository; + + public LoadForecast forecastLoad(int days) { + // Получаем исторические данные + List historicalData = metricsRepository + .findByDateRange(LocalDateTime.now().minusDays(30), LocalDateTime.now()); + + // Анализируем тренды + double growthRate = calculateGrowthRate(historicalData); + + // Прогнозируем нагрузку + double currentLoad = getCurrentLoad(); + double forecastedLoad = currentLoad * Math.pow(1 + growthRate, days); + + return new LoadForecast(forecastedLoad, growthRate, days); + } + + private double calculateGrowthRate(List data) { + // Простой расчет темпа роста + if (data.size() < 2) return 0.0; + + double firstValue = data.get(0).getValue(); + double lastValue = data.get(data.size() - 1).getValue(); + + return (lastValue - firstValue) / firstValue / data.size(); + } +} +``` + +### 3. Resource Planning +```yaml +# Планирование ресурсов +capacity_planning: + current_capacity: + cpu: "4 cores" + memory: "8 GB" + storage: "100 GB" + network: "1 Gbps" + + forecasted_load: + peak_rps: 1000 + average_rps: 500 + growth_rate: "20% per month" + + resource_requirements: + cpu: + current: "80% utilization" + target: "70% utilization" + required: "6 cores" + + memory: + current: "75% utilization" + target: "65% utilization" + required: "12 GB" + + storage: + current: "60% utilization" + target: "50% utilization" + required: "150 GB" +``` diff --git a/modules/module10/sections/08-performance-testing.md b/modules/module10/sections/08-performance-testing.md new file mode 100644 index 0000000..d75def4 --- /dev/null +++ b/modules/module10/sections/08-performance-testing.md @@ -0,0 +1,174 @@ +# Performance Testing + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Типы тестов производительности + +### 1. Load Testing +```java +// Load тестирование с JMeter +@Component +public class LoadTestRunner { + public void runLoadTest() { + // Настройка JMeter теста + JMeterEngine engine = new StandardJMeterEngine(); + + // Загрузка тестового плана + HashTree testPlanTree = new HashTree(); + TestPlan testPlan = new TestPlan(); + testPlan.setName("User Service Load Test"); + + // Настройка потока + ThreadGroup threadGroup = new ThreadGroup(); + threadGroup.setName("Users"); + threadGroup.setNumThreads(100); + threadGroup.setRampUp(60); + threadGroup.setDuration(300); // 5 минут + + // Настройка HTTP запроса + HTTPSamplerProxy httpSampler = new HTTPSamplerProxy(); + httpSampler.setDomain("localhost"); + httpSampler.setPort(8080); + httpSampler.setPath("/api/users"); + httpSampler.setMethod("GET"); + + // Добавление элементов в тест + testPlanTree.add(testPlan); + testPlanTree.add(testPlan, threadGroup); + testPlanTree.add(threadGroup, httpSampler); + + // Запуск теста + engine.configure(testPlanTree); + engine.run(); + } +} +``` + +### 2. Stress Testing +```java +// Stress тестирование +@Service +public class StressTestService { + @Autowired + private UserService userService; + + public void runStressTest() { + int numberOfThreads = 200; + int requestsPerThread = 1000; + + ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); + CountDownLatch latch = new CountDownLatch(numberOfThreads); + + for (int i = 0; i < numberOfThreads; i++) { + executor.submit(() -> { + try { + for (int j = 0; j < requestsPerThread; j++) { + // Выполняем запрос + userService.getUser((long) (Math.random() * 1000)); + + // Небольшая задержка + Thread.sleep(10); + } + } catch (Exception e) { + log.error("Stress test error", e); + } finally { + latch.countDown(); + } + }); + } + + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + executor.shutdown(); + } +} +``` + +### 3. Endurance Testing +```java +// Endurance тестирование +@Component +public class EnduranceTestRunner { + @Autowired + private UserService userService; + + @Scheduled(fixedRate = 1000) // каждую секунду + public void runEnduranceTest() { + try { + // Выполняем запрос + userService.getUser(1L); + + // Логируем метрики + log.info("Endurance test - request completed"); + + } catch (Exception e) { + log.error("Endurance test error", e); + } + } +} +``` + +## Автоматизация тестов производительности + +### 1. Automated Performance Tests +```java +// Автоматизированные performance тесты +@SpringBootTest +@TestPropertySource(properties = { + "spring.datasource.url=jdbc:h2:mem:testdb", + "spring.jpa.hibernate.ddl-auto=create-drop" +}) +public class PerformanceTestSuite { + + @Autowired + private UserService userService; + + @Test + public void testUserServicePerformance() { + // Подготовка данных + List users = createTestUsers(1000); + + // Измерение времени выполнения + long startTime = System.currentTimeMillis(); + + for (User user : users) { + userService.createUser(user); + } + + long endTime = System.currentTimeMillis(); + long duration = endTime - startTime; + + // Проверка производительности + assertThat(duration).isLessThan(5000); // менее 5 секунд + + // Проверка пропускной способности + double throughput = (double) users.size() / duration * 1000; + assertThat(throughput).isGreaterThan(100); // более 100 RPS + } + + @Test + public void testDatabaseQueryPerformance() { + // Подготовка данных + createTestUsers(10000); + + // Измерение времени выполнения запроса + long startTime = System.currentTimeMillis(); + + List users = userService.findUsersByName("Test"); + + long endTime = System.currentTimeMillis(); + long duration = endTime - startTime; + + // Проверка производительности + assertThat(duration).isLessThan(1000); // менее 1 секунды + } +} +``` diff --git a/modules/module10/sections/08-practical-tasks.md b/modules/module10/sections/08-practical-tasks.md new file mode 100644 index 0000000..d048514 --- /dev/null +++ b/modules/module10/sections/08-practical-tasks.md @@ -0,0 +1,24 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +### Задание 1: Профилирование +1. Настройте профилирование приложения +2. Проведите анализ производительности +3. Выявите узкие места + +### Задание 2: Оптимизация базы данных +1. Проанализируйте медленные запросы +2. Оптимизируйте индексы +3. Настройте пул соединений + +### Задание 3: Performance Testing +1. Создайте load тесты +2. Проведите stress тестирование +3. Проанализируйте результаты + +--- \ No newline at end of file diff --git a/modules/module10/sections/09-practical-tasks.md b/modules/module10/sections/09-practical-tasks.md new file mode 100644 index 0000000..4e7fb1d --- /dev/null +++ b/modules/module10/sections/09-practical-tasks.md @@ -0,0 +1,42 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 10](../README.md) + +--- + +## Задание 1: Профилирование +1. Настройте профилирование приложения +2. Проведите анализ производительности +3. Выявите узкие места + +## Задание 2: Оптимизация базы данных +1. Проанализируйте медленные запросы +2. Оптимизируйте индексы +3. Настройте пул соединений + +## Задание 3: Performance Testing +1. Создайте load тесты +2. Проведите stress тестирование +3. Проанализируйте результаты + +## Дополнительные ресурсы + +### Инструменты +- **Profiling**: JProfiler, VisualVM, YourKit +- **APM**: New Relic, AppDynamics, Datadog +- **Load Testing**: JMeter, Gatling, K6 +- **Database**: pgAdmin, MySQL Workbench, MongoDB Compass + +### Книги +- "Java Performance" by Scott Oaks +- "High Performance Browser Networking" by Ilya Grigorik +- "Web Performance in Action" by Jeremy Wagner + +## Контрольные вопросы +1. Какие метрики производительности наиболее важны? +2. Как определить узкие места в приложении? +3. Какие стратегии кэширования вы знаете? +4. Как оптимизировать базу данных? +5. Что такое capacity planning? diff --git a/modules/module10/theory.md b/modules/module10/theory.md new file mode 100644 index 0000000..d0d89a6 --- /dev/null +++ b/modules/module10/theory.md @@ -0,0 +1,2141 @@ +# Модуль 10: Производительность и оптимизация + +## Содержание модуля +1. [Основы производительности](#основы-производительности) +2. [Профилирование и анализ](#профилирование-и-анализ) +3. [Оптимизация базы данных](#оптимизация-базы-данных) +4. [Кэширование](#кэширование) +5. [Оптимизация кода и минификация](#оптимизация-кода-и-минификация) +6. [Оптимизация сети](#оптимизация-сети) +7. [Capacity Planning](#capacity-planning) +8. [Performance Testing](#performance-testing) + +--- + +## Основы производительности + +### Что такое производительность + +**Производительность** — это способность системы выполнять задачи за определенное время с использованием доступных ресурсов. + +### Метрики производительности + +#### 1. Throughput (Пропускная способность) +**Определение:** Количество операций, выполняемых за единицу времени. + +**Технический пример - Advanced Performance Monitoring System:** + +```java +// Advanced Performance Metrics Collector +@Component +public class AdvancedPerformanceMetrics { + + private final MeterRegistry meterRegistry; + private final Counter requestCounter; + private final Timer requestTimer; + private final Gauge activeConnections; + private final DistributionSummary responseSize; + private final Counter errorCounter; + + public AdvancedPerformanceMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + + // Request metrics + this.requestCounter = Counter.builder("requests.total") + .description("Total number of requests") + .tag("service", "user-service") + .register(meterRegistry); + + this.requestTimer = Timer.builder("request.duration") + .description("Request processing time") + .publishPercentiles(0.5, 0.95, 0.99) + .publishPercentileHistogram() + .register(meterRegistry); + + // Connection metrics + this.activeConnections = Gauge.builder("connections.active") + .description("Number of active connections") + .register(meterRegistry, this, AdvancedPerformanceMetrics::getActiveConnections); + + // Response size metrics + this.responseSize = DistributionSummary.builder("response.size") + .description("Response size in bytes") + .publishPercentiles(0.5, 0.95, 0.99) + .register(meterRegistry); + + // Error metrics + this.errorCounter = Counter.builder("errors.total") + .description("Total number of errors") + .register(meterRegistry); + } + + public void recordRequest(Duration duration, int responseSizeBytes, boolean isError) { + requestCounter.increment(); + requestTimer.record(duration); + this.responseSize.record(responseSizeBytes); + + if (isError) { + errorCounter.increment(); + } + } + + public PerformanceReport generateReport() { + return PerformanceReport.builder() + .throughput(getThroughput()) + .averageLatency(getAverageLatency()) + .p95Latency(getP95Latency()) + .p99Latency(getP99Latency()) + .errorRate(getErrorRate()) + .activeConnections(getActiveConnections()) + .averageResponseSize(getAverageResponseSize()) + .timestamp(LocalDateTime.now()) + .build(); + } + + public double getThroughput() { + double totalTime = requestTimer.totalTime(TimeUnit.SECONDS); + return totalTime > 0 ? requestCounter.count() / totalTime : 0; + } + + public double getAverageLatency() { + return requestTimer.mean(TimeUnit.MILLISECONDS); + } + + public double getP95Latency() { + return requestTimer.percentile(0.95, TimeUnit.MILLISECONDS); + } + + public double getP99Latency() { + return requestTimer.percentile(0.99, TimeUnit.MILLISECONDS); + } + + public double getErrorRate() { + double totalRequests = requestCounter.count(); + return totalRequests > 0 ? (errorCounter.count() / totalRequests) * 100 : 0; + } + + private double getActiveConnections() { + return 0; // Placeholder + } + + public double getAverageResponseSize() { + return responseSize.mean(); + } +} + +// Performance Optimization Service +@Service +public class PerformanceOptimizationService { + + @Autowired + private AdvancedPerformanceMetrics metrics; + + @Autowired + private DatabaseOptimizationService dbOptimizationService; + + // Automatic performance optimization + @Scheduled(fixedRate = 60000) // Every minute + public void optimizePerformance() { + PerformanceReport report = metrics.generateReport(); + + if (needsOptimization(report)) { + log.warn("Performance optimization triggered: {}", report); + + if (report.getAverageLatency() > 1000) { + optimizeLatency(); + } + + if (report.getErrorRate() > 5) { + optimizeErrorHandling(); + } + + if (report.getThroughput() < 100) { + optimizeThroughput(); + } + } + } + + private boolean needsOptimization(PerformanceReport report) { + return report.getAverageLatency() > 1000 || + report.getErrorRate() > 5 || + report.getThroughput() < 100 || + report.getP95Latency() > 2000; + } + + private void optimizeLatency() { + log.info("Optimizing latency..."); + increaseConnectionPoolSize(); + enableCompression(); + optimizeJVMSettings(); + } + + private void optimizeErrorHandling() { + log.info("Optimizing error handling..."); + enableCircuitBreaker(); + enableRetryLogic(); + improveErrorLogging(); + } + + private void optimizeThroughput() { + log.info("Optimizing throughput..."); + increaseThreadPoolSize(); + enableAsyncProcessing(); + optimizeBatchProcessing(); + } + + private void increaseConnectionPoolSize() { + log.info("Increasing connection pool size"); + } + + private void enableCompression() { + log.info("Enabling compression"); + } + + private void optimizeJVMSettings() { + log.info("Optimizing JVM settings"); + } + + private void enableCircuitBreaker() { + log.info("Enabling circuit breaker"); + } + + private void enableRetryLogic() { + log.info("Enabling retry logic"); + } + + private void improveErrorLogging() { + log.info("Improving error logging"); + } + + private void increaseThreadPoolSize() { + log.info("Increasing thread pool size"); + } + + private void enableAsyncProcessing() { + log.info("Enabling async processing"); + } + + private void optimizeBatchProcessing() { + log.info("Optimizing batch processing"); + } +} +``` + +#### 2. Latency (Задержка) +**Определение:** Время выполнения одной операции. + +```java +// Измерение latency +@Service +public class UserService { + private final Timer requestTimer; + + public UserService(MeterRegistry meterRegistry) { + this.requestTimer = Timer.builder("user.service.duration") + .register(meterRegistry); + } + + public User getUser(Long id) { + return requestTimer.recordCallable(() -> { + // бизнес-логика + return userRepository.findById(id); + }); + } +} +``` + +#### 3. Resource Utilization (Использование ресурсов) +**Определение:** Процент использования доступных ресурсов. + +```java +// Мониторинг ресурсов +@Component +public class ResourceMonitor { + private final Gauge cpuUsage; + private final Gauge memoryUsage; + private final Gauge diskUsage; + + public ResourceMonitor(MeterRegistry meterRegistry) { + this.cpuUsage = Gauge.builder("system.cpu.usage") + .register(meterRegistry, this, ResourceMonitor::getCpuUsage); + this.memoryUsage = Gauge.builder("system.memory.usage") + .register(meterRegistry, this, ResourceMonitor::getMemoryUsage); + this.diskUsage = Gauge.builder("system.disk.usage") + .register(meterRegistry, this, ResourceMonitor::getDiskUsage); + } + + private double getCpuUsage() { + OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); + return osBean.getProcessCpuLoad(); + } + + private double getMemoryUsage() { + MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); + return (double) heapUsage.getUsed() / heapUsage.getMax(); + } + + private double getDiskUsage() { + File root = new File("/"); + long totalSpace = root.getTotalSpace(); + long freeSpace = root.getFreeSpace(); + return (double) (totalSpace - freeSpace) / totalSpace; + } +} +``` + +### Производительность по уровням + +#### 1. Application Level +```java +// Оптимизация на уровне приложения +@Service +public class OptimizedUserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private UserCache userCache; + + // Оптимизация: кэширование + @Cacheable("users") + public User getUser(Long id) { + return userRepository.findById(id); + } + + // Оптимизация: batch операции + public List getUsers(List ids) { + return userRepository.findByIdIn(ids); + } + + // Оптимизация: асинхронная обработка + @Async + public CompletableFuture processUserAsync(User user) { + // асинхронная обработка + return CompletableFuture.completedFuture(null); + } +} +``` + +#### 2. Database Level +```sql +-- Оптимизация запросов +-- Плохо: N+1 проблема +SELECT * FROM users; +SELECT * FROM orders WHERE user_id = 1; +SELECT * FROM orders WHERE user_id = 2; +-- ... для каждого пользователя + +-- Хорошо: JOIN запрос +SELECT u.*, o.* +FROM users u +LEFT JOIN orders o ON u.id = o.user_id; + +-- Хорошо: индексы +CREATE INDEX idx_user_email ON users(email); +CREATE INDEX idx_order_user_id ON orders(user_id); +CREATE INDEX idx_order_status ON orders(status); +``` + +#### 3. Infrastructure Level +```yaml +# Оптимизация инфраструктуры +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service +spec: + replicas: 3 + template: + spec: + containers: + - name: user-service + image: user-service:latest + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + env: + - name: JAVA_OPTS + value: "-Xms256m -Xmx512m -XX:+UseG1GC" +``` + +--- + +## Профилирование и анализ + +### Инструменты профилирования + +#### 1. JProfiler +```java +// Профилирование с JProfiler +@Service +public class ProfiledUserService { + @Autowired + private UserRepository userRepository; + + public User getUser(Long id) { + // JProfiler покажет время выполнения этого метода + return userRepository.findById(id); + } + + public List searchUsers(String query) { + // Профилирование покажет узкие места + List users = userRepository.findByNameContaining(query); + + // Профилирование покажет время обработки + return users.stream() + .filter(user -> user.isActive()) + .collect(Collectors.toList()); + } +} +``` + +#### 2. VisualVM +```java +// Мониторинг с VisualVM +@Component +public class PerformanceMonitor { + private final MemoryMXBean memoryBean; + private final ThreadMXBean threadBean; + + public PerformanceMonitor() { + this.memoryBean = ManagementFactory.getMemoryMXBean(); + this.threadBean = ManagementFactory.getThreadMXBean(); + } + + @Scheduled(fixedRate = 5000) + public void logPerformanceMetrics() { + // Мониторинг памяти + MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); + log.info("Heap usage: {} / {}", heapUsage.getUsed(), heapUsage.getMax()); + + // Мониторинг потоков + int threadCount = threadBean.getThreadCount(); + log.info("Thread count: {}", threadCount); + + // Мониторинг CPU + OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); + double cpuUsage = osBean.getProcessCpuLoad(); + log.info("CPU usage: {}%", cpuUsage * 100); + } +} +``` + +#### 3. Application Performance Monitoring (APM) +```java +// APM интеграция +@Service +public class APMUserService { + @Autowired + private NewRelic newRelic; + + public User getUser(Long id) { + newRelic.addCustomParameter("user.id", id); + + try { + User user = userRepository.findById(id); + newRelic.recordMetric("User/Found", 1); + return user; + } catch (Exception e) { + newRelic.recordMetric("User/Error", 1); + newRelic.recordException(e); + throw e; + } + } + + public List searchUsers(String query) { + newRelic.addCustomParameter("search.query", query); + + List users = userRepository.findByNameContaining(query); + newRelic.recordMetric("User/SearchResults", users.size()); + + return users; + } +} +``` + +### Анализ производительности + +#### 1. Bottleneck Analysis +```java +// Анализ узких мест +@Service +public class BottleneckAnalyzer { + private final Timer databaseTimer; + private final Timer cacheTimer; + private final Timer networkTimer; + + public BottleneckAnalyzer(MeterRegistry meterRegistry) { + this.databaseTimer = Timer.builder("database.operation.duration") + .register(meterRegistry); + this.cacheTimer = Timer.builder("cache.operation.duration") + .register(meterRegistry); + this.networkTimer = Timer.builder("network.operation.duration") + .register(meterRegistry); + } + + public User getUser(Long id) { + // Анализ времени выполнения каждой операции + User user = databaseTimer.recordCallable(() -> { + return userRepository.findById(id); + }); + + if (user != null) { + cacheTimer.recordCallable(() -> { + userCache.put(id, user); + }); + } + + return user; + } +} +``` + +#### 2. Memory Analysis +```java +// Анализ памяти +@Component +public class MemoryAnalyzer { + private final MemoryMXBean memoryBean; + + public MemoryAnalyzer() { + this.memoryBean = ManagementFactory.getMemoryMXBean(); + } + + @Scheduled(fixedRate = 10000) + public void analyzeMemory() { + MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); + MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage(); + + log.info("Heap: {} / {} ({}%)", + heapUsage.getUsed(), + heapUsage.getMax(), + (heapUsage.getUsed() * 100) / heapUsage.getMax()); + + log.info("Non-Heap: {} / {} ({}%)", + nonHeapUsage.getUsed(), + nonHeapUsage.getMax(), + (nonHeapUsage.getUsed() * 100) / nonHeapUsage.getMax()); + + // Анализ утечек памяти + if (heapUsage.getUsed() > heapUsage.getMax() * 0.8) { + log.warn("High memory usage detected!"); + } + } +} +``` + +--- + +## Оптимизация базы данных + +### Оптимизация запросов + +#### 1. Query Optimization +```sql +-- Оптимизация запросов +-- Плохо: медленный запрос +SELECT * FROM users u +JOIN orders o ON u.id = o.user_id +WHERE u.created_at > '2023-01-01' +AND o.status = 'COMPLETED'; + +-- Хорошо: оптимизированный запрос +SELECT u.id, u.name, u.email, COUNT(o.id) as order_count +FROM users u +JOIN orders o ON u.id = o.user_id +WHERE u.created_at > '2023-01-01' +AND o.status = 'COMPLETED' +GROUP BY u.id, u.name, u.email; + +-- Хорошо: использование индексов +CREATE INDEX idx_user_created_at ON users(created_at); +CREATE INDEX idx_order_status ON orders(status); +CREATE INDEX idx_order_user_status ON orders(user_id, status); +``` + +#### 2. Connection Pooling +```java +// Настройка пула соединений +@Configuration +public class DatabaseConfig { + @Bean + public DataSource dataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb"); + config.setUsername("user"); + config.setPassword("password"); + + // Оптимизация пула соединений + config.setMaximumPoolSize(20); + config.setMinimumIdle(5); + config.setConnectionTimeout(30000); + config.setIdleTimeout(600000); + config.setMaxLifetime(1800000); + config.setLeakDetectionThreshold(60000); + + return new HikariDataSource(config); + } +} +``` + +#### 3. Batch Operations +```java +// Batch операции +@Repository +public class OptimizedUserRepository { + @Autowired + private JdbcTemplate jdbcTemplate; + + // Batch вставка + public void saveUsers(List users) { + String sql = "INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)"; + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + User user = users.get(i); + ps.setString(1, user.getName()); + ps.setString(2, user.getEmail()); + ps.setTimestamp(3, Timestamp.valueOf(user.getCreatedAt())); + } + + @Override + public int getBatchSize() { + return users.size(); + } + }); + } + + // Batch обновление + public void updateUsers(List users) { + String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?"; + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + User user = users.get(i); + ps.setString(1, user.getName()); + ps.setString(2, user.getEmail()); + ps.setLong(3, user.getId()); + } + + @Override + public int getBatchSize() { + return users.size(); + } + }); + } +} +``` + +### Настройка базы данных + +#### 1. Index Optimization +```sql +-- Оптимизация индексов +-- Анализ использования индексов +EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'user@example.com'; + +-- Создание составных индексов +CREATE INDEX idx_user_email_status ON users(email, status); + +-- Создание частичных индексов +CREATE INDEX idx_active_users ON users(email) WHERE status = 'ACTIVE'; + +-- Создание функциональных индексов +CREATE INDEX idx_user_lower_email ON users(LOWER(email)); +``` + +#### 2. Query Plan Analysis +```sql +-- Анализ планов выполнения +EXPLAIN (ANALYZE, BUFFERS) +SELECT u.name, COUNT(o.id) as order_count +FROM users u +LEFT JOIN orders o ON u.id = o.user_id +WHERE u.created_at > '2023-01-01' +GROUP BY u.id, u.name; + +-- Анализ статистики +ANALYZE users; +ANALYZE orders; + +-- Обновление статистики +UPDATE pg_stat_user_tables +SET n_tup_ins = 0, n_tup_upd = 0, n_tup_del = 0 +WHERE relname = 'users'; +``` + +--- + +## Кэширование + +### Стратегии кэширования + +#### 1. Cache-Aside Pattern +**Технический пример - Advanced Multi-Level Caching System:** + +```java +// Advanced Cache Management Service +@Service +public class AdvancedCacheService { + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private CacheMetricsService cacheMetricsService; + + // Multi-level caching + private final Map localCache = new ConcurrentHashMap<>(); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); + + public T get(String cacheKey, Class type, Supplier dataLoader) { + // Level 1: Local cache + T value = getFromLocalCache(cacheKey, type); + if (value != null) { + cacheMetricsService.recordCacheHit(cacheKey, CacheLevel.LOCAL); + return value; + } + + // Level 2: Redis cache + value = getFromRedisCache(cacheKey, type); + if (value != null) { + putToLocalCache(cacheKey, value, Duration.ofMinutes(5)); + cacheMetricsService.recordCacheHit(cacheKey, CacheLevel.REDIS); + return value; + } + + // Level 3: Data source + cacheMetricsService.recordCacheMiss(cacheKey); + value = dataLoader.get(); + + if (value != null) { + putToLocalCache(cacheKey, value, Duration.ofMinutes(5)); + putToRedisCache(cacheKey, value, Duration.ofMinutes(30)); + } + + return value; + } + + public CompletableFuture getAsync(String cacheKey, Class type, Supplier dataLoader) { + return CompletableFuture.supplyAsync(() -> get(cacheKey, type, dataLoader), scheduler); + } + + public List getBatch(List cacheKeys, Class type, Function, List> dataLoader) { + List results = new ArrayList<>(); + List missingKeys = new ArrayList<>(); + + // Check local cache first + for (String key : cacheKeys) { + T value = getFromLocalCache(key, type); + if (value != null) { + results.add(value); + cacheMetricsService.recordCacheHit(key, CacheLevel.LOCAL); + } else { + missingKeys.add(key); + } + } + + if (missingKeys.isEmpty()) { + return results; + } + + // Check Redis cache + List redisValues = getFromRedisCacheBatch(missingKeys, type); + List stillMissingKeys = new ArrayList<>(); + + for (int i = 0; i < missingKeys.size(); i++) { + String key = missingKeys.get(i); + T value = redisValues.get(i); + + if (value != null) { + results.add(value); + putToLocalCache(key, value, Duration.ofMinutes(5)); + cacheMetricsService.recordCacheHit(key, CacheLevel.REDIS); + } else { + stillMissingKeys.add(key); + } + } + + // Load missing data + if (!stillMissingKeys.isEmpty()) { + List loadedData = dataLoader.apply(stillMissingKeys); + + for (int i = 0; i < stillMissingKeys.size(); i++) { + String key = stillMissingKeys.get(i); + T value = loadedData.get(i); + + if (value != null) { + results.add(value); + putToLocalCache(key, value, Duration.ofMinutes(5)); + putToRedisCache(key, value, Duration.ofMinutes(30)); + } + + cacheMetricsService.recordCacheMiss(key); + } + } + + return results; + } + + public void put(String cacheKey, Object value) { + putToLocalCache(cacheKey, value, Duration.ofMinutes(5)); + putToRedisCache(cacheKey, value, Duration.ofMinutes(30)); + cacheMetricsService.recordCachePut(cacheKey); + } + + public void evict(String cacheKey) { + localCache.remove(cacheKey); + redisTemplate.delete(cacheKey); + cacheMetricsService.recordCacheEvict(cacheKey); + } + + public void evictPattern(String pattern) { + localCache.entrySet().removeIf(entry -> entry.getKey().matches(pattern)); + + Set keys = redisTemplate.keys(pattern); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + } + + cacheMetricsService.recordCacheEvictPattern(pattern); + } + + // Cache warming + public void warmCache(List keys, Supplier> dataLoader) { + log.info("Warming cache with {} keys", keys.size()); + + List data = dataLoader.get(); + + for (int i = 0; i < keys.size() && i < data.size(); i++) { + String key = keys.get(i); + Object value = data.get(i); + + if (value != null) { + putToLocalCache(key, value, Duration.ofMinutes(5)); + putToRedisCache(key, value, Duration.ofMinutes(30)); + } + } + + log.info("Cache warming completed"); + } + + private T getFromLocalCache(String key, Class type) { + Object value = localCache.get(key); + return type.cast(value); + } + + private T getFromRedisCache(String key, Class type) { + try { + Object value = redisTemplate.opsForValue().get(key); + return type.cast(value); + } catch (Exception e) { + log.warn("Error getting from Redis cache: {}", e.getMessage()); + return null; + } + } + + private List getFromRedisCacheBatch(List keys, Class type) { + try { + List values = redisTemplate.opsForValue().multiGet(keys); + return values.stream() + .map(type::cast) + .collect(Collectors.toList()); + } catch (Exception e) { + log.warn("Error getting batch from Redis cache: {}", e.getMessage()); + return keys.stream().map(k -> (T) null).collect(Collectors.toList()); + } + } + + private void putToLocalCache(String key, Object value, Duration ttl) { + localCache.put(key, value); + + scheduler.schedule(() -> localCache.remove(key), ttl.toMillis(), TimeUnit.MILLISECONDS); + } + + private void putToRedisCache(String key, Object value, Duration ttl) { + try { + redisTemplate.opsForValue().set(key, value, ttl); + } catch (Exception e) { + log.warn("Error putting to Redis cache: {}", e.getMessage()); + } + } +} + +// Cache Metrics Service +@Service +public class CacheMetricsService { + + private final MeterRegistry meterRegistry; + private final Map metrics = new ConcurrentHashMap<>(); + + public CacheMetricsService(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public void recordCacheHit(String cacheKey, CacheLevel level) { + String cacheName = extractCacheName(cacheKey); + CacheMetrics cacheMetrics = getOrCreateMetrics(cacheName); + cacheMetrics.recordHit(level); + + Counter.builder("cache.hits") + .tag("cache", cacheName) + .tag("level", level.name()) + .register(meterRegistry) + .increment(); + } + + public void recordCacheMiss(String cacheKey) { + String cacheName = extractCacheName(cacheKey); + CacheMetrics cacheMetrics = getOrCreateMetrics(cacheName); + cacheMetrics.recordMiss(); + + Counter.builder("cache.misses") + .tag("cache", cacheName) + .register(meterRegistry) + .increment(); + } + + public void recordCachePut(String cacheKey) { + String cacheName = extractCacheName(cacheKey); + + Counter.builder("cache.puts") + .tag("cache", cacheName) + .register(meterRegistry) + .increment(); + } + + public void recordCacheEvict(String cacheKey) { + String cacheName = extractCacheName(cacheKey); + + Counter.builder("cache.evicts") + .tag("cache", cacheName) + .register(meterRegistry) + .increment(); + } + + public void recordCacheEvictPattern(String pattern) { + Counter.builder("cache.evicts.pattern") + .tag("pattern", pattern) + .register(meterRegistry) + .increment(); + } + + public double getHitRate(String cacheName) { + CacheMetrics cacheMetrics = metrics.get(cacheName); + return cacheMetrics != null ? cacheMetrics.getHitRate() : 0.0; + } + + public double getMissRate(String cacheName) { + CacheMetrics cacheMetrics = metrics.get(cacheName); + return cacheMetrics != null ? cacheMetrics.getMissRate() : 0.0; + } + + public double getAverageAccessTime(String cacheName) { + CacheMetrics cacheMetrics = metrics.get(cacheName); + return cacheMetrics != null ? cacheMetrics.getAverageAccessTime() : 0.0; + } + + private CacheMetrics getOrCreateMetrics(String cacheName) { + return metrics.computeIfAbsent(cacheName, k -> new CacheMetrics()); + } + + private String extractCacheName(String cacheKey) { + if (cacheKey.contains(":")) { + return cacheKey.split(":")[0]; + } + return "default"; + } +} + +// Cache Level Enum +public enum CacheLevel { + LOCAL, REDIS, DATABASE +} + +// Cache Metrics +public class CacheMetrics { + private final AtomicLong hits = new AtomicLong(0); + private final AtomicLong misses = new AtomicLong(0); + private final AtomicLong puts = new AtomicLong(0); + private final AtomicLong evicts = new AtomicLong(0); + private final AtomicLong totalAccessTime = new AtomicLong(0); + private final AtomicLong accessCount = new AtomicLong(0); + + public void recordHit(CacheLevel level) { + hits.incrementAndGet(); + recordAccessTime(); + } + + public void recordMiss() { + misses.incrementAndGet(); + recordAccessTime(); + } + + public void recordPut() { + puts.incrementAndGet(); + } + + public void recordEvict() { + evicts.incrementAndGet(); + } + + private void recordAccessTime() { + long startTime = System.nanoTime(); + accessCount.incrementAndGet(); + totalAccessTime.addAndGet(System.nanoTime() - startTime); + } + + public double getHitRate() { + long total = hits.get() + misses.get(); + return total > 0 ? (double) hits.get() / total : 0.0; + } + + public double getMissRate() { + long total = hits.get() + misses.get(); + return total > 0 ? (double) misses.get() / total : 0.0; + } + + public double getAverageAccessTime() { + long count = accessCount.get(); + return count > 0 ? (double) totalAccessTime.get() / count : 0.0; + } +} +``` + +#### 2. Write-Through Pattern +```java +// Write-Through реализация +@Service +public class WriteThroughUserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + public void createUser(User user) { + // Сохраняем в БД + user = userRepository.save(user); + + // Сохраняем в кэш + String cacheKey = "user:" + user.getId(); + redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); + } + + public void updateUser(User user) { + // Обновляем БД + user = userRepository.save(user); + + // Обновляем кэш + String cacheKey = "user:" + user.getId(); + redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); + } +} +``` + +#### 3. Write-Behind Pattern +```java +// Write-Behind реализация +@Service +public class WriteBehindUserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + @Async + public void createUser(User user) { + // Сохраняем в кэш сразу + String cacheKey = "user:" + user.getId(); + redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); + + // Сохраняем в БД асинхронно + CompletableFuture.runAsync(() -> { + userRepository.save(user); + }); + } +} +``` + +### Многоуровневое кэширование + +#### 1. L1 Cache (Application Level) +```java +// L1 кэш (в памяти приложения) +@Service +public class L1CacheUserService { + private final Map localCache = new ConcurrentHashMap<>(); + private final Timer cacheTimer; + + public L1CacheUserService(MeterRegistry meterRegistry) { + this.cacheTimer = Timer.builder("l1.cache.duration") + .register(meterRegistry); + } + + public User getUser(Long id) { + return cacheTimer.recordCallable(() -> { + // Проверяем L1 кэш + User user = localCache.get(id); + if (user != null) { + return user; + } + + // Загружаем из L2 кэша или БД + user = loadUserFromL2OrDatabase(id); + if (user != null) { + localCache.put(id, user); + } + + return user; + }); + } +} +``` + +#### 2. L2 Cache (Distributed Cache) +```java +// L2 кэш (распределенный) +@Service +public class L2CacheUserService { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private UserRepository userRepository; + + public User getUser(Long id) { + String cacheKey = "user:" + id; + + // Проверяем L2 кэш + User user = redisTemplate.opsForValue().get(cacheKey); + if (user != null) { + return user; + } + + // Загружаем из БД + user = userRepository.findById(id); + if (user != null) { + // Сохраняем в L2 кэш + redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); + } + + return user; + } +} +``` + +--- + +## Оптимизация кода и минификация + +### Принципы оптимизации кода + +#### 1. Минификация JavaScript/TypeScript + +**Цели минификации:** +- Уменьшение размера файлов +- Ускорение загрузки страниц +- Снижение трафика +- Улучшение производительности + +**Инструменты минификации:** + +```javascript +// webpack.config.js - Конфигурация минификации +const TerserPlugin = require('terser-webpack-plugin'); +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); + +module.exports = { + mode: 'production', + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: { + drop_console: true, // Удаление console.log + drop_debugger: true, // Удаление debugger + pure_funcs: ['console.log'], // Удаление чистых функций + dead_code: true, // Удаление мертвого кода + unused: true, // Удаление неиспользуемых переменных + }, + mangle: { + toplevel: true, // Обфускация глобальных переменных + properties: { + regex: /^_/ // Обфускация приватных свойств + } + }, + format: { + comments: false // Удаление комментариев + } + }, + extractComments: false + }), + new CssMinimizerPlugin({ + minimizerOptions: { + preset: [ + 'default', + { + discardComments: { removeAll: true }, + normalizeWhitespace: true, + colormin: true, + minifySelectors: true + } + ] + } + }) + ], + splitChunks: { + chunks: 'all', + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + chunks: 'all', + }, + common: { + name: 'common', + minChunks: 2, + chunks: 'all', + enforce: true + } + } + } + } +}; +``` + +**Java примеры - Оптимизация сборки:** + +```gradle +// build.gradle - Оптимизация Java приложения +plugins { + id 'java' + id 'org.springframework.boot' version '3.0.0' +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType(JavaCompile) { + options.compilerArgs += [ + '-parameters', // Сохранение имен параметров + '-Xlint:unchecked', // Предупреждения о небезопасных операциях + '-Xlint:deprecation' // Предупреждения об устаревших API + ] +} + +// Оптимизация JAR файла +jar { + enabled = false + archiveClassifier = '' +} + +bootJar { + enabled = true + archiveClassifier = '' + + // Исключение ненужных файлов + exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA' + + // Оптимизация манифеста + manifest { + attributes( + 'Main-Class': 'com.example.Application', + 'Implementation-Version': project.version, + 'Built-By': System.getProperty('user.name'), + 'Built-Date': new Date().toString() + ) + } +} + +// ProGuard для обфускации и минификации +buildscript { + dependencies { + classpath 'com.guardsquare:proguard-gradle:7.2.2' + } +} + +apply plugin: 'com.guardsquare.proguard' + +proguard { + configurations { + release { + defaultConfiguration 'proguard-android-optimize.txt' + + // Правила для Spring Boot + configuration ''' + -keep class org.springframework.** { *; } + -keep class com.example.** { *; } + -dontwarn org.springframework.** + -dontwarn javax.annotation.** + + # Оптимизация + -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* + -optimizationpasses 5 + -allowaccessmodification + -repackageclasses '' + ''' + } + } +} +``` + +**Rust примеры - Оптимизация компиляции:** + +```toml +# Cargo.toml - Оптимизация Rust приложения +[package] +name = "my-app" +version = "0.1.0" +edition = "2021" + +[profile.release] +# Максимальная оптимизация +opt-level = 3 +# Оптимизация размера +lto = true +# Удаление отладочной информации +debug = false +# Оптимизация кода +codegen-units = 1 +# Оптимизация паники +panic = "abort" +# Оптимизация размера +strip = true + +[profile.release.package."*"] +# Оптимизация всех зависимостей +opt-level = 3 + +# Дополнительные флаги компиляции +[build] +rustflags = [ + "-C", "target-cpu=native", # Оптимизация под текущий CPU + "-C", "link-arg=-s", # Удаление символов + "-C", "opt-level=3", # Максимальная оптимизация + "-C", "lto=fat", # Link Time Optimization +] +``` + +```rust +// main.rs - Оптимизированный код +#![allow(dead_code)] + +use std::sync::Arc; +use tokio::sync::RwLock; + +// Оптимизированная структура данных +#[derive(Debug, Clone)] +pub struct OptimizedUser { + id: u64, + name: String, + email: String, + // Использование более компактных типов + status: u8, // вместо enum + created_at: u32, // timestamp как u32 +} + +impl OptimizedUser { + pub fn new(id: u64, name: String, email: String) -> Self { + Self { + id, + name, + email, + status: 1, // ACTIVE + created_at: chrono::Utc::now().timestamp() as u32, + } + } + + // Inline функции для критического пути + #[inline(always)] + pub fn is_active(&self) -> bool { + self.status == 1 + } + + #[inline(always)] + pub fn get_id(&self) -> u64 { + self.id + } +} + +// Оптимизированный сервис с пулом соединений +pub struct OptimizedUserService { + users: Arc>>, + // Предварительно выделенная память + capacity: usize, +} + +impl OptimizedUserService { + pub fn new(capacity: usize) -> Self { + Self { + users: Arc::new(RwLock::new(Vec::with_capacity(capacity))), + capacity, + } + } + + // Batch операции для оптимизации + pub async fn add_users_batch(&self, users: Vec) { + let mut user_list = self.users.write().await; + user_list.reserve(users.len()); + user_list.extend(users); + } + + // Оптимизированный поиск + pub async fn find_user_by_id(&self, id: u64) -> Option { + let user_list = self.users.read().await; + user_list.iter().find(|u| u.id == id).cloned() + } +} +``` + +**Python примеры - Оптимизация:** + +```python +# Оптимизация Python приложения +import sys +from typing import List, Dict, Optional +from dataclasses import dataclass +from functools import lru_cache +import asyncio +import aiohttp + +# Оптимизированная структура данных +@dataclass(frozen=True, slots=True) # slots для экономии памяти +class OptimizedUser: + id: int + name: str + email: str + status: int # 1 = active, 0 = inactive + + def is_active(self) -> bool: + return self.status == 1 + +# Кэширование для оптимизации +class OptimizedUserService: + def __init__(self): + self._users_cache: Dict[int, OptimizedUser] = {} + self._session: Optional[aiohttp.ClientSession] = None + + async def __aenter__(self): + self._session = aiohttp.ClientSession( + connector=aiohttp.TCPConnector( + limit=100, # Пул соединений + limit_per_host=30, + ttl_dns_cache=300, + use_dns_cache=True, + ), + timeout=aiohttp.ClientTimeout(total=30) + ) + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self._session: + await self._session.close() + + # LRU кэш для часто используемых методов + @lru_cache(maxsize=1000) + def get_user_by_id_cached(self, user_id: int) -> Optional[OptimizedUser]: + return self._users_cache.get(user_id) + + # Batch операции + async def get_users_batch(self, user_ids: List[int]) -> List[OptimizedUser]: + tasks = [self._fetch_user_async(user_id) for user_id in user_ids] + results = await asyncio.gather(*tasks, return_exceptions=True) + + users = [] + for result in results: + if isinstance(result, OptimizedUser): + users.append(result) + + return users + + async def _fetch_user_async(self, user_id: int) -> Optional[OptimizedUser]: + if user_id in self._users_cache: + return self._users_cache[user_id] + + # Асинхронный запрос к API + if self._session: + async with self._session.get(f'/api/users/{user_id}') as response: + if response.status == 200: + data = await response.json() + user = OptimizedUser( + id=data['id'], + name=data['name'], + email=data['email'], + status=data['status'] + ) + self._users_cache[user_id] = user + return user + + return None + +# Оптимизация с помощью Cython +# user_service.pyx +cdef class FastUserService: + cdef dict _users_cache + cdef int _max_cache_size + + def __init__(self, max_cache_size: int = 1000): + self._users_cache = {} + self._max_cache_size = max_cache_size + + cdef OptimizedUser _get_user_fast(self, int user_id): + return self._users_cache.get(user_id) + + def get_user(self, user_id: int) -> Optional[OptimizedUser]: + return self._get_user_fast(user_id) +``` + +#### 2. Оптимизация изображений + +```javascript +// webpack.config.js - Оптимизация изображений +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin'); + +module.exports = { + module: { + rules: [ + { + test: /\.(jpe?g|png|gif|svg)$/i, + type: 'asset', + parser: { + dataUrlCondition: { + maxSize: 8 * 1024 // 8kb + } + }, + generator: { + filename: 'images/[name].[hash:8][ext]' + } + } + ] + }, + optimization: { + minimizer: [ + new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + ['gifsicle', { interlaced: true }], + ['jpegtran', { progressive: true }], + ['optipng', { optimizationLevel: 5 }], + [ + 'svgo', + { + plugins: [ + { + name: 'preset-default', + params: { + overrides: { + removeViewBox: false, + }, + }, + }, + ], + }, + ], + ], + }, + }, + }), + ], + }, +}; +``` + +#### 3. Оптимизация CSS + +```css +/* Оптимизированный CSS */ +/* Использование CSS переменных */ +:root { + --primary-color: #007bff; + --secondary-color: #6c757d; + --font-size-base: 1rem; + --line-height-base: 1.5; +} + +/* Минимизация селекторов */ +.btn { + display: inline-block; + padding: 0.375rem 0.75rem; + font-size: var(--font-size-base); + line-height: var(--line-height-base); + border: 1px solid transparent; + border-radius: 0.25rem; + cursor: pointer; + transition: all 0.15s ease-in-out; +} + +.btn-primary { + color: #fff; + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +.btn-primary:hover { + background-color: #0056b3; + border-color: #004085; +} + +/* Критический CSS */ +.critical { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-size: 16px; + line-height: 1.5; + color: #333; +} +``` + +#### 4. Оптимизация загрузки ресурсов + +```html + + + + + + + Optimized App + + + + + + + + + + + + + + + + + + + + + + +
+

Optimized App

+
+ +
+
+
+ + + + + +``` + +#### 5. Оптимизация базы данных + +```java +// Оптимизация запросов к БД +@Repository +public class OptimizedUserRepository { + + @Autowired + private JdbcTemplate jdbcTemplate; + + // Batch операции для оптимизации + public void saveUsersBatch(List users) { + String sql = "INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)"; + + List batchArgs = users.stream() + .map(user -> new Object[]{user.getName(), user.getEmail(), user.getCreatedAt()}) + .collect(Collectors.toList()); + + jdbcTemplate.batchUpdate(sql, batchArgs); + } + + // Оптимизированный поиск с индексами + public List findUsersByEmailDomain(String domain) { + String sql = "SELECT * FROM users WHERE email LIKE ? ORDER BY created_at DESC LIMIT 100"; + return jdbcTemplate.query(sql, new Object[]{"%" + domain}, new UserRowMapper()); + } + + // Использование prepared statements + public Optional findUserById(Long id) { + String sql = "SELECT * FROM users WHERE id = ?"; + try { + User user = jdbcTemplate.queryForObject(sql, new Object[]{id}, new UserRowMapper()); + return Optional.ofNullable(user); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + // Оптимизация с помощью кэширования + @Cacheable(value = "users", key = "#id") + public User findUserByIdCached(Long id) { + return findUserById(id).orElse(null); + } +} +``` + +--- + +## Оптимизация сети + +### Оптимизация сети + +#### 1. Connection Pooling +```java +// Оптимизация HTTP соединений +@Configuration +public class HttpClientConfig { + @Bean + public RestTemplate restTemplate() { + HttpComponentsClientHttpRequestFactory factory = + new HttpComponentsClientHttpRequestFactory(); + + // Настройка пула соединений + PoolingHttpClientConnectionManager connectionManager = + new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(100); + connectionManager.setDefaultMaxPerRoute(20); + + CloseableHttpClient httpClient = HttpClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(RequestConfig.custom() + .setConnectTimeout(5000) + .setSocketTimeout(10000) + .build()) + .build(); + + factory.setHttpClient(httpClient); + return new RestTemplate(factory); + } +} +``` + +#### 2. Compression +```java +// Сжатие HTTP ответов +@Configuration +public class CompressionConfig { + @Bean + public FilterRegistrationBean compressionFilter() { + FilterRegistrationBean registration = + new FilterRegistrationBean<>(); + registration.setFilter(new CompressionFilter()); + registration.addUrlPatterns("/*"); + registration.setName("compressionFilter"); + registration.setOrder(1); + return registration; + } +} + +@Component +public class CompressionFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + String acceptEncoding = httpRequest.getHeader("Accept-Encoding"); + if (acceptEncoding != null && acceptEncoding.contains("gzip")) { + httpResponse.setHeader("Content-Encoding", "gzip"); + GzipResponseWrapper gzipResponse = new GzipResponseWrapper(httpResponse); + chain.doFilter(request, gzipResponse); + gzipResponse.finish(); + } else { + chain.doFilter(request, response); + } + } +} +``` + +#### 3. CDN Integration +```java +// Интеграция с CDN +@Service +public class CDNService { + @Value("${cdn.base-url}") + private String cdnBaseUrl; + + public String getCDNUrl(String path) { + return cdnBaseUrl + "/" + path; + } + + public void uploadToCDN(String path, byte[] content) { + // Загрузка контента в CDN + // Реализация зависит от выбранного CDN провайдера + } + + public void invalidateCDNCache(String path) { + // Инвалидация кэша CDN + // Реализация зависит от выбранного CDN провайдера + } +} +``` + +--- + +## Capacity Planning + +### Процесс планирования мощности + +#### 1. Current Capacity Analysis +```java +// Анализ текущей производительности +@Component +public class CapacityAnalyzer { + private final MeterRegistry meterRegistry; + + public CapacityAnalyzer(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @Scheduled(fixedRate = 60000) // каждую минуту + public void analyzeCapacity() { + // Анализ CPU + double cpuUsage = getCpuUsage(); + log.info("CPU usage: {}%", cpuUsage * 100); + + // Анализ памяти + double memoryUsage = getMemoryUsage(); + log.info("Memory usage: {}%", memoryUsage * 100); + + // Анализ пропускной способности + double throughput = getThroughput(); + log.info("Throughput: {} RPS", throughput); + + // Анализ задержки + double latency = getLatency(); + log.info("Latency: {} ms", latency); + + // Предупреждения + if (cpuUsage > 0.8) { + log.warn("High CPU usage detected!"); + } + if (memoryUsage > 0.8) { + log.warn("High memory usage detected!"); + } + } +} +``` + +#### 2. Load Forecasting +```java +// Прогнозирование нагрузки +@Service +public class LoadForecaster { + @Autowired + private MetricsRepository metricsRepository; + + public LoadForecast forecastLoad(int days) { + // Получаем исторические данные + List historicalData = metricsRepository + .findByDateRange(LocalDateTime.now().minusDays(30), LocalDateTime.now()); + + // Анализируем тренды + double growthRate = calculateGrowthRate(historicalData); + + // Прогнозируем нагрузку + double currentLoad = getCurrentLoad(); + double forecastedLoad = currentLoad * Math.pow(1 + growthRate, days); + + return new LoadForecast(forecastedLoad, growthRate, days); + } + + private double calculateGrowthRate(List data) { + // Простой расчет темпа роста + if (data.size() < 2) return 0.0; + + double firstValue = data.get(0).getValue(); + double lastValue = data.get(data.size() - 1).getValue(); + + return (lastValue - firstValue) / firstValue / data.size(); + } +} +``` + +#### 3. Resource Planning +```yaml +# Планирование ресурсов +capacity_planning: + current_capacity: + cpu: "4 cores" + memory: "8 GB" + storage: "100 GB" + network: "1 Gbps" + + forecasted_load: + peak_rps: 1000 + average_rps: 500 + growth_rate: "20% per month" + + resource_requirements: + cpu: + current: "80% utilization" + target: "70% utilization" + required: "6 cores" + + memory: + current: "75% utilization" + target: "65% utilization" + required: "12 GB" + + storage: + current: "60% utilization" + target: "50% utilization" + required: "150 GB" +``` + +--- + +## Performance Testing + +### Типы тестов производительности + +#### 1. Load Testing +```java +// Load тестирование с JMeter +@Component +public class LoadTestRunner { + public void runLoadTest() { + // Настройка JMeter теста + JMeterEngine engine = new StandardJMeterEngine(); + + // Загрузка тестового плана + HashTree testPlanTree = new HashTree(); + TestPlan testPlan = new TestPlan(); + testPlan.setName("User Service Load Test"); + + // Настройка потока + ThreadGroup threadGroup = new ThreadGroup(); + threadGroup.setName("Users"); + threadGroup.setNumThreads(100); + threadGroup.setRampUp(60); + threadGroup.setDuration(300); // 5 минут + + // Настройка HTTP запроса + HTTPSamplerProxy httpSampler = new HTTPSamplerProxy(); + httpSampler.setDomain("localhost"); + httpSampler.setPort(8080); + httpSampler.setPath("/api/users"); + httpSampler.setMethod("GET"); + + // Добавление элементов в тест + testPlanTree.add(testPlan); + testPlanTree.add(testPlan, threadGroup); + testPlanTree.add(threadGroup, httpSampler); + + // Запуск теста + engine.configure(testPlanTree); + engine.run(); + } +} +``` + +#### 2. Stress Testing +```java +// Stress тестирование +@Service +public class StressTestService { + @Autowired + private UserService userService; + + public void runStressTest() { + int numberOfThreads = 200; + int requestsPerThread = 1000; + + ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); + CountDownLatch latch = new CountDownLatch(numberOfThreads); + + for (int i = 0; i < numberOfThreads; i++) { + executor.submit(() -> { + try { + for (int j = 0; j < requestsPerThread; j++) { + // Выполняем запрос + userService.getUser((long) (Math.random() * 1000)); + + // Небольшая задержка + Thread.sleep(10); + } + } catch (Exception e) { + log.error("Stress test error", e); + } finally { + latch.countDown(); + } + }); + } + + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + executor.shutdown(); + } +} +``` + +#### 3. Endurance Testing +```java +// Endurance тестирование +@Component +public class EnduranceTestRunner { + @Autowired + private UserService userService; + + @Scheduled(fixedRate = 1000) // каждую секунду + public void runEnduranceTest() { + try { + // Выполняем запрос + userService.getUser(1L); + + // Логируем метрики + log.info("Endurance test - request completed"); + + } catch (Exception e) { + log.error("Endurance test error", e); + } + } +} +``` + +### Автоматизация тестов производительности + +#### 1. Automated Performance Tests +```java +// Автоматизированные performance тесты +@SpringBootTest +@TestPropertySource(properties = { + "spring.datasource.url=jdbc:h2:mem:testdb", + "spring.jpa.hibernate.ddl-auto=create-drop" +}) +public class PerformanceTestSuite { + + @Autowired + private UserService userService; + + @Test + public void testUserServicePerformance() { + // Подготовка данных + List users = createTestUsers(1000); + + // Измерение времени выполнения + long startTime = System.currentTimeMillis(); + + for (User user : users) { + userService.createUser(user); + } + + long endTime = System.currentTimeMillis(); + long duration = endTime - startTime; + + // Проверка производительности + assertThat(duration).isLessThan(5000); // менее 5 секунд + + // Проверка пропускной способности + double throughput = (double) users.size() / duration * 1000; + assertThat(throughput).isGreaterThan(100); // более 100 RPS + } + + @Test + public void testDatabaseQueryPerformance() { + // Подготовка данных + createTestUsers(10000); + + // Измерение времени выполнения запроса + long startTime = System.currentTimeMillis(); + + List users = userService.findUsersByName("Test"); + + long endTime = System.currentTimeMillis(); + long duration = endTime - startTime; + + // Проверка производительности + assertThat(duration).isLessThan(1000); // менее 1 секунды + } +} +``` + +--- + +## Практические задания + +### Задание 1: Профилирование +1. Настройте профилирование приложения +2. Проведите анализ производительности +3. Выявите узкие места + +### Задание 2: Оптимизация базы данных +1. Проанализируйте медленные запросы +2. Оптимизируйте индексы +3. Настройте пул соединений + +### Задание 3: Performance Testing +1. Создайте load тесты +2. Проведите stress тестирование +3. Проанализируйте результаты + +--- + +## Дополнительные ресурсы + +### Инструменты +- **Profiling**: JProfiler, VisualVM, YourKit +- **APM**: New Relic, AppDynamics, Datadog +- **Load Testing**: JMeter, Gatling, K6 +- **Database**: pgAdmin, MySQL Workbench, MongoDB Compass + +### Книги +- "Java Performance" by Scott Oaks +- "High Performance MySQL" by Baron Schwartz +- "Systems Performance" by Brendan Gregg + +--- + +## Контрольные вопросы + +1. Какие метрики производительности вы знаете? +2. Как проводить профилирование приложения? +3. Как оптимизировать базу данных? +4. Какие стратегии кэширования существуют? +5. Как оптимизировать сетевые запросы? +6. Что такое Capacity Planning? +7. Какие типы performance тестов вы знаете? +8. Как автоматизировать performance тестирование? diff --git a/modules/module11/README.md b/modules/module11/README.md new file mode 100644 index 0000000..f08a9db --- /dev/null +++ b/modules/module11/README.md @@ -0,0 +1,70 @@ +# Модуль 11: Финальный проект + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля +Этот модуль представляет собой финальный проект курса, где студенты применяют все изученные навыки и знания для проектирования и документирования архитектуры крупной распределенной системы. Модуль включает в себя полный цикл от анализа требований до презентации решения. + +## Содержание модуля + +### 1. [Обзор финального проекта](sections/01-project-overview.md) +- Цель и требования к проекту +- Выбор предметной области +- Варианты проектов (E-commerce, Social Media, IoT) + +### 2. [Архитектурное проектирование](sections/02-architecture-design.md) +- Анализ домена и bounded context mapping +- Проектирование высокоуровневой архитектуры +- Выбор технологического стека + +### 3. [Реализация архитектуры](sections/03-implementation.md) +- Структура проекта (Monorepo) +- Реализация сервисов +- Проектирование API и базы данных + +### 4. [Документирование решения](sections/04-documentation.md) +- Architecture Decision Records (ADR) +- API документация +- Документация развертывания и мониторинга + +### 5. [Презентация проекта](sections/05-presentation.md) +- Структура презентации +- Executive Summary и Technical Deep Dive +- Сценарии демонстрации + +### 6. [Подготовка к собеседованию](sections/06-interview-preparation.md) +- Типовые вопросы Solution Architect +- Архитектурные принципы и паттерны +- Системное проектирование и поведенческие вопросы + +### 7. [Оценка и рефлексия](sections/07-evaluation-reflection.md) +- Критерии оценки проекта +- Вопросы для рефлексии +- Практические задания + +## Ключевые компетенции +- Умение анализировать требования и выбирать архитектурные решения +- Навыки проектирования микросервисной архитектуры +- Способность документировать архитектурные решения +- Умение презентовать технические решения различным аудиториям +- Навыки подготовки к собеседованиям на позицию Solution Architect + +## Практические задачи +1. **Архитектурное проектирование** - анализ домена, проектирование архитектуры, выбор технологий +2. **Реализация** - создание структуры проекта, реализация сервисов, настройка инфраструктуры +3. **Документирование** - создание ADR, API документации, deployment guides +4. **Презентация** - подготовка и проведение презентации проекта + +## Дополнительные ресурсы +- **Инструменты**: C4-PlantUML, Swagger, GitBook, PowerPoint +- **Шаблоны**: ADR Template, OpenAPI Specification, Kubernetes manifests +- **Книги**: "Building Microservices", "Domain-Driven Design", "System Design Interview" + +## Критерии оценки +- Качество архитектурного проектирования (25%) +- Качество реализации и кода (25%) +- Качество документации (25%) +- Качество презентации и коммуникации (25%) diff --git a/modules/module11/sections/01-project-overview.md b/modules/module11/sections/01-project-overview.md new file mode 100644 index 0000000..7bfdfd7 --- /dev/null +++ b/modules/module11/sections/01-project-overview.md @@ -0,0 +1,95 @@ +# Обзор финального проекта + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 11](../README.md) + +--- + +## Цель проекта + +**Финальный проект** — это комплексное применение всех изученных навыков и знаний для проектирования и документирования архитектуры крупной распределенной системы. + +## Требования к проекту + +### 1. Технические требования +- Микросервисная архитектура +- Облачное развертывание +- Мониторинг и observability +- Безопасность и соответствие требованиям +- Масштабируемость и отказоустойчивость + +### 2. Функциональные требования +- Управление пользователями +- Обработка заказов +- Платежная система +- Система уведомлений +- Аналитика и отчетность + +### 3. Нефункциональные требования +- Производительность: 95% запросов < 200ms +- Доступность: 99.9% uptime +- Масштабируемость: до 10,000 RPS +- Безопасность: PCI DSS compliance + +## Выбор предметной области + +### Варианты проектов: + +#### 1. E-commerce Platform +```markdown +# E-commerce Platform + +## Основные функции +- Каталог товаров +- Корзина покупок +- Обработка заказов +- Платежная система +- Управление пользователями +- Система отзывов +- Рекомендации товаров + +## Технические вызовы +- Высокая нагрузка на каталог +- Сложная логика заказов +- Интеграция с платежными системами +- Персонализация рекомендаций +``` + +#### 2. Social Media Platform +```markdown +# Social Media Platform + +## Основные функции +- Профили пользователей +- Публикация контента +- Социальные взаимодействия +- Система уведомлений +- Контент-модерация +- Аналитика активности + +## Технические вызовы +- Обработка медиа-контента +- Real-time уведомления +- Масштабирование социального графа +- Контент-модерация в реальном времени +``` + +#### 3. IoT Platform +```markdown +# IoT Platform + +## Основные функции +- Управление устройствами +- Сбор телеметрии +- Обработка данных в реальном времени +- Аналитика и алертинг +- Управление пользователями +- Интеграция с внешними системами + +## Технические вызовы +- Обработка больших объемов данных +- Real-time обработка +- Управление устройствами +- Аналитика временных рядов +``` diff --git a/modules/module11/sections/02-architecture-design.md b/modules/module11/sections/02-architecture-design.md new file mode 100644 index 0000000..8f6e9e8 --- /dev/null +++ b/modules/module11/sections/02-architecture-design.md @@ -0,0 +1,243 @@ +# Архитектурное проектирование + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 11](../README.md) + +--- + +## Анализ домена + +### 1. Domain Identification +```markdown +# Domain Analysis для E-commerce Platform + +## Core Domains +- **User Management**: управление пользователями, аутентификация, авторизация +- **Product Catalog**: каталог товаров, категории, поиск +- **Order Management**: обработка заказов, управление состоянием +- **Payment Processing**: обработка платежей, интеграция с платежными системами +- **Inventory Management**: управление запасами, резервирование товаров + +## Supporting Domains +- **Notification Service**: отправка уведомлений +- **Analytics Service**: сбор и анализ данных +- **Recommendation Service**: рекомендации товаров +- **Review Service**: система отзывов + +## Generic Domains +- **Logging Service**: централизованное логирование +- **Monitoring Service**: мониторинг системы +- **Configuration Service**: управление конфигурацией +``` + +### 2. Bounded Context Mapping +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml + +title E-commerce Platform - Context Map + +Person(customer, "Customer", "A customer who shops online") +Person(admin, "Admin", "Platform administrator") + +System_Boundary(core, "Core Business") { + System(userMgmt, "User Management", "Manages user accounts and authentication") + System(productCatalog, "Product Catalog", "Manages product information and search") + System(orderMgmt, "Order Management", "Processes and manages orders") + System(payment, "Payment Processing", "Handles payment transactions") + System(inventory, "Inventory Management", "Manages product stock") +} + +System_Boundary(supporting, "Supporting Services") { + System(notification, "Notification Service", "Sends notifications to users") + System(analytics, "Analytics Service", "Collects and analyzes data") + System(recommendation, "Recommendation Service", "Provides product recommendations") +} + +System_Boundary(generic, "Generic Services") { + System(logging, "Logging Service", "Centralized logging") + System(monitoring, "Monitoring Service", "System monitoring") +} + +Rel(customer, userMgmt, "Uses", "HTTPS") +Rel(customer, productCatalog, "Browses", "HTTPS") +Rel(customer, orderMgmt, "Places orders", "HTTPS") +Rel(customer, payment, "Makes payments", "HTTPS") + +Rel(admin, userMgmt, "Manages", "HTTPS") +Rel(admin, productCatalog, "Manages", "HTTPS") +Rel(admin, orderMgmt, "Monitors", "HTTPS") + +Rel(orderMgmt, inventory, "Reserves items", "HTTP") +Rel(orderMgmt, payment, "Processes payment", "HTTP") +Rel(orderMgmt, notification, "Sends notifications", "Events") + +Rel(productCatalog, recommendation, "Gets recommendations", "HTTP") +Rel(customer, analytics, "Generates events", "Events") + +@enduml +``` + +## Проектирование архитектуры + +### 1. High-Level Architecture +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml + +title E-commerce Platform - Container Diagram + +Person(customer, "Customer", "A customer who shops online") +Person(admin, "Admin", "Platform administrator") + +System_Boundary(platform, "E-commerce Platform") { + Container(webApp, "Web Application", "React", "Single-page application for customers") + Container(adminApp, "Admin Application", "React", "Administrative interface") + Container(apiGateway, "API Gateway", "Kong", "Routes requests to microservices") + + Container(userService, "User Service", "Java Spring Boot", "Manages user accounts and authentication") + Container(productService, "Product Service", "Java Spring Boot", "Manages product catalog") + Container(orderService, "Order Service", "Java Spring Boot", "Processes orders") + Container(paymentService, "Payment Service", "Java Spring Boot", "Handles payments") + Container(inventoryService, "Inventory Service", "Java Spring Boot", "Manages stock") + + ContainerDb(userDb, "User Database", "PostgreSQL", "Stores user data") + ContainerDb(productDb, "Product Database", "PostgreSQL", "Stores product data") + ContainerDb(orderDb, "Order Database", "PostgreSQL", "Stores order data") + ContainerDb(paymentDb, "Payment Database", "PostgreSQL", "Stores payment data") + ContainerDb(inventoryDb, "Inventory Database", "PostgreSQL", "Stores inventory data") + + Container(redis, "Redis Cache", "Redis", "Caches frequently accessed data") + Container(messageQueue, "Message Queue", "Apache Kafka", "Handles asynchronous communication") +} + +System_Ext(paymentGateway, "Payment Gateway", "External payment processing service") +System_Ext(emailService, "Email Service", "External email service") +System_Ext(analyticsService, "Analytics Service", "External analytics service") + +Rel(customer, webApp, "Uses", "HTTPS") +Rel(admin, adminApp, "Uses", "HTTPS") + +Rel(webApp, apiGateway, "Makes API calls", "HTTPS") +Rel(adminApp, apiGateway, "Makes API calls", "HTTPS") + +Rel(apiGateway, userService, "Routes requests", "HTTP") +Rel(apiGateway, productService, "Routes requests", "HTTP") +Rel(apiGateway, orderService, "Routes requests", "HTTP") +Rel(apiGateway, paymentService, "Routes requests", "HTTP") +Rel(apiGateway, inventoryService, "Routes requests", "HTTP") + +Rel(userService, userDb, "Reads from and writes to", "JDBC") +Rel(productService, productDb, "Reads from and writes to", "JDBC") +Rel(orderService, orderDb, "Reads from and writes to", "JDBC") +Rel(paymentService, paymentDb, "Reads from and writes to", "JDBC") +Rel(inventoryService, inventoryDb, "Reads from and writes to", "JDBC") + +Rel(userService, redis, "Caches data", "Redis Protocol") +Rel(productService, redis, "Caches data", "Redis Protocol") +Rel(orderService, redis, "Caches data", "Redis Protocol") + +Rel(orderService, messageQueue, "Publishes events", "Kafka") +Rel(paymentService, messageQueue, "Consumes events", "Kafka") +Rel(inventoryService, messageQueue, "Consumes events", "Kafka") + +Rel(paymentService, paymentGateway, "Processes payments", "HTTPS") +Rel(userService, emailService, "Sends emails", "SMTP") +Rel(webApp, analyticsService, "Tracks events", "HTTPS") + +@enduml +``` + +### 2. Detailed Component Design +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml + +title Order Service - Component Diagram + +Container_Boundary(orderService, "Order Service") { + Component(controllers, "Controllers", "Spring MVC", "Handles HTTP requests") + Component(services, "Services", "Spring", "Contains business logic") + Component(repositories, "Repositories", "Spring Data", "Handles data access") + Component(events, "Event Handlers", "Spring", "Handles domain events") + Component(validators, "Validators", "Spring", "Validates input data") +} + +ContainerDb(orderDb, "Order Database", "PostgreSQL", "Stores order data") +Container(redis, "Redis Cache", "Redis", "Caches order data") +Container(messageQueue, "Message Queue", "Apache Kafka", "Publishes and consumes events") + +Rel(controllers, services, "Uses") +Rel(services, repositories, "Uses") +Rel(services, events, "Publishes") +Rel(services, validators, "Uses") +Rel(repositories, orderDb, "Reads from and writes to", "JDBC") +Rel(services, redis, "Caches data", "Redis Protocol") +Rel(events, messageQueue, "Publishes events", "Kafka") + +@enduml +``` + +## Выбор технологического стека + +### 1. Backend Technologies +```yaml +# Technology Stack +backend: + language: "Java 17" + framework: "Spring Boot 3.x" + build_tool: "Maven" + testing: "JUnit 5, Mockito, TestContainers" + + databases: + primary: "PostgreSQL 15" + cache: "Redis 7" + search: "Elasticsearch 8" + + messaging: + broker: "Apache Kafka" + protocol: "Avro" + + security: + authentication: "JWT" + authorization: "Spring Security" + encryption: "AES-256" +``` + +### 2. Frontend Technologies +```yaml +# Frontend Stack +frontend: + framework: "React 18" + language: "TypeScript" + state_management: "Redux Toolkit" + styling: "Material-UI" + build_tool: "Vite" + testing: "Jest, React Testing Library" +``` + +### 3. Infrastructure Technologies +```yaml +# Infrastructure Stack +infrastructure: + containerization: "Docker" + orchestration: "Kubernetes" + service_mesh: "Istio" + api_gateway: "Kong" + + monitoring: + metrics: "Prometheus" + visualization: "Grafana" + logging: "ELK Stack" + tracing: "Jaeger" + + cloud: + provider: "AWS" + services: + - "EKS (Kubernetes)" + - "RDS (PostgreSQL)" + - "ElastiCache (Redis)" + - "S3 (File Storage)" + - "CloudWatch (Monitoring)" +``` diff --git a/modules/module11/sections/03-implementation.md b/modules/module11/sections/03-implementation.md new file mode 100644 index 0000000..23c18ea --- /dev/null +++ b/modules/module11/sections/03-implementation.md @@ -0,0 +1,364 @@ +# Реализация архитектуры + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 11](../README.md) + +--- + +## Структура проекта + +### 1. Monorepo Structure +``` +ecommerce-platform/ +├── services/ +│ ├── user-service/ +│ │ ├── src/ +│ │ ├── Dockerfile +│ │ ├── pom.xml +│ │ └── README.md +│ ├── product-service/ +│ ├── order-service/ +│ ├── payment-service/ +│ └── inventory-service/ +├── frontend/ +│ ├── customer-app/ +│ ├── admin-app/ +│ └── shared/ +├── infrastructure/ +│ ├── kubernetes/ +│ ├── terraform/ +│ └── docker-compose/ +├── docs/ +│ ├── architecture/ +│ ├── api/ +│ └── deployment/ +└── scripts/ + ├── build.sh + ├── deploy.sh + └── test.sh +``` + +### 2. Service Implementation +```java +// Order Service Implementation +@RestController +@RequestMapping("/api/orders") +@Validated +public class OrderController { + + @Autowired + private OrderService orderService; + + @PostMapping + public ResponseEntity createOrder( + @Valid @RequestBody CreateOrderRequest request) { + + Order order = orderService.createOrder(request); + OrderResponse response = OrderMapper.toResponse(order); + + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + @GetMapping("/{id}") + public ResponseEntity getOrder(@PathVariable Long id) { + Order order = orderService.getOrder(id); + OrderResponse response = OrderMapper.toResponse(order); + + return ResponseEntity.ok(response); + } + + @PutMapping("/{id}/status") + public ResponseEntity updateOrderStatus( + @PathVariable Long id, + @Valid @RequestBody UpdateOrderStatusRequest request) { + + Order order = orderService.updateOrderStatus(id, request.getStatus()); + OrderResponse response = OrderMapper.toResponse(order); + + return ResponseEntity.ok(response); + } +} + +@Service +@Transactional +public class OrderService { + + @Autowired + private OrderRepository orderRepository; + + @Autowired + private OrderEventPublisher eventPublisher; + + @Autowired + private InventoryServiceClient inventoryClient; + + @Autowired + private PaymentServiceClient paymentClient; + + public Order createOrder(CreateOrderRequest request) { + // Валидация заказа + validateOrderRequest(request); + + // Создание заказа + Order order = new Order(request); + order = orderRepository.save(order); + + // Резервирование товаров + inventoryClient.reserveItems(order.getId(), request.getItems()); + + // Публикация события + eventPublisher.publishOrderCreated(order); + + return order; + } + + public Order updateOrderStatus(Long orderId, OrderStatus status) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new OrderNotFoundException("Order not found: " + orderId)); + + order.updateStatus(status); + order = orderRepository.save(order); + + // Публикация события + eventPublisher.publishOrderStatusUpdated(order); + + return order; + } + + private void validateOrderRequest(CreateOrderRequest request) { + if (request.getItems().isEmpty()) { + throw new ValidationException("Order must contain at least one item"); + } + + // Дополнительные проверки... + } +} +``` + +## Проектирование API + +### 1. REST API Specification +```yaml +# OpenAPI Specification +openapi: 3.0.0 +info: + title: E-commerce Platform API + version: 1.0.0 + description: API for e-commerce platform + +servers: + - url: https://api.ecommerce.com/v1 + description: Production server + +paths: + /orders: + post: + summary: Create a new order + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateOrderRequest' + responses: + '201': + description: Order created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/OrderResponse' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /orders/{id}: + get: + summary: Get order by ID + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '200': + description: Order retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/OrderResponse' + '404': + description: Order not found + +components: + schemas: + CreateOrderRequest: + type: object + required: + - customerId + - items + properties: + customerId: + type: integer + format: int64 + items: + type: array + items: + $ref: '#/components/schemas/OrderItem' + shippingAddress: + $ref: '#/components/schemas/Address' + paymentMethod: + $ref: '#/components/schemas/PaymentMethod' + + OrderResponse: + type: object + properties: + id: + type: integer + format: int64 + customerId: + type: integer + format: int64 + status: + type: string + enum: [PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED] + totalAmount: + type: number + format: decimal + items: + type: array + items: + $ref: '#/components/schemas/OrderItem' + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time +``` + +### 2. Event Schema +```json +{ + "orderCreated": { + "eventType": "OrderCreated", + "version": "1.0", + "timestamp": "2023-12-01T10:00:00Z", + "data": { + "orderId": "12345", + "customerId": "67890", + "items": [ + { + "productId": "prod-001", + "quantity": 2, + "price": 29.99 + } + ], + "totalAmount": 59.98, + "status": "PENDING" + } + }, + "orderStatusUpdated": { + "eventType": "OrderStatusUpdated", + "version": "1.0", + "timestamp": "2023-12-01T10:05:00Z", + "data": { + "orderId": "12345", + "oldStatus": "PENDING", + "newStatus": "CONFIRMED", + "updatedBy": "system" + } + } +} +``` + +## Проектирование базы данных + +### 1. Database Schema +```sql +-- Order Service Database Schema +CREATE TABLE orders ( + id BIGSERIAL PRIMARY KEY, + customer_id BIGINT NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'PENDING', + total_amount DECIMAL(10,2) NOT NULL, + shipping_address JSONB, + payment_method JSONB, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE order_items ( + id BIGSERIAL PRIMARY KEY, + order_id BIGINT NOT NULL REFERENCES orders(id), + product_id VARCHAR(50) NOT NULL, + quantity INTEGER NOT NULL, + unit_price DECIMAL(10,2) NOT NULL, + total_price DECIMAL(10,2) NOT NULL +); + +CREATE TABLE order_events ( + id BIGSERIAL PRIMARY KEY, + order_id BIGINT NOT NULL, + event_type VARCHAR(50) NOT NULL, + event_data JSONB NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Индексы для оптимизации +CREATE INDEX idx_orders_customer_id ON orders(customer_id); +CREATE INDEX idx_orders_status ON orders(status); +CREATE INDEX idx_orders_created_at ON orders(created_at); +CREATE INDEX idx_order_items_order_id ON order_items(order_id); +CREATE INDEX idx_order_events_order_id ON order_events(order_id); +``` + +### 2. Data Access Layer +```java +@Repository +public interface OrderRepository extends JpaRepository { + + List findByCustomerId(Long customerId); + + List findByStatus(OrderStatus status); + + @Query("SELECT o FROM Order o WHERE o.createdAt BETWEEN :startDate AND :endDate") + List findOrdersByDateRange( + @Param("startDate") LocalDateTime startDate, + @Param("endDate") LocalDateTime endDate + ); + + @Query("SELECT o FROM Order o WHERE o.customerId = :customerId AND o.status = :status") + List findByCustomerIdAndStatus( + @Param("customerId") Long customerId, + @Param("status") OrderStatus status + ); +} + +@Repository +public class OrderEventRepository { + + @Autowired + private JdbcTemplate jdbcTemplate; + + public void saveEvent(OrderEvent event) { + String sql = "INSERT INTO order_events (order_id, event_type, event_data) VALUES (?, ?, ?)"; + + jdbcTemplate.update(sql, + event.getOrderId(), + event.getEventType(), + event.getEventData() + ); + } + + public List findEventsByOrderId(Long orderId) { + String sql = "SELECT * FROM order_events WHERE order_id = ? ORDER BY created_at"; + + return jdbcTemplate.query(sql, new OrderEventRowMapper(), orderId); + } +} +``` diff --git a/modules/module11/sections/04-documentation.md b/modules/module11/sections/04-documentation.md new file mode 100644 index 0000000..ced1711 --- /dev/null +++ b/modules/module11/sections/04-documentation.md @@ -0,0 +1,159 @@ +# Документирование решения + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 11](../README.md) + +--- + +## Документация архитектуры + +### 1. Architecture Decision Records +```markdown +# ADR-001: Микросервисная архитектура + +## Статус +Принято + +## Дата +2024-01-15 + +## Контекст +E-commerce платформа должна обрабатывать высокую нагрузку и обеспечивать быструю разработку новых функций. Текущая монолитная архитектура создает проблемы с масштабированием и развертыванием. + +## Решение +Переход к микросервисной архитектуре со следующими принципами: +- Каждый сервис отвечает за один бизнес-домен +- Сервисы независимо развертываются +- Сервисы имеют собственные базы данных +- Коммуникация через REST API и события + +## Альтернативы +1. **Модульный монолит** — разделение на модули внутри одного приложения +2. **SOA** — сервисно-ориентированная архитектура +3. **Оставить монолит** — продолжить развитие текущей архитектуры + +## Последствия +### Положительные +- Независимое развертывание сервисов +- Возможность использования разных технологий +- Лучшая масштабируемость +- Изоляция отказов + +### Отрицательные +- Увеличение сложности системы +- Проблемы с распределенными транзакциями +- Сложность мониторинга +- Overhead коммуникации +``` + +### 2. API Documentation +```markdown +# API Documentation + +## Order Service API + +### Создание заказа +**POST** `/api/orders` + +Creates a new order for a customer. + +#### Тело запроса +```json +{ + "customerId": 123, + "items": [ + { + "productId": 456, + "quantity": 2, + "unitPrice": 29.99 + } + ] +} +``` + +#### Ответ +```json +{ + "id": 789, + "customerId": 123, + "status": "CREATED", + "totalAmount": 59.98, + "items": [ + { + "productId": 456, + "quantity": 2, + "unitPrice": 29.99 + } + ], + "createdAt": "2024-01-15T10:30:00Z" +} +``` + +#### Ответы об ошибках +- **400 Bad Request**: Invalid request data +- **404 Not Found**: Customer not found +- **500 Internal Server Error**: Server error +``` + +## Документация развертывания + +### 1. Deployment Guide +```markdown +# Deployment Guide + +## Prerequisites +- Docker and Docker Compose +- Kubernetes cluster +- kubectl configured +- Helm 3.x + +## Local Development +1. Clone the repository +2. Run `docker-compose up -d` to start dependencies +3. Run `./scripts/build.sh` to build all services +4. Run `./scripts/test.sh` to run tests + +## Production Deployment +1. Build Docker images: `./scripts/build.sh` +2. Push images to registry: `./scripts/push.sh` +3. Deploy to Kubernetes: `./scripts/deploy.sh` +4. Verify deployment: `kubectl get pods` + +## Environment Configuration +- Development: `config/dev/` +- Staging: `config/staging/` +- Production: `config/prod/` +``` + +### 2. Monitoring Setup +```yaml +# Monitoring Configuration +monitoring: + prometheus: + scrape_configs: + - job_name: 'order-service' + static_configs: + - targets: ['order-service:8080'] + metrics_path: '/actuator/prometheus' + scrape_interval: 15s + + grafana: + dashboards: + - name: 'Order Service Dashboard' + panels: + - title: 'Request Rate' + query: 'rate(http_requests_total[5m])' + - title: 'Response Time' + query: 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))' + - title: 'Error Rate' + query: 'rate(http_requests_total{status=~"5.."}[5m])' + + alerts: + - name: 'High Error Rate' + condition: 'rate(http_requests_total{status=~"5.."}[5m]) > 0.05' + severity: 'critical' + - name: 'High Response Time' + condition: 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1' + severity: 'warning' +``` diff --git a/modules/module11/sections/05-presentation.md b/modules/module11/sections/05-presentation.md new file mode 100644 index 0000000..694e441 --- /dev/null +++ b/modules/module11/sections/05-presentation.md @@ -0,0 +1,113 @@ +# Презентация проекта + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 11](../README.md) + +--- + +## Структура презентации + +### 1. Executive Summary +```markdown +# E-commerce Platform Architecture +## Executive Summary + +### Постановка проблемы +- Current monolithic architecture limits scalability +- High deployment risk and slow time-to-market +- Difficult to maintain and extend + +### Предлагаемое решение +- Microservices architecture with cloud-native design +- Independent deployment and scaling +- Improved developer productivity + +### Бизнес-ценность +- 50% faster time-to-market +- 99.9% availability +- 10x better scalability +- Reduced operational costs + +### План реализации +- Phase 1: Core services (3 months) +- Phase 2: Advanced features (2 months) +- Phase 3: Optimization (1 month) +``` + +### 2. Technical Deep Dive +```markdown +# Technical Architecture + +## Architecture Principles +- Domain-Driven Design +- Microservices +- Event-Driven Architecture +- Cloud-Native + +## Technology Stack +- Backend: Java 17, Spring Boot 3.x +- Frontend: React 18, TypeScript +- Database: PostgreSQL, Redis +- Infrastructure: Kubernetes, AWS + +## Key Features +- High availability (99.9%) +- Horizontal scaling +- Real-time processing +- Comprehensive monitoring + +## Security +- JWT authentication +- Role-based authorization +- Data encryption +- PCI DSS compliance +``` + +## Сценарии демонстрации + +### 1. User Journey Demo +```markdown +# Demo Scenario: Complete User Journey + +## Scenario 1: Customer Places Order +1. Customer browses products +2. Adds items to cart +3. Proceeds to checkout +4. Enters payment information +5. Confirms order +6. Receives confirmation email + +## Scenario 2: Order Processing +1. Order service creates order +2. Inventory service reserves items +3. Payment service processes payment +4. Order status updated +5. Customer notified + +## Scenario 3: Admin Management +1. Admin views order dashboard +2. Updates order status +3. Monitors system metrics +4. Handles customer inquiries +``` + +### 2. Technical Demo +```markdown +# Technical Demo + +## Microservices Communication +- Show service-to-service communication +- Demonstrate event-driven architecture +- Display real-time monitoring + +## Scalability Demo +- Load test with increasing traffic +- Show auto-scaling in action +- Demonstrate failover scenarios + +## Monitoring Demo +- Real-time metrics dashboard +- Alert notifications +- Performance analysis +``` diff --git a/modules/module11/sections/06-interview-preparation.md b/modules/module11/sections/06-interview-preparation.md new file mode 100644 index 0000000..a835099 --- /dev/null +++ b/modules/module11/sections/06-interview-preparation.md @@ -0,0 +1,203 @@ +# Подготовка к собеседованию + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 11](../README.md) + +--- + +## Типовые вопросы на собеседовании Solution Architect + +### 1. Архитектурные принципы и паттерны + +**Вопрос:** "Объясните разницу между монолитной и микросервисной архитектурой. Когда бы вы выбрали каждую из них?" + +**Структура ответа:** +```markdown +# Монолитная vs Микросервисная архитектура + +## Монолитная архитектура +**Определение:** Единое приложение, развертываемое как один компонент + +**Преимущества:** +- Простота разработки и развертывания +- Легкое тестирование +- Простая отладка +- Низкие накладные расходы + +**Недостатки:** +- Сложность масштабирования +- Технологическая привязка +- Риск каскадных сбоев +- Сложность в больших командах + +**Когда выбирать:** +- Небольшие команды (1-5 разработчиков) +- Простые приложения +- Быстрый MVP +- Ограниченные ресурсы + +## Микросервисная архитектура +**Определение:** Набор независимых сервисов, взаимодействующих через API + +**Преимущества:** +- Независимое масштабирование +- Технологическое разнообразие +- Отказоустойчивость +- Независимые команды + +**Недостатки:** +- Сложность управления +- Сетевые задержки +- Сложность тестирования +- Высокие накладные расходы + +**Когда выбирать:** +- Большие команды (10+ разработчиков) +- Сложные домены +- Высокие требования к масштабированию +- Разные технологические потребности +``` + +### 2. Технические вопросы + +**Вопрос:** "Объясните паттерн Circuit Breaker и как его реализовать." + +**Ответ:** +```java +// Circuit Breaker Implementation +@Component +public class CircuitBreakerService { + + private final CircuitBreaker circuitBreaker; + private final RestTemplate restTemplate; + + public CircuitBreakerService() { + this.circuitBreaker = CircuitBreaker.ofDefaults("external-service"); + this.restTemplate = new RestTemplate(); + } + + public String callExternalService(String url) { + return circuitBreaker.executeSupplier(() -> { + try { + ResponseEntity response = restTemplate.getForEntity(url, String.class); + return response.getBody(); + } catch (Exception e) { + throw new RuntimeException("External service call failed", e); + } + }); + } + + // Fallback method + public String getFallbackResponse() { + return "Service temporarily unavailable"; + } +} +``` + +### 3. Системное проектирование + +**Вопрос:** "Спроектируйте систему коротких ссылок (URL shortener) типа bit.ly" + +**Структура ответа:** +```markdown +# URL Shortener System Design + +## 1. Требования +- Функциональные: + - Сокращение длинных URL + - Перенаправление по коротким ссылкам + - Аналитика кликов +- Нефункциональные: + - Высокая доступность + - Низкая задержка + - Масштабируемость + +## 2. Оценка масштаба +- 100M новых URL в день +- 10:1 соотношение чтения к записи +- 100B записей в год +- 6 месяцев хранения + +## 3. Высокоуровневая архитектура +- API Gateway +- URL Service +- Redirect Service +- Analytics Service +- Database (PostgreSQL + Redis) +- CDN + +## 4. Детальное проектирование +- Алгоритм генерации коротких URL +- Схема базы данных +- Стратегия кэширования +- Обработка дубликатов + +## 5. Масштабирование +- Горизонтальное масштабирование +- Шардирование базы данных +- Кэширование +- CDN для статического контента +``` + +### 4. Поведенческие вопросы + +**Вопрос:** "Расскажите о случае, когда вам пришлось принимать сложное техническое решение." + +**Структура ответа (STAR):** +```markdown +# Situation (Ситуация) +В проекте e-commerce платформы мы столкнулись с проблемой производительности базы данных. +Система обрабатывала 10,000 запросов в секунду, но время отклика увеличилось до 2 секунд. + +# Task (Задача) +Мне нужно было найти решение для оптимизации производительности базы данных, +сохранив при этом консистентность данных и не нарушив работу системы. + +# Action (Действия) +1. Провел анализ медленных запросов с помощью EXPLAIN ANALYZE +2. Выявил узкие места в индексах и запросах +3. Реализовал стратегию кэширования с Redis +4. Настроил read replicas для распределения нагрузки +5. Оптимизировал запросы и добавил недостающие индексы +6. Внедрил мониторинг производительности + +# Result (Результат) +- Время отклика снизилось с 2 секунд до 200ms +- Пропускная способность увеличилась в 5 раз +- Система стала более стабильной +- Команда получила опыт работы с высоконагруженными системами +``` + +### 5. Вопросы о лидерстве + +**Вопрос:** "Как вы обеспечиваете техническое лидерство в команде?" + +**Ответ:** +```markdown +# Техническое лидерство + +## 1. Создание обучающей среды +- Проведение code review с объяснением решений +- Технические презентации и воркшопы +- Документирование архитектурных решений +- Создание технических стандартов + +## 2. Практический подход +- Участие в сложных технических задачах +- Менторство junior разработчиков +- Помощь в решении технических проблем +- Демонстрация лучших практик на примерах + +## 3. Создание успешных кейсов +- Внедрение новых технологий +- Оптимизация производительности +- Улучшение качества кода +- Повышение эффективности команды + +## 4. Поддержка и ресурсы +- Обеспечение доступа к обучающим материалам +- Организация технических конференций +- Создание внутренних технических сообществ +- Поощрение экспериментов и инноваций +``` diff --git a/modules/module11/sections/07-evaluation-reflection.md b/modules/module11/sections/07-evaluation-reflection.md new file mode 100644 index 0000000..5df0343 --- /dev/null +++ b/modules/module11/sections/07-evaluation-reflection.md @@ -0,0 +1,155 @@ +# Оценка и рефлексия + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 11](../README.md) + +--- + +## Критерии оценки проекта + +### 1. Technical Excellence +```markdown +# Technical Evaluation + +## Architecture Quality +- [ ] Clear separation of concerns +- [ ] Appropriate use of patterns +- [ ] Scalable and maintainable design +- [ ] Security considerations + +## Implementation Quality +- [ ] Clean, readable code +- [ ] Proper error handling +- [ ] Comprehensive testing +- [ ] Good documentation + +## Technology Choices +- [ ] Appropriate technology stack +- [ ] Modern best practices +- [ ] Performance considerations +- [ ] Security best practices +``` + +### 2. Documentation Quality +```markdown +# Documentation Evaluation + +## Architecture Documentation +- [ ] Clear architecture diagrams +- [ ] Well-documented ADRs +- [ ] Comprehensive API documentation +- [ ] Deployment guides + +## Code Documentation +- [ ] Clear code comments +- [ ] README files +- [ ] API examples +- [ ] Troubleshooting guides +``` + +### 3. Presentation Quality +```markdown +# Presentation Evaluation + +## Content Quality +- [ ] Clear problem statement +- [ ] Well-justified solution +- [ ] Comprehensive technical details +- [ ] Business value demonstration + +## Presentation Skills +- [ ] Clear communication +- [ ] Engaging delivery +- [ ] Effective use of visuals +- [ ] Good time management +``` + +## Вопросы для рефлексии + +### 1. Technical Reflection +```markdown +# Technical Reflection + +## What went well? +- Successful implementation of microservices +- Good separation of concerns +- Effective use of design patterns +- Comprehensive monitoring setup + +## What could be improved? +- Better error handling in some services +- More comprehensive testing +- Improved documentation +- Better performance optimization + +## Lessons learned? +- Importance of proper service boundaries +- Value of event-driven architecture +- Need for comprehensive monitoring +- Importance of documentation +``` + +### 2. Process Reflection +```markdown +# Process Reflection + +## Planning +- Good initial architecture planning +- Effective use of ADRs +- Clear project structure +- Good time management + +## Implementation +- Smooth development process +- Effective use of tools +- Good collaboration +- Regular progress reviews + +## Documentation +- Comprehensive documentation +- Clear API specifications +- Good deployment guides +- Effective presentation materials +``` + +## Практические задания + +### Задание 1: Архитектурное проектирование +1. Выберите предметную область для проекта +2. Проведите domain analysis +3. Спроектируйте микросервисную архитектуру +4. Создайте архитектурные диаграммы + +### Задание 2: Реализация +1. Реализуйте ключевые сервисы +2. Настройте инфраструктуру +3. Реализуйте мониторинг +4. Настройте CI/CD + +### Задание 3: Документирование +1. Создайте ADRs для ключевых решений +2. Документируйте API +3. Создайте deployment guide +4. Подготовьте презентацию + +## Дополнительные ресурсы + +### Инструменты +- **Architecture**: C4-PlantUML, Structurizr, Lucidchart +- **API**: Swagger, Postman, Insomnia +- **Documentation**: GitBook, Confluence, Notion +- **Presentation**: PowerPoint, Keynote, Prezi + +### Шаблоны +- **ADR Template**: Architecture Decision Record +- **API Template**: OpenAPI Specification +- **Deployment Template**: Kubernetes manifests +- **Presentation Template**: Executive summary + +## Контрольные вопросы +1. Какие принципы архитектуры вы применили в проекте? +2. Как вы обеспечили масштабируемость системы? +3. Какие паттерны проектирования использовали? +4. Как вы обеспечили безопасность системы? +5. Какие метрики мониторинга важны для вашей системы? diff --git a/modules/module11/sections/08-practical-tasks.md b/modules/module11/sections/08-practical-tasks.md new file mode 100644 index 0000000..27a5ec4 --- /dev/null +++ b/modules/module11/sections/08-practical-tasks.md @@ -0,0 +1,27 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 11](../README.md) + +--- + +### Задание 1: Архитектурное проектирование +1. Выберите предметную область для проекта +2. Проведите domain analysis +3. Спроектируйте микросервисную архитектуру +4. Создайте архитектурные диаграммы + +### Задание 2: Реализация +1. Реализуйте ключевые сервисы +2. Настройте инфраструктуру +3. Реализуйте мониторинг +4. Настройте CI/CD + +### Задание 3: Документирование +1. Создайте ADRs для ключевых решений +2. Документируйте API +3. Создайте deployment guide +4. Подготовьте презентацию + +--- \ No newline at end of file diff --git a/modules/module11/theory.md b/modules/module11/theory.md new file mode 100644 index 0000000..22d7f42 --- /dev/null +++ b/modules/module11/theory.md @@ -0,0 +1,1831 @@ +# Модуль 11: Финальный проект + +## Содержание модуля +1. [Обзор финального проекта](#обзор-финального-проекта) +2. [Архитектурное проектирование](#архитектурное-проектирование) +3. [Реализация архитектуры](#реализация-архитектуры) +4. [Документирование решения](#документирование-решения) +5. [Презентация проекта](#презентация-проекта) +6. [Подготовка к собеседованию](#подготовка-к-собеседованию) +7. [Оценка и рефлексия](#оценка-и-рефлексия) + +--- + +## Обзор финального проекта + +### Цель проекта + +**Финальный проект** — это комплексное применение всех изученных навыков и знаний для проектирования и документирования архитектуры крупной распределенной системы. + +### Требования к проекту + +#### 1. Технические требования +- Микросервисная архитектура +- Облачное развертывание +- Мониторинг и observability +- Безопасность и соответствие требованиям +- Масштабируемость и отказоустойчивость + +#### 2. Функциональные требования +- Управление пользователями +- Обработка заказов +- Платежная система +- Система уведомлений +- Аналитика и отчетность + +#### 3. Нефункциональные требования +- Производительность: 95% запросов < 200ms +- Доступность: 99.9% uptime +- Масштабируемость: до 10,000 RPS +- Безопасность: PCI DSS compliance + +### Выбор предметной области + +#### Варианты проектов: + +##### 1. E-commerce Platform +```markdown +# E-commerce Platform + +## Основные функции +- Каталог товаров +- Корзина покупок +- Обработка заказов +- Платежная система +- Управление пользователями +- Система отзывов +- Рекомендации товаров + +## Технические вызовы +- Высокая нагрузка на каталог +- Сложная логика заказов +- Интеграция с платежными системами +- Персонализация рекомендаций +``` + +##### 2. Social Media Platform +```markdown +# Social Media Platform + +## Основные функции +- Профили пользователей +- Публикация контента +- Социальные взаимодействия +- Система уведомлений +- Контент-модерация +- Аналитика активности + +## Технические вызовы +- Обработка медиа-контента +- Real-time уведомления +- Масштабирование социального графа +- Контент-модерация в реальном времени +``` + +##### 3. IoT Platform +```markdown +# IoT Platform + +## Основные функции +- Управление устройствами +- Сбор телеметрии +- Обработка данных в реальном времени +- Аналитика и алертинг +- Управление пользователями +- Интеграция с внешними системами + +## Технические вызовы +- Обработка больших объемов данных +- Real-time обработка +- Управление устройствами +- Аналитика временных рядов +``` + +--- + +## Архитектурное проектирование + +### Анализ домена + +#### 1. Domain Identification +```markdown +# Domain Analysis для E-commerce Platform + +## Core Domains +- **User Management**: управление пользователями, аутентификация, авторизация +- **Product Catalog**: каталог товаров, категории, поиск +- **Order Management**: обработка заказов, управление состоянием +- **Payment Processing**: обработка платежей, интеграция с платежными системами +- **Inventory Management**: управление запасами, резервирование товаров + +## Supporting Domains +- **Notification Service**: отправка уведомлений +- **Analytics Service**: сбор и анализ данных +- **Recommendation Service**: рекомендации товаров +- **Review Service**: система отзывов + +## Generic Domains +- **Logging Service**: централизованное логирование +- **Monitoring Service**: мониторинг системы +- **Configuration Service**: управление конфигурацией +``` + +#### 2. Bounded Context Mapping +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml + +title E-commerce Platform - Context Map + +Person(customer, "Customer", "A customer who shops online") +Person(admin, "Admin", "Platform administrator") + +System_Boundary(core, "Core Business") { + System(userMgmt, "User Management", "Manages user accounts and authentication") + System(productCatalog, "Product Catalog", "Manages product information and search") + System(orderMgmt, "Order Management", "Processes and manages orders") + System(payment, "Payment Processing", "Handles payment transactions") + System(inventory, "Inventory Management", "Manages product stock") +} + +System_Boundary(supporting, "Supporting Services") { + System(notification, "Notification Service", "Sends notifications to users") + System(analytics, "Analytics Service", "Collects and analyzes data") + System(recommendation, "Recommendation Service", "Provides product recommendations") +} + +System_Boundary(generic, "Generic Services") { + System(logging, "Logging Service", "Centralized logging") + System(monitoring, "Monitoring Service", "System monitoring") +} + +Rel(customer, userMgmt, "Uses", "HTTPS") +Rel(customer, productCatalog, "Browses", "HTTPS") +Rel(customer, orderMgmt, "Places orders", "HTTPS") +Rel(customer, payment, "Makes payments", "HTTPS") + +Rel(admin, userMgmt, "Manages", "HTTPS") +Rel(admin, productCatalog, "Manages", "HTTPS") +Rel(admin, orderMgmt, "Monitors", "HTTPS") + +Rel(orderMgmt, inventory, "Reserves items", "HTTP") +Rel(orderMgmt, payment, "Processes payment", "HTTP") +Rel(orderMgmt, notification, "Sends notifications", "Events") + +Rel(productCatalog, recommendation, "Gets recommendations", "HTTP") +Rel(customer, analytics, "Generates events", "Events") + +@enduml +``` + +### Проектирование архитектуры + +#### 1. High-Level Architecture +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml + +title E-commerce Platform - Container Diagram + +Person(customer, "Customer", "A customer who shops online") +Person(admin, "Admin", "Platform administrator") + +System_Boundary(platform, "E-commerce Platform") { + Container(webApp, "Web Application", "React", "Single-page application for customers") + Container(adminApp, "Admin Application", "React", "Administrative interface") + Container(apiGateway, "API Gateway", "Kong", "Routes requests to microservices") + + Container(userService, "User Service", "Java Spring Boot", "Manages user accounts and authentication") + Container(productService, "Product Service", "Java Spring Boot", "Manages product catalog") + Container(orderService, "Order Service", "Java Spring Boot", "Processes orders") + Container(paymentService, "Payment Service", "Java Spring Boot", "Handles payments") + Container(inventoryService, "Inventory Service", "Java Spring Boot", "Manages stock") + + ContainerDb(userDb, "User Database", "PostgreSQL", "Stores user data") + ContainerDb(productDb, "Product Database", "PostgreSQL", "Stores product data") + ContainerDb(orderDb, "Order Database", "PostgreSQL", "Stores order data") + ContainerDb(paymentDb, "Payment Database", "PostgreSQL", "Stores payment data") + ContainerDb(inventoryDb, "Inventory Database", "PostgreSQL", "Stores inventory data") + + Container(redis, "Redis Cache", "Redis", "Caches frequently accessed data") + Container(messageQueue, "Message Queue", "Apache Kafka", "Handles asynchronous communication") +} + +System_Ext(paymentGateway, "Payment Gateway", "External payment processing service") +System_Ext(emailService, "Email Service", "External email service") +System_Ext(analyticsService, "Analytics Service", "External analytics service") + +Rel(customer, webApp, "Uses", "HTTPS") +Rel(admin, adminApp, "Uses", "HTTPS") + +Rel(webApp, apiGateway, "Makes API calls", "HTTPS") +Rel(adminApp, apiGateway, "Makes API calls", "HTTPS") + +Rel(apiGateway, userService, "Routes requests", "HTTP") +Rel(apiGateway, productService, "Routes requests", "HTTP") +Rel(apiGateway, orderService, "Routes requests", "HTTP") +Rel(apiGateway, paymentService, "Routes requests", "HTTP") +Rel(apiGateway, inventoryService, "Routes requests", "HTTP") + +Rel(userService, userDb, "Reads from and writes to", "JDBC") +Rel(productService, productDb, "Reads from and writes to", "JDBC") +Rel(orderService, orderDb, "Reads from and writes to", "JDBC") +Rel(paymentService, paymentDb, "Reads from and writes to", "JDBC") +Rel(inventoryService, inventoryDb, "Reads from and writes to", "JDBC") + +Rel(userService, redis, "Caches data", "Redis Protocol") +Rel(productService, redis, "Caches data", "Redis Protocol") +Rel(orderService, redis, "Caches data", "Redis Protocol") + +Rel(orderService, messageQueue, "Publishes events", "Kafka") +Rel(paymentService, messageQueue, "Consumes events", "Kafka") +Rel(inventoryService, messageQueue, "Consumes events", "Kafka") + +Rel(paymentService, paymentGateway, "Processes payments", "HTTPS") +Rel(userService, emailService, "Sends emails", "SMTP") +Rel(webApp, analyticsService, "Tracks events", "HTTPS") + +@enduml +``` + +#### 2. Detailed Component Design +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml + +title Order Service - Component Diagram + +Container_Boundary(orderService, "Order Service") { + Component(controllers, "Controllers", "Spring MVC", "Handles HTTP requests") + Component(services, "Services", "Spring", "Contains business logic") + Component(repositories, "Repositories", "Spring Data", "Handles data access") + Component(events, "Event Handlers", "Spring", "Handles domain events") + Component(validators, "Validators", "Spring", "Validates input data") +} + +ContainerDb(orderDb, "Order Database", "PostgreSQL", "Stores order data") +Container(redis, "Redis Cache", "Redis", "Caches order data") +Container(messageQueue, "Message Queue", "Apache Kafka", "Publishes and consumes events") + +Rel(controllers, services, "Uses") +Rel(services, repositories, "Uses") +Rel(services, events, "Publishes") +Rel(services, validators, "Uses") +Rel(repositories, orderDb, "Reads from and writes to", "JDBC") +Rel(services, redis, "Caches data", "Redis Protocol") +Rel(events, messageQueue, "Publishes events", "Kafka") + +@enduml +``` + +### Выбор технологического стека + +#### 1. Backend Technologies +```yaml +# Technology Stack +backend: + language: "Java 17" + framework: "Spring Boot 3.x" + build_tool: "Maven" + testing: "JUnit 5, Mockito, TestContainers" + + databases: + primary: "PostgreSQL 15" + cache: "Redis 7" + search: "Elasticsearch 8" + + messaging: + broker: "Apache Kafka" + protocol: "Avro" + + security: + authentication: "JWT" + authorization: "Spring Security" + encryption: "AES-256" +``` + +#### 2. Frontend Technologies +```yaml +# Frontend Stack +frontend: + framework: "React 18" + language: "TypeScript" + state_management: "Redux Toolkit" + styling: "Material-UI" + build_tool: "Vite" + testing: "Jest, React Testing Library" +``` + +#### 3. Infrastructure Technologies +```yaml +# Infrastructure Stack +infrastructure: + containerization: "Docker" + orchestration: "Kubernetes" + service_mesh: "Istio" + api_gateway: "Kong" + + monitoring: + metrics: "Prometheus" + visualization: "Grafana" + logging: "ELK Stack" + tracing: "Jaeger" + + cloud: + provider: "AWS" + services: + - "EKS (Kubernetes)" + - "RDS (PostgreSQL)" + - "ElastiCache (Redis)" + - "S3 (File Storage)" + - "CloudWatch (Monitoring)" +``` + +--- + +## Реализация архитектуры + +### Структура проекта + +#### 1. Monorepo Structure +``` +ecommerce-platform/ +├── services/ +│ ├── user-service/ +│ │ ├── src/ +│ │ ├── Dockerfile +│ │ ├── pom.xml +│ │ └── README.md +│ ├── product-service/ +│ ├── order-service/ +│ ├── payment-service/ +│ └── inventory-service/ +├── frontend/ +│ ├── customer-app/ +│ ├── admin-app/ +│ └── shared/ +├── infrastructure/ +│ ├── kubernetes/ +│ ├── terraform/ +│ └── docker-compose/ +├── docs/ +│ ├── architecture/ +│ ├── api/ +│ └── deployment/ +└── scripts/ + ├── build.sh + ├── deploy.sh + └── test.sh +``` + +#### 2. Service Implementation +```java +// Order Service Implementation +@RestController +@RequestMapping("/api/orders") +@Validated +public class OrderController { + + @Autowired + private OrderService orderService; + + @PostMapping + public ResponseEntity createOrder( + @Valid @RequestBody CreateOrderRequest request) { + + Order order = orderService.createOrder(request); + OrderResponse response = OrderMapper.toResponse(order); + + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + @GetMapping("/{id}") + public ResponseEntity getOrder(@PathVariable Long id) { + Order order = orderService.getOrder(id); + OrderResponse response = OrderMapper.toResponse(order); + + return ResponseEntity.ok(response); + } + + @PutMapping("/{id}/status") + public ResponseEntity updateOrderStatus( + @PathVariable Long id, + @Valid @RequestBody UpdateOrderStatusRequest request) { + + Order order = orderService.updateOrderStatus(id, request.getStatus()); + OrderResponse response = OrderMapper.toResponse(order); + + return ResponseEntity.ok(response); + } +} + +@Service +@Transactional +public class OrderService { + + @Autowired + private OrderRepository orderRepository; + + @Autowired + private OrderEventPublisher eventPublisher; + + @Autowired + private InventoryServiceClient inventoryClient; + + @Autowired + private PaymentServiceClient paymentClient; + + public Order createOrder(CreateOrderRequest request) { + // Валидация заказа + validateOrderRequest(request); + + // Создание заказа + Order order = new Order(request); + order = orderRepository.save(order); + + // Резервирование товаров + inventoryClient.reserveItems(order.getId(), request.getItems()); + + // Публикация события + eventPublisher.publishOrderCreated(order); + + return order; + } + + public Order updateOrderStatus(Long orderId, OrderStatus status) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new OrderNotFoundException("Order not found: " + orderId)); + + order.updateStatus(status); + order = orderRepository.save(order); + + // Публикация события + eventPublisher.publishOrderStatusUpdated(order); + + return order; + } + + private void validateOrderRequest(CreateOrderRequest request) { + if (request.getItems().isEmpty()) { + throw new ValidationException("Order must contain at least one item"); + } + + // Дополнительные проверки... + } +} +``` + +### Проектирование API + +#### 1. REST API Specification +```yaml +# OpenAPI Specification +openapi: 3.0.0 +info: + title: E-commerce Platform API + version: 1.0.0 + description: API for e-commerce platform + +servers: + - url: https://api.ecommerce.com/v1 + description: Production server + +paths: + /orders: + post: + summary: Create a new order + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateOrderRequest' + responses: + '201': + description: Order created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/OrderResponse' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /orders/{id}: + get: + summary: Get order by ID + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '200': + description: Order retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/OrderResponse' + '404': + description: Order not found + +components: + schemas: + CreateOrderRequest: + type: object + required: + - customerId + - items + properties: + customerId: + type: integer + items: + type: array + items: + $ref: '#/components/schemas/OrderItem' + + OrderResponse: + type: object + properties: + id: + type: integer + customerId: + type: integer + status: + type: string + enum: [CREATED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED] + totalAmount: + type: number + format: decimal + items: + type: array + items: + $ref: '#/components/schemas/OrderItem' + createdAt: + type: string + format: date-time +``` + +#### 2. Event Schema +```yaml +# Event Schema +events: + order_created: + schema: + type: object + properties: + eventId: + type: string + eventType: + type: string + enum: [order_created] + timestamp: + type: string + format: date-time + data: + type: object + properties: + orderId: + type: integer + customerId: + type: integer + totalAmount: + type: number + items: + type: array + items: + type: object + properties: + productId: + type: integer + quantity: + type: integer + unitPrice: + type: number +``` + +### Проектирование базы данных + +#### 1. Database Schema +```sql +-- Order Service Database Schema +CREATE TABLE orders ( + id BIGSERIAL PRIMARY KEY, + customer_id BIGINT NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'CREATED', + total_amount DECIMAL(10,2) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE order_items ( + id BIGSERIAL PRIMARY KEY, + order_id BIGINT NOT NULL REFERENCES orders(id), + product_id BIGINT NOT NULL, + quantity INTEGER NOT NULL, + unit_price DECIMAL(10,2) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE order_events ( + id BIGSERIAL PRIMARY KEY, + order_id BIGINT NOT NULL REFERENCES orders(id), + event_type VARCHAR(50) NOT NULL, + event_data JSONB NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- Индексы +CREATE INDEX idx_orders_customer_id ON orders(customer_id); +CREATE INDEX idx_orders_status ON orders(status); +CREATE INDEX idx_orders_created_at ON orders(created_at); +CREATE INDEX idx_order_items_order_id ON order_items(order_id); +CREATE INDEX idx_order_items_product_id ON order_items(product_id); +CREATE INDEX idx_order_events_order_id ON order_events(order_id); +CREATE INDEX idx_order_events_event_type ON order_events(event_type); +``` + +#### 2. Data Access Layer +```java +// Repository Implementation +@Repository +public interface OrderRepository extends JpaRepository { + + List findByCustomerId(Long customerId); + + List findByStatus(OrderStatus status); + + List findByCreatedAtBetween(LocalDateTime start, LocalDateTime end); + + @Query("SELECT o FROM Order o WHERE o.customerId = :customerId AND o.status = :status") + List findByCustomerIdAndStatus(@Param("customerId") Long customerId, + @Param("status") OrderStatus status); +} + +@Entity +@Table(name = "orders") +public class Order { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "customer_id", nullable = false) + private Long customerId; + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false) + private OrderStatus status; + + @Column(name = "total_amount", nullable = false, precision = 10, scale = 2) + private BigDecimal totalAmount; + + @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List items = new ArrayList<>(); + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @Column(name = "updated_at", nullable = false) + private LocalDateTime updatedAt; + + // Constructors, getters, setters, and business methods +} +``` + +--- + +## Документирование решения + +### Документация архитектуры + +#### 1. Architecture Decision Records +```markdown +# ADR-001: Микросервисная архитектура + +## Статус +Принято + +## Дата +2024-01-15 + +## Контекст +E-commerce платформа должна обрабатывать высокую нагрузку и обеспечивать быструю разработку новых функций. Текущая монолитная архитектура создает проблемы с масштабированием и развертыванием. + +## Решение +Переход к микросервисной архитектуре со следующими принципами: +- Каждый сервис отвечает за один бизнес-домен +- Сервисы независимо развертываются +- Сервисы имеют собственные базы данных +- Коммуникация через REST API и события + +## Альтернативы +1. **Модульный монолит** — разделение на модули внутри одного приложения +2. **SOA** — сервисно-ориентированная архитектура +3. **Оставить монолит** — продолжить развитие текущей архитектуры + +## Последствия +### Положительные +- Независимое развертывание сервисов +- Возможность использования разных технологий +- Лучшая масштабируемость +- Изоляция отказов + +### Отрицательные +- Увеличение сложности системы +- Проблемы с распределенными транзакциями +- Сложность мониторинга +- Overhead коммуникации +``` + +#### 2. API Documentation +```markdown +# API Documentation + +## Order Service API + +### Создание заказа +**POST** `/api/orders` + +Creates a new order for a customer. + +#### Тело запроса +```json +{ + "customerId": 123, + "items": [ + { + "productId": 456, + "quantity": 2, + "unitPrice": 29.99 + } + ] +} +``` + +#### Ответ +```json +{ + "id": 789, + "customerId": 123, + "status": "CREATED", + "totalAmount": 59.98, + "items": [ + { + "productId": 456, + "quantity": 2, + "unitPrice": 29.99 + } + ], + "createdAt": "2024-01-15T10:30:00Z" +} +``` + +#### Ответы об ошибках +- **400 Bad Request**: Invalid request data +- **404 Not Found**: Customer not found +- **500 Internal Server Error**: Server error +``` + +### Документация развертывания + +#### 1. Deployment Guide +```markdown +# Deployment Guide + +## Prerequisites +- Docker and Docker Compose +- Kubernetes cluster +- kubectl configured +- Helm 3.x + +## Local Development +1. Clone the repository +2. Run `docker-compose up -d` to start dependencies +3. Run `./scripts/build.sh` to build all services +4. Run `./scripts/test.sh` to run tests + +## Production Deployment +1. Build Docker images: `./scripts/build.sh` +2. Push images to registry: `./scripts/push.sh` +3. Deploy to Kubernetes: `./scripts/deploy.sh` +4. Verify deployment: `kubectl get pods` + +## Environment Configuration +- Development: `config/dev/` +- Staging: `config/staging/` +- Production: `config/prod/` +``` + +#### 2. Monitoring Setup +```yaml +# Monitoring Configuration +monitoring: + prometheus: + scrape_configs: + - job_name: 'order-service' + static_configs: + - targets: ['order-service:8080'] + metrics_path: '/actuator/prometheus' + scrape_interval: 15s + + grafana: + dashboards: + - name: 'Order Service Dashboard' + panels: + - title: 'Request Rate' + query: 'rate(http_requests_total[5m])' + - title: 'Response Time' + query: 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))' + - title: 'Error Rate' + query: 'rate(http_requests_total{status=~"5.."}[5m])' + + alerts: + - name: 'High Error Rate' + condition: 'rate(http_requests_total{status=~"5.."}[5m]) > 0.05' + severity: 'critical' + - name: 'High Response Time' + condition: 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1' + severity: 'warning' +``` + +--- + +## Презентация проекта + +### Структура презентации + +#### 1. Executive Summary +```markdown +# E-commerce Platform Architecture +## Executive Summary + +### Постановка проблемы +- Current monolithic architecture limits scalability +- High deployment risk and slow time-to-market +- Difficult to maintain and extend + +### Предлагаемое решение +- Microservices architecture with cloud-native design +- Independent deployment and scaling +- Improved developer productivity + +### Бизнес-ценность +- 50% faster time-to-market +- 99.9% availability +- 10x better scalability +- Reduced operational costs + +### План реализации +- Phase 1: Core services (3 months) +- Phase 2: Advanced features (2 months) +- Phase 3: Optimization (1 month) +``` + +#### 2. Technical Deep Dive +```markdown +# Technical Architecture + +## Architecture Principles +- Domain-Driven Design +- Microservices +- Event-Driven Architecture +- Cloud-Native + +## Technology Stack +- Backend: Java 17, Spring Boot 3.x +- Frontend: React 18, TypeScript +- Database: PostgreSQL, Redis +- Infrastructure: Kubernetes, AWS + +## Key Features +- High availability (99.9%) +- Horizontal scaling +- Real-time processing +- Comprehensive monitoring + +## Security +- JWT authentication +- Role-based authorization +- Data encryption +- PCI DSS compliance +``` + +### Сценарии демонстрации + +#### 1. User Journey Demo +```markdown +# Demo Scenario: Complete User Journey + +## Scenario 1: Customer Places Order +1. Customer browses products +2. Adds items to cart +3. Proceeds to checkout +4. Enters payment information +5. Confirms order +6. Receives confirmation email + +## Scenario 2: Order Processing +1. Order service creates order +2. Inventory service reserves items +3. Payment service processes payment +4. Order status updated +5. Customer notified + +## Scenario 3: Admin Management +1. Admin views order dashboard +2. Updates order status +3. Monitors system metrics +4. Handles customer inquiries +``` + +#### 2. Technical Demo +```markdown +# Technical Demo + +## Microservices Communication +- Show service-to-service communication +- Demonstrate event-driven architecture +- Display real-time monitoring + +## Scalability Demo +- Load test with increasing traffic +- Show auto-scaling in action +- Demonstrate failover scenarios + +## Monitoring Demo +- Real-time metrics dashboard +- Alert notifications +- Performance analysis +``` + +--- + +## Подготовка к собеседованию + +### Типовые вопросы на собеседовании Solution Architect + +#### 1. Архитектурные принципы и паттерны + +**Вопрос:** "Объясните разницу между монолитной и микросервисной архитектурой. Когда бы вы выбрали каждую из них?" + +**Структура ответа:** +```markdown +# Монолитная vs Микросервисная архитектура + +## Монолитная архитектура +**Определение:** Единое приложение, развертываемое как один компонент + +**Преимущества:** +- Простота разработки и развертывания +- Легкое тестирование +- Простая отладка +- Низкие накладные расходы + +**Недостатки:** +- Сложность масштабирования +- Технологическая привязка +- Риск каскадных сбоев +- Сложность в больших командах + +**Когда выбирать:** +- Небольшие команды (1-5 разработчиков) +- Простые приложения +- Быстрый MVP +- Ограниченные ресурсы + +## Микросервисная архитектура +**Определение:** Набор независимых сервисов, взаимодействующих через API + +**Преимущества:** +- Независимое масштабирование +- Технологическое разнообразие +- Отказоустойчивость +- Независимые команды + +**Недостатки:** +- Сложность управления +- Сетевые задержки +- Сложность тестирования +- Высокие накладные расходы + +**Когда выбирать:** +- Большие команды (10+ разработчиков) +- Сложные домены +- Высокие требования к масштабированию +- Разные технологические потребности + +## Пример решения +```java +// Монолитная архитектура +@SpringBootApplication +public class EcommerceApplication { + public static void main(String[] args) { + SpringApplication.run(EcommerceApplication.class, args); + } +} + +// Микросервисная архитектура +@RestController +@RequestMapping("/api/users") +public class UserController { + // Только пользовательская логика +} + +@RestController +@RequestMapping("/api/orders") +public class OrderController { + // Только логика заказов +} +``` + +**Вопрос:** "Как бы вы спроектировали систему для обработки 1 миллиона запросов в секунду?" + +**Структура ответа:** +```markdown +# Архитектура высоконагруженной системы + +## 1. Анализ требований +- 1M RPS = 1,000,000 запросов/сек +- Пиковая нагрузка может быть в 3-5 раз выше +- Требуется горизонтальное масштабирование + +## 2. Архитектурные решения + +### Балансировка нагрузки +- Multiple load balancers (HAProxy, NGINX) +- DNS round-robin +- Geographic distribution + +### Стратегия кэширования +- CDN для статического контента +- Redis для сессий и кэша +- Application-level caching + +### Шардирование базы данных +- Горизонтальное разделение данных +- Consistent hashing +- Read replicas + +### Микросервисы +- Stateless services +- Auto-scaling +- Circuit breakers + +## 3. Техническая реализация +```yaml +# Kubernetes HPA +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: api-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: api-service + minReplicas: 100 + maxReplicas: 1000 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +``` + +## 4. Мониторинг и алертинг +- Real-time metrics +- Auto-scaling triggers +- Performance monitoring +``` + +#### 2. Технические вопросы + +**Вопрос:** "Объясните паттерн Circuit Breaker и как его реализовать." + +**Ответ:** +```java +// Circuit Breaker Implementation +@Component +public class CircuitBreakerService { + + private final CircuitBreaker circuitBreaker; + private final RestTemplate restTemplate; + + public CircuitBreakerService() { + this.circuitBreaker = CircuitBreaker.ofDefaults("external-service"); + this.restTemplate = new RestTemplate(); + } + + public String callExternalService(String url) { + return circuitBreaker.executeSupplier(() -> { + try { + ResponseEntity response = restTemplate.getForEntity(url, String.class); + return response.getBody(); + } catch (Exception e) { + throw new RuntimeException("External service call failed", e); + } + }); + } + + // Fallback method + public String getFallbackResponse() { + return "Service temporarily unavailable"; + } +} + +// Configuration +@Configuration +public class CircuitBreakerConfig { + + @Bean + public CircuitBreaker circuitBreaker() { + return CircuitBreaker.ofDefaults("external-service") + .toBuilder() + .slidingWindowSize(10) + .minimumNumberOfCalls(5) + .failureRateThreshold(50) + .waitDurationInOpenState(Duration.ofSeconds(30)) + .build(); + } +} +``` + +**Вопрос:** "Как бы вы обеспечили консистентность данных в распределенной системе?" + +**Ответ:** +```markdown +# Консистентность данных в распределенных системах + +## 1. CAP Theorem +- Consistency: Все узлы видят одинаковые данные +- Availability: Система остается доступной +- Partition Tolerance: Система работает при разделении сети + +## 2. Стратегии консистентности + +### Строгая консистентность +- ACID транзакции +- Синхронная репликация +- Высокая задержка + +### Итоговая консистентность +- Асинхронная репликация +- Низкая задержка +- Возможны временные несоответствия + +### Каузальная консистентность +- Причинно-следственные связи +- Баланс между консистентностью и производительностью + +## 3. Паттерны реализации + +### Паттерн Saga +```java +@Component +public class OrderSaga { + + @SagaOrchestrationStart + public void startOrderSaga(OrderCreatedEvent event) { + // 1. Создание заказа + orderService.createOrder(event.getOrder()); + + // 2. Резервирование товаров + inventoryService.reserveItems(event.getOrderItems()); + + // 3. Обработка платежа + paymentService.processPayment(event.getPayment()); + } + + @SagaOrchestrationOnEvent + public void handlePaymentProcessed(PaymentProcessedEvent event) { + if (event.isSuccess()) { + orderService.confirmOrder(event.getOrderId()); + } else { + orderService.cancelOrder(event.getOrderId()); + inventoryService.releaseItems(event.getOrderItems()); + } + } +} +``` + +### Event Sourcing +```java +@Entity +public class OrderAggregate { + + @Id + private String orderId; + private List uncommittedEvents = new ArrayList<>(); + + public void createOrder(CreateOrderCommand command) { + OrderCreatedEvent event = new OrderCreatedEvent( + command.getOrderId(), + command.getCustomerId(), + command.getItems() + ); + + apply(event); + } + + private void apply(DomainEvent event) { + uncommittedEvents.add(event); + // Обновление состояния + handle(event); + } + + public List getUncommittedEvents() { + return uncommittedEvents; + } +} +``` + +## 4. Технологические решения +- Distributed transactions (2PC, 3PC) +- Consensus algorithms (Raft, PBFT) +- Conflict-free replicated data types (CRDTs) +- Vector clocks +``` + +#### 3. Системное проектирование + +**Вопрос:** "Спроектируйте систему коротких ссылок (URL shortener) типа bit.ly" + +**Структура ответа:** +```markdown +# URL Shortener System Design + +## 1. Требования +- Функциональные: + - Сокращение длинных URL + - Перенаправление по коротким ссылкам + - Аналитика кликов + +- Нефункциональные: + - 100M URL/день + - 1000 RPS + - 99.9% availability + - < 200ms response time + +## 2. Оценка масштаба +- 100M URL/день = 100M записей/день +- 1000 RPS = 86.4M запросов/день +- Хранение: 100M * 500 bytes = 50GB/день +- За 5 лет: 50GB * 365 * 5 = 91TB + +## 3. Высокоуровневая архитектура +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Client │───▶│ Load │───▶│ API │ +│ │ │ Balancer │ │ Gateway │ +└─────────────┘ └─────────────┘ └─────────────┘ + │ + ▼ +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Analytics │◄───│ URL │───▶│ Database │ +│ Service │ │ Service │ │ Cluster │ +└─────────────┘ └─────────────┘ └─────────────┘ +``` + +## 4. Детальное проектирование + +### URL кодирование +```java +public class URLEncoder { + private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + private static final int BASE = ALPHABET.length(); + + public String encode(long id) { + StringBuilder sb = new StringBuilder(); + while (id > 0) { + sb.append(ALPHABET.charAt((int)(id % BASE))); + id /= BASE; + } + return sb.reverse().toString(); + } + + public long decode(String shortUrl) { + long id = 0; + for (char c : shortUrl.toCharArray()) { + id = id * BASE + ALPHABET.indexOf(c); + } + return id; + } +} +``` + +### Схема базы данных +```sql +-- URLs table +CREATE TABLE urls ( + id BIGINT PRIMARY KEY, + short_url VARCHAR(10) UNIQUE NOT NULL, + long_url TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + expires_at TIMESTAMP, + user_id BIGINT, + INDEX idx_short_url (short_url), + INDEX idx_long_url (long_url(255)) +); + +-- Analytics table +CREATE TABLE url_analytics ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + short_url VARCHAR(10) NOT NULL, + ip_address VARCHAR(45), + user_agent TEXT, + referer TEXT, + clicked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX idx_short_url (short_url), + INDEX idx_clicked_at (clicked_at) +); +``` + +### Стратегия кэширования +```java +@Service +public class URLService { + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private URLRepository urlRepository; + + public String shortenUrl(String longUrl) { + // Check cache first + String cachedShortUrl = redisTemplate.opsForValue().get("url:" + longUrl); + if (cachedShortUrl != null) { + return cachedShortUrl; + } + + // Generate new short URL + Long id = generateId(); + String shortUrl = encode(id); + + // Save to database + URL url = new URL(id, shortUrl, longUrl); + urlRepository.save(url); + + // Cache the mapping + redisTemplate.opsForValue().set("url:" + longUrl, shortUrl, Duration.ofDays(30)); + redisTemplate.opsForValue().set("short:" + shortUrl, longUrl, Duration.ofDays(30)); + + return shortUrl; + } + + public String getLongUrl(String shortUrl) { + // Check cache first + String longUrl = redisTemplate.opsForValue().get("short:" + shortUrl); + if (longUrl != null) { + return longUrl; + } + + // Query database + URL url = urlRepository.findByShortUrl(shortUrl); + if (url != null) { + // Cache the result + redisTemplate.opsForValue().set("short:" + shortUrl, url.getLongUrl(), Duration.ofDays(30)); + return url.getLongUrl(); + } + + return null; + } +} +``` + +## 5. Масштабирование +- Database sharding по short_url +- Read replicas для аналитики +- CDN для статического контента +- Horizontal scaling API сервисов +``` + +#### 4. Поведенческие вопросы + +**Вопрос:** "Расскажите о сложном техническом решении, которое вы принимали. Как вы к нему пришли?" + +**Структура ответа (STAR метод):** +```markdown +# Пример ответа по STAR методу + +## Situation (Ситуация) +В нашей компании была монолитная система электронной коммерции, которая не справлялась с ростом нагрузки. +Система падала во время пиковых нагрузок (Black Friday), что приводило к потере продаж. + +## Task (Задача) +Мне нужно было спроектировать решение для масштабирования системы, которое: +- Выдержит 10x увеличение нагрузки +- Обеспечит 99.9% доступность +- Будет реализовано за 6 месяцев +- Не нарушит текущую функциональность + +## Action (Действия) +1. **Анализ текущей системы:** + - Провел профилирование производительности + - Выявил узкие места (база данных, внешние API) + - Проанализировал паттерны нагрузки + +2. **Проектирование решения:** + - Выбрал микросервисную архитектуру + - Спроектировал API Gateway + - Реализовал кэширование (Redis) + - Настроил мониторинг (Prometheus + Grafana) + +3. **Реализация:** + - Поэтапная миграция сервисов + - A/B тестирование производительности + - Обучение команды новым технологиям + +## Result (Результат) +- Увеличили пропускную способность в 15 раз +- Достигли 99.95% доступности +- Сократили время отклика с 2 секунд до 200ms +- Команда стала более эффективной благодаря независимой разработке +``` + +**Вопрос:** "Как вы управляете техническим долгом?" + +**Ответ:** +```markdown +# Управление техническим долгом + +## 1. Идентификация технического долга +- Code review процесс +- Статический анализ кода +- Метрики качества (SonarQube) +- Обратная связь от команды + +## 2. Приоритизация +```java +// Матрица приоритизации +public class TechnicalDebtPrioritization { + + public Priority calculatePriority(TechnicalDebt debt) { + int impact = calculateImpact(debt); + int effort = calculateEffort(debt); + int urgency = calculateUrgency(debt); + + int score = (impact * 3) + (urgency * 2) - effort; + + if (score >= 8) return Priority.HIGH; + if (score >= 5) return Priority.MEDIUM; + return Priority.LOW; + } + + private int calculateImpact(TechnicalDebt debt) { + // Влияние на производительность, безопасность, поддерживаемость + return debt.getPerformanceImpact() + + debt.getSecurityImpact() + + debt.getMaintainabilityImpact(); + } +} +``` + +## 3. Стратегии погашения +- **Рефакторинг в рамках новых фич** +- **Выделение времени на техническое улучшение** +- **Технические спринты** +- **Постепенная миграция** + +## 4. Предотвращение +- Coding standards +- Automated testing +- Continuous integration +- Architecture reviews +``` + +#### 5. Вопросы о лидерстве + +**Вопрос:** "Как вы мотивируете команду на изучение новых технологий?" + +**Ответ:** +```markdown +# Мотивация команды к изучению новых технологий + +## 1. Создание обучающей среды +- **Tech talks** - еженедельные презентации +- **Hackathons** - соревнования с новыми технологиями +- **Learning budget** - выделение времени и ресурсов +- **Mentoring** - парное программирование + +## 2. Практический подход +```java +// Пример: Внедрение новой технологии через реальную задачу +@RestController +public class NewTechController { + + // Показать преимущества новой технологии на конкретном примере + @GetMapping("/api/users/{id}") + public Mono getUser(@PathVariable String id) { + // Используем Reactive Programming для демонстрации + return userService.findById(id) + .map(this::enrichUserData) + .doOnNext(user -> log.info("User retrieved: {}", user.getId())); + } +} +``` + +## 3. Создание успешных кейсов +- **Pilot projects** - небольшие проекты с новыми технологиями +- **Success stories** - демонстрация преимуществ +- **Performance improvements** - показ улучшений производительности + +## 4. Поддержка и ресурсы +- **Documentation** - создание внутренней документации +- **Training materials** - курсы и материалы +- **Community** - создание внутреннего сообщества +- **Recognition** - признание достижений команды +``` + +### Подготовка к техническому интервью + +#### 1. Структура подготовки + +```markdown +# План подготовки к собеседованию + +## За 4 недели до собеседования +- [ ] Повторить основы архитектуры +- [ ] Изучить последние тренды +- [ ] Подготовить портфолио проектов +- [ ] Практиковаться в системном проектировании + +## За 2 недели до собеседования +- [ ] Решить 10+ задач системного проектирования +- [ ] Подготовить примеры из опыта +- [ ] Изучить компанию и их технологии +- [ ] Практиковаться в презентации решений + +## За 1 неделю до собеседования +- [ ] Повторить ключевые концепции +- [ ] Подготовить вопросы для интервьюера +- [ ] Протестировать техническое оборудование +- [ ] Отдохнуть и выспаться +``` + +#### 2. Практические упражнения + +```markdown +# Упражнения для подготовки + +## Системное проектирование +1. **Chat System** - система обмена сообщениями +2. **Social Media Feed** - лента новостей +3. **Video Streaming** - стриминг видео +4. **Search Engine** - поисковая система +5. **Distributed Cache** - распределенный кэш + +## Архитектурные паттерны +1. **Microservices vs Monolith** - сравнение подходов +2. **Event-Driven Architecture** - проектирование событийной системы +3. **CQRS Implementation** - разделение команд и запросов +4. **Saga Pattern** - управление распределенными транзакциями + +## Технические вопросы +1. **Database Design** - проектирование схемы БД +2. **Caching Strategies** - стратегии кэширования +3. **Load Balancing** - балансировка нагрузки +4. **Security** - обеспечение безопасности +``` + +#### 3. Типичные ошибки и как их избежать + +```markdown +# Частые ошибки на собеседованиях + +## 1. Слишком быстрое решение +**Проблема:** Сразу переходить к техническим деталям +**Решение:** Сначала уточнить требования, ограничения, масштаб + +## 2. Игнорирование нефункциональных требований +**Проблема:** Фокус только на функциональности +**Решение:** Обсудить производительность, масштабируемость, безопасность + +## 3. Недооценка сложности +**Проблема:** Упрощение реальных проблем +**Решение:** Учитывать реальные ограничения и компромиссы + +## 4. Отсутствие примеров из опыта +**Проблема:** Теоретические знания без практики +**Решение:** Подготовить конкретные примеры из проектов + +## 5. Неумение объяснить решения +**Проблема:** Неспособность обосновать выбор +**Решение:** Практиковаться в объяснении архитектурных решений +``` + +### Карьерное развитие Solution Architect + +#### 1. Пути развития + +```markdown +# Карьерные пути Solution Architect + +## Техническое направление +- **Senior Solution Architect** - более сложные проекты +- **Principal Solution Architect** - архитектурная стратегия +- **Chief Technology Officer (CTO)** - техническое лидерство + +## Управленческое направление +- **Engineering Manager** - управление командами +- **Director of Engineering** - управление несколькими командами +- **VP of Engineering** - стратегическое планирование + +## Специализированные роли +- **Cloud Architect** - специализация на облачных технологиях +- **Security Architect** - фокус на безопасности +- **Data Architect** - работа с большими данными +- **Enterprise Architect** - корпоративная архитектура +``` + +#### 2. Непрерывное обучение + +```markdown +# План непрерывного развития + +## Технические навыки +- [ ] Изучение новых технологий (ежеквартально) +- [ ] Сертификации (AWS, Azure, GCP) +- [ ] Участие в конференциях +- [ ] Open source проекты + +## Soft Skills +- [ ] Презентационные навыки +- [ ] Управление конфликтами +- [ ] Техническое лидерство +- [ ] Менторство + +## Бизнес-навыки +- [ ] Понимание бизнес-процессов +- [ ] Финансовое планирование +- [ ] Управление проектами +- [ ] Стратегическое мышление +``` + +--- + +## Оценка и рефлексия + +### Критерии оценки проекта + +#### 1. Technical Excellence +```markdown +# Technical Evaluation + +## Architecture Quality +- [ ] Clear separation of concerns +- [ ] Appropriate use of patterns +- [ ] Scalable and maintainable design +- [ ] Security considerations + +## Implementation Quality +- [ ] Clean, readable code +- [ ] Proper error handling +- [ ] Comprehensive testing +- [ ] Good documentation + +## Technology Choices +- [ ] Appropriate technology stack +- [ ] Modern best practices +- [ ] Performance considerations +- [ ] Security best practices +``` + +#### 2. Documentation Quality +```markdown +# Documentation Evaluation + +## Architecture Documentation +- [ ] Clear architecture diagrams +- [ ] Well-documented ADRs +- [ ] Comprehensive API documentation +- [ ] Deployment guides + +## Code Documentation +- [ ] Clear code comments +- [ ] README files +- [ ] API examples +- [ ] Troubleshooting guides +``` + +#### 3. Presentation Quality +```markdown +# Presentation Evaluation + +## Content Quality +- [ ] Clear problem statement +- [ ] Well-justified solution +- [ ] Comprehensive technical details +- [ ] Business value demonstration + +## Presentation Skills +- [ ] Clear communication +- [ ] Engaging delivery +- [ ] Effective use of visuals +- [ ] Good time management +``` + +### Вопросы для рефлексии + +#### 1. Technical Reflection +```markdown +# Technical Reflection + +## What went well? +- Successful implementation of microservices +- Good separation of concerns +- Effective use of design patterns +- Comprehensive monitoring setup + +## What could be improved? +- Better error handling in some services +- More comprehensive testing +- Improved documentation +- Better performance optimization + +## Lessons learned? +- Importance of proper service boundaries +- Value of event-driven architecture +- Need for comprehensive monitoring +- Importance of documentation +``` + +#### 2. Process Reflection +```markdown +# Process Reflection + +## Planning +- Good initial architecture planning +- Effective use of ADRs +- Clear project structure +- Good time management + +## Implementation +- Smooth development process +- Effective use of tools +- Good collaboration +- Regular progress reviews + +## Documentation +- Comprehensive documentation +- Clear API specifications +- Good deployment guides +- Effective presentation materials +``` + +--- + +## Практические задания + +### Задание 1: Архитектурное проектирование +1. Выберите предметную область для проекта +2. Проведите domain analysis +3. Спроектируйте микросервисную архитектуру +4. Создайте архитектурные диаграммы + +### Задание 2: Реализация +1. Реализуйте ключевые сервисы +2. Настройте инфраструктуру +3. Реализуйте мониторинг +4. Настройте CI/CD + +### Задание 3: Документирование +1. Создайте ADRs для ключевых решений +2. Документируйте API +3. Создайте deployment guide +4. Подготовьте презентацию + +--- + +## Дополнительные ресурсы + +### Инструменты +- **Architecture**: C4-PlantUML, Structurizr, Lucidchart +- **API**: Swagger, Postman, Insomnia +- **Documentation**: GitBook, Confluence, Notion +- **Presentation**: PowerPoint, Keynote, Prezi + +### Шаблоны +- **ADR Template**: Architecture Decision Record +- **API Template**: OpenAPI Specification +- **Deployment Template**: Kubernetes manifests +- **Presentation Template**: Executive summary + +--- + +## Контрольные вопросы + +1. Как выбрать подходящую предметную область для проекта? +2. Как провести domain analysis? +3. Как спроектировать микросервисную архитектуру? +4. Как выбрать подходящий технологический стек? +5. Как документировать архитектурные решения? +6. Как подготовить эффективную презентацию? +7. Как оценить качество архитектурного решения? +8. Какие уроки можно извлечь из проекта? diff --git a/modules/module2/README.md b/modules/module2/README.md new file mode 100644 index 0000000..5402d7b --- /dev/null +++ b/modules/module2/README.md @@ -0,0 +1,102 @@ +# Модуль 2: Микросервисная архитектура + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля + +Этот модуль посвящен изучению микросервисной архитектуры - современного подхода к проектированию и разработке крупномасштабных распределенных систем. Студенты изучат принципы, паттерны и практики создания микросервисов, а также связанные технологии и инструменты. + +## Содержание модуля + +### 1. [Основы микросервисной архитектуры](sections/01-microservices-fundamentals.md) +- Определение микросервисов +- Принципы микросервисной архитектуры +- Преимущества и недостатки +- Когда использовать микросервисы + +### 2. [Domain-Driven Design (DDD)](sections/02-domain-driven-design.md) +- Стратегическое проектирование +- Тактическое проектирование +- Bounded Context и Context Map +- Event Storming + +### 3. [Коммуникация между сервисами](sections/03-service-communication.md) +- Синхронная коммуникация (HTTP/REST, GraphQL) +- Асинхронная коммуникация (Message Queues, Event Streaming) +- Паттерны коммуникации +- Service Discovery + +### 4. [Управление данными в микросервисах](sections/04-data-management.md) +- База данных на сервис +- Saga Pattern +- CQRS и Event Sourcing +- Консистентность данных + +### 5. [Serverless архитектура и FaaS](sections/05-serverless-architecture.md) +- Основы Serverless +- AWS Lambda, Azure Functions, Google Cloud Functions +- Преимущества и недостатки +- Паттерны использования + +### 6. [Микрофронтенды](sections/06-microfrontends.md) +- Принципы микрофронтендов +- Архитектурные паттерны +- Управление состоянием +- Коммуникация между микрофронтендами + +## Ключевые компетенции + +После изучения модуля студенты будут уметь: + +- Проектировать микросервисную архитектуру +- Применять принципы DDD в микросервисах +- Выбирать подходящие паттерны коммуникации +- Управлять данными в распределенных системах +- Использовать Serverless технологии +- Создавать микрофронтенды + +## Практические задания + +1. **Проектирование микросервисов** + - Разбить монолитное приложение на микросервисы + - Определить границы сервисов + - Спроектировать API + +2. **Реализация коммуникации** + - Создать REST API для синхронной коммуникации + - Настроить асинхронную коммуникацию через очереди + - Реализовать Service Discovery + +3. **Управление данными** + - Реализовать Saga Pattern + - Настроить CQRS + - Создать Event Store + +4. **Serverless функции** + - Создать AWS Lambda функции + - Настроить триггеры + - Реализовать обработку ошибок + +5. **Микрофронтенды** + - Создать базовую структуру микрофронтендов + - Настроить коммуникацию между ними + - Реализовать общее состояние + +## Дополнительные ресурсы + +- [Microservices.io](https://microservices.io/) - паттерны микросервисов +- [Domain-Driven Design Reference](https://www.domainlanguage.com/ddd/reference/) - справочник по DDD +- [AWS Serverless Application Model](https://aws.amazon.com/serverless/sam/) - SAM для AWS +- [Single-SPA](https://single-spa.js.org/) - фреймворк для микрофронтендов + +## Критерии оценки + +- Понимание принципов микросервисной архитектуры (25%) +- Умение применять DDD (20%) +- Навыки проектирования коммуникации (20%) +- Знание паттернов управления данными (15%) +- Опыт работы с Serverless (10%) +- Понимание микрофронтендов (10%) diff --git a/modules/module2/sections/01-microservices-fundamentals.md b/modules/module2/sections/01-microservices-fundamentals.md new file mode 100644 index 0000000..84ee491 --- /dev/null +++ b/modules/module2/sections/01-microservices-fundamentals.md @@ -0,0 +1,967 @@ +# Основы микросервисной архитектуры + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 2](../README.md) + +--- + +## Определение микросервисов + +**Микросервис** — это небольшая, независимая, слабо связанная служба, которая: +- Имеет единственную ответственность +- Развертывается независимо +- Имеет собственные данные +- Коммуницирует через четко определенные API + +## Принципы микросервисной архитектуры + +### 1. Single Responsibility Principle +Каждый микросервис должен решать одну бизнес-задачу. + +**Пример:** +``` +User Service → управление пользователями +Payment Service → обработка платежей +Order Service → управление заказами +Notification Service → отправка уведомлений +``` + +**Технические примеры:** + +**Java (Spring Boot) - User Service:** +```java +// User Service - только управление пользователями +@SpringBootApplication +public class UserServiceApplication { + public static void main(String[] args) { + SpringApplication.run(UserServiceApplication.class, args); + } +} + +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @PostMapping + public ResponseEntity createUser(@RequestBody CreateUserRequest request) { + User user = userService.createUser(request); + return ResponseEntity.ok(user); + } + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + User user = userService.getUser(id); + return ResponseEntity.ok(user); + } + + @PutMapping("/{id}") + public ResponseEntity updateUser(@PathVariable Long id, @RequestBody UpdateUserRequest request) { + User user = userService.updateUser(id, request); + return ResponseEntity.ok(user); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteUser(@PathVariable Long id) { + userService.deleteUser(id); + return ResponseEntity.ok().build(); + } +} + +@Service +@Transactional +public class UserService { + @Autowired + private UserRepository userRepository; + + public User createUser(CreateUserRequest request) { + // Валидация + if (userRepository.existsByEmail(request.getEmail())) { + throw new UserAlreadyExistsException("User with email already exists"); + } + + // Создание пользователя + User user = new User(); + user.setName(request.getName()); + user.setEmail(request.getEmail()); + user.setPasswordHash(passwordEncoder.encode(request.getPassword())); + user.setCreatedAt(LocalDateTime.now()); + + return userRepository.save(user); + } + + public User getUser(Long id) { + return userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found")); + } + + public User updateUser(Long id, UpdateUserRequest request) { + User user = getUser(id); + user.setName(request.getName()); + user.setUpdatedAt(LocalDateTime.now()); + return userRepository.save(user); + } + + public void deleteUser(Long id) { + User user = getUser(id); + userRepository.delete(user); + } +} + +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(unique = true, nullable = false) + private String email; + + @Column(nullable = false) + private String name; + + @Column(name = "password_hash", nullable = false) + private String passwordHash; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + // геттеры и сеттеры +} +``` + +**Payment Service - только обработка платежей:** +```java +// Payment Service - только обработка платежей +@SpringBootApplication +public class PaymentServiceApplication { + public static void main(String[] args) { + SpringApplication.run(PaymentServiceApplication.class, args); + } +} + +@RestController +@RequestMapping("/api/payments") +public class PaymentController { + @Autowired + private PaymentService paymentService; + + @PostMapping("/process") + public ResponseEntity processPayment(@RequestBody PaymentRequest request) { + PaymentResult result = paymentService.processPayment(request); + return ResponseEntity.ok(result); + } + + @GetMapping("/{id}") + public ResponseEntity getPayment(@PathVariable Long id) { + Payment payment = paymentService.getPayment(id); + return ResponseEntity.ok(payment); + } + + @PostMapping("/{id}/refund") + public ResponseEntity refundPayment(@PathVariable Long id, @RequestBody RefundRequest request) { + RefundResult result = paymentService.refundPayment(id, request); + return ResponseEntity.ok(result); + } +} + +@Service +@Transactional +public class PaymentService { + @Autowired + private PaymentRepository paymentRepository; + @Autowired + private PaymentGatewayClient paymentGatewayClient; + + public PaymentResult processPayment(PaymentRequest request) { + // Валидация платежа + validatePaymentRequest(request); + + // Создание записи о платеже + Payment payment = new Payment(); + payment.setAmount(request.getAmount()); + payment.setCurrency(request.getCurrency()); + payment.setPaymentMethod(request.getPaymentMethod()); + payment.setStatus(PaymentStatus.PENDING); + payment.setCreatedAt(LocalDateTime.now()); + + payment = paymentRepository.save(payment); + + try { + // Обработка через платежный шлюз + PaymentGatewayResponse response = paymentGatewayClient.processPayment(request); + + if (response.isSuccess()) { + payment.setStatus(PaymentStatus.COMPLETED); + payment.setTransactionId(response.getTransactionId()); + payment.setProcessedAt(LocalDateTime.now()); + } else { + payment.setStatus(PaymentStatus.FAILED); + payment.setFailureReason(response.getErrorMessage()); + } + + payment = paymentRepository.save(payment); + + return new PaymentResult( + payment.getId(), + payment.getStatus(), + response.getTransactionId(), + response.getErrorMessage() + ); + + } catch (Exception e) { + payment.setStatus(PaymentStatus.FAILED); + payment.setFailureReason(e.getMessage()); + paymentRepository.save(payment); + + throw new PaymentProcessingException("Payment processing failed", e); + } + } + + private void validatePaymentRequest(PaymentRequest request) { + if (request.getAmount().compareTo(BigDecimal.ZERO) <= 0) { + throw new InvalidPaymentAmountException("Payment amount must be positive"); + } + + if (request.getPaymentMethod() == null) { + throw new InvalidPaymentMethodException("Payment method is required"); + } + } + + public Payment getPayment(Long id) { + return paymentRepository.findById(id) + .orElseThrow(() -> new PaymentNotFoundException("Payment not found")); + } + + public RefundResult refundPayment(Long id, RefundRequest request) { + Payment payment = getPayment(id); + + if (payment.getStatus() != PaymentStatus.COMPLETED) { + throw new InvalidRefundException("Only completed payments can be refunded"); + } + + // Логика возврата средств + RefundGatewayResponse response = paymentGatewayClient.refundPayment( + payment.getTransactionId(), + request.getAmount() + ); + + if (response.isSuccess()) { + payment.setStatus(PaymentStatus.REFUNDED); + payment.setRefundedAt(LocalDateTime.now()); + paymentRepository.save(payment); + } + + return new RefundResult(response.isSuccess(), response.getRefundId()); + } +} + +@Entity +@Table(name = "payments") +public class Payment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private BigDecimal amount; + + @Column(nullable = false) + private String currency; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private PaymentMethod paymentMethod; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private PaymentStatus status; + + @Column(name = "transaction_id") + private String transactionId; + + @Column(name = "failure_reason") + private String failureReason; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @Column(name = "processed_at") + private LocalDateTime processedAt; + + @Column(name = "refunded_at") + private LocalDateTime refundedAt; + + // геттеры и сеттеры +} +``` + +**Node.js (Express + TypeScript + Prisma) - User Service:** +```typescript +// User Service - только управление пользователями +import express, { Request, Response, NextFunction } from 'express'; +import { PrismaClient } from '@prisma/client'; +import { z } from 'zod'; +import bcrypt from 'bcrypt'; + +const app = express(); +const prisma = new PrismaClient(); + +// Interfaces +interface CreateUserRequest { + name: string; + email: string; + password: string; +} + +interface UpdateUserRequest { + name: string; +} + +interface User { + id: number; + name: string; + email: string; + passwordHash: string; + createdAt: Date; + updatedAt: Date | null; +} + +// User Controller +class UserController { + constructor(private userService: UserService) {} + + async createUser(req: Request, res: Response, next: NextFunction) { + try { + const user = await this.userService.createUser(req.body); + res.json(user); + } catch (error) { + next(error); + } + } + + async getUser(req: Request, res: Response, next: NextFunction) { + try { + const id = parseInt(req.params.id); + const user = await this.userService.getUser(id); + res.json(user); + } catch (error) { + next(error); + } + } + + async updateUser(req: Request, res: Response, next: NextFunction) { + try { + const id = parseInt(req.params.id); + const user = await this.userService.updateUser(id, req.body); + res.json(user); + } catch (error) { + next(error); + } + } + + async deleteUser(req: Request, res: Response, next: NextFunction) { + try { + const id = parseInt(req.params.id); + await this.userService.deleteUser(id); + res.status(204).send(); + } catch (error) { + next(error); + } + } +} + +// User Service +class UserService { + constructor(private userRepository: UserRepository) {} + + async createUser(request: CreateUserRequest): Promise { + // Валидация + const existingUser = await this.userRepository.findByEmail(request.email); + if (existingUser) { + throw new Error('User with email already exists'); + } + + // Создание пользователя + const passwordHash = await bcrypt.hash(request.password, 10); + const user = { + name: request.name, + email: request.email, + passwordHash, + createdAt: new Date(), + updatedAt: null + }; + + return await this.userRepository.save(user); + } + + async getUser(id: number): Promise { + const user = await this.userRepository.findById(id); + if (!user) { + throw new Error('User not found'); + } + return user; + } + + async updateUser(id: number, request: UpdateUserRequest): Promise { + const user = await this.getUser(id); + user.name = request.name; + user.updatedAt = new Date(); + return await this.userRepository.save(user); + } + + async deleteUser(id: number): Promise { + const user = await this.getUser(id); + await this.userRepository.delete(user.id); + } +} + +// User Repository +interface UserRepository { + save(user: Omit): Promise; + findById(id: number): Promise; + findByEmail(email: string): Promise; + delete(id: number): Promise; +} + +class PrismaUserRepository implements UserRepository { + async save(user: Omit): Promise { + const savedUser = await prisma.user.create({ + data: { + name: user.name, + email: user.email, + passwordHash: user.passwordHash, + createdAt: user.createdAt, + updatedAt: user.updatedAt + } + }); + + return { + id: savedUser.id, + name: savedUser.name, + email: savedUser.email, + passwordHash: savedUser.passwordHash, + createdAt: savedUser.createdAt, + updatedAt: savedUser.updatedAt + }; + } + + async findById(id: number): Promise { + const user = await prisma.user.findUnique({ + where: { id } + }); + + if (!user) return null; + + return { + id: user.id, + name: user.name, + email: user.email, + passwordHash: user.passwordHash, + createdAt: user.createdAt, + updatedAt: user.updatedAt + }; + } + + async findByEmail(email: string): Promise { + const user = await prisma.user.findUnique({ + where: { email } + }); + + if (!user) return null; + + return { + id: user.id, + name: user.name, + email: user.email, + passwordHash: user.passwordHash, + createdAt: user.createdAt, + updatedAt: user.updatedAt + }; + } + + async delete(id: number): Promise { + await prisma.user.delete({ + where: { id } + }); + } +} + +// Validation schemas +const createUserSchema = z.object({ + name: z.string().min(1, 'Name is required'), + email: z.string().email('Valid email is required'), + password: z.string().min(6, 'Password must be at least 6 characters') +}); + +const updateUserSchema = z.object({ + name: z.string().min(1, 'Name is required') +}); + +// Dependency injection +const userRepository = new PrismaUserRepository(); +const userService = new UserService(userRepository); +const userController = new UserController(userService); + +// Routes +app.post('/api/users', async (req: Request, res: Response, next: NextFunction) => { + try { + const validatedData = createUserSchema.parse(req.body); + await userController.createUser({ ...req, body: validatedData }, res, next); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.get('/api/users/:id', (req: Request, res: Response, next: NextFunction) => { + userController.getUser(req, res, next); +}); + +app.put('/api/users/:id', async (req: Request, res: Response, next: NextFunction) => { + try { + const validatedData = updateUserSchema.parse(req.body); + await userController.updateUser({ ...req, body: validatedData }, res, next); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.delete('/api/users/:id', (req: Request, res: Response, next: NextFunction) => { + userController.deleteUser(req, res, next); +}); + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +app.listen(3001, () => { + console.log('User Service running on port 3001'); +}); +``` + +**Rust (Actix Web + Diesel) - User Service:** +```rust +// User Service - только управление пользователями +use actix_web::{web, App, HttpResponse, HttpServer, Result}; +use diesel::prelude::*; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Serialize, Deserialize)] +struct CreateUserRequest { + name: String, + email: String, + password: String, +} + +#[derive(Serialize, Deserialize)] +struct UpdateUserRequest { + name: String, +} + +#[derive(Serialize, Deserialize, Queryable)] +struct User { + id: Uuid, + name: String, + email: String, + password_hash: String, + created_at: chrono::DateTime, + updated_at: Option>, +} + +// User Service +struct UserService { + pool: diesel::r2d2::Pool>, +} + +impl UserService { + fn new(database_url: &str) -> Self { + let manager = diesel::r2d2::ConnectionManager::::new(database_url); + let pool = diesel::r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool"); + + Self { pool } + } + + async fn create_user(&self, request: CreateUserRequest) -> Result> { + use crate::schema::users::dsl::*; + + // Проверка существования пользователя + let existing_user = users + .filter(email.eq(&request.email)) + .first::(&mut self.pool.get()?) + .optional()?; + + if existing_user.is_some() { + return Err("User with email already exists".into()); + } + + // Хеширование пароля + let password_hash = bcrypt::hash(&request.password, bcrypt::DEFAULT_COST)?; + + let new_user = NewUser { + id: Uuid::new_v4(), + name: request.name, + email: request.email, + password_hash, + created_at: chrono::Utc::now(), + updated_at: None, + }; + + let user = diesel::insert_into(users) + .values(&new_user) + .get_result::(&mut self.pool.get()?)?; + + Ok(user) + } + + async fn get_user(&self, user_id: Uuid) -> Result> { + use crate::schema::users::dsl::*; + + let user = users + .filter(id.eq(user_id)) + .first::(&mut self.pool.get()?)?; + + Ok(user) + } + + async fn update_user(&self, user_id: Uuid, request: UpdateUserRequest) -> Result> { + use crate::schema::users::dsl::*; + + let user = diesel::update(users.filter(id.eq(user_id))) + .set(( + name.eq(request.name), + updated_at.eq(Some(chrono::Utc::now())), + )) + .get_result::(&mut self.pool.get()?)?; + + Ok(user) + } + + async fn delete_user(&self, user_id: Uuid) -> Result<(), Box> { + use crate::schema::users::dsl::*; + + diesel::delete(users.filter(id.eq(user_id))) + .execute(&mut self.pool.get()?)?; + + Ok(()) + } +} + +#[derive(Insertable)] +#[diesel(table_name = users)] +struct NewUser { + id: Uuid, + name: String, + email: String, + password_hash: String, + created_at: chrono::DateTime, + updated_at: Option>, +} + +// Handlers +async fn create_user( + user_service: web::Data, + request: web::Json, +) -> Result { + match user_service.create_user(request.into_inner()).await { + Ok(user) => Ok(HttpResponse::Ok().json(user)), + Err(e) => Ok(HttpResponse::BadRequest().json(serde_json::json!({ + "error": e.to_string() + }))), + } +} + +async fn get_user( + user_service: web::Data, + path: web::Path, +) -> Result { + let user_id = path.into_inner(); + + match user_service.get_user(user_id).await { + Ok(user) => Ok(HttpResponse::Ok().json(user)), + Err(_) => Ok(HttpResponse::NotFound().json(serde_json::json!({ + "error": "User not found" + }))), + } +} + +async fn update_user( + user_service: web::Data, + path: web::Path, + request: web::Json, +) -> Result { + let user_id = path.into_inner(); + + match user_service.update_user(user_id, request.into_inner()).await { + Ok(user) => Ok(HttpResponse::Ok().json(user)), + Err(e) => Ok(HttpResponse::BadRequest().json(serde_json::json!({ + "error": e.to_string() + }))), + } +} + +async fn delete_user( + user_service: web::Data, + path: web::Path, +) -> Result { + let user_id = path.into_inner(); + + match user_service.delete_user(user_id).await { + Ok(_) => Ok(HttpResponse::NoContent().finish()), + Err(e) => Ok(HttpResponse::BadRequest().json(serde_json::json!({ + "error": e.to_string() + }))), + } +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let database_url = std::env::var("DATABASE_URL") + .expect("DATABASE_URL must be set"); + + let user_service = UserService::new(&database_url); + + HttpServer::new(move || { + App::new() + .app_data(web::Data::new(user_service.clone())) + .route("/api/users", web::post().to(create_user)) + .route("/api/users/{id}", web::get().to(get_user)) + .route("/api/users/{id}", web::put().to(update_user)) + .route("/api/users/{id}", web::delete().to(delete_user)) + }) + .bind("127.0.0.1:3001")? + .run() + .await +} +``` + +**Python (FastAPI + SQLAlchemy) - User Service:** +```python +# User Service - только управление пользователей +from fastapi import FastAPI, HTTPException, Depends +from sqlalchemy import create_engine, Column, String, DateTime, Boolean +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, Session +from pydantic import BaseModel, EmailStr +from passlib.context import CryptContext +from datetime import datetime +from typing import Optional +import uuid + +app = FastAPI(title="User Service", version="1.0.0") + +# Database setup +DATABASE_URL = "postgresql://user:password@localhost/userdb" +engine = create_engine(DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + +# Password hashing +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +# Database Models +class User(Base): + __tablename__ = "users" + + id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) + name = Column(String, nullable=False) + email = Column(String, unique=True, nullable=False) + password_hash = Column(String, nullable=False) + created_at = Column(DateTime, default=datetime.utcnow) + updated_at = Column(DateTime, nullable=True) + +# Pydantic Models +class CreateUserRequest(BaseModel): + name: str + email: EmailStr + password: str + +class UpdateUserRequest(BaseModel): + name: str + +class UserResponse(BaseModel): + id: str + name: str + email: str + created_at: datetime + updated_at: Optional[datetime] + + class Config: + from_attributes = True + +# Dependency +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +# User Service +class UserService: + def __init__(self, db: Session): + self.db = db + + def create_user(self, request: CreateUserRequest) -> User: + # Проверка существования пользователя + existing_user = self.db.query(User).filter(User.email == request.email).first() + if existing_user: + raise HTTPException(status_code=400, detail="User with email already exists") + + # Хеширование пароля + password_hash = pwd_context.hash(request.password) + + # Создание пользователя + user = User( + name=request.name, + email=request.email, + password_hash=password_hash + ) + + self.db.add(user) + self.db.commit() + self.db.refresh(user) + + return user + + def get_user(self, user_id: str) -> User: + user = self.db.query(User).filter(User.id == user_id).first() + if not user: + raise HTTPException(status_code=404, detail="User not found") + return user + + def update_user(self, user_id: str, request: UpdateUserRequest) -> User: + user = self.get_user(user_id) + user.name = request.name + user.updated_at = datetime.utcnow() + + self.db.commit() + self.db.refresh(user) + + return user + + def delete_user(self, user_id: str) -> None: + user = self.get_user(user_id) + self.db.delete(user) + self.db.commit() + +# API Endpoints +@app.post("/api/users", response_model=UserResponse) +async def create_user( + request: CreateUserRequest, + db: Session = Depends(get_db) +): + user_service = UserService(db) + user = user_service.create_user(request) + return user + +@app.get("/api/users/{user_id}", response_model=UserResponse) +async def get_user( + user_id: str, + db: Session = Depends(get_db) +): + user_service = UserService(db) + user = user_service.get_user(user_id) + return user + +@app.put("/api/users/{user_id}", response_model=UserResponse) +async def update_user( + user_id: str, + request: UpdateUserRequest, + db: Session = Depends(get_db) +): + user_service = UserService(db) + user = user_service.update_user(user_id, request) + return user + +@app.delete("/api/users/{user_id}") +async def delete_user( + user_id: str, + db: Session = Depends(get_db) +): + user_service = UserService(db) + user_service.delete_user(user_id) + return {"message": "User deleted successfully"} + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=3001) +``` + +### 2. Decentralized Governance +- Каждая команда выбирает свои технологии +- Нет единого технологического стека +- Самостоятельное принятие решений + +### 3. Decentralized Data Management +- Каждый сервис владеет своими данными +- Нет единой базы данных +- Разные типы БД для разных сервисов + +### 4. Infrastructure Automation +- Автоматизированное развертывание +- CI/CD пайплайны +- Мониторинг и логирование + +### 5. Design for Failure +- Сервисы могут отказывать +- Необходима отказоустойчивость +- Circuit breaker паттерны + +## Преимущества микросервисов + +### Технические преимущества +- **Независимое развертывание** — можно обновлять сервисы отдельно +- **Технологическое разнообразие** — разные технологии для разных задач +- **Масштабируемость** — масштабирование только нужных сервисов +- **Отказоустойчивость** — отказ одного сервиса не влияет на другие + +### Организационные преимущества +- **Независимые команды** — каждая команда работает со своим сервисом +- **Быстрая разработка** — параллельная работа команд +- **Специализация** — команды могут специализироваться на доменах + +## Недостатки микросервисов + +### Сложность +- **Распределенная система** — сложнее отладка и мониторинг +- **Сетевая задержка** — коммуникация через сеть +- **Сложность данных** — распределенные транзакции + +### Операционные проблемы +- **Мониторинг** — нужно отслеживать множество сервисов +- **Развертывание** — сложность управления множеством сервисов +- **Тестирование** — интеграционное тестирование усложняется + +## Когда использовать микросервисы + +### Подходящие случаи: +- Большие команды (8+ разработчиков) +- Сложные домены +- Высокие требования к масштабируемости +- Необходимость независимого развертывания +- Разные технологические требования + +### Неподходящие случаи: +- Маленькие команды +- Простые приложения +- Низкие требования к масштабируемости +- Недостаток DevOps экспертизы diff --git a/modules/module2/sections/02-domain-driven-design.md b/modules/module2/sections/02-domain-driven-design.md new file mode 100644 index 0000000..0a1ac6f --- /dev/null +++ b/modules/module2/sections/02-domain-driven-design.md @@ -0,0 +1,173 @@ +# Domain-Driven Design (DDD) + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 2](../README.md) + +--- + +## Что такое DDD + +**Domain-Driven Design** — это подход к разработке программного обеспечения, который: +- Фокусируется на бизнес-домене +- Использует единый язык (Ubiquitous Language) +- Разделяет систему на ограниченные контексты (Bounded Contexts) + +## Стратегическое проектирование + +### 1. Bounded Context (Ограниченный контекст) +**Определение:** Четко определенная граница, внутри которой модель домена применима. + +**Пример:** +``` +E-commerce система: +- Order Context → заказы, платежи +- Inventory Context → товары, склад +- Customer Context → клиенты, профили +- Shipping Context → доставка, логистика +``` + +### 2. Context Map (Контекстная карта) +**Определение:** Диаграмма, показывающая отношения между ограниченными контекстами. + +**Типы отношений:** +- **Shared Kernel** — общая модель +- **Customer-Supplier** — клиент-поставщик +- **Conformist** — соответствие +- **Anticorruption Layer** — антикоррупционный слой +- **Open Host Service** — открытый хост-сервис +- **Published Language** — опубликованный язык + +### 3. Ubiquitous Language (Единый язык) +**Определение:** Общий язык, используемый командой для описания домена. + +**Принципы:** +- Используется в коде +- Используется в документации +- Используется в общении +- Эволюционирует с пониманием домена + +## Тактическое проектирование + +### 1. Entity (Сущность) +**Определение:** Объект с уникальной идентичностью. + +```java +public class Order { + private OrderId id; // уникальный идентификатор + private CustomerId customerId; + private List items; + private OrderStatus status; + + // методы бизнес-логики + public void addItem(ProductId productId, int quantity) { + // бизнес-правила + } +} +``` + +### 2. Value Object (Объект-значение) +**Определение:** Объект без идентичности, определяемый только своими атрибутами. + +```java +public class Money { + private final BigDecimal amount; + private final Currency currency; + + public Money(BigDecimal amount, Currency currency) { + this.amount = amount; + this.currency = currency; + } + + // методы для работы с деньгами + public Money add(Money other) { + if (!this.currency.equals(other.currency)) { + throw new IllegalArgumentException("Different currencies"); + } + return new Money(this.amount.add(other.amount), this.currency); + } +} +``` + +### 3. Aggregate (Агрегат) +**Определение:** Граница консистентности данных. + +**Правила агрегатов:** +- Один агрегат = одна транзакция +- Ссылки только на корень агрегата +- Обновления только через корень + +```java +public class Order { // корень агрегата + private OrderId id; + private CustomerId customerId; + private List items; // сущности внутри агрегата + private OrderStatus status; + + // методы для изменения состояния + public void addItem(ProductId productId, int quantity) { + // валидация бизнес-правил + items.add(new OrderItem(productId, quantity)); + } +} +``` + +### 4. Repository (Репозиторий) +**Определение:** Интерфейс для доступа к агрегатам. + +```java +public interface OrderRepository { + Order findById(OrderId id); + void save(Order order); + List findByCustomerId(CustomerId customerId); +} +``` + +### 5. Domain Service (Доменный сервис) +**Определение:** Операция, которая не принадлежит ни одной сущности. + +```java +public class OrderPricingService { + public Money calculateTotalPrice(Order order, PricingRules rules) { + // сложная логика расчета цены + // не принадлежит ни Order, ни PricingRules + } +} +``` + +### 6. Domain Event (Доменное событие) +**Определение:** Событие, произошедшее в домене. + +```java +public class OrderCreatedEvent { + private final OrderId orderId; + private final CustomerId customerId; + private final LocalDateTime occurredAt; + + // конструктор и геттеры +} +``` + +## Event Storming (Мозговой штурм событий) + +**Event Storming** — методология для изучения домена через события. + +### Процесс: +1. **Доменные события** — что происходит в системе +2. **Команды** — что вызывает события +3. **Агрегаты** — что обрабатывает команды +4. **Политики** — бизнес-правила +5. **Read Models** — что нужно для отображения + +### Пример: +``` +Команда: CreateOrder + ↓ +Агрегат: Order + ↓ +Событие: OrderCreated + ↓ +Политика: SendConfirmationEmail + ↓ +Команда: SendEmail +``` diff --git a/modules/module2/sections/03-service-communication.md b/modules/module2/sections/03-service-communication.md new file mode 100644 index 0000000..e976fcc --- /dev/null +++ b/modules/module2/sections/03-service-communication.md @@ -0,0 +1,541 @@ +# Коммуникация между сервисами + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 2](../README.md) + +--- + +## Синхронная коммуникация + +### HTTP/REST +**Преимущества:** +- Простота +- Широкая поддержка +- Стандартизация + +**Недостатки:** +- Связанность +- Блокирующие вызовы +- Проблемы с производительностью + +**Технический пример - HTTP/REST коммуникация:** + +```java +// Order Service - клиент для Payment Service +@RestController +@RequestMapping("/api/orders") +public class OrderController { + @Autowired + private PaymentServiceClient paymentServiceClient; + @Autowired + private OrderService orderService; + + @PostMapping + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + try { + // Синхронный вызов Payment Service + PaymentRequest paymentRequest = new PaymentRequest( + request.getAmount(), + request.getCurrency(), + request.getPaymentMethod(), + request.getPaymentDetails() + ); + + PaymentResult paymentResult = paymentServiceClient.processPayment(paymentRequest); + + if (paymentResult.isSuccess()) { + // Создаем заказ только после успешной оплаты + Order order = orderService.createOrder(request, paymentResult.getTransactionId()); + return ResponseEntity.ok(order); + } else { + return ResponseEntity.badRequest() + .body(new ErrorResponse("Payment failed: " + paymentResult.getErrorMessage())); + } + + } catch (PaymentServiceException e) { + // Обработка ошибок коммуникации + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse("Payment service unavailable")); + } + } +} + +// HTTP клиент для Payment Service +@Component +public class PaymentServiceClient { + @Autowired + private RestTemplate restTemplate; + + private static final String PAYMENT_SERVICE_URL = "http://payment-service:8080"; + + public PaymentResult processPayment(PaymentRequest request) { + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(request, headers); + + ResponseEntity response = restTemplate.postForEntity( + PAYMENT_SERVICE_URL + "/api/payments/process", + entity, + PaymentResult.class + ); + + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + throw new PaymentServiceException("Payment service returned error: " + response.getStatusCode()); + } + + } catch (RestClientException e) { + throw new PaymentServiceException("Failed to communicate with payment service", e); + } + } + + public PaymentResult refundPayment(String transactionId, BigDecimal amount) { + try { + RefundRequest refundRequest = new RefundRequest(transactionId, amount); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(refundRequest, headers); + + ResponseEntity response = restTemplate.postForEntity( + PAYMENT_SERVICE_URL + "/api/payments/refund", + entity, + RefundResult.class + ); + + if (response.getStatusCode() == HttpStatus.OK) { + RefundResult refundResult = response.getBody(); + return new PaymentResult( + null, + refundResult.isSuccess() ? PaymentStatus.REFUNDED : PaymentStatus.FAILED, + transactionId, + refundResult.isSuccess() ? null : "Refund failed" + ); + } else { + throw new PaymentServiceException("Refund service returned error: " + response.getStatusCode()); + } + + } catch (RestClientException e) { + throw new PaymentServiceException("Failed to communicate with refund service", e); + } + } +} + +// Конфигурация RestTemplate с таймаутами и retry +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); + factory.setConnectTimeout(5000); // 5 секунд на подключение + factory.setReadTimeout(10000); // 10 секунд на чтение + + RestTemplate restTemplate = new RestTemplate(factory); + + // Добавляем retry логику + restTemplate.setInterceptors(Arrays.asList(new RetryInterceptor())); + + return restTemplate; + } +} + +// Interceptor для retry логики +public class RetryInterceptor implements ClientHttpRequestInterceptor { + private static final int MAX_RETRIES = 3; + + @Override + public ClientHttpResponse intercept( + HttpRequest request, + byte[] body, + ClientHttpRequestExecution execution) throws IOException { + + int retryCount = 0; + IOException lastException = null; + + while (retryCount < MAX_RETRIES) { + try { + return execution.execute(request, body); + } catch (IOException e) { + lastException = e; + retryCount++; + + if (retryCount < MAX_RETRIES) { + try { + Thread.sleep(1000 * retryCount); // Exponential backoff + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new IOException("Retry interrupted", ie); + } + } + } + } + + throw lastException; + } +} +``` + +### GraphQL +**Преимущества:** +- Гибкость запросов +- Типизация +- Единая точка входа + +**Недостатки:** +- Сложность кэширования +- Потенциальные проблемы с производительностью + +## Асинхронная коммуникация + +### Очереди сообщений +**Преимущества:** +- Слабая связанность +- Отказоустойчивость +- Масштабируемость + +**Недостатки:** +- Сложность отладки +- Потенциальные проблемы с порядком + +**Технический пример - асинхронная коммуникация с RabbitMQ:** + +```java +// Order Service - публикует события +@Service +public class OrderService { + @Autowired + private OrderRepository orderRepository; + @Autowired + private RabbitTemplate rabbitTemplate; + + public Order createOrder(CreateOrderRequest request) { + // Создаем заказ + Order order = new Order(); + order.setCustomerId(request.getCustomerId()); + order.setItems(request.getItems()); + order.setStatus(OrderStatus.CREATED); + order.setCreatedAt(LocalDateTime.now()); + + order = orderRepository.save(order); + + // Публикуем событие о создании заказа + OrderCreatedEvent event = new OrderCreatedEvent( + order.getId(), + order.getCustomerId(), + order.getItems(), + order.getTotalAmount(), + order.getCreatedAt() + ); + + rabbitTemplate.convertAndSend("order.exchange", "order.created", event); + + return order; + } + + public void cancelOrder(Long orderId) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new OrderNotFoundException("Order not found")); + + if (order.getStatus() == OrderStatus.CREATED) { + order.setStatus(OrderStatus.CANCELLED); + orderRepository.save(order); + + // Публикуем событие об отмене заказа + OrderCancelledEvent event = new OrderCancelledEvent( + order.getId(), + order.getCustomerId(), + order.getItems(), + order.getTotalAmount(), + LocalDateTime.now() + ); + + rabbitTemplate.convertAndSend("order.exchange", "order.cancelled", event); + } + } +} + +// Notification Service - подписывается на события +@Component +public class OrderEventHandler { + @Autowired + private NotificationService notificationService; + + @RabbitListener(queues = "order.created.queue") + public void handleOrderCreated(OrderCreatedEvent event) { + try { + // Отправляем подтверждение заказа + notificationService.sendOrderConfirmation( + event.getCustomerId(), + event.getOrderId(), + event.getTotalAmount() + ); + + // Отправляем уведомление о статусе заказа + notificationService.sendOrderStatusUpdate( + event.getCustomerId(), + event.getOrderId(), + "Your order has been created and is being processed" + ); + + } catch (Exception e) { + // Логируем ошибку, но не прерываем обработку + log.error("Failed to send order confirmation for order: " + event.getOrderId(), e); + } + } + + @RabbitListener(queues = "order.cancelled.queue") + public void handleOrderCancelled(OrderCancelledEvent event) { + try { + // Отправляем уведомление об отмене + notificationService.sendOrderCancellation( + event.getCustomerId(), + event.getOrderId(), + event.getTotalAmount() + ); + + } catch (Exception e) { + log.error("Failed to send order cancellation for order: " + event.getOrderId(), e); + } + } +} + +// Inventory Service - подписывается на события +@Component +public class InventoryEventHandler { + @Autowired + private InventoryService inventoryService; + + @RabbitListener(queues = "order.created.queue") + public void handleOrderCreated(OrderCreatedEvent event) { + try { + // Резервируем товары + for (OrderItem item : event.getItems()) { + inventoryService.reserveItem(item.getProductId(), item.getQuantity()); + } + + // Публикуем событие о резервировании + InventoryReservedEvent inventoryEvent = new InventoryReservedEvent( + event.getOrderId(), + event.getItems(), + LocalDateTime.now() + ); + + rabbitTemplate.convertAndSend("inventory.exchange", "inventory.reserved", inventoryEvent); + + } catch (InsufficientStockException e) { + // Если товаров недостаточно, отменяем заказ + OrderCancelledEvent cancelEvent = new OrderCancelledEvent( + event.getOrderId(), + event.getCustomerId(), + event.getItems(), + event.getTotalAmount(), + LocalDateTime.now() + ); + + rabbitTemplate.convertAndSend("order.exchange", "order.cancelled", cancelEvent); + + } catch (Exception e) { + log.error("Failed to reserve inventory for order: " + event.getOrderId(), e); + } + } + + @RabbitListener(queues = "order.cancelled.queue") + public void handleOrderCancelled(OrderCancelledEvent event) { + try { + // Освобождаем зарезервированные товары + for (OrderItem item : event.getItems()) { + inventoryService.releaseReservation(item.getProductId(), item.getQuantity()); + } + + } catch (Exception e) { + log.error("Failed to release inventory for order: " + event.getOrderId(), e); + } + } +} + +// Конфигурация RabbitMQ +@Configuration +@EnableRabbit +public class RabbitMQConfig { + + // Exchange для заказов + @Bean + public TopicExchange orderExchange() { + return new TopicExchange("order.exchange"); + } + + // Очереди для событий заказов + @Bean + public Queue orderCreatedQueue() { + return new Queue("order.created.queue", true); + } + + @Bean + public Queue orderCancelledQueue() { + return new Queue("order.cancelled.queue", true); + } + + // Привязки очередей к exchange + @Bean + public Binding orderCreatedBinding() { + return BindingBuilder + .bind(orderCreatedQueue()) + .to(orderExchange()) + .with("order.created"); + } + + @Bean + public Binding orderCancelledBinding() { + return BindingBuilder + .bind(orderCancelledQueue()) + .to(orderExchange()) + .with("order.cancelled"); + } + + // Exchange для инвентаря + @Bean + public TopicExchange inventoryExchange() { + return new TopicExchange("inventory.exchange"); + } + + @Bean + public Queue inventoryReservedQueue() { + return new Queue("inventory.reserved.queue", true); + } + + @Bean + public Binding inventoryReservedBinding() { + return BindingBuilder + .bind(inventoryReservedQueue()) + .to(inventoryExchange()) + .with("inventory.reserved"); + } + + // Конфигурация RabbitTemplate + @Bean + public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { + RabbitTemplate template = new RabbitTemplate(connectionFactory); + template.setMessageConverter(new Jackson2JsonMessageConverter()); + return template; + } + + // Конфигурация Jackson для сериализации + @Bean + public MessageConverter messageConverter() { + return new Jackson2JsonMessageConverter(); + } +} + +// События (DTO) +public class OrderCreatedEvent { + private Long orderId; + private Long customerId; + private List items; + private BigDecimal totalAmount; + private LocalDateTime createdAt; + + // конструкторы, геттеры, сеттеры +} + +public class OrderCancelledEvent { + private Long orderId; + private Long customerId; + private List items; + private BigDecimal totalAmount; + private LocalDateTime cancelledAt; + + // конструкторы, геттеры, сеттеры +} + +public class InventoryReservedEvent { + private Long orderId; + private List items; + private LocalDateTime reservedAt; + + // конструкторы, геттеры, сеттеры +} +``` + +### Потоковая обработка событий +**Преимущества:** +- Высокая пропускная способность +- Исторические данные +- Real-time обработка + +**Технологии:** +- Apache Kafka +- Apache Pulsar +- AWS Kinesis + +## Паттерны коммуникации + +### 1. Request-Response +``` +Client → Service A → Service B +Client ← Service A ← Service B +``` + +### 2. Publish-Subscribe +``` +Publisher → Event Bus → Subscriber 1 + → Subscriber 2 + → Subscriber 3 +``` + +### 3. Request-Reply +``` +Client → Message Queue → Service +Client ← Message Queue ← Service +``` + +### 4. Event Sourcing +``` +Command → Aggregate → Event Store +Query → Read Model ← Event Store +``` + +## Обнаружение сервисов + +**Проблема:** Как сервисы находят друг друга в динамической среде? + +### Решения: + +#### 1. Client-Side Discovery +```java +@Service +public class PaymentServiceClient { + @Autowired + private ServiceDiscovery serviceDiscovery; + + public PaymentResult processPayment(PaymentRequest request) { + // клиент сам находит сервис + ServiceInstance instance = serviceDiscovery.getInstance("payment-service"); + String url = "http://" + instance.getHost() + ":" + instance.getPort(); + + return restTemplate.postForObject(url + "/payments", request, PaymentResult.class); + } +} +``` + +#### 2. Server-Side Discovery +```java +@Service +public class PaymentServiceClient { + public PaymentResult processPayment(PaymentRequest request) { + // сервис находится через load balancer + String url = "http://payment-service/payments"; + return restTemplate.postForObject(url, request, PaymentResult.class); + } +} +``` + +### Инструменты: +- **Consul** — service discovery и configuration +- **Eureka** — Netflix service discovery +- **Kubernetes** — встроенный service discovery diff --git a/modules/module2/sections/04-data-management.md b/modules/module2/sections/04-data-management.md new file mode 100644 index 0000000..7047ef9 --- /dev/null +++ b/modules/module2/sections/04-data-management.md @@ -0,0 +1,201 @@ +# Управление данными в микросервисах + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 2](../README.md) + +--- + +## База данных на сервис + +**Принцип:** Каждый микросервис имеет собственную базу данных. + +### Преимущества: +- Независимость данных +- Разные типы БД для разных задач +- Независимое масштабирование + +### Недостатки: +- Сложность транзакций +- Проблемы с консистентностью +- Дублирование данных + +## Паттерны управления данными + +### 1. Saga Pattern +**Определение:** Паттерн для управления распределенными транзакциями. + +#### Choreography Saga +```java +// Order Service +public class OrderService { + public void createOrder(CreateOrderRequest request) { + Order order = new Order(request); + orderRepository.save(order); + + // публикуем событие + eventPublisher.publish(new OrderCreatedEvent(order.getId(), request.getItems())); + } +} + +// Inventory Service +@EventHandler +public class InventoryEventHandler { + public void handleOrderCreated(OrderCreatedEvent event) { + try { + inventoryService.reserveItems(event.getItems()); + eventPublisher.publish(new ItemsReservedEvent(event.getOrderId())); + } catch (InsufficientStockException e) { + eventPublisher.publish(new OrderCancelledEvent(event.getOrderId())); + } + } +} + +// Payment Service +@EventHandler +public class PaymentEventHandler { + public void handleItemsReserved(ItemsReservedEvent event) { + try { + paymentService.processPayment(event.getOrderId()); + eventPublisher.publish(new PaymentProcessedEvent(event.getOrderId())); + } catch (PaymentFailedException e) { + eventPublisher.publish(new OrderCancelledEvent(event.getOrderId())); + } + } +} +``` + +#### Orchestration Saga +```java +@Service +public class OrderOrchestrator { + public void processOrder(CreateOrderRequest request) { + Order order = orderService.createOrder(request); + + try { + // шаг 1: резервирование товаров + inventoryService.reserveItems(order.getId(), request.getItems()); + + // шаг 2: обработка платежа + paymentService.processPayment(order.getId(), request.getPayment()); + + // шаг 3: подтверждение заказа + orderService.confirmOrder(order.getId()); + + } catch (Exception e) { + // компенсирующие транзакции + compensateOrder(order.getId()); + } + } + + private void compensateOrder(OrderId orderId) { + try { + inventoryService.releaseItems(orderId); + paymentService.refundPayment(orderId); + orderService.cancelOrder(orderId); + } catch (Exception e) { + // логирование ошибок компенсации + } + } +} +``` + +### 2. CQRS (Command Query Responsibility Segregation) +**Определение:** Разделение команд и запросов. + +```java +// Command Side +@Service +public class OrderCommandService { + public void createOrder(CreateOrderCommand command) { + Order order = new Order(command); + orderRepository.save(order); + + // обновляем read model + orderReadModelService.updateReadModel(order); + } +} + +// Query Side +@Service +public class OrderQueryService { + public OrderView getOrderView(OrderId orderId) { + return orderReadModelRepository.findById(orderId); + } + + public List getOrdersByCustomer(CustomerId customerId) { + return orderReadModelRepository.findByCustomerId(customerId); + } +} +``` + +### 3. Event Sourcing +**Определение:** Хранение событий вместо состояния. + +```java +@Entity +public class OrderEvent { + private Long id; + private String aggregateId; + private String eventType; + private String eventData; + private LocalDateTime occurredAt; +} + +@Service +public class OrderEventStore { + public void saveEvent(OrderEvent event) { + eventRepository.save(event); + } + + public List getEvents(String aggregateId) { + return eventRepository.findByAggregateIdOrderByOccurredAt(aggregateId); + } +} + +@Service +public class OrderProjectionService { + public OrderView buildOrderView(String orderId) { + List events = eventStore.getEvents(orderId); + OrderView view = new OrderView(); + + for (OrderEvent event : events) { + applyEvent(view, event); + } + + return view; + } +} +``` + +## Консистентность данных + +### Типы консистентности: + +#### 1. Strong Consistency +- Все узлы видят одинаковые данные +- Сложно в распределенных системах +- Высокая задержка + +#### 2. Eventual Consistency +- Данные в итоге станут консистентными +- Подходит для большинства случаев +- Низкая задержка + +#### 3. Weak Consistency +- Нет гарантий консистентности +- Используется редко + +### Паттерны для консистентности: + +#### 1. Two-Phase Commit (2PC) +**Проблема:** Блокирующий протокол +**Решение:** Использовать Saga вместо 2PC + +#### 2. Three-Phase Commit (3PC) +**Улучшение:** Меньше блокировок +**Проблема:** Все еще сложный + +#### 3. Consensus Algorithms +- **Raft** — выбор лидера +- **Paxos** — консенсус в распределенных системах diff --git a/modules/module2/sections/05-serverless-architecture.md b/modules/module2/sections/05-serverless-architecture.md new file mode 100644 index 0000000..725d55e --- /dev/null +++ b/modules/module2/sections/05-serverless-architecture.md @@ -0,0 +1,257 @@ +# Serverless архитектура и FaaS + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 2](../README.md) + +--- + +## Что такое Serverless + +**Serverless** — это модель облачных вычислений, где разработчики не управляют серверами, а платформа автоматически выделяет ресурсы по требованию. + +## Ключевые характеристики Serverless + +### 1. Event-Driven Execution +- Функции запускаются по событиям +- Автоматическое масштабирование +- Оплата только за время выполнения + +### 2. Stateless Functions +- Функции не хранят состояние +- Внешнее хранение данных +- Горизонтальное масштабирование + +## FaaS (Функция как сервис) + +**FaaS** — это подмодель Serverless, где код выполняется в виде отдельных функций. + +### AWS Lambda + +```python +# AWS Lambda Function +import json +import boto3 + +def lambda_handler(event, context): + """ + Lambda function для обработки заказов + """ + # Парсинг события + order_data = json.loads(event['body']) + + # Валидация данных + if not validate_order(order_data): + return { + 'statusCode': 400, + 'body': json.dumps({'error': 'Invalid order data'}) + } + + # Обработка заказа + order_id = process_order(order_data) + + # Отправка уведомления + send_notification(order_id) + + return { + 'statusCode': 200, + 'body': json.dumps({ + 'orderId': order_id, + 'status': 'processed' + }) + } + +def validate_order(order_data): + """Валидация данных заказа""" + required_fields = ['customerId', 'items', 'total'] + return all(field in order_data for field in required_fields) + +def process_order(order_data): + """Обработка заказа""" + # Логика обработки заказа + return f"ORD-{order_data.get('customerId')}-{int(time.time())}" + +def send_notification(order_id): + """Отправка уведомления""" + sns = boto3.client('sns') + sns.publish( + TopicArn='arn:aws:sns:us-east-1:123456789012:order-notifications', + Message=f'Order {order_id} has been processed', + Subject='Order Processed' + ) +``` + +### Azure Functions + +```csharp +// Azure Function +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +public static class OrderProcessor +{ + [FunctionName("ProcessOrder")] + public static async Task Run( + [HttpTrigger(AuthorizationLevel.Function, "post", Route = "orders")] HttpRequest req, + ILogger log) + { + log.LogInformation("Processing order request"); + + try + { + // Чтение данных запроса + string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); + var orderData = JsonSerializer.Deserialize(requestBody); + + // Валидация + if (!ValidateOrder(orderData)) + { + return new BadRequestObjectResult("Invalid order data"); + } + + // Обработка заказа + var orderId = await ProcessOrderAsync(orderData); + + // Отправка уведомления + await SendNotificationAsync(orderId); + + return new OkObjectResult(new { orderId, status = "processed" }); + } + catch (Exception ex) + { + log.LogError(ex, "Error processing order"); + return new StatusCodeResult(500); + } + } + + private static bool ValidateOrder(OrderData orderData) + { + return !string.IsNullOrEmpty(orderData.CustomerId) && + orderData.Items?.Count > 0 && + orderData.Total > 0; + } + + private static async Task ProcessOrderAsync(OrderData orderData) + { + // Логика обработки заказа + await Task.Delay(100); // Имитация обработки + return $"ORD-{orderData.CustomerId}-{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}"; + } + + private static async Task SendNotificationAsync(string orderId) + { + // Отправка уведомления + await Task.Delay(50); // Имитация отправки + } +} + +public class OrderData +{ + public string CustomerId { get; set; } + public List Items { get; set; } + public decimal Total { get; set; } +} +``` + +### Google Cloud Functions + +```javascript +// Google Cloud Function +const functions = require('@google-cloud/functions-framework'); +const { PubSub } = require('@google-cloud/pubsub'); + +const pubsub = new PubSub(); + +functions.http('processOrder', async (req, res) => { + // Установка CORS + res.set('Access-Control-Allow-Origin', '*'); + res.set('Access-Control-Allow-Methods', 'POST'); + res.set('Access-Control-Allow-Headers', 'Content-Type'); + + if (req.method === 'OPTIONS') { + res.status(204).send(''); + return; + } + + if (req.method !== 'POST') { + res.status(405).send('Method Not Allowed'); + return; + } + + try { + const orderData = req.body; + + // Валидация данных + if (!validateOrder(orderData)) { + res.status(400).json({ error: 'Invalid order data' }); + return; + } + + // Обработка заказа + const orderId = await processOrder(orderData); + + // Отправка уведомления + await sendNotification(orderId); + + res.status(200).json({ + orderId: orderId, + status: 'processed' + }); + } catch (error) { + console.error('Error processing order:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +function validateOrder(orderData) { + const requiredFields = ['customerId', 'items', 'total']; + return requiredFields.every(field => orderData.hasOwnProperty(field)); +} + +async function processOrder(orderData) { + // Логика обработки заказа + await new Promise(resolve => setTimeout(resolve, 100)); + return `ORD-${orderData.customerId}-${Date.now()}`; +} + +async function sendNotification(orderId) { + const topic = pubsub.topic('order-notifications'); + const message = { + data: Buffer.from(JSON.stringify({ + orderId: orderId, + message: `Order ${orderId} has been processed` + })) + }; + + await topic.publish(message); +} +``` + +## Преимущества Serverless + +### Технические: +- Автоматическое масштабирование +- Оплата только за использование +- Отсутствие управления серверами +- Быстрое развертывание + +### Экономические: +- Снижение операционных расходов +- Предсказуемые затраты +- Отсутствие простоев серверов + +## Недостатки Serverless + +### Ограничения: +- Cold Start задержки +- Ограничения времени выполнения +- Ограничения памяти +- Vendor Lock-in + +### Сложности: +- Сложность отладки +- Ограниченный контроль +- Проблемы с состоянием diff --git a/modules/module2/sections/06-microfrontends.md b/modules/module2/sections/06-microfrontends.md new file mode 100644 index 0000000..8fcbbb3 --- /dev/null +++ b/modules/module2/sections/06-microfrontends.md @@ -0,0 +1,240 @@ +# Микрофронтенды + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 2](../README.md) + +--- + +## Что такое микрофронтенды + +**Микрофронтенды** — это архитектурный подход, где фронтенд приложения разбивается на независимые части. + +## Принципы микрофронтендов + +### 1. Независимость +- Независимая разработка +- Независимое развертывание +- Независимые команды + +### 2. Технологическое разнообразие +- Разные фреймворки +- Разные библиотеки +- Разные подходы + +### 3. Единый пользовательский интерфейс +- Общий дизайн +- Общие компоненты +- Общий пользовательский опыт + +## Архитектурные паттерны + +### 1. Module Federation (Webpack 5) +```javascript +// shell/ModuleFederationPlugin.js +new ModuleFederationPlugin({ + name: 'shell', + remotes: { + 'product': 'product@http://localhost:3001/remoteEntry.js', + 'cart': 'cart@http://localhost:3002/remoteEntry.js', + }, +}); + +// product/webpack.config.js +new ModuleFederationPlugin({ + name: 'product', + filename: 'remoteEntry.js', + exposes: { + './ProductList': './src/ProductList', + }, +}); +``` + +### 2. Single-SPA +```javascript +// main.js +import { registerApplication, start } from 'single-spa'; + +registerApplication({ + name: 'product', + app: () => System.import('product'), + activeWhen: ['/products'], +}); + +registerApplication({ + name: 'cart', + app: () => System.import('cart'), + activeWhen: ['/cart'], +}); + +start(); +``` + +### 3. iframe-based +```html + +
+ +
+ +
+ +
+``` + +## Управление состоянием + +### 1. Shared State +```javascript +// shared-state/store.js +class SharedStore { + constructor() { + this.state = {}; + this.subscribers = []; + } + + setState(newState) { + this.state = { ...this.state, ...newState }; + this.notifySubscribers(); + } + + subscribe(callback) { + this.subscribers.push(callback); + } + + notifySubscribers() { + this.subscribers.forEach(callback => callback(this.state)); + } +} + +export const sharedStore = new SharedStore(); +``` + +### 2. Event Bus +```javascript +// shared/eventBus.js +class EventBus { + constructor() { + this.events = {}; + } + + on(event, callback) { + if (!this.events[event]) { + this.events[event] = []; + } + this.events[event].push(callback); + } + + emit(event, data) { + if (this.events[event]) { + this.events[event].forEach(callback => callback(data)); + } + } +} + +export const eventBus = new EventBus(); +``` + +## Коммуникация между микрофронтендами + +### 1. Props/Events +```javascript +// shell/App.js +import ProductMicrofrontend from 'product/ProductList'; +import CartMicrofrontend from 'cart/CartList'; + +function App() { + const [cartItems, setCartItems] = useState([]); + + const handleAddToCart = (product) => { + setCartItems([...cartItems, product]); + }; + + return ( +
+ + +
+ ); +} +``` + +### 2. Custom Events +```javascript +// product/ProductList.js +function ProductList() { + const handleAddToCart = (product) => { + // отправляем событие + window.dispatchEvent(new CustomEvent('addToCart', { + detail: product + })); + }; + + return ( +
+ {products.map(product => ( + + ))} +
+ ); +} + +// cart/CartList.js +function CartList() { + const [items, setItems] = useState([]); + + useEffect(() => { + const handleAddToCart = (event) => { + setItems([...items, event.detail]); + }; + + window.addEventListener('addToCart', handleAddToCart); + + return () => { + window.removeEventListener('addToCart', handleAddToCart); + }; + }, [items]); + + return ( +
+ {items.map(item => ( +
{item.name}
+ ))} +
+ ); +} +``` + +## Преимущества микрофронтендов + +### Технические: +- Независимая разработка +- Технологическое разнообразие +- Независимое развертывание +- Масштабируемость команд + +### Организационные: +- Независимые команды +- Быстрая разработка +- Специализация команд + +## Недостатки микрофронтендов + +### Сложность: +- Сложность интеграции +- Дублирование кода +- Проблемы с производительностью +- Сложность тестирования + +### Операционные: +- Сложность развертывания +- Проблемы с версионированием +- Сложность мониторинга diff --git a/modules/module2/sections/08-practical-tasks.md b/modules/module2/sections/08-practical-tasks.md new file mode 100644 index 0000000..16890c1 --- /dev/null +++ b/modules/module2/sections/08-practical-tasks.md @@ -0,0 +1,31 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 2](../README.md) + +--- + +### Задание 1: Проектирование микросервисов +Спроектируйте микросервисную архитектуру для системы интернет-магазина: +1. Определите домены и ограниченные контексты +2. Спроектируйте сервисы и их взаимодействие +3. Определите данные для каждого сервиса +4. Создайте контекстную карту + +### Задание 2: Реализация Saga +Реализуйте Saga для процесса создания заказа: +1. Создание заказа +2. Резервирование товаров +3. Обработка платежа +4. Подтверждение заказа +5. Компенсирующие транзакции + +### Задание 3: Микрофронтенды +Создайте простую систему микрофронтендов: +1. Shell приложение +2. Product микрофронтенд +3. Cart микрофронтенд +4. Коммуникация между ними + +--- \ No newline at end of file diff --git a/modules/module2/theory.md b/modules/module2/theory.md new file mode 100644 index 0000000..b50309c --- /dev/null +++ b/modules/module2/theory.md @@ -0,0 +1,3367 @@ +# Модуль 2: Микросервисная архитектура + +## Содержание модуля +1. [Основы микросервисной архитектуры](#основы-микросервисной-архитектуры) +2. [Domain-Driven Design (DDD)](#domain-driven-design-ddd) +3. [Коммуникация между сервисами](#коммуникация-между-сервисами) +4. [Управление данными в микросервисах](#управление-данными-в-микросервисах) +5. [Микрофронтенды](#микрофронтенды) +6. [Serverless архитектура и FaaS](#serverless-архитектура-и-faas) +7. [Паттерны микросервисов](#паттерны-микросервисов) +8. [Антипаттерны и проблемы](#антипаттерны-и-проблемы) + +--- + +## Основы микросервисной архитектуры + +### Определение микросервисов + +**Микросервис** — это небольшая, независимая, слабо связанная служба, которая: +- Имеет единственную ответственность +- Развертывается независимо +- Имеет собственные данные +- Коммуницирует через четко определенные API + +### Принципы микросервисной архитектуры + +#### 1. Single Responsibility Principle +Каждый микросервис должен решать одну бизнес-задачу. + +**Пример:** +``` +User Service → управление пользователями +Payment Service → обработка платежей +Order Service → управление заказами +Notification Service → отправка уведомлений +``` + +**Технические примеры:** + +**Java (Spring Boot) - User Service:** +```java +// User Service - только управление пользователями +@SpringBootApplication +public class UserServiceApplication { + public static void main(String[] args) { + SpringApplication.run(UserServiceApplication.class, args); + } +} + +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @PostMapping + public ResponseEntity createUser(@RequestBody CreateUserRequest request) { + User user = userService.createUser(request); + return ResponseEntity.ok(user); + } + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + User user = userService.getUser(id); + return ResponseEntity.ok(user); + } + + @PutMapping("/{id}") + public ResponseEntity updateUser(@PathVariable Long id, @RequestBody UpdateUserRequest request) { + User user = userService.updateUser(id, request); + return ResponseEntity.ok(user); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteUser(@PathVariable Long id) { + userService.deleteUser(id); + return ResponseEntity.ok().build(); + } +} + +@Service +@Transactional +public class UserService { + @Autowired + private UserRepository userRepository; + + public User createUser(CreateUserRequest request) { + // Валидация + if (userRepository.existsByEmail(request.getEmail())) { + throw new UserAlreadyExistsException("User with email already exists"); + } + + // Создание пользователя + User user = new User(); + user.setName(request.getName()); + user.setEmail(request.getEmail()); + user.setPasswordHash(passwordEncoder.encode(request.getPassword())); + user.setCreatedAt(LocalDateTime.now()); + + return userRepository.save(user); + } + + public User getUser(Long id) { + return userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found")); + } + + public User updateUser(Long id, UpdateUserRequest request) { + User user = getUser(id); + user.setName(request.getName()); + user.setUpdatedAt(LocalDateTime.now()); + return userRepository.save(user); + } + + public void deleteUser(Long id) { + User user = getUser(id); + userRepository.delete(user); + } +} + +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(unique = true, nullable = false) + private String email; + + @Column(nullable = false) + private String name; + + @Column(name = "password_hash", nullable = false) + private String passwordHash; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + // геттеры и сеттеры +} +``` + +**Payment Service - только обработка платежей:** +```java +// Payment Service - только обработка платежей +@SpringBootApplication +public class PaymentServiceApplication { + public static void main(String[] args) { + SpringApplication.run(PaymentServiceApplication.class, args); + } +} + +@RestController +@RequestMapping("/api/payments") +public class PaymentController { + @Autowired + private PaymentService paymentService; + + @PostMapping("/process") + public ResponseEntity processPayment(@RequestBody PaymentRequest request) { + PaymentResult result = paymentService.processPayment(request); + return ResponseEntity.ok(result); + } + + @GetMapping("/{id}") + public ResponseEntity getPayment(@PathVariable Long id) { + Payment payment = paymentService.getPayment(id); + return ResponseEntity.ok(payment); + } + + @PostMapping("/{id}/refund") + public ResponseEntity refundPayment(@PathVariable Long id, @RequestBody RefundRequest request) { + RefundResult result = paymentService.refundPayment(id, request); + return ResponseEntity.ok(result); + } +} + +@Service +@Transactional +public class PaymentService { + @Autowired + private PaymentRepository paymentRepository; + @Autowired + private PaymentGatewayClient paymentGatewayClient; + + public PaymentResult processPayment(PaymentRequest request) { + // Валидация платежа + validatePaymentRequest(request); + + // Создание записи о платеже + Payment payment = new Payment(); + payment.setAmount(request.getAmount()); + payment.setCurrency(request.getCurrency()); + payment.setPaymentMethod(request.getPaymentMethod()); + payment.setStatus(PaymentStatus.PENDING); + payment.setCreatedAt(LocalDateTime.now()); + + payment = paymentRepository.save(payment); + + try { + // Обработка через платежный шлюз + PaymentGatewayResponse response = paymentGatewayClient.processPayment(request); + + if (response.isSuccess()) { + payment.setStatus(PaymentStatus.COMPLETED); + payment.setTransactionId(response.getTransactionId()); + payment.setProcessedAt(LocalDateTime.now()); + } else { + payment.setStatus(PaymentStatus.FAILED); + payment.setFailureReason(response.getErrorMessage()); + } + + payment = paymentRepository.save(payment); + + return new PaymentResult( + payment.getId(), + payment.getStatus(), + response.getTransactionId(), + response.getErrorMessage() + ); + + } catch (Exception e) { + payment.setStatus(PaymentStatus.FAILED); + payment.setFailureReason(e.getMessage()); + paymentRepository.save(payment); + + throw new PaymentProcessingException("Payment processing failed", e); + } + } + + private void validatePaymentRequest(PaymentRequest request) { + if (request.getAmount().compareTo(BigDecimal.ZERO) <= 0) { + throw new InvalidPaymentAmountException("Payment amount must be positive"); + } + + if (request.getPaymentMethod() == null) { + throw new InvalidPaymentMethodException("Payment method is required"); + } + } + + public Payment getPayment(Long id) { + return paymentRepository.findById(id) + .orElseThrow(() -> new PaymentNotFoundException("Payment not found")); + } + + public RefundResult refundPayment(Long id, RefundRequest request) { + Payment payment = getPayment(id); + + if (payment.getStatus() != PaymentStatus.COMPLETED) { + throw new InvalidRefundException("Only completed payments can be refunded"); + } + + // Логика возврата средств + RefundGatewayResponse response = paymentGatewayClient.refundPayment( + payment.getTransactionId(), + request.getAmount() + ); + + if (response.isSuccess()) { + payment.setStatus(PaymentStatus.REFUNDED); + payment.setRefundedAt(LocalDateTime.now()); + paymentRepository.save(payment); + } + + return new RefundResult(response.isSuccess(), response.getRefundId()); + } +} + +@Entity +@Table(name = "payments") +public class Payment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private BigDecimal amount; + + @Column(nullable = false) + private String currency; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private PaymentMethod paymentMethod; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private PaymentStatus status; + + @Column(name = "transaction_id") + private String transactionId; + + @Column(name = "failure_reason") + private String failureReason; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @Column(name = "processed_at") + private LocalDateTime processedAt; + + @Column(name = "refunded_at") + private LocalDateTime refundedAt; + + // геттеры и сеттеры +} +``` + +**Node.js (Express + TypeScript + Prisma) - User Service:** +```typescript +// User Service - только управление пользователями +import express, { Request, Response, NextFunction } from 'express'; +import { PrismaClient } from '@prisma/client'; +import { z } from 'zod'; +import bcrypt from 'bcrypt'; + +const app = express(); +const prisma = new PrismaClient(); + +// Interfaces +interface CreateUserRequest { + name: string; + email: string; + password: string; +} + +interface UpdateUserRequest { + name: string; +} + +interface User { + id: number; + name: string; + email: string; + passwordHash: string; + createdAt: Date; + updatedAt: Date | null; +} + +// User Controller +class UserController { + constructor(private userService: UserService) {} + + async createUser(req: Request, res: Response, next: NextFunction) { + try { + const user = await this.userService.createUser(req.body); + res.json(user); + } catch (error) { + next(error); + } + } + + async getUser(req: Request, res: Response, next: NextFunction) { + try { + const id = parseInt(req.params.id); + const user = await this.userService.getUser(id); + res.json(user); + } catch (error) { + next(error); + } + } + + async updateUser(req: Request, res: Response, next: NextFunction) { + try { + const id = parseInt(req.params.id); + const user = await this.userService.updateUser(id, req.body); + res.json(user); + } catch (error) { + next(error); + } + } + + async deleteUser(req: Request, res: Response, next: NextFunction) { + try { + const id = parseInt(req.params.id); + await this.userService.deleteUser(id); + res.status(204).send(); + } catch (error) { + next(error); + } + } +} + +// User Service +class UserService { + constructor(private userRepository: UserRepository) {} + + async createUser(request: CreateUserRequest): Promise { + // Валидация + const existingUser = await this.userRepository.findByEmail(request.email); + if (existingUser) { + throw new Error('User with email already exists'); + } + + // Создание пользователя + const passwordHash = await bcrypt.hash(request.password, 10); + const user = { + name: request.name, + email: request.email, + passwordHash, + createdAt: new Date(), + updatedAt: null + }; + + return await this.userRepository.save(user); + } + + async getUser(id: number): Promise { + const user = await this.userRepository.findById(id); + if (!user) { + throw new Error('User not found'); + } + return user; + } + + async updateUser(id: number, request: UpdateUserRequest): Promise { + const user = await this.getUser(id); + user.name = request.name; + user.updatedAt = new Date(); + return await this.userRepository.save(user); + } + + async deleteUser(id: number): Promise { + const user = await this.getUser(id); + await this.userRepository.delete(user.id); + } +} + +// User Repository +interface UserRepository { + save(user: Omit): Promise; + findById(id: number): Promise; + findByEmail(email: string): Promise; + delete(id: number): Promise; +} + +class PrismaUserRepository implements UserRepository { + async save(user: Omit): Promise { + const savedUser = await prisma.user.create({ + data: { + name: user.name, + email: user.email, + passwordHash: user.passwordHash, + createdAt: user.createdAt, + updatedAt: user.updatedAt + } + }); + + return { + id: savedUser.id, + name: savedUser.name, + email: savedUser.email, + passwordHash: savedUser.passwordHash, + createdAt: savedUser.createdAt, + updatedAt: savedUser.updatedAt + }; + } + + async findById(id: number): Promise { + const user = await prisma.user.findUnique({ + where: { id } + }); + + if (!user) return null; + + return { + id: user.id, + name: user.name, + email: user.email, + passwordHash: user.passwordHash, + createdAt: user.createdAt, + updatedAt: user.updatedAt + }; + } + + async findByEmail(email: string): Promise { + const user = await prisma.user.findUnique({ + where: { email } + }); + + if (!user) return null; + + return { + id: user.id, + name: user.name, + email: user.email, + passwordHash: user.passwordHash, + createdAt: user.createdAt, + updatedAt: user.updatedAt + }; + } + + async delete(id: number): Promise { + await prisma.user.delete({ + where: { id } + }); + } +} + +// Validation schemas +const createUserSchema = z.object({ + name: z.string().min(1, 'Name is required'), + email: z.string().email('Valid email is required'), + password: z.string().min(6, 'Password must be at least 6 characters') +}); + +const updateUserSchema = z.object({ + name: z.string().min(1, 'Name is required') +}); + +// Dependency injection +const userRepository = new PrismaUserRepository(); +const userService = new UserService(userRepository); +const userController = new UserController(userService); + +// Routes +app.post('/api/users', async (req: Request, res: Response, next: NextFunction) => { + try { + const validatedData = createUserSchema.parse(req.body); + await userController.createUser({ ...req, body: validatedData }, res, next); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.get('/api/users/:id', (req: Request, res: Response, next: NextFunction) => { + userController.getUser(req, res, next); +}); + +app.put('/api/users/:id', async (req: Request, res: Response, next: NextFunction) => { + try { + const validatedData = updateUserSchema.parse(req.body); + await userController.updateUser({ ...req, body: validatedData }, res, next); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.delete('/api/users/:id', (req: Request, res: Response, next: NextFunction) => { + userController.deleteUser(req, res, next); +}); + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +app.listen(3001, () => { + console.log('User Service running on port 3001'); +}); +``` + +**Node.js (Express + TypeScript + Prisma) - Payment Service:** +```typescript +// Payment Service - только обработка платежей +import express, { Request, Response, NextFunction } from 'express'; +import { PrismaClient } from '@prisma/client'; +import { z } from 'zod'; + +const app = express(); +const prisma = new PrismaClient(); + +// Interfaces +interface PaymentRequest { + amount: number; + currency: string; + paymentMethod: string; + orderId: number; + customerId: number; +} + +interface PaymentResult { + id: number; + status: string; + transactionId?: string; + errorMessage?: string; +} + +interface RefundRequest { + amount: number; + reason: string; +} + +interface RefundResult { + id: number; + status: string; + refundId?: string; + errorMessage?: string; +} + +// Payment Controller +class PaymentController { + constructor(private paymentService: PaymentService) {} + + async processPayment(req: Request, res: Response, next: NextFunction) { + try { + const result = await this.paymentService.processPayment(req.body); + res.json(result); + } catch (error) { + next(error); + } + } + + async getPayment(req: Request, res: Response, next: NextFunction) { + try { + const id = parseInt(req.params.id); + const payment = await this.paymentService.getPayment(id); + res.json(payment); + } catch (error) { + next(error); + } + } + + async refundPayment(req: Request, res: Response, next: NextFunction) { + try { + const id = parseInt(req.params.id); + const result = await this.paymentService.refundPayment(id, req.body); + res.json(result); + } catch (error) { + next(error); + } + } +} + +// Payment Service +class PaymentService { + constructor( + private paymentRepository: PaymentRepository, + private paymentGateway: PaymentGateway + ) {} + + async processPayment(request: PaymentRequest): Promise { + // Валидация + this.validatePaymentRequest(request); + + // Создание платежа + const payment = { + amount: request.amount, + currency: request.currency, + paymentMethod: request.paymentMethod, + orderId: request.orderId, + customerId: request.customerId, + status: 'PENDING', + createdAt: new Date(), + processedAt: null, + refundedAt: null + }; + + const savedPayment = await this.paymentRepository.save(payment); + + try { + // Обработка через платежный шлюз + const response = await this.paymentGateway.processPayment(request); + + if (response.success) { + savedPayment.status = 'COMPLETED'; + savedPayment.transactionId = response.transactionId; + savedPayment.processedAt = new Date(); + } else { + savedPayment.status = 'FAILED'; + savedPayment.failureReason = response.errorMessage; + } + + await this.paymentRepository.save(savedPayment); + + return new PaymentResult( + savedPayment.id, + savedPayment.status, + response.transactionId, + response.errorMessage + ); + + } catch (error) { + savedPayment.status = 'FAILED'; + savedPayment.failureReason = error.message; + await this.paymentRepository.save(savedPayment); + + throw new Error(`Payment processing failed: ${error.message}`); + } + } + + private validatePaymentRequest(request: PaymentRequest): void { + if (request.amount <= 0) { + throw new Error('Payment amount must be positive'); + } + + if (!request.paymentMethod) { + throw new Error('Payment method is required'); + } + } + + async getPayment(id: number): Promise { + const payment = await this.paymentRepository.findById(id); + if (!payment) { + throw new Error('Payment not found'); + } + return payment; + } + + async refundPayment(id: number, request: RefundRequest): Promise { + const payment = await this.getPayment(id); + + if (payment.status !== 'COMPLETED') { + throw new Error('Only completed payments can be refunded'); + } + + try { + const refundResponse = await this.paymentGateway.refundPayment({ + transactionId: payment.transactionId, + amount: request.amount, + reason: request.reason + }); + + payment.status = 'REFUNDED'; + payment.refundedAt = new Date(); + await this.paymentRepository.save(payment); + + return new RefundResult( + payment.id, + 'REFUNDED', + refundResponse.refundId + ); + + } catch (error) { + throw new Error(`Refund failed: ${error.message}`); + } + } +} + +// Payment Repository +interface PaymentRepository { + save(payment: any): Promise; + findById(id: number): Promise; +} + +class PrismaPaymentRepository implements PaymentRepository { + async save(payment: any): Promise { + return await prisma.payment.create({ + data: { + amount: payment.amount, + currency: payment.currency, + paymentMethod: payment.paymentMethod, + orderId: payment.orderId, + customerId: payment.customerId, + status: payment.status, + transactionId: payment.transactionId, + failureReason: payment.failureReason, + createdAt: payment.createdAt, + processedAt: payment.processedAt, + refundedAt: payment.refundedAt + } + }); + } + + async findById(id: number): Promise { + return await prisma.payment.findUnique({ + where: { id } + }); + } +} + +// Payment Gateway (mock implementation) +class PaymentGateway { + async processPayment(request: PaymentRequest): Promise { + // Mock implementation - в реальности здесь будет интеграция с платежным шлюзом + return new Promise((resolve) => { + setTimeout(() => { + if (Math.random() > 0.1) { // 90% success rate + resolve({ + success: true, + transactionId: `txn_${Date.now()}`, + errorMessage: null + }); + } else { + resolve({ + success: false, + transactionId: null, + errorMessage: 'Payment gateway error' + }); + } + }, 1000); + }); + } + + async refundPayment(request: any): Promise { + // Mock implementation + return { + refundId: `refund_${Date.now()}` + }; + } +} + +// Validation schemas +const paymentRequestSchema = z.object({ + amount: z.number().positive('Amount must be positive'), + currency: z.string().min(3, 'Currency is required'), + paymentMethod: z.string().min(1, 'Payment method is required'), + orderId: z.number().positive('Order ID is required'), + customerId: z.number().positive('Customer ID is required') +}); + +const refundRequestSchema = z.object({ + amount: z.number().positive('Amount must be positive'), + reason: z.string().min(1, 'Reason is required') +}); + +// Dependency injection +const paymentRepository = new PrismaPaymentRepository(); +const paymentGateway = new PaymentGateway(); +const paymentService = new PaymentService(paymentRepository, paymentGateway); +const paymentController = new PaymentController(paymentService); + +// Routes +app.post('/api/payments/process', async (req: Request, res: Response, next: NextFunction) => { + try { + const validatedData = paymentRequestSchema.parse(req.body); + await paymentController.processPayment({ ...req, body: validatedData }, res, next); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.get('/api/payments/:id', (req: Request, res: Response, next: NextFunction) => { + paymentController.getPayment(req, res, next); +}); + +app.post('/api/payments/:id/refund', async (req: Request, res: Response, next: NextFunction) => { + try { + const validatedData = refundRequestSchema.parse(req.body); + await paymentController.refundPayment({ ...req, body: validatedData }, res, next); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +app.listen(3002, () => { + console.log('Payment Service running on port 3002'); +}); +``` + +**Rust (Actix Web + Diesel) - User Service:** +```rust +// User Service - только управление пользователями +use actix_web::{web, App, HttpResponse, HttpServer, Result}; +use diesel::prelude::*; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Serialize, Deserialize)] +struct CreateUserRequest { + name: String, + email: String, + password: String, +} + +#[derive(Serialize, Deserialize)] +struct UpdateUserRequest { + name: String, +} + +#[derive(Serialize, Deserialize, Queryable)] +struct User { + id: Uuid, + name: String, + email: String, + password_hash: String, + created_at: chrono::DateTime, + updated_at: Option>, +} + +// User Service +struct UserService { + pool: diesel::r2d2::Pool>, +} + +impl UserService { + fn new(database_url: &str) -> Self { + let manager = diesel::r2d2::ConnectionManager::::new(database_url); + let pool = diesel::r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool"); + + Self { pool } + } + + async fn create_user(&self, request: CreateUserRequest) -> Result> { + use crate::schema::users::dsl::*; + + // Проверка существования пользователя + let existing_user = users + .filter(email.eq(&request.email)) + .first::(&mut self.pool.get()?) + .optional()?; + + if existing_user.is_some() { + return Err("User with email already exists".into()); + } + + // Хеширование пароля + let password_hash = bcrypt::hash(&request.password, bcrypt::DEFAULT_COST)?; + + let new_user = NewUser { + id: Uuid::new_v4(), + name: request.name, + email: request.email, + password_hash, + created_at: chrono::Utc::now(), + updated_at: None, + }; + + let user = diesel::insert_into(users) + .values(&new_user) + .get_result::(&mut self.pool.get()?)?; + + Ok(user) + } + + async fn get_user(&self, user_id: Uuid) -> Result> { + use crate::schema::users::dsl::*; + + let user = users + .filter(id.eq(user_id)) + .first::(&mut self.pool.get()?)?; + + Ok(user) + } + + async fn update_user(&self, user_id: Uuid, request: UpdateUserRequest) -> Result> { + use crate::schema::users::dsl::*; + + let user = diesel::update(users.filter(id.eq(user_id))) + .set(( + name.eq(request.name), + updated_at.eq(Some(chrono::Utc::now())), + )) + .get_result::(&mut self.pool.get()?)?; + + Ok(user) + } + + async fn delete_user(&self, user_id: Uuid) -> Result<(), Box> { + use crate::schema::users::dsl::*; + + diesel::delete(users.filter(id.eq(user_id))) + .execute(&mut self.pool.get()?)?; + + Ok(()) + } +} + +#[derive(Insertable)] +#[diesel(table_name = users)] +struct NewUser { + id: Uuid, + name: String, + email: String, + password_hash: String, + created_at: chrono::DateTime, + updated_at: Option>, +} + +// Handlers +async fn create_user( + user_service: web::Data, + request: web::Json, +) -> Result { + match user_service.create_user(request.into_inner()).await { + Ok(user) => Ok(HttpResponse::Ok().json(user)), + Err(e) => Ok(HttpResponse::BadRequest().json(serde_json::json!({ + "error": e.to_string() + }))), + } +} + +async fn get_user( + user_service: web::Data, + path: web::Path, +) -> Result { + let user_id = path.into_inner(); + + match user_service.get_user(user_id).await { + Ok(user) => Ok(HttpResponse::Ok().json(user)), + Err(_) => Ok(HttpResponse::NotFound().json(serde_json::json!({ + "error": "User not found" + }))), + } +} + +async fn update_user( + user_service: web::Data, + path: web::Path, + request: web::Json, +) -> Result { + let user_id = path.into_inner(); + + match user_service.update_user(user_id, request.into_inner()).await { + Ok(user) => Ok(HttpResponse::Ok().json(user)), + Err(e) => Ok(HttpResponse::BadRequest().json(serde_json::json!({ + "error": e.to_string() + }))), + } +} + +async fn delete_user( + user_service: web::Data, + path: web::Path, +) -> Result { + let user_id = path.into_inner(); + + match user_service.delete_user(user_id).await { + Ok(_) => Ok(HttpResponse::NoContent().finish()), + Err(e) => Ok(HttpResponse::BadRequest().json(serde_json::json!({ + "error": e.to_string() + }))), + } +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let database_url = std::env::var("DATABASE_URL") + .expect("DATABASE_URL must be set"); + + let user_service = UserService::new(&database_url); + + HttpServer::new(move || { + App::new() + .app_data(web::Data::new(user_service.clone())) + .route("/api/users", web::post().to(create_user)) + .route("/api/users/{id}", web::get().to(get_user)) + .route("/api/users/{id}", web::put().to(update_user)) + .route("/api/users/{id}", web::delete().to(delete_user)) + }) + .bind("127.0.0.1:3001")? + .run() + .await +} +``` + +**Python (FastAPI + SQLAlchemy) - User Service:** +```python +# User Service - только управление пользователей +from fastapi import FastAPI, HTTPException, Depends +from sqlalchemy import create_engine, Column, String, DateTime, Boolean +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, Session +from pydantic import BaseModel, EmailStr +from passlib.context import CryptContext +from datetime import datetime +from typing import Optional +import uuid + +app = FastAPI(title="User Service", version="1.0.0") + +# Database setup +DATABASE_URL = "postgresql://user:password@localhost/userdb" +engine = create_engine(DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + +# Password hashing +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +# Database Models +class User(Base): + __tablename__ = "users" + + id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) + name = Column(String, nullable=False) + email = Column(String, unique=True, nullable=False) + password_hash = Column(String, nullable=False) + created_at = Column(DateTime, default=datetime.utcnow) + updated_at = Column(DateTime, nullable=True) + +# Pydantic Models +class CreateUserRequest(BaseModel): + name: str + email: EmailStr + password: str + +class UpdateUserRequest(BaseModel): + name: str + +class UserResponse(BaseModel): + id: str + name: str + email: str + created_at: datetime + updated_at: Optional[datetime] + + class Config: + from_attributes = True + +# Dependency +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +# User Service +class UserService: + def __init__(self, db: Session): + self.db = db + + def create_user(self, request: CreateUserRequest) -> User: + # Проверка существования пользователя + existing_user = self.db.query(User).filter(User.email == request.email).first() + if existing_user: + raise HTTPException(status_code=400, detail="User with email already exists") + + # Хеширование пароля + password_hash = pwd_context.hash(request.password) + + # Создание пользователя + user = User( + name=request.name, + email=request.email, + password_hash=password_hash + ) + + self.db.add(user) + self.db.commit() + self.db.refresh(user) + + return user + + def get_user(self, user_id: str) -> User: + user = self.db.query(User).filter(User.id == user_id).first() + if not user: + raise HTTPException(status_code=404, detail="User not found") + return user + + def update_user(self, user_id: str, request: UpdateUserRequest) -> User: + user = self.get_user(user_id) + user.name = request.name + user.updated_at = datetime.utcnow() + + self.db.commit() + self.db.refresh(user) + + return user + + def delete_user(self, user_id: str) -> None: + user = self.get_user(user_id) + self.db.delete(user) + self.db.commit() + +# API Endpoints +@app.post("/api/users", response_model=UserResponse) +async def create_user( + request: CreateUserRequest, + db: Session = Depends(get_db) +): + user_service = UserService(db) + user = user_service.create_user(request) + return user + +@app.get("/api/users/{user_id}", response_model=UserResponse) +async def get_user( + user_id: str, + db: Session = Depends(get_db) +): + user_service = UserService(db) + user = user_service.get_user(user_id) + return user + +@app.put("/api/users/{user_id}", response_model=UserResponse) +async def update_user( + user_id: str, + request: UpdateUserRequest, + db: Session = Depends(get_db) +): + user_service = UserService(db) + user = user_service.update_user(user_id, request) + return user + +@app.delete("/api/users/{user_id}") +async def delete_user( + user_id: str, + db: Session = Depends(get_db) +): + user_service = UserService(db) + user_service.delete_user(user_id) + return {"message": "User deleted successfully"} + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=3001) +``` + +#### 2. Decentralized Governance +- Каждая команда выбирает свои технологии +- Нет единого технологического стека +- Самостоятельное принятие решений + +#### 3. Decentralized Data Management +- Каждый сервис владеет своими данными +- Нет единой базы данных +- Разные типы БД для разных сервисов + +#### 4. Infrastructure Automation +- Автоматизированное развертывание +- CI/CD пайплайны +- Мониторинг и логирование + +#### 5. Design for Failure +- Сервисы могут отказывать +- Необходима отказоустойчивость +- Circuit breaker паттерны + +### Преимущества микросервисов + +#### Технические преимущества +- **Независимое развертывание** — можно обновлять сервисы отдельно +- **Технологическое разнообразие** — разные технологии для разных задач +- **Масштабируемость** — масштабирование только нужных сервисов +- **Отказоустойчивость** — отказ одного сервиса не влияет на другие + +#### Организационные преимущества +- **Независимые команды** — каждая команда работает со своим сервисом +- **Быстрая разработка** — параллельная работа команд +- **Специализация** — команды могут специализироваться на доменах + +### Недостатки микросервисов + +#### Сложность +- **Распределенная система** — сложнее отладка и мониторинг +- **Сетевая задержка** — коммуникация через сеть +- **Сложность данных** — распределенные транзакции + +#### Операционные проблемы +- **Мониторинг** — нужно отслеживать множество сервисов +- **Развертывание** — сложность управления множеством сервисов +- **Тестирование** — интеграционное тестирование усложняется + +### Когда использовать микросервисы + +#### Подходящие случаи: +- Большие команды (8+ разработчиков) +- Сложные домены +- Высокие требования к масштабируемости +- Необходимость независимого развертывания +- Разные технологические требования + +#### Неподходящие случаи: +- Маленькие команды +- Простые приложения +- Низкие требования к масштабируемости +- Недостаток DevOps экспертизы + +--- + +## Domain-Driven Design (DDD) + +### Что такое DDD + +**Domain-Driven Design** — это подход к разработке программного обеспечения, который: +- Фокусируется на бизнес-домене +- Использует единый язык (Ubiquitous Language) +- Разделяет систему на ограниченные контексты (Bounded Contexts) + +### Стратегическое проектирование + +#### 1. Bounded Context (Ограниченный контекст) +**Определение:** Четко определенная граница, внутри которой модель домена применима. + +**Пример:** +``` +E-commerce система: +- Order Context → заказы, платежи +- Inventory Context → товары, склад +- Customer Context → клиенты, профили +- Shipping Context → доставка, логистика +``` + +#### 2. Context Map (Контекстная карта) +**Определение:** Диаграмма, показывающая отношения между ограниченными контекстами. + +**Типы отношений:** +- **Shared Kernel** — общая модель +- **Customer-Supplier** — клиент-поставщик +- **Conformist** — соответствие +- **Anticorruption Layer** — антикоррупционный слой +- **Open Host Service** — открытый хост-сервис +- **Published Language** — опубликованный язык + +#### 3. Ubiquitous Language (Единый язык) +**Определение:** Общий язык, используемый командой для описания домена. + +**Принципы:** +- Используется в коде +- Используется в документации +- Используется в общении +- Эволюционирует с пониманием домена + +### Тактическое проектирование + +#### 1. Entity (Сущность) +**Определение:** Объект с уникальной идентичностью. + +```java +public class Order { + private OrderId id; // уникальный идентификатор + private CustomerId customerId; + private List items; + private OrderStatus status; + + // методы бизнес-логики + public void addItem(ProductId productId, int quantity) { + // бизнес-правила + } +} +``` + +#### 2. Value Object (Объект-значение) +**Определение:** Объект без идентичности, определяемый только своими атрибутами. + +```java +public class Money { + private final BigDecimal amount; + private final Currency currency; + + public Money(BigDecimal amount, Currency currency) { + this.amount = amount; + this.currency = currency; + } + + // методы для работы с деньгами + public Money add(Money other) { + if (!this.currency.equals(other.currency)) { + throw new IllegalArgumentException("Different currencies"); + } + return new Money(this.amount.add(other.amount), this.currency); + } +} +``` + +#### 3. Aggregate (Агрегат) +**Определение:** Граница консистентности данных. + +**Правила агрегатов:** +- Один агрегат = одна транзакция +- Ссылки только на корень агрегата +- Обновления только через корень + +```java +public class Order { // корень агрегата + private OrderId id; + private CustomerId customerId; + private List items; // сущности внутри агрегата + private OrderStatus status; + + // методы для изменения состояния + public void addItem(ProductId productId, int quantity) { + // валидация бизнес-правил + items.add(new OrderItem(productId, quantity)); + } +} +``` + +#### 4. Repository (Репозиторий) +**Определение:** Интерфейс для доступа к агрегатам. + +```java +public interface OrderRepository { + Order findById(OrderId id); + void save(Order order); + List findByCustomerId(CustomerId customerId); +} +``` + +#### 5. Domain Service (Доменный сервис) +**Определение:** Операция, которая не принадлежит ни одной сущности. + +```java +public class OrderPricingService { + public Money calculateTotalPrice(Order order, PricingRules rules) { + // сложная логика расчета цены + // не принадлежит ни Order, ни PricingRules + } +} +``` + +#### 6. Domain Event (Доменное событие) +**Определение:** Событие, произошедшее в домене. + +```java +public class OrderCreatedEvent { + private final OrderId orderId; + private final CustomerId customerId; + private final LocalDateTime occurredAt; + + // конструктор и геттеры +} +``` + +### Event Storming (Мозговой штурм событий) + +**Event Storming** — методология для изучения домена через события. + +#### Процесс: +1. **Доменные события** — что происходит в системе +2. **Команды** — что вызывает события +3. **Агрегаты** — что обрабатывает команды +4. **Политики** — бизнес-правила +5. **Read Models** — что нужно для отображения + +#### Пример: +``` +Команда: CreateOrder + ↓ +Агрегат: Order + ↓ +Событие: OrderCreated + ↓ +Политика: SendConfirmationEmail + ↓ +Команда: SendEmail +``` + +--- + +## Коммуникация между сервисами + +### Синхронная коммуникация + +#### HTTP/REST +**Преимущества:** +- Простота +- Широкая поддержка +- Стандартизация + +**Недостатки:** +- Связанность +- Блокирующие вызовы +- Проблемы с производительностью + +**Технический пример - HTTP/REST коммуникация:** + +```java +// Order Service - клиент для Payment Service +@RestController +@RequestMapping("/api/orders") +public class OrderController { + @Autowired + private PaymentServiceClient paymentServiceClient; + @Autowired + private OrderService orderService; + + @PostMapping + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + try { + // Синхронный вызов Payment Service + PaymentRequest paymentRequest = new PaymentRequest( + request.getAmount(), + request.getCurrency(), + request.getPaymentMethod(), + request.getPaymentDetails() + ); + + PaymentResult paymentResult = paymentServiceClient.processPayment(paymentRequest); + + if (paymentResult.isSuccess()) { + // Создаем заказ только после успешной оплаты + Order order = orderService.createOrder(request, paymentResult.getTransactionId()); + return ResponseEntity.ok(order); + } else { + return ResponseEntity.badRequest() + .body(new ErrorResponse("Payment failed: " + paymentResult.getErrorMessage())); + } + + } catch (PaymentServiceException e) { + // Обработка ошибок коммуникации + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .body(new ErrorResponse("Payment service unavailable")); + } + } +} + +// HTTP клиент для Payment Service +@Component +public class PaymentServiceClient { + @Autowired + private RestTemplate restTemplate; + + private static final String PAYMENT_SERVICE_URL = "http://payment-service:8080"; + + public PaymentResult processPayment(PaymentRequest request) { + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(request, headers); + + ResponseEntity response = restTemplate.postForEntity( + PAYMENT_SERVICE_URL + "/api/payments/process", + entity, + PaymentResult.class + ); + + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + throw new PaymentServiceException("Payment service returned error: " + response.getStatusCode()); + } + + } catch (RestClientException e) { + throw new PaymentServiceException("Failed to communicate with payment service", e); + } + } + + public PaymentResult refundPayment(String transactionId, BigDecimal amount) { + try { + RefundRequest refundRequest = new RefundRequest(transactionId, amount); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(refundRequest, headers); + + ResponseEntity response = restTemplate.postForEntity( + PAYMENT_SERVICE_URL + "/api/payments/refund", + entity, + RefundResult.class + ); + + if (response.getStatusCode() == HttpStatus.OK) { + RefundResult refundResult = response.getBody(); + return new PaymentResult( + null, + refundResult.isSuccess() ? PaymentStatus.REFUNDED : PaymentStatus.FAILED, + transactionId, + refundResult.isSuccess() ? null : "Refund failed" + ); + } else { + throw new PaymentServiceException("Refund service returned error: " + response.getStatusCode()); + } + + } catch (RestClientException e) { + throw new PaymentServiceException("Failed to communicate with refund service", e); + } + } +} + +// Конфигурация RestTemplate с таймаутами и retry +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); + factory.setConnectTimeout(5000); // 5 секунд на подключение + factory.setReadTimeout(10000); // 10 секунд на чтение + + RestTemplate restTemplate = new RestTemplate(factory); + + // Добавляем retry логику + restTemplate.setInterceptors(Arrays.asList(new RetryInterceptor())); + + return restTemplate; + } +} + +// Interceptor для retry логики +public class RetryInterceptor implements ClientHttpRequestInterceptor { + private static final int MAX_RETRIES = 3; + + @Override + public ClientHttpResponse intercept( + HttpRequest request, + byte[] body, + ClientHttpRequestExecution execution) throws IOException { + + int retryCount = 0; + IOException lastException = null; + + while (retryCount < MAX_RETRIES) { + try { + return execution.execute(request, body); + } catch (IOException e) { + lastException = e; + retryCount++; + + if (retryCount < MAX_RETRIES) { + try { + Thread.sleep(1000 * retryCount); // Exponential backoff + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new IOException("Retry interrupted", ie); + } + } + } + } + + throw lastException; + } +} +``` + +#### GraphQL +**Преимущества:** +- Гибкость запросов +- Типизация +- Единая точка входа + +**Недостатки:** +- Сложность кэширования +- Потенциальные проблемы с производительностью + +### Асинхронная коммуникация + +#### Очереди сообщений +**Преимущества:** +- Слабая связанность +- Отказоустойчивость +- Масштабируемость + +**Недостатки:** +- Сложность отладки +- Потенциальные проблемы с порядком + +**Технический пример - асинхронная коммуникация с RabbitMQ:** + +```java +// Order Service - публикует события +@Service +public class OrderService { + @Autowired + private OrderRepository orderRepository; + @Autowired + private RabbitTemplate rabbitTemplate; + + public Order createOrder(CreateOrderRequest request) { + // Создаем заказ + Order order = new Order(); + order.setCustomerId(request.getCustomerId()); + order.setItems(request.getItems()); + order.setStatus(OrderStatus.CREATED); + order.setCreatedAt(LocalDateTime.now()); + + order = orderRepository.save(order); + + // Публикуем событие о создании заказа + OrderCreatedEvent event = new OrderCreatedEvent( + order.getId(), + order.getCustomerId(), + order.getItems(), + order.getTotalAmount(), + order.getCreatedAt() + ); + + rabbitTemplate.convertAndSend("order.exchange", "order.created", event); + + return order; + } + + public void cancelOrder(Long orderId) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new OrderNotFoundException("Order not found")); + + if (order.getStatus() == OrderStatus.CREATED) { + order.setStatus(OrderStatus.CANCELLED); + orderRepository.save(order); + + // Публикуем событие об отмене заказа + OrderCancelledEvent event = new OrderCancelledEvent( + order.getId(), + order.getCustomerId(), + order.getItems(), + order.getTotalAmount(), + LocalDateTime.now() + ); + + rabbitTemplate.convertAndSend("order.exchange", "order.cancelled", event); + } + } +} + +// Notification Service - подписывается на события +@Component +public class OrderEventHandler { + @Autowired + private NotificationService notificationService; + + @RabbitListener(queues = "order.created.queue") + public void handleOrderCreated(OrderCreatedEvent event) { + try { + // Отправляем подтверждение заказа + notificationService.sendOrderConfirmation( + event.getCustomerId(), + event.getOrderId(), + event.getTotalAmount() + ); + + // Отправляем уведомление о статусе заказа + notificationService.sendOrderStatusUpdate( + event.getCustomerId(), + event.getOrderId(), + "Your order has been created and is being processed" + ); + + } catch (Exception e) { + // Логируем ошибку, но не прерываем обработку + log.error("Failed to send order confirmation for order: " + event.getOrderId(), e); + } + } + + @RabbitListener(queues = "order.cancelled.queue") + public void handleOrderCancelled(OrderCancelledEvent event) { + try { + // Отправляем уведомление об отмене + notificationService.sendOrderCancellation( + event.getCustomerId(), + event.getOrderId(), + event.getTotalAmount() + ); + + } catch (Exception e) { + log.error("Failed to send order cancellation for order: " + event.getOrderId(), e); + } + } +} + +// Inventory Service - подписывается на события +@Component +public class InventoryEventHandler { + @Autowired + private InventoryService inventoryService; + + @RabbitListener(queues = "order.created.queue") + public void handleOrderCreated(OrderCreatedEvent event) { + try { + // Резервируем товары + for (OrderItem item : event.getItems()) { + inventoryService.reserveItem(item.getProductId(), item.getQuantity()); + } + + // Публикуем событие о резервировании + InventoryReservedEvent inventoryEvent = new InventoryReservedEvent( + event.getOrderId(), + event.getItems(), + LocalDateTime.now() + ); + + rabbitTemplate.convertAndSend("inventory.exchange", "inventory.reserved", inventoryEvent); + + } catch (InsufficientStockException e) { + // Если товаров недостаточно, отменяем заказ + OrderCancelledEvent cancelEvent = new OrderCancelledEvent( + event.getOrderId(), + event.getCustomerId(), + event.getItems(), + event.getTotalAmount(), + LocalDateTime.now() + ); + + rabbitTemplate.convertAndSend("order.exchange", "order.cancelled", cancelEvent); + + } catch (Exception e) { + log.error("Failed to reserve inventory for order: " + event.getOrderId(), e); + } + } + + @RabbitListener(queues = "order.cancelled.queue") + public void handleOrderCancelled(OrderCancelledEvent event) { + try { + // Освобождаем зарезервированные товары + for (OrderItem item : event.getItems()) { + inventoryService.releaseReservation(item.getProductId(), item.getQuantity()); + } + + } catch (Exception e) { + log.error("Failed to release inventory for order: " + event.getOrderId(), e); + } + } +} + +// Конфигурация RabbitMQ +@Configuration +@EnableRabbit +public class RabbitMQConfig { + + // Exchange для заказов + @Bean + public TopicExchange orderExchange() { + return new TopicExchange("order.exchange"); + } + + // Очереди для событий заказов + @Bean + public Queue orderCreatedQueue() { + return new Queue("order.created.queue", true); + } + + @Bean + public Queue orderCancelledQueue() { + return new Queue("order.cancelled.queue", true); + } + + // Привязки очередей к exchange + @Bean + public Binding orderCreatedBinding() { + return BindingBuilder + .bind(orderCreatedQueue()) + .to(orderExchange()) + .with("order.created"); + } + + @Bean + public Binding orderCancelledBinding() { + return BindingBuilder + .bind(orderCancelledQueue()) + .to(orderExchange()) + .with("order.cancelled"); + } + + // Exchange для инвентаря + @Bean + public TopicExchange inventoryExchange() { + return new TopicExchange("inventory.exchange"); + } + + @Bean + public Queue inventoryReservedQueue() { + return new Queue("inventory.reserved.queue", true); + } + + @Bean + public Binding inventoryReservedBinding() { + return BindingBuilder + .bind(inventoryReservedQueue()) + .to(inventoryExchange()) + .with("inventory.reserved"); + } + + // Конфигурация RabbitTemplate + @Bean + public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { + RabbitTemplate template = new RabbitTemplate(connectionFactory); + template.setMessageConverter(new Jackson2JsonMessageConverter()); + return template; + } + + // Конфигурация Jackson для сериализации + @Bean + public MessageConverter messageConverter() { + return new Jackson2JsonMessageConverter(); + } +} + +// События (DTO) +public class OrderCreatedEvent { + private Long orderId; + private Long customerId; + private List items; + private BigDecimal totalAmount; + private LocalDateTime createdAt; + + // конструкторы, геттеры, сеттеры +} + +public class OrderCancelledEvent { + private Long orderId; + private Long customerId; + private List items; + private BigDecimal totalAmount; + private LocalDateTime cancelledAt; + + // конструкторы, геттеры, сеттеры +} + +public class InventoryReservedEvent { + private Long orderId; + private List items; + private LocalDateTime reservedAt; + + // конструкторы, геттеры, сеттеры +} +``` + +#### Потоковая обработка событий +**Преимущества:** +- Высокая пропускная способность +- Исторические данные +- Real-time обработка + +**Технологии:** +- Apache Kafka +- Apache Pulsar +- AWS Kinesis + +### Паттерны коммуникации + +#### 1. Request-Response +``` +Client → Service A → Service B +Client ← Service A ← Service B +``` + +#### 2. Publish-Subscribe +``` +Publisher → Event Bus → Subscriber 1 + → Subscriber 2 + → Subscriber 3 +``` + +#### 3. Request-Reply +``` +Client → Message Queue → Service +Client ← Message Queue ← Service +``` + +#### 4. Event Sourcing +``` +Command → Aggregate → Event Store +Query → Read Model ← Event Store +``` + +### Обнаружение сервисов + +**Проблема:** Как сервисы находят друг друга в динамической среде? + +#### Решения: + +##### 1. Client-Side Discovery +```java +@Service +public class PaymentServiceClient { + @Autowired + private ServiceDiscovery serviceDiscovery; + + public PaymentResult processPayment(PaymentRequest request) { + // клиент сам находит сервис + ServiceInstance instance = serviceDiscovery.getInstance("payment-service"); + String url = "http://" + instance.getHost() + ":" + instance.getPort(); + + return restTemplate.postForObject(url + "/payments", request, PaymentResult.class); + } +} +``` + +##### 2. Server-Side Discovery +```java +@Service +public class PaymentServiceClient { + public PaymentResult processPayment(PaymentRequest request) { + // сервис находится через load balancer + String url = "http://payment-service/payments"; + return restTemplate.postForObject(url, request, PaymentResult.class); + } +} +``` + +#### Инструменты: +- **Consul** — service discovery и configuration +- **Eureka** — Netflix service discovery +- **Kubernetes** — встроенный service discovery + +--- + +## Управление данными в микросервисах + +### База данных на сервис + +**Принцип:** Каждый микросервис имеет собственную базу данных. + +#### Преимущества: +- Независимость данных +- Разные типы БД для разных задач +- Независимое масштабирование + +#### Недостатки: +- Сложность транзакций +- Проблемы с консистентностью +- Дублирование данных + +### Паттерны управления данными + +#### 1. Saga Pattern +**Определение:** Паттерн для управления распределенными транзакциями. + +##### Choreography Saga +```java +// Order Service +public class OrderService { + public void createOrder(CreateOrderRequest request) { + Order order = new Order(request); + orderRepository.save(order); + + // публикуем событие + eventPublisher.publish(new OrderCreatedEvent(order.getId(), request.getItems())); + } +} + +// Inventory Service +@EventHandler +public class InventoryEventHandler { + public void handleOrderCreated(OrderCreatedEvent event) { + try { + inventoryService.reserveItems(event.getItems()); + eventPublisher.publish(new ItemsReservedEvent(event.getOrderId())); + } catch (InsufficientStockException e) { + eventPublisher.publish(new OrderCancelledEvent(event.getOrderId())); + } + } +} + +// Payment Service +@EventHandler +public class PaymentEventHandler { + public void handleItemsReserved(ItemsReservedEvent event) { + try { + paymentService.processPayment(event.getOrderId()); + eventPublisher.publish(new PaymentProcessedEvent(event.getOrderId())); + } catch (PaymentFailedException e) { + eventPublisher.publish(new OrderCancelledEvent(event.getOrderId())); + } + } +} +``` + +##### Orchestration Saga +```java +@Service +public class OrderOrchestrator { + public void processOrder(CreateOrderRequest request) { + Order order = orderService.createOrder(request); + + try { + // шаг 1: резервирование товаров + inventoryService.reserveItems(order.getId(), request.getItems()); + + // шаг 2: обработка платежа + paymentService.processPayment(order.getId(), request.getPayment()); + + // шаг 3: подтверждение заказа + orderService.confirmOrder(order.getId()); + + } catch (Exception e) { + // компенсирующие транзакции + compensateOrder(order.getId()); + } + } + + private void compensateOrder(OrderId orderId) { + try { + inventoryService.releaseItems(orderId); + paymentService.refundPayment(orderId); + orderService.cancelOrder(orderId); + } catch (Exception e) { + // логирование ошибок компенсации + } + } +} +``` + +#### 2. CQRS (Command Query Responsibility Segregation) +**Определение:** Разделение команд и запросов. + +```java +// Command Side +@Service +public class OrderCommandService { + public void createOrder(CreateOrderCommand command) { + Order order = new Order(command); + orderRepository.save(order); + + // обновляем read model + orderReadModelService.updateReadModel(order); + } +} + +// Query Side +@Service +public class OrderQueryService { + public OrderView getOrderView(OrderId orderId) { + return orderReadModelRepository.findById(orderId); + } + + public List getOrdersByCustomer(CustomerId customerId) { + return orderReadModelRepository.findByCustomerId(customerId); + } +} +``` + +#### 3. Event Sourcing +**Определение:** Хранение событий вместо состояния. + +```java +@Entity +public class OrderEvent { + private Long id; + private String aggregateId; + private String eventType; + private String eventData; + private LocalDateTime occurredAt; +} + +@Service +public class OrderEventStore { + public void saveEvent(OrderEvent event) { + eventRepository.save(event); + } + + public List getEvents(String aggregateId) { + return eventRepository.findByAggregateIdOrderByOccurredAt(aggregateId); + } +} + +@Service +public class OrderProjectionService { + public OrderView buildOrderView(String orderId) { + List events = eventStore.getEvents(orderId); + OrderView view = new OrderView(); + + for (OrderEvent event : events) { + applyEvent(view, event); + } + + return view; + } +} +``` + +### Консистентность данных + +#### Типы консистентности: + +##### 1. Strong Consistency +- Все узлы видят одинаковые данные +- Сложно в распределенных системах +- Высокая задержка + +##### 2. Eventual Consistency +- Данные в итоге станут консистентными +- Подходит для большинства случаев +- Низкая задержка + +##### 3. Weak Consistency +- Нет гарантий консистентности +- Используется редко + +#### Паттерны для консистентности: + +##### 1. Two-Phase Commit (2PC) +**Проблема:** Блокирующий протокол +**Решение:** Использовать Saga вместо 2PC + +##### 2. Three-Phase Commit (3PC) +**Улучшение:** Меньше блокировок +**Проблема:** Все еще сложный + +##### 3. Consensus Algorithms +- **Raft** — выбор лидера +- **Paxos** — консенсус в распределенных системах + +--- + +## Микрофронтенды + +### Что такое микрофронтенды + +**Микрофронтенды** — это архитектурный подход, где фронтенд приложения разбивается на независимые части. + +### Принципы микрофронтендов + +#### 1. Независимость +- Независимая разработка +- Независимое развертывание +- Независимые команды + +#### 2. Технологическое разнообразие +- Разные фреймворки +- Разные библиотеки +- Разные подходы + +#### 3. Единый пользовательский интерфейс +- Общий дизайн +- Общие компоненты +- Общий пользовательский опыт + +### Архитектурные паттерны + +#### 1. Module Federation (Webpack 5) +```javascript +// shell/ModuleFederationPlugin.js +new ModuleFederationPlugin({ + name: 'shell', + remotes: { + 'product': 'product@http://localhost:3001/remoteEntry.js', + 'cart': 'cart@http://localhost:3002/remoteEntry.js', + }, +}); + +// product/webpack.config.js +new ModuleFederationPlugin({ + name: 'product', + filename: 'remoteEntry.js', + exposes: { + './ProductList': './src/ProductList', + }, +}); +``` + +#### 2. Single-SPA +```javascript +// main.js +import { registerApplication, start } from 'single-spa'; + +registerApplication({ + name: 'product', + app: () => System.import('product'), + activeWhen: ['/products'], +}); + +registerApplication({ + name: 'cart', + app: () => System.import('cart'), + activeWhen: ['/cart'], +}); + +start(); +``` + +#### 3. iframe-based +```html + +
+ +
+ +
+ +
+``` + +### Управление состоянием + +#### 1. Shared State +```javascript +// shared-state/store.js +class SharedStore { + constructor() { + this.state = {}; + this.subscribers = []; + } + + setState(newState) { + this.state = { ...this.state, ...newState }; + this.notifySubscribers(); + } + + subscribe(callback) { + this.subscribers.push(callback); + } + + notifySubscribers() { + this.subscribers.forEach(callback => callback(this.state)); + } +} + +export const sharedStore = new SharedStore(); +``` + +#### 2. Event Bus +```javascript +// shared/eventBus.js +class EventBus { + constructor() { + this.events = {}; + } + + on(event, callback) { + if (!this.events[event]) { + this.events[event] = []; + } + this.events[event].push(callback); + } + + emit(event, data) { + if (this.events[event]) { + this.events[event].forEach(callback => callback(data)); + } + } +} + +export const eventBus = new EventBus(); +``` + +### Коммуникация между микрофронтендами + +#### 1. Props/Events +```javascript +// shell/App.js +import ProductMicrofrontend from 'product/ProductList'; +import CartMicrofrontend from 'cart/CartList'; + +function App() { + const [cartItems, setCartItems] = useState([]); + + const handleAddToCart = (product) => { + setCartItems([...cartItems, product]); + }; + + return ( +
+ + +
+ ); +} +``` + +#### 2. Custom Events +```javascript +// product/ProductList.js +function ProductList() { + const handleAddToCart = (product) => { + // отправляем событие + window.dispatchEvent(new CustomEvent('addToCart', { + detail: product + })); + }; + + return ( +
+ {products.map(product => ( + + ))} +
+ ); +} + +// cart/CartList.js +function CartList() { + const [items, setItems] = useState([]); + + useEffect(() => { + const handleAddToCart = (event) => { + setItems([...items, event.detail]); + }; + + window.addEventListener('addToCart', handleAddToCart); + + return () => { + window.removeEventListener('addToCart', handleAddToCart); + }; + }, [items]); + + return ( +
+ {items.map(item => ( +
{item.name}
+ ))} +
+ ); +} +``` + +### Преимущества микрофронтендов + +#### Технические: +- Независимая разработка +- Технологическое разнообразие +- Независимое развертывание +- Масштабируемость команд + +#### Организационные: +- Независимые команды +- Быстрая разработка +- Специализация команд + +### Недостатки микрофронтендов + +#### Сложность: +- Сложность интеграции +- Дублирование кода +- Проблемы с производительностью +- Сложность тестирования + +#### Операционные: +- Сложность развертывания +- Проблемы с версионированием +- Сложность мониторинга + +--- + +## Serverless архитектура и FaaS + +### Что такое Serverless + +**Serverless** — это модель облачных вычислений, где разработчики не управляют серверами, а платформа автоматически выделяет ресурсы по требованию. + +### Ключевые характеристики Serverless + +#### 1. Event-Driven Execution +- Функции запускаются по событиям +- Автоматическое масштабирование +- Оплата только за время выполнения + +#### 2. Stateless Functions +- Функции не хранят состояние +- Внешнее хранение данных +- Горизонтальное масштабирование + +### FaaS (Функция как сервис) + +**FaaS** — это подмодель Serverless, где код выполняется в виде отдельных функций. + +#### AWS Lambda + +```python +# AWS Lambda Function +import json +import boto3 + +def lambda_handler(event, context): + """ + Lambda function для обработки заказов + """ + # Парсинг события + order_data = json.loads(event['body']) + + # Валидация данных + if not validate_order(order_data): + return { + 'statusCode': 400, + 'body': json.dumps({'error': 'Invalid order data'}) + } + + # Обработка заказа + order_id = process_order(order_data) + + # Отправка уведомления + send_notification(order_id) + + return { + 'statusCode': 200, + 'body': json.dumps({ + 'orderId': order_id, + 'status': 'processed' + }) + } + +def validate_order(order_data): + """Валидация данных заказа""" + required_fields = ['customerId', 'items', 'total'] + return all(field in order_data for field in required_fields) + +def process_order(order_data): + """Обработка заказа""" + # Логика обработки заказа + return f"ORD-{order_data.get('customerId')}-{int(time.time())}" + +def send_notification(order_id): + """Отправка уведомления""" + sns = boto3.client('sns') + sns.publish( + TopicArn='arn:aws:sns:us-east-1:123456789012:order-notifications', + Message=f'Order {order_id} has been processed', + Subject='Order Processed' + ) +``` + +#### Azure Functions + +```csharp +// Azure Function +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +public static class OrderProcessor +{ + [FunctionName("ProcessOrder")] + public static async Task Run( + [HttpTrigger(AuthorizationLevel.Function, "post", Route = "orders")] HttpRequest req, + ILogger log) + { + log.LogInformation("Processing order request"); + + try + { + // Чтение данных запроса + string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); + var orderData = JsonSerializer.Deserialize(requestBody); + + // Валидация + if (!ValidateOrder(orderData)) + { + return new BadRequestObjectResult("Invalid order data"); + } + + // Обработка заказа + var orderId = await ProcessOrderAsync(orderData); + + // Отправка уведомления + await SendNotificationAsync(orderId); + + return new OkObjectResult(new { orderId, status = "processed" }); + } + catch (Exception ex) + { + log.LogError(ex, "Error processing order"); + return new StatusCodeResult(500); + } + } + + private static bool ValidateOrder(OrderData orderData) + { + return !string.IsNullOrEmpty(orderData.CustomerId) && + orderData.Items?.Count > 0 && + orderData.Total > 0; + } + + private static async Task ProcessOrderAsync(OrderData orderData) + { + // Логика обработки заказа + await Task.Delay(100); // Имитация обработки + return $"ORD-{orderData.CustomerId}-{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}"; + } + + private static async Task SendNotificationAsync(string orderId) + { + // Отправка уведомления + await Task.Delay(50); // Имитация отправки + } +} + +public class OrderData +{ + public string CustomerId { get; set; } + public List Items { get; set; } + public decimal Total { get; set; } +} +``` + +#### Google Cloud Functions + +```javascript +// Google Cloud Function +const functions = require('@google-cloud/functions-framework'); +const { PubSub } = require('@google-cloud/pubsub'); + +const pubsub = new PubSub(); + +functions.http('processOrder', async (req, res) => { + // Установка CORS + res.set('Access-Control-Allow-Origin', '*'); + res.set('Access-Control-Allow-Methods', 'POST'); + res.set('Access-Control-Allow-Headers', 'Content-Type'); + + if (req.method === 'OPTIONS') { + res.status(204).send(''); + return; + } + + try { + // Валидация данных + if (!validateOrder(req.body)) { + res.status(400).json({ error: 'Invalid order data' }); + return; + } + + // Обработка заказа + const orderId = await processOrder(req.body); + + // Отправка уведомления + await sendNotification(orderId); + + res.status(200).json({ + orderId: orderId, + status: 'processed' + }); + } catch (error) { + console.error('Error processing order:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +function validateOrder(orderData) { + return orderData.customerId && + orderData.items && + orderData.items.length > 0 && + orderData.total > 0; +} + +async function processOrder(orderData) { + // Логика обработки заказа + const orderId = `ORD-${orderData.customerId}-${Date.now()}`; + + // Сохранение в базу данных + // await saveOrderToDatabase(orderData, orderId); + + return orderId; +} + +async function sendNotification(orderId) { + const topic = pubsub.topic('order-notifications'); + const message = { + data: Buffer.from(JSON.stringify({ + orderId: orderId, + message: 'Order has been processed', + timestamp: new Date().toISOString() + })) + }; + + await topic.publish(message); +} +``` + +### Архитектурные паттерны Serverless + +#### 1. Event-Driven Architecture + +```yaml +# Serverless Event-Driven Architecture +apiVersion: v1 +kind: ConfigMap +metadata: + name: event-driven-config +data: + # Event sources + event-sources: | + - name: order-created + type: api-gateway + handler: order-created-handler + - name: payment-processed + type: message-queue + handler: payment-processed-handler + - name: inventory-updated + type: database-trigger + handler: inventory-updated-handler + + # Event handlers + event-handlers: | + - name: order-created-handler + function: process-order + events: [order-created] + - name: payment-processed-handler + function: update-order-status + events: [payment-processed] + - name: inventory-updated-handler + function: update-product-availability + events: [inventory-updated] +``` + +#### 2. API Gateway + Lambda Pattern + +```python +# API Gateway + Lambda Pattern +import json +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +logger = Logger() +tracer = Tracer() + +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext) -> dict: + """ + API Gateway + Lambda для микросервиса пользователей + """ + try: + # Извлечение HTTP метода и пути + http_method = event['httpMethod'] + path = event['path'] + + # Маршрутизация запросов + if http_method == 'GET' and path == '/users': + return get_users(event) + elif http_method == 'GET' and path.startswith('/users/'): + user_id = path.split('/')[-1] + return get_user(user_id) + elif http_method == 'POST' and path == '/users': + return create_user(event) + elif http_method == 'PUT' and path.startswith('/users/'): + user_id = path.split('/')[-1] + return update_user(user_id, event) + elif http_method == 'DELETE' and path.startswith('/users/'): + user_id = path.split('/')[-1] + return delete_user(user_id) + else: + return { + 'statusCode': 404, + 'body': json.dumps({'error': 'Not found'}) + } + + except Exception as e: + logger.error(f"Error processing request: {str(e)}") + return { + 'statusCode': 500, + 'body': json.dumps({'error': 'Internal server error'}) + } + +@tracer.capture_method +def get_users(event: dict) -> dict: + """Получение списка пользователей""" + # Логика получения пользователей + users = [ + {'id': '1', 'name': 'John Doe', 'email': 'john@example.com'}, + {'id': '2', 'name': 'Jane Smith', 'email': 'jane@example.com'} + ] + + return { + 'statusCode': 200, + 'headers': { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + 'body': json.dumps(users) + } + +@tracer.capture_method +def get_user(user_id: str) -> dict: + """Получение пользователя по ID""" + # Логика получения пользователя + user = {'id': user_id, 'name': 'John Doe', 'email': 'john@example.com'} + + return { + 'statusCode': 200, + 'headers': { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + 'body': json.dumps(user) + } + +@tracer.capture_method +def create_user(event: dict) -> dict: + """Создание нового пользователя""" + try: + body = json.loads(event['body']) + + # Валидация данных + if not body.get('name') or not body.get('email'): + return { + 'statusCode': 400, + 'body': json.dumps({'error': 'Name and email are required'}) + } + + # Создание пользователя + user = { + 'id': str(int(time.time())), + 'name': body['name'], + 'email': body['email'] + } + + return { + 'statusCode': 201, + 'headers': { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + 'body': json.dumps(user) + } + except json.JSONDecodeError: + return { + 'statusCode': 400, + 'body': json.dumps({'error': 'Invalid JSON'}) + } +``` + +#### 3. Event Sourcing с Serverless + +```python +# Event Sourcing с Serverless +import json +from datetime import datetime +from typing import List, Dict, Any + +class EventStore: + """Event Store для Serverless функций""" + + def __init__(self, dynamodb_client): + self.dynamodb = dynamodb_client + self.table_name = 'events' + + def save_event(self, aggregate_id: str, event_type: str, event_data: Dict[Any, Any]) -> str: + """Сохранение события""" + event_id = f"{aggregate_id}_{int(datetime.utcnow().timestamp() * 1000)}" + + event = { + 'eventId': event_id, + 'aggregateId': aggregate_id, + 'eventType': event_type, + 'eventData': event_data, + 'timestamp': datetime.utcnow().isoformat(), + 'version': self.get_next_version(aggregate_id) + } + + self.dynamodb.put_item( + TableName=self.table_name, + Item=event + ) + + return event_id + + def get_events(self, aggregate_id: str) -> List[Dict[Any, Any]]: + """Получение событий для агрегата""" + response = self.dynamodb.query( + TableName=self.table_name, + KeyConditionExpression='aggregateId = :aggregate_id', + ExpressionAttributeValues={':aggregate_id': aggregate_id} + ) + + return response.get('Items', []) + + def get_next_version(self, aggregate_id: str) -> int: + """Получение следующей версии агрегата""" + events = self.get_events(aggregate_id) + return len(events) + 1 + +# Lambda функция для обработки событий +def process_order_event(event, context): + """Обработка события заказа""" + event_store = EventStore(boto3.client('dynamodb')) + + # Парсинг события + order_data = json.loads(event['body']) + + # Создание события + event_id = event_store.save_event( + aggregate_id=order_data['orderId'], + event_type='OrderCreated', + event_data={ + 'customerId': order_data['customerId'], + 'items': order_data['items'], + 'total': order_data['total'] + } + ) + + # Обработка события + if order_data.get('paymentProcessed'): + event_store.save_event( + aggregate_id=order_data['orderId'], + event_type='PaymentProcessed', + event_data={'paymentId': order_data['paymentId']} + ) + + return { + 'statusCode': 200, + 'body': json.dumps({ + 'eventId': event_id, + 'status': 'processed' + }) + } +``` + +### Преимущества и недостатки Serverless + +#### Преимущества + +1. **Автоматическое масштабирование** + - От 0 до тысяч экземпляров + - Оплата только за использование + - Нет необходимости в управлении серверами + +2. **Быстрая разработка** + - Фокус на бизнес-логике + - Минимальная инфраструктура + - Быстрое развертывание + +3. **Стоимость** + - Оплата только за время выполнения + - Нет простоя серверов + - Эффективное использование ресурсов + +#### Недостатки + +1. **Cold Start** + - Задержка при первом запуске + - Влияние на производительность + - Необходимость оптимизации + +2. **Ограничения выполнения** + - Таймауты (обычно 15 минут) + - Ограничения памяти + - Ограничения размера пакета + +3. **Vendor Lock-in** + - Привязка к конкретному провайдеру + - Сложность миграции + - Зависимость от API провайдера + +### Оптимизация Serverless функций + +#### 1. Уменьшение Cold Start + +```python +# Оптимизация Cold Start +import json +import boto3 + +# Инициализация клиентов вне handler'а +dynamodb = boto3.client('dynamodb') +sns = boto3.client('sns') + +# Кэширование соединений +def get_database_connection(): + # Переиспользование соединений + pass + +def lambda_handler(event, context): + """Оптимизированная Lambda функция""" + # Минимальная инициализация в handler'е + try: + # Быстрая обработка + result = process_request(event) + return { + 'statusCode': 200, + 'body': json.dumps(result) + } + except Exception as e: + return { + 'statusCode': 500, + 'body': json.dumps({'error': str(e)}) + } + +def process_request(event): + """Основная логика обработки""" + # Использование предварительно инициализированных клиентов + return {'message': 'Success'} +``` + +#### 2. Мониторинг и логирование + +```python +# Мониторинг Serverless функций +import json +import time +from aws_lambda_powertools import Logger, Tracer, Metrics +from aws_lambda_powertools.utilities.typing import LambdaContext + +logger = Logger() +tracer = Tracer() +metrics = Metrics() + +@tracer.capture_lambda_handler +@metrics.log_metrics +def lambda_handler(event: dict, context: LambdaContext) -> dict: + """Lambda функция с мониторингом""" + + # Логирование начала выполнения + logger.info("Function started", extra={ + "requestId": context.aws_request_id, + "functionName": context.function_name + }) + + start_time = time.time() + + try: + # Обработка запроса + result = process_request(event) + + # Метрики успешного выполнения + metrics.add_metric(name="SuccessCount", unit="Count", value=1) + metrics.add_metric(name="Duration", unit="Milliseconds", + value=(time.time() - start_time) * 1000) + + logger.info("Function completed successfully") + + return { + 'statusCode': 200, + 'body': json.dumps(result) + } + + except Exception as e: + # Метрики ошибок + metrics.add_metric(name="ErrorCount", unit="Count", value=1) + + logger.error(f"Function failed: {str(e)}", extra={ + "error": str(e), + "requestId": context.aws_request_id + }) + + return { + 'statusCode': 500, + 'body': json.dumps({'error': 'Internal server error'}) + } + +def process_request(event): + """Обработка запроса с трейсингом""" + with tracer.capture_method("process_request"): + # Логика обработки + return {'message': 'Success'} +``` + +### Serverless против Микросервисов + +| Аспект | Serverless | Микросервисы | +|--------|------------|--------------| +| **Управление инфраструктурой** | Полностью автоматизировано | Требует управления | +| **Масштабирование** | Автоматическое | Ручное или полуавтоматическое | +| **Стоимость** | Оплата за использование | Постоянные затраты | +| **Производительность** | Cold start проблемы | Стабильная производительность | +| **Сложность** | Низкая | Высокая | +| **Контроль** | Ограниченный | Полный | +| **Vendor Lock-in** | Высокий | Низкий | + +### Когда использовать Serverless + +#### Подходящие случаи: +- **Event-driven приложения** +- **API с переменной нагрузкой** +- **Обработка файлов и данных** +- **Интеграции между системами** +- **Быстрые прототипы** + +#### Неподходящие случаи: +- **Длительные вычисления** +- **Приложения с постоянной нагрузкой** +- **Требования к низкой задержке** +- **Сложные stateful приложения** +- **Критичные к производительности системы** + +--- + +## Паттерны микросервисов + +### 1. API Gateway +**Определение:** Единая точка входа для всех клиентских запросов. + +#### Функции: +- Маршрутизация запросов +- Аутентификация и авторизация +- Rate limiting +- Кэширование +- Мониторинг + +```yaml +# nginx.conf +upstream user_service { + server user-service:8080; +} + +upstream order_service { + server order-service:8080; +} + +server { + listen 80; + + location /api/users/ { + proxy_pass http://user_service/; + } + + location /api/orders/ { + proxy_pass http://order_service/; + } +} +``` + +### 2. Circuit Breaker +**Определение:** Паттерн для предотвращения каскадных отказов. + +```java +@Component +public class PaymentServiceClient { + private final CircuitBreaker circuitBreaker; + + public PaymentServiceClient() { + this.circuitBreaker = CircuitBreaker.ofDefaults("payment-service"); + } + + public PaymentResult processPayment(PaymentRequest request) { + return circuitBreaker.executeSupplier(() -> { + return restTemplate.postForObject( + "http://payment-service/payments", + request, + PaymentResult.class + ); + }); + } +} +``` + +### 3. Bulkhead +**Определение:** Изоляция ресурсов для предотвращения каскадных отказов. + +```java +@Configuration +public class BulkheadConfig { + @Bean + public ThreadPoolExecutor paymentExecutor() { + return new ThreadPoolExecutor( + 10, 10, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(100) + ); + } + + @Bean + public ThreadPoolExecutor notificationExecutor() { + return new ThreadPoolExecutor( + 5, 5, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(50) + ); + } +} +``` + +### 4. Retry +**Определение:** Повторные попытки при временных сбоях. + +```java +@Retryable(value = {ConnectException.class}, maxAttempts = 3) +public PaymentResult processPayment(PaymentRequest request) { + return restTemplate.postForObject( + "http://payment-service/payments", + request, + PaymentResult.class + ); +} + +@Recover +public PaymentResult recover(ConnectException ex, PaymentRequest request) { + // логика восстановления + return new PaymentResult(false, "Service unavailable"); +} +``` + +### 5. Timeout +**Определение:** Ограничение времени ожидания ответа. + +```java +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate() { + HttpComponentsClientHttpRequestFactory factory = + new HttpComponentsClientHttpRequestFactory(); + factory.setConnectTimeout(5000); + factory.setReadTimeout(10000); + + return new RestTemplate(factory); + } +} +``` + +### 6. Service Mesh +**Определение:** Инфраструктурный слой для управления коммуникацией между сервисами. + +#### Пример Istio: +```yaml +# istio-gateway.yaml +apiVersion: networking.istio.io/v1alpha3 +kind: Gateway +metadata: + name: api-gateway +spec: + selector: + istio: ingressgateway + servers: + - port: + number: 80 + name: http + protocol: HTTP + hosts: + - api.example.com +--- +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: api-routes +spec: + hosts: + - api.example.com + gateways: + - api-gateway + http: + - match: + - uri: + prefix: /api/users + route: + - destination: + host: user-service + port: + number: 8080 + - match: + - uri: + prefix: /api/orders + route: + - destination: + host: order-service + port: + number: 8080 +``` + +--- + +## Антипаттерны и проблемы + +### 1. Distributed Monolith +**Проблема:** Микросервисы тесно связаны друг с другом. + +**Симптомы:** +- Изменения в одном сервисе требуют изменений в других +- Общие базы данных +- Синхронные вызовы между всеми сервисами + +**Решение:** +- Правильное разделение доменов +- Асинхронная коммуникация +- Независимые базы данных + +### 2. Database per Service Anti-pattern +**Проблема:** Неправильное разделение данных. + +**Симптомы:** +- Сложные JOIN'ы между сервисами +- Дублирование данных +- Проблемы с консистентностью + +**Решение:** +- Правильное разделение данных по доменам +- Использование событий для синхронизации +- CQRS для разделения чтения и записи + +### 3. Chatty Services +**Проблема:** Слишком много мелких запросов между сервисами. + +**Симптомы:** +- Высокая сетевая задержка +- Проблемы с производительностью +- Сложность отладки + +**Решение:** +- Batch операции +- Кэширование +- Агрегация данных + +### 4. Shared Database +**Проблема:** Несколько сервисов используют одну базу данных. + +**Симптомы:** +- Связанность сервисов +- Проблемы с развертыванием +- Сложность масштабирования + +**Решение:** +- Разделение баз данных +- Миграция данных +- Использование событий + +### 5. Service Explosion +**Проблема:** Слишком много мелких сервисов. + +**Симптомы:** +- Сложность управления +- Высокий overhead +- Проблемы с мониторингом + +**Решение:** +- Объединение связанных сервисов +- Правильное разделение доменов +- Использование модулей вместо сервисов + +### 6. Versioning Hell +**Проблема:** Проблемы с версионированием API. + +**Симптомы:** +- Breaking changes +- Сложность миграции +- Проблемы с совместимостью + +**Решение:** +- Semantic versioning +- Backward compatibility +- Graceful deprecation + +--- + +## Практические задания + +### Задание 1: Проектирование микросервисов +Спроектируйте микросервисную архитектуру для системы интернет-магазина: +1. Определите домены и ограниченные контексты +2. Спроектируйте сервисы и их взаимодействие +3. Определите данные для каждого сервиса +4. Создайте контекстную карту + +### Задание 2: Реализация Saga +Реализуйте Saga для процесса создания заказа: +1. Создание заказа +2. Резервирование товаров +3. Обработка платежа +4. Подтверждение заказа +5. Компенсирующие транзакции + +### Задание 3: Микрофронтенды +Создайте простую систему микрофронтендов: +1. Shell приложение +2. Product микрофронтенд +3. Cart микрофронтенд +4. Коммуникация между ними + +--- + +## Дополнительные ресурсы + +### Книги +- "Building Microservices" by Sam Newman +- "Microservices Patterns" by Chris Richardson +- "Domain-Driven Design" by Eric Evans +- "Implementing Domain-Driven Design" by Vaughn Vernon + +### Онлайн ресурсы +- [Microservices.io](https://microservices.io/) +- [Martin Fowler's Microservices](https://martinfowler.com/articles/microservices.html) +- [Micro Frontends](https://micro-frontends.org/) + +### Инструменты +- **Service Mesh:** Istio, Linkerd, Consul Connect +- **API Gateway:** Kong, Zuul, AWS API Gateway +- **Event Streaming:** Apache Kafka, Apache Pulsar +- **Service Discovery:** Consul, Eureka, etcd + +--- + +## Контрольные вопросы + +1. Что такое микросервисная архитектура и каковы её принципы? +2. Объясните концепцию Domain-Driven Design. +3. Какие паттерны коммуникации между сервисами вы знаете? +4. Как управлять данными в микросервисах? +5. Что такое микрофронтенды и как они работают? +6. Какие антипаттерны микросервисов вы знаете? +7. Как обеспечить отказоустойчивость в микросервисах? +8. Когда стоит использовать микросервисы, а когда нет? diff --git a/modules/module3/README.md b/modules/module3/README.md new file mode 100644 index 0000000..4a24b4b --- /dev/null +++ b/modules/module3/README.md @@ -0,0 +1,108 @@ +# Модуль 3: Контейнеризация и оркестрация + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля + +Этот модуль посвящен изучению современных технологий контейнеризации и оркестрации, которые являются основой для развертывания и управления микросервисными приложениями. Студенты изучат Docker, Kubernetes, Infrastructure as Code и связанные инструменты. + +## Содержание модуля + +### 1. [Основы контейнеризации](sections/01-containerization-basics.md) +- Что такое контейнеризация +- Преимущества контейнеризации +- Контейнеры vs Виртуальные машины +- Компоненты контейнеризации + +### 2. [Docker](sections/02-docker.md) +- Основы Docker +- Dockerfile и лучшие практики +- Docker Compose +- Команды Docker + +### 3. [Kubernetes](sections/03-kubernetes.md) +- Архитектура Kubernetes +- Основные объекты (Pod, Deployment, Service, Ingress) +- ConfigMap и Secret +- Сетевая модель и хранилище + +### 4. [Infrastructure as Code](sections/04-infrastructure-as-code.md) +- Основы IaC +- Terraform +- Ansible +- Модули и переменные + +### 5. [CI/CD с контейнерами](sections/05-cicd-containers.md) +- CI/CD Pipeline +- GitOps +- ArgoCD +- Автоматизация развертывания + +### 6. [Мониторинг контейнеров](sections/06-container-monitoring.md) +- Prometheus + Grafana +- ELK Stack +- Логирование и метрики +- Дашборды и алерты + +## Ключевые компетенции + +После изучения модуля студенты будут уметь: + +- Создавать и оптимизировать Dockerfile +- Настраивать Docker Compose для микросервисов +- Развертывать приложения в Kubernetes +- Управлять инфраструктурой через код +- Настраивать CI/CD пайплайны +- Мониторить контейнеры и приложения + +## Практические задания + +1. **Создание Dockerfile** + - Оптимизация размера образа + - Многоэтапная сборка + - Безопасность и best practices + +2. **Docker Compose** + - Настройка микросервисной архитектуры + - Сети и volumes + - Health checks + +3. **Kubernetes развертывание** + - Создание манифестов + - Настройка Service и Ingress + - ConfigMap и Secret + +4. **Terraform инфраструктура** + - Создание модулей + - Управление состоянием + - Переменные и outputs + +5. **CI/CD Pipeline** + - GitHub Actions + - Автоматическое развертывание + - GitOps с ArgoCD + +6. **Мониторинг** + - Настройка Prometheus + - Создание дашбордов Grafana + - Централизованное логирование + +## Дополнительные ресурсы + +- [Docker Documentation](https://docs.docker.com/) - официальная документация Docker +- [Kubernetes Documentation](https://kubernetes.io/docs/) - официальная документация Kubernetes +- [Terraform Documentation](https://terraform.io/docs/) - документация Terraform +- [Prometheus Documentation](https://prometheus.io/docs/) - документация Prometheus +- [Grafana Documentation](https://grafana.com/docs/) - документация Grafana + +## Критерии оценки + +- Понимание принципов контейнеризации (20%) +- Навыки работы с Docker (25%) +- Опыт работы с Kubernetes (25%) +- Знание Infrastructure as Code (15%) +- Навыки настройки CI/CD (10%) +- Понимание мониторинга (5%) diff --git a/modules/module3/sections/01-containerization-basics.md b/modules/module3/sections/01-containerization-basics.md new file mode 100644 index 0000000..ebdfba4 --- /dev/null +++ b/modules/module3/sections/01-containerization-basics.md @@ -0,0 +1,60 @@ +# Основы контейнеризации + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 3](../README.md) + +--- + +## Что такое контейнеризация + +**Контейнеризация** — это технология упаковки приложений и их зависимостей в легковесные, переносимые контейнеры. + +## Преимущества контейнеризации + +### 1. Портабельность +- Одинаковая среда на всех платформах +- "Работает на моей машине" больше не проблема +- Легкая миграция между средами + +### 2. Изоляция +- Приложения изолированы друг от друга +- Контроль ресурсов +- Безопасность + +### 3. Масштабируемость +- Быстрое создание и удаление контейнеров +- Горизонтальное масштабирование +- Эффективное использование ресурсов + +### 4. Консистентность +- Одинаковая среда разработки, тестирования и продакшена +- Предсказуемое поведение +- Упрощение развертывания + +## Контейнеры vs Виртуальные машины + +| Аспект | Контейнеры | Виртуальные машины | +|--------|------------|-------------------| +| Изоляция | Процессная | Аппаратная | +| Размер | МБ | ГБ | +| Запуск | Секунды | Минуты | +| Ресурсы | Общие | Выделенные | +| Безопасность | Процессная | Аппаратная | + +## Компоненты контейнеризации + +### 1. Container Runtime +- **containerd** — стандартный runtime +- **runc** — низкоуровневый runtime +- **CRI-O** — Kubernetes-специфичный runtime + +### 2. Container Image +- Неизменяемый образ приложения +- Слоистая структура +- Версионирование + +### 3. Container Registry +- Хранение образов +- Версионирование +- Безопасность diff --git a/modules/module3/sections/02-docker.md b/modules/module3/sections/02-docker.md new file mode 100644 index 0000000..2877a01 --- /dev/null +++ b/modules/module3/sections/02-docker.md @@ -0,0 +1,369 @@ +# Docker + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 3](../README.md) + +--- + +## Основы Docker + +**Docker** — платформа для разработки, доставки и запуска приложений в контейнерах. + +## Архитектура Docker + +``` +┌─────────────────────────────────────┐ +│ Docker Client │ +├─────────────────────────────────────┤ +│ Docker Daemon │ +│ ┌─────────────┐ ┌─────────────────┐ │ +│ │ Images │ │ Containers │ │ +│ └─────────────┘ └─────────────────┘ │ +├─────────────────────────────────────┤ +│ Container Runtime │ +└─────────────────────────────────────┘ +``` + +## Dockerfile + +**Dockerfile** — текстовый файл с инструкциями для создания образа. + +### Технический пример - Dockerfile для Spring Boot приложения: + +```dockerfile +# Многоэтапная сборка для оптимизации размера образа +# Этап 1: Сборка приложения +FROM maven:3.8.6-openjdk-17-slim AS build + +# Установка рабочей директории +WORKDIR /app + +# Копирование файлов pom.xml для кэширования зависимостей +COPY pom.xml . +COPY src ./src + +# Сборка приложения +RUN mvn clean package -DskipTests + +# Этап 2: Создание финального образа +FROM openjdk:17-jre-slim + +# Установка рабочей директории +WORKDIR /app + +# Создание пользователя для безопасности +RUN groupadd -r appuser && useradd -r -g appuser appuser + +# Копирование собранного JAR файла +COPY --from=build /app/target/*.jar app.jar + +# Изменение владельца файлов +RUN chown -R appuser:appuser /app +USER appuser + +# Открытие порта +EXPOSE 8080 + +# Переменные окружения +ENV JAVA_OPTS="-Xmx512m -Xms256m" +ENV SPRING_PROFILES_ACTIVE=production + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:8080/actuator/health || exit 1 + +# Запуск приложения +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"] +``` + +### Dockerfile для микросервиса на Node.js: + +```dockerfile +# Базовый образ Node.js +FROM node:18-alpine AS base + +# Установка рабочей директории +WORKDIR /app + +# Установка системных зависимостей +RUN apk add --no-cache \ + dumb-init \ + curl + +# Создание пользователя для безопасности +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nextjs -u 1001 + +# Этап установки зависимостей +FROM base AS deps +COPY package*.json ./ +RUN npm ci --only=production && npm cache clean --force + +# Этап сборки +FROM base AS builder +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Финальный этап +FROM base AS runner +ENV NODE_ENV=production + +# Копирование зависимостей +COPY --from=deps /app/node_modules ./node_modules +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/public ./public + +# Копирование конфигурационных файлов +COPY --from=builder /app/package.json ./package.json + +# Изменение владельца файлов +RUN chown -R nextjs:nodejs /app +USER nextjs + +# Открытие порта +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000/health || exit 1 + +# Запуск приложения +ENTRYPOINT ["dumb-init", "--"] +CMD ["node", "dist/index.js"] +``` + +## Docker Compose + +**Docker Compose** — инструмент для определения и запуска многоконтейнерных приложений. + +### Docker Compose для микросервисной архитектуры: + +```yaml +version: '3.8' + +services: + # API Gateway + api-gateway: + build: + context: ./api-gateway + dockerfile: Dockerfile + ports: + - "8080:8080" + environment: + - SPRING_PROFILES_ACTIVE=docker + - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka:8761/eureka/ + depends_on: + - eureka + - user-service + - order-service + - payment-service + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Service Discovery + eureka: + image: steeltoeoss/eureka-server:latest + ports: + - "8761:8761" + environment: + - EUREKA_CLIENT_REGISTER_WITH_EUREKA=false + - EUREKA_CLIENT_FETCH_REGISTRY=false + networks: + - microservices-network + + # User Service + user-service: + build: + context: ./user-service + dockerfile: Dockerfile + ports: + - "8081:8080" + environment: + - SPRING_PROFILES_ACTIVE=docker + - SPRING_DATASOURCE_URL=jdbc:postgresql://user-db:5432/userdb + - SPRING_DATASOURCE_USERNAME=user + - SPRING_DATASOURCE_PASSWORD=password + - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka:8761/eureka/ + depends_on: + - user-db + - eureka + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Databases + user-db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=userdb + - POSTGRES_USER=user + - POSTGRES_PASSWORD=password + volumes: + - user_data:/var/lib/postgresql/data + - ./init-scripts/user-db-init.sql:/docker-entrypoint-initdb.d/init.sql + networks: + - microservices-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U user -d userdb"] + interval: 10s + timeout: 5s + retries: 5 + + # Message Queue + rabbitmq: + image: rabbitmq:3.11-management-alpine + ports: + - "5672:5672" + - "15672:15672" + environment: + - RABBITMQ_DEFAULT_USER=admin + - RABBITMQ_DEFAULT_PASS=admin + volumes: + - rabbitmq_data:/var/lib/rabbitmq + networks: + - microservices-network + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "ping"] + interval: 30s + timeout: 10s + retries: 3 + + # Monitoring + prometheus: + image: prom/prometheus:latest + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--storage.tsdb.retention.time=200h' + - '--web.enable-lifecycle' + networks: + - microservices-network + + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards + - ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources + networks: + - microservices-network + +volumes: + user_data: + rabbitmq_data: + prometheus_data: + grafana_data: + +networks: + microservices-network: + driver: bridge +``` + +## Команды Docker + +### Основные команды: +```bash +# Сборка образа +docker build -t myapp:latest . + +# Запуск контейнера +docker run -p 3000:3000 myapp:latest + +# Просмотр запущенных контейнеров +docker ps + +# Просмотр всех контейнеров +docker ps -a + +# Остановка контейнера +docker stop + +# Удаление контейнера +docker rm + +# Просмотр образов +docker images + +# Удаление образа +docker rmi +``` + +### Продвинутые команды: +```bash +# Запуск в фоновом режиме +docker run -d -p 3000:3000 myapp:latest + +# Подключение к контейнеру +docker exec -it /bin/bash + +# Просмотр логов +docker logs + +# Мониторинг ресурсов +docker stats +``` + +## Лучшие практики Docker + +### 1. Оптимизация размера образа +```dockerfile +# Используйте многоэтапную сборку +FROM node:18-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:18-alpine AS production +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only=production +COPY --from=builder /app/dist ./dist +CMD ["npm", "start"] +``` + +### 2. Безопасность +```dockerfile +# Создайте пользователя без root прав +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nextjs -u 1001 +USER nextjs +``` + +### 3. Кэширование слоев +```dockerfile +# Копируйте package.json перед исходным кодом +COPY package*.json ./ +RUN npm ci +COPY . . +``` + +### 4. Health Checks +```dockerfile +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000/health || exit 1 +``` diff --git a/modules/module3/sections/03-kubernetes.md b/modules/module3/sections/03-kubernetes.md new file mode 100644 index 0000000..a3997a8 --- /dev/null +++ b/modules/module3/sections/03-kubernetes.md @@ -0,0 +1,486 @@ +# Kubernetes + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 3](../README.md) + +--- + +## Что такое Kubernetes + +**Kubernetes (K8s)** — система оркестрации контейнеров для автоматизации развертывания, масштабирования и управления приложениями. + +## Архитектура Kubernetes + +``` +┌─────────────────────────────────────────────────────────┐ +│ Control Plane │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ +│ │ API Server │ │ Scheduler │ │ Controller Mgr │ │ +│ └─────────────┘ └─────────────┘ └─────────────────┘ │ +│ ┌─────────────┐ ┌─────────────┐ │ +│ │ etcd │ │ Cloud Ctlr │ │ +│ └─────────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────┐ +│ Worker Nodes │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ +│ │ kubelet │ │ kube-proxy │ │ Container Runtime│ │ +│ └─────────────┘ └─────────────┘ └─────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +## Основные компоненты + +### Плоскость управления: +- **API Server** — центральный компонент управления +- **etcd** — хранилище конфигурации +- **Scheduler** — планировщик подов +- **Controller Manager** — контроллеры системы + +### Рабочие узлы: +- **kubelet** — агент на каждом узле +- **kube-proxy** — сетевой прокси +- **Container Runtime** — Docker, containerd, CRI-O + +## Объекты Kubernetes + +### 1. Pod +**Pod** — наименьшая единица развертывания в Kubernetes. + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: user-service-pod + labels: + app: user-service + version: v1.0.0 + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/actuator/prometheus" +spec: + containers: + - name: user-service + image: user-service:latest + ports: + - containerPort: 8080 + name: http + protocol: TCP + env: + - name: SPRING_PROFILES_ACTIVE + value: "kubernetes" + - name: SPRING_DATASOURCE_URL + valueFrom: + secretKeyRef: + name: user-db-secret + key: url + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + volumeMounts: + - name: config-volume + mountPath: /app/config + readOnly: true + volumes: + - name: config-volume + configMap: + name: user-service-config + restartPolicy: Always + terminationGracePeriodSeconds: 30 +``` + +### 2. Deployment +**Deployment** — управляет репликами подов. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service-deployment + labels: + app: user-service + version: v1.0.0 +spec: + replicas: 3 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + version: v1.0.0 + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/actuator/prometheus" + spec: + containers: + - name: user-service + image: user-service:v1.0.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + name: http + protocol: TCP + env: + - name: SPRING_PROFILES_ACTIVE + value: "kubernetes" + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + volumeMounts: + - name: config-volume + mountPath: /app/config + readOnly: true + - name: logs-volume + mountPath: /app/logs + volumes: + - name: config-volume + configMap: + name: user-service-config + - name: logs-volume + emptyDir: {} + restartPolicy: Always + terminationGracePeriodSeconds: 30 + dnsPolicy: ClusterFirst + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 2000 +``` + +### 3. Service +**Service** — обеспечивает сетевой доступ к подам. + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: user-service + labels: + app: user-service + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/actuator/prometheus" +spec: + selector: + app: user-service + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 8080 + - name: metrics + protocol: TCP + port: 9090 + targetPort: 9090 + type: ClusterIP + sessionAffinity: None +``` + +### 4. Ingress +**Ingress** — управляет внешним доступом к сервисам. + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: microservices-ingress + annotations: + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" +spec: + tls: + - hosts: + - api.example.com + secretName: api-tls-secret + rules: + - host: api.example.com + http: + paths: + - path: /api/users + pathType: Prefix + backend: + service: + name: user-service + port: + number: 80 + - path: /api/orders + pathType: Prefix + backend: + service: + name: order-service + port: + number: 80 + - path: /api/payments + pathType: Prefix + backend: + service: + name: payment-service + port: + number: 80 +``` + +### 5. ConfigMap +**ConfigMap** — хранит конфигурационные данные. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: user-service-config + labels: + app: user-service +data: + application.yml: | + server: + port: 8080 + spring: + application: + name: user-service + datasource: + driver-class-name: org.postgresql.Driver + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 20000 + idle-timeout: 300000 + max-lifetime: 1200000 + jpa: + hibernate: + ddl-auto: validate + show-sql: false + properties: + hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect + format_sql: true + rabbitmq: + host: rabbitmq-service + port: 5672 + username: admin + password: admin + management: + endpoints: + web: + exposure: + include: health,info,prometheus,metrics + endpoint: + health: + show-details: always + metrics: + export: + prometheus: + enabled: true +``` + +### 6. Secret +**Secret** — хранит чувствительные данные. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: user-db-secret + labels: + app: user-service +type: Opaque +data: + url: cG9zdGdyZXNxbDovL3VzZXItZGItc2VydmljZToyNTQzMi91c2VyZGI= # base64 encoded + username: dXNlcg== # base64 encoded + password: cGFzc3dvcmQ= # base64 encoded +``` + +### 7. HorizontalPodAutoscaler +**HPA** — автоматическое масштабирование подов. + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: user-service-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: user-service-deployment + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 10 + periodSeconds: 60 + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Percent + value: 50 + periodSeconds: 60 +``` + +## Команды Kubernetes + +### Основные команды: +```bash +# Применение манифеста +kubectl apply -f deployment.yaml + +# Просмотр подов +kubectl get pods + +# Просмотр сервисов +kubectl get services + +# Просмотр деплойментов +kubectl get deployments + +# Описание ресурса +kubectl describe pod + +# Логи пода +kubectl logs + +# Подключение к поду +kubectl exec -it -- /bin/bash + +# Масштабирование деплоймента +kubectl scale deployment my-deployment --replicas=5 +``` + +## Сетевая модель Kubernetes + +### 1. Cluster Networking +- Каждый под имеет уникальный IP +- Поды могут общаться друг с другом +- Сервисы обеспечивают стабильные IP + +### 2. Service Types +- **ClusterIP** — внутренний IP кластера +- **NodePort** — доступ через порт узла +- **LoadBalancer** — внешний балансировщик нагрузки +- **ExternalName** — внешний DNS + +## Хранилище в Kubernetes + +### 1. Volumes +```yaml +apiVersion: v1 +kind: Pod +spec: + containers: + - name: my-container + image: nginx + volumeMounts: + - name: my-volume + mountPath: /data + volumes: + - name: my-volume + persistentVolumeClaim: + claimName: my-pvc +``` + +### 2. PersistentVolume +```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: my-pv +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: fast-ssd +``` + +### 3. PersistentVolumeClaim +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: my-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: fast-ssd +``` diff --git a/modules/module3/sections/04-infrastructure-as-code.md b/modules/module3/sections/04-infrastructure-as-code.md new file mode 100644 index 0000000..ee7fa2e --- /dev/null +++ b/modules/module3/sections/04-infrastructure-as-code.md @@ -0,0 +1,177 @@ +# Infrastructure as Code + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 3](../README.md) + +--- + +## Что такое IaC + +**Infrastructure as Code (IaC)** — подход к управлению инфраструктурой через код. + +## Преимущества IaC + +### 1. Версионирование +- История изменений инфраструктуры +- Откат к предыдущим версиям +- Code review для инфраструктуры + +### 2. Повторяемость +- Одинаковая инфраструктура в разных средах +- Автоматизация развертывания +- Устранение дрейфа конфигурации + +### 3. Документация +- Код как документация +- Понятная структура инфраструктуры +- Автоматическая генерация диаграмм + +## Terraform + +**Terraform** — инструмент для управления инфраструктурой через код. + +### Основные концепции: + +#### 1. Provider +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.0" + } + } +} + +provider "aws" { + region = "us-west-2" +} +``` + +#### 2. Resource +```hcl +resource "aws_instance" "web" { + ami = "ami-0c02fb55956c7d316" + instance_type = "t2.micro" + + tags = { + Name = "HelloWorld" + } +} +``` + +#### 3. Data Source +```hcl +data "aws_ami" "ubuntu" { + most_recent = true + owners = ["099720109477"] + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] + } +} +``` + +#### 4. Variables +```hcl +variable "instance_type" { + description = "EC2 instance type" + type = string + default = "t2.micro" +} + +variable "environment" { + description = "Environment name" + type = string + validation { + condition = contains(["dev", "staging", "prod"], var.environment) + error_message = "Environment must be dev, staging, or prod." + } +} +``` + +#### 5. Outputs +```hcl +output "instance_ip" { + description = "Public IP of the instance" + value = aws_instance.web.public_ip +} +``` + +### Команды Terraform + +```bash +# Инициализация +terraform init + +# Планирование изменений +terraform plan + +# Применение изменений +terraform apply + +# Уничтожение инфраструктуры +terraform destroy + +# Просмотр состояния +terraform show + +# Форматирование кода +terraform fmt + +# Валидация конфигурации +terraform validate +``` + +### Модули Terraform + +```hcl +# main.tf +module "vpc" { + source = "./modules/vpc" + + environment = var.environment + cidr_block = "10.0.0.0/16" +} + +module "ec2" { + source = "./modules/ec2" + + vpc_id = module.vpc.vpc_id + subnet_id = module.vpc.public_subnet_id +} +``` + +## Ansible + +**Ansible** — инструмент автоматизации конфигурации и развертывания. + +### Пример Playbook: +```yaml +--- +- name: Deploy web application + hosts: web_servers + become: yes + vars: + app_name: myapp + app_port: 3000 + + tasks: + - name: Install Node.js + package: + name: nodejs + state: present + + - name: Install npm dependencies + npm: + path: /opt/{{ app_name }} + state: present + + - name: Start application + systemd: + name: "{{ app_name }}" + state: started + enabled: yes +``` diff --git a/modules/module3/sections/05-cicd-containers.md b/modules/module3/sections/05-cicd-containers.md new file mode 100644 index 0000000..038daba --- /dev/null +++ b/modules/module3/sections/05-cicd-containers.md @@ -0,0 +1,61 @@ +# CI/CD с контейнерами + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 3](../README.md) + +--- + +## CI/CD Pipeline + +```yaml +# .github/workflows/deploy.yml +name: Deploy to Kubernetes + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Build Docker image + run: docker build -t ${{ secrets.REGISTRY }}/myapp:${{ github.sha }} . + + - name: Push to registry + run: docker push ${{ secrets.REGISTRY }}/myapp:${{ github.sha }} + + - name: Deploy to Kubernetes + run: | + kubectl set image deployment/myapp myapp=${{ secrets.REGISTRY }}/myapp:${{ github.sha }} + kubectl rollout status deployment/myapp +``` + +## GitOps + +**GitOps** — подход к управлению инфраструктурой через Git. + +### Пример ArgoCD: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: myapp + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/myorg/myapp + targetRevision: HEAD + path: k8s + destination: + server: https://kubernetes.default.svc + namespace: default + syncPolicy: + automated: + prune: true + selfHeal: true +``` diff --git a/modules/module3/sections/06-container-monitoring.md b/modules/module3/sections/06-container-monitoring.md new file mode 100644 index 0000000..631db4c --- /dev/null +++ b/modules/module3/sections/06-container-monitoring.md @@ -0,0 +1,68 @@ +# Мониторинг контейнеров + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 3](../README.md) + +--- + +## Prometheus + Grafana + +### Конфигурация Prometheus: +```yaml +# prometheus.yml +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'kubernetes-pods' + kubernetes_sd_configs: + - role: pod +``` + +### Дашборд Grafana: +```json +{ + "dashboard": { + "title": "Kubernetes Cluster", + "panels": [ + { + "title": "CPU Usage", + "type": "graph", + "targets": [ + { + "expr": "rate(container_cpu_usage_seconds_total[5m])" + } + ] + } + ] + } +} +``` + +## ELK Stack + +### Elasticsearch + Logstash + Kibana: +```yaml +# docker-compose.yml +version: '3.8' +services: + elasticsearch: + image: elasticsearch:7.14.0 + environment: + - discovery.type=single-node + ports: + - "9200:9200" + + logstash: + image: logstash:7.14.0 + volumes: + - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf + ports: + - "5044:5044" + + kibana: + image: kibana:7.14.0 + ports: + - "5601:5601" +``` diff --git a/modules/module3/sections/08-practical-tasks.md b/modules/module3/sections/08-practical-tasks.md new file mode 100644 index 0000000..1359fd6 --- /dev/null +++ b/modules/module3/sections/08-practical-tasks.md @@ -0,0 +1,25 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 3](../README.md) + +--- + +### Задание 1: Docker +1. Создайте Dockerfile для Node.js приложения +2. Соберите образ и запустите контейнер +3. Создайте docker-compose.yml для многоконтейнерного приложения + +### Задание 2: Kubernetes +1. Создайте Deployment для веб-приложения +2. Создайте Service для доступа к приложению +3. Настройте ConfigMap и Secret +4. Разверните приложение в кластере + +### Задание 3: Terraform +1. Создайте инфраструктуру в AWS с помощью Terraform +2. Используйте модули для организации кода +3. Настройте переменные и outputs + +--- \ No newline at end of file diff --git a/modules/module3/theory.md b/modules/module3/theory.md new file mode 100644 index 0000000..d8953a6 --- /dev/null +++ b/modules/module3/theory.md @@ -0,0 +1,2008 @@ +# Модуль 3: Контейнеризация и оркестрация + +## Содержание модуля +1. [Основы контейнеризации](#основы-контейнеризации) +2. [Docker](#docker) +3. [Kubernetes](#kubernetes) +4. [Infrastructure as Code](#infrastructure-as-code) +5. [CI/CD с контейнерами](#cicd-с-контейнерами) +6. [Мониторинг контейнеров](#мониторинг-контейнеров) + +--- + +## Основы контейнеризации + +### Что такое контейнеризация + +**Контейнеризация** — это технология упаковки приложений и их зависимостей в легковесные, переносимые контейнеры. + +### Преимущества контейнеризации + +#### 1. Портабельность +- Одинаковая среда на всех платформах +- "Работает на моей машине" больше не проблема +- Легкая миграция между средами + +#### 2. Изоляция +- Приложения изолированы друг от друга +- Контроль ресурсов +- Безопасность + +#### 3. Масштабируемость +- Быстрое создание и удаление контейнеров +- Горизонтальное масштабирование +- Эффективное использование ресурсов + +#### 4. Консистентность +- Одинаковая среда разработки, тестирования и продакшена +- Предсказуемое поведение +- Упрощение развертывания + +### Контейнеры vs Виртуальные машины + +| Аспект | Контейнеры | Виртуальные машины | +|--------|------------|-------------------| +| Изоляция | Процессная | Аппаратная | +| Размер | МБ | ГБ | +| Запуск | Секунды | Минуты | +| Ресурсы | Общие | Выделенные | +| Безопасность | Процессная | Аппаратная | + +### Компоненты контейнеризации + +#### 1. Container Runtime +- **containerd** — стандартный runtime +- **runc** — низкоуровневый runtime +- **CRI-O** — Kubernetes-специфичный runtime + +#### 2. Container Image +- Неизменяемый образ приложения +- Слоистая структура +- Версионирование + +#### 3. Container Registry +- Хранение образов +- Версионирование +- Безопасность + +--- + +## Docker + +### Основы Docker + +**Docker** — платформа для разработки, доставки и запуска приложений в контейнерах. + +### Архитектура Docker + +``` +┌─────────────────────────────────────┐ +│ Docker Client │ +├─────────────────────────────────────┤ +│ Docker Daemon │ +│ ┌─────────────┐ ┌─────────────────┐ │ +│ │ Images │ │ Containers │ │ +│ └─────────────┘ └─────────────────┘ │ +├─────────────────────────────────────┤ +│ Container Runtime │ +└─────────────────────────────────────┘ +``` + +### Dockerfile + +**Dockerfile** — текстовый файл с инструкциями для создания образа. + +**Технический пример - Dockerfile для Spring Boot приложения:** + +```dockerfile +# Многоэтапная сборка для оптимизации размера образа +# Этап 1: Сборка приложения +FROM maven:3.8.6-openjdk-17-slim AS build + +# Установка рабочей директории +WORKDIR /app + +# Копирование файлов pom.xml для кэширования зависимостей +COPY pom.xml . +COPY src ./src + +# Сборка приложения +RUN mvn clean package -DskipTests + +# Этап 2: Создание финального образа +FROM openjdk:17-jre-slim + +# Установка рабочей директории +WORKDIR /app + +# Создание пользователя для безопасности +RUN groupadd -r appuser && useradd -r -g appuser appuser + +# Копирование собранного JAR файла +COPY --from=build /app/target/*.jar app.jar + +# Изменение владельца файлов +RUN chown -R appuser:appuser /app +USER appuser + +# Открытие порта +EXPOSE 8080 + +# Переменные окружения +ENV JAVA_OPTS="-Xmx512m -Xms256m" +ENV SPRING_PROFILES_ACTIVE=production + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:8080/actuator/health || exit 1 + +# Запуск приложения +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"] +``` + +**Dockerfile для микросервиса на Node.js:** + +```dockerfile +# Базовый образ Node.js +FROM node:18-alpine AS base + +# Установка рабочей директории +WORKDIR /app + +# Установка системных зависимостей +RUN apk add --no-cache \ + dumb-init \ + curl + +# Создание пользователя для безопасности +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nextjs -u 1001 + +# Этап установки зависимостей +FROM base AS deps +COPY package*.json ./ +RUN npm ci --only=production && npm cache clean --force + +# Этап сборки +FROM base AS builder +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Финальный этап +FROM base AS runner +ENV NODE_ENV=production + +# Копирование зависимостей +COPY --from=deps /app/node_modules ./node_modules +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/public ./public + +# Копирование конфигурационных файлов +COPY --from=builder /app/package.json ./package.json + +# Изменение владельца файлов +RUN chown -R nextjs:nodejs /app +USER nextjs + +# Открытие порта +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000/health || exit 1 + +# Запуск приложения +ENTRYPOINT ["dumb-init", "--"] +CMD ["node", "dist/index.js"] +``` + +**Дополнительные Node.js Dockerfile примеры:** + +**Dockerfile для Express.js микросервиса с TypeScript:** +```dockerfile +# Многоэтапная сборка для TypeScript приложения +# Этап 1: Сборка приложения +FROM node:18-alpine AS build + +WORKDIR /app + +# Копирование package.json и package-lock.json +COPY package*.json ./ +COPY tsconfig.json ./ + +# Установка всех зависимостей (включая dev) +RUN npm ci + +# Копирование исходного кода +COPY src ./src + +# Компиляция TypeScript +RUN npm run build + +# Этап 2: Продакшн образ +FROM node:18-alpine AS production + +WORKDIR /app + +# Копирование package.json для установки только продакшн зависимостей +COPY package*.json ./ + +# Установка только продакшн зависимостей +RUN npm ci --only=production && npm cache clean --force + +# Копирование собранного приложения +COPY --from=build /app/dist ./dist + +# Создание пользователя для безопасности +RUN addgroup -g 1001 -S nodejs && \ + adduser -S express -u 1001 + +# Изменение владельца файлов +RUN chown -R express:nodejs /app +USER express + +# Открытие порта +EXPOSE 3000 + +# Переменные окружения +ENV NODE_ENV=production +ENV PORT=3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000/health || exit 1 + +# Запуск приложения +CMD ["node", "dist/index.js"] +``` + +**Dockerfile для NestJS микросервиса:** +```dockerfile +# Многоэтапная сборка для NestJS приложения +FROM node:18-alpine AS build + +WORKDIR /app + +# Копирование файлов конфигурации +COPY package*.json ./ +COPY tsconfig*.json ./ +COPY nest-cli.json ./ + +# Установка зависимостей +RUN npm ci + +# Копирование исходного кода +COPY src ./src + +# Сборка приложения +RUN npm run build + +# Продакшн образ +FROM node:18-alpine AS production + +WORKDIR /app + +# Копирование package.json +COPY package*.json ./ + +# Установка только продакшн зависимостей +RUN npm ci --only=production && npm cache clean --force + +# Копирование собранного приложения +COPY --from=build /app/dist ./dist + +# Создание пользователя +RUN addgroup -g 1001 -S nestjs && \ + adduser -S nestjs -u 1001 + +# Изменение владельца +RUN chown -R nestjs:nestjs /app +USER nestjs + +# Открытие порта +EXPOSE 3000 + +# Переменные окружения +ENV NODE_ENV=production +ENV PORT=3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000/health || exit 1 + +# Запуск приложения +CMD ["node", "dist/main.js"] +``` + +**Dockerfile для Fastify микросервиса:** +```dockerfile +# Оптимизированный Dockerfile для Fastify +FROM node:18-alpine AS base + +# Установка необходимых пакетов +RUN apk add --no-cache dumb-init + +WORKDIR /app + +# Копирование package.json +COPY package*.json ./ + +# Установка зависимостей +RUN npm ci --only=production && npm cache clean --force + +# Копирование исходного кода +COPY . . + +# Создание пользователя +RUN addgroup -g 1001 -S fastify && \ + adduser -S fastify -u 1001 + +# Изменение владельца +RUN chown -R fastify:fastify /app +USER fastify + +# Открытие порта +EXPOSE 3000 + +# Переменные окружения +ENV NODE_ENV=production +ENV PORT=3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000/health || exit 1 + +# Использование dumb-init для правильной обработки сигналов +ENTRYPOINT ["dumb-init", "--"] +CMD ["node", "server.js"] +``` + +**Docker Compose для микросервисной архитектуры:** + +```yaml +version: '3.8' + +services: + # API Gateway + api-gateway: + build: + context: ./api-gateway + dockerfile: Dockerfile + ports: + - "8080:8080" + environment: + - SPRING_PROFILES_ACTIVE=docker + - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka:8761/eureka/ + depends_on: + - eureka + - user-service + - order-service + - payment-service + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Service Discovery + eureka: + image: steeltoeoss/eureka-server:latest + ports: + - "8761:8761" + environment: + - EUREKA_CLIENT_REGISTER_WITH_EUREKA=false + - EUREKA_CLIENT_FETCH_REGISTRY=false + networks: + - microservices-network + + # User Service + user-service: + build: + context: ./user-service + dockerfile: Dockerfile + ports: + - "8081:8080" + environment: + - SPRING_PROFILES_ACTIVE=docker + - SPRING_DATASOURCE_URL=jdbc:postgresql://user-db:5432/userdb + - SPRING_DATASOURCE_USERNAME=user + - SPRING_DATASOURCE_PASSWORD=password + - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka:8761/eureka/ + depends_on: + - user-db + - eureka + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Order Service + order-service: + build: + context: ./order-service + dockerfile: Dockerfile + ports: + - "8082:8080" + environment: + - SPRING_PROFILES_ACTIVE=docker + - SPRING_DATASOURCE_URL=jdbc:postgresql://order-db:5432/orderdb + - SPRING_DATASOURCE_USERNAME=order + - SPRING_DATASOURCE_PASSWORD=password + - USER_SERVICE_URL=http://user-service:8080 + - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka:8761/eureka/ + depends_on: + - order-db + - user-service + - eureka + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Payment Service + payment-service: + build: + context: ./payment-service + dockerfile: Dockerfile + ports: + - "8083:8080" + environment: + - SPRING_PROFILES_ACTIVE=docker + - SPRING_DATASOURCE_URL=jdbc:postgresql://payment-db:5432/paymentdb + - SPRING_DATASOURCE_USERNAME=payment + - SPRING_DATASOURCE_PASSWORD=password + - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka:8761/eureka/ + depends_on: + - payment-db + - eureka + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Databases + user-db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=userdb + - POSTGRES_USER=user + - POSTGRES_PASSWORD=password + volumes: + - user_data:/var/lib/postgresql/data + - ./init-scripts/user-db-init.sql:/docker-entrypoint-initdb.d/init.sql + networks: + - microservices-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U user -d userdb"] + interval: 10s + timeout: 5s + retries: 5 + + order-db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=orderdb + - POSTGRES_USER=order + - POSTGRES_PASSWORD=password + volumes: + - order_data:/var/lib/postgresql/data + - ./init-scripts/order-db-init.sql:/docker-entrypoint-initdb.d/init.sql + networks: + - microservices-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U order -d orderdb"] + interval: 10s + timeout: 5s + retries: 5 + + payment-db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=paymentdb + - POSTGRES_USER=payment + - POSTGRES_PASSWORD=password + volumes: + - payment_data:/var/lib/postgresql/data + - ./init-scripts/payment-db-init.sql:/docker-entrypoint-initdb.d/init.sql + networks: + - microservices-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U payment -d paymentdb"] + interval: 10s + timeout: 5s + retries: 5 + + # Message Queue + rabbitmq: + image: rabbitmq:3.11-management-alpine + ports: + - "5672:5672" + - "15672:15672" + environment: + - RABBITMQ_DEFAULT_USER=admin + - RABBITMQ_DEFAULT_PASS=admin + volumes: + - rabbitmq_data:/var/lib/rabbitmq + networks: + - microservices-network + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "ping"] + interval: 30s + timeout: 10s + retries: 3 + + # Monitoring + prometheus: + image: prom/prometheus:latest + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--storage.tsdb.retention.time=200h' + - '--web.enable-lifecycle' + networks: + - microservices-network + + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards + - ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources + networks: + - microservices-network + +volumes: + user_data: + order_data: + payment_data: + rabbitmq_data: + prometheus_data: + grafana_data: + +networks: + microservices-network: + driver: bridge +``` + +**Docker Compose для Node.js микросервисной архитектуры:** + +```yaml +version: '3.8' + +services: + # API Gateway (Express.js) + api-gateway: + build: + context: ./api-gateway + dockerfile: Dockerfile + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - PORT=3000 + - USER_SERVICE_URL=http://user-service:3001 + - ORDER_SERVICE_URL=http://order-service:3002 + - PAYMENT_SERVICE_URL=http://payment-service:3003 + depends_on: + - user-service + - order-service + - payment-service + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + + # User Service (Express.js + TypeScript) + user-service: + build: + context: ./user-service + dockerfile: Dockerfile + ports: + - "3001:3001" + environment: + - NODE_ENV=production + - PORT=3001 + - DATABASE_URL=postgresql://user:password@user-db:5432/userdb + - REDIS_URL=redis://redis:6379 + depends_on: + - user-db + - redis + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3001/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Order Service (NestJS) + order-service: + build: + context: ./order-service + dockerfile: Dockerfile + ports: + - "3002:3002" + environment: + - NODE_ENV=production + - PORT=3002 + - DATABASE_URL=postgresql://order:password@order-db:5432/orderdb + - PAYMENT_SERVICE_URL=http://payment-service:3003 + - RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672 + depends_on: + - order-db + - rabbitmq + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3002/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Payment Service (Fastify) + payment-service: + build: + context: ./payment-service + dockerfile: Dockerfile + ports: + - "3003:3003" + environment: + - NODE_ENV=production + - PORT=3003 + - DATABASE_URL=postgresql://payment:password@payment-db:5432/paymentdb + - PAYMENT_GATEWAY_URL=https://api.payment-gateway.com + - PAYMENT_GATEWAY_KEY=${PAYMENT_GATEWAY_KEY} + depends_on: + - payment-db + networks: + - microservices-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3003/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Databases + user-db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=userdb + - POSTGRES_USER=user + - POSTGRES_PASSWORD=password + volumes: + - user_data:/var/lib/postgresql/data + networks: + - microservices-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U user -d userdb"] + interval: 10s + timeout: 5s + retries: 5 + + order-db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=orderdb + - POSTGRES_USER=order + - POSTGRES_PASSWORD=password + volumes: + - order_data:/var/lib/postgresql/data + networks: + - microservices-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U order -d orderdb"] + interval: 10s + timeout: 5s + retries: 5 + + payment-db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=paymentdb + - POSTGRES_USER=payment + - POSTGRES_PASSWORD=password + volumes: + - payment_data:/var/lib/postgresql/data + networks: + - microservices-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U payment -d paymentdb"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis for caching + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + networks: + - microservices-network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 30s + timeout: 10s + retries: 3 + + # RabbitMQ for messaging + rabbitmq: + image: rabbitmq:3-management-alpine + ports: + - "5672:5672" + - "15672:15672" + environment: + - RABBITMQ_DEFAULT_USER=guest + - RABBITMQ_DEFAULT_PASS=guest + volumes: + - rabbitmq_data:/var/lib/rabbitmq + networks: + - microservices-network + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "ping"] + interval: 30s + timeout: 10s + retries: 3 + + # Monitoring + prometheus: + image: prom/prometheus:latest + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + networks: + - microservices-network + + grafana: + image: grafana/grafana:latest + ports: + - "3001:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + networks: + - microservices-network + +volumes: + user_data: + order_data: + payment_data: + redis_data: + rabbitmq_data: + grafana_data: + +networks: + microservices-network: + driver: bridge +``` + +**Docker команды для управления контейнерами:** + +```bash +# Сборка образа +docker build -t user-service:latest ./user-service + +# Запуск контейнера +docker run -d --name user-service -p 8081:8080 \ + -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/userdb \ + user-service:latest + +# Просмотр логов +docker logs -f user-service + +# Выполнение команд в контейнере +docker exec -it user-service bash + +# Мониторинг ресурсов +docker stats user-service + +# Остановка и удаление +docker stop user-service +docker rm user-service + +# Очистка неиспользуемых образов +docker system prune -a + +# Запуск с Docker Compose +docker-compose up -d +docker-compose down +docker-compose logs -f user-service +``` + +# Копирование файлов зависимостей +COPY package*.json ./ + +# Установка зависимостей +RUN npm ci --only=production + +# Копирование исходного кода +COPY . . + +# Открытие порта +EXPOSE 3000 + +# Команда запуска +CMD ["npm", "start"] +``` + +### Команды Docker + +#### Основные команды: +```bash +# Сборка образа +docker build -t myapp:latest . + +# Запуск контейнера +docker run -p 3000:3000 myapp:latest + +# Просмотр запущенных контейнеров +docker ps + +# Просмотр всех контейнеров +docker ps -a + +# Остановка контейнера +docker stop + +# Удаление контейнера +docker rm + +# Просмотр образов +docker images + +# Удаление образа +docker rmi +``` + +#### Продвинутые команды: +```bash +# Запуск в фоновом режиме +docker run -d -p 3000:3000 myapp:latest + +# Подключение к контейнеру +docker exec -it /bin/bash + +# Просмотр логов +docker logs + +# Мониторинг ресурсов +docker stats +``` + +### Docker Compose + +**Docker Compose** — инструмент для определения и запуска многоконтейнерных приложений. + +```yaml +# docker-compose.yml +version: '3.8' + +services: + web: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + depends_on: + - db + - redis + + db: + image: postgres:13 + environment: + POSTGRES_DB: myapp + POSTGRES_USER: user + POSTGRES_PASSWORD: password + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + image: redis:6-alpine + ports: + - "6379:6379" + +volumes: + postgres_data: +``` + +### Лучшие практики Docker + +#### 1. Оптимизация размера образа +```dockerfile +# Используйте многоэтапную сборку +FROM node:18-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:18-alpine AS production +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only=production +COPY --from=builder /app/dist ./dist +CMD ["npm", "start"] +``` + +#### 2. Безопасность +```dockerfile +# Создайте пользователя без root прав +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nextjs -u 1001 +USER nextjs +``` + +#### 3. Кэширование слоев +```dockerfile +# Копируйте package.json перед исходным кодом +COPY package*.json ./ +RUN npm ci +COPY . . +``` + +#### 4. Health Checks +```dockerfile +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000/health || exit 1 +``` + +--- + +## Kubernetes + +### Что такое Kubernetes + +**Kubernetes (K8s)** — система оркестрации контейнеров для автоматизации развертывания, масштабирования и управления приложениями. + +### Архитектура Kubernetes + +``` +┌─────────────────────────────────────────────────────────┐ +│ Control Plane │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ +│ │ API Server │ │ Scheduler │ │ Controller Mgr │ │ +│ └─────────────┘ └─────────────┘ └─────────────────┘ │ +│ ┌─────────────┐ ┌─────────────┐ │ +│ │ etcd │ │ Cloud Ctlr │ │ +│ └─────────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────┐ +│ Worker Nodes │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ +│ │ kubelet │ │ kube-proxy │ │ Container Runtime│ │ +│ └─────────────┘ └─────────────┘ └─────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +### Основные компоненты + +#### Плоскость управления: +- **API Server** — центральный компонент управления +- **etcd** — хранилище конфигурации +- **Scheduler** — планировщик подов +- **Controller Manager** — контроллеры системы + +#### Рабочие узлы: +- **kubelet** — агент на каждом узле +- **kube-proxy** — сетевой прокси +- **Container Runtime** — Docker, containerd, CRI-O + +### Объекты Kubernetes + +#### 1. Pod +**Pod** — наименьшая единица развертывания в Kubernetes. + +**Технический пример - Pod для микросервиса:** + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: user-service-pod + labels: + app: user-service + version: v1.0.0 + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/actuator/prometheus" +spec: + containers: + - name: user-service + image: user-service:latest + ports: + - containerPort: 8080 + name: http + protocol: TCP + env: + - name: SPRING_PROFILES_ACTIVE + value: "kubernetes" + - name: SPRING_DATASOURCE_URL + valueFrom: + secretKeyRef: + name: user-db-secret + key: url + - name: SPRING_DATASOURCE_USERNAME + valueFrom: + secretKeyRef: + name: user-db-secret + key: username + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: user-db-secret + key: password + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + volumeMounts: + - name: config-volume + mountPath: /app/config + readOnly: true + volumes: + - name: config-volume + configMap: + name: user-service-config + restartPolicy: Always + terminationGracePeriodSeconds: 30 +``` + +#### 2. Deployment +**Deployment** — управляет репликами подов. + +**Технический пример - Deployment для микросервиса:** + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service-deployment + labels: + app: user-service + version: v1.0.0 +spec: + replicas: 3 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + version: v1.0.0 + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/actuator/prometheus" + spec: + containers: + - name: user-service + image: user-service:v1.0.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + name: http + protocol: TCP + env: + - name: SPRING_PROFILES_ACTIVE + value: "kubernetes" + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SPRING_DATASOURCE_URL + valueFrom: + secretKeyRef: + name: user-db-secret + key: url + - name: SPRING_DATASOURCE_USERNAME + valueFrom: + secretKeyRef: + name: user-db-secret + key: username + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: user-db-secret + key: password + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + volumeMounts: + - name: config-volume + mountPath: /app/config + readOnly: true + - name: logs-volume + mountPath: /app/logs + volumes: + - name: config-volume + configMap: + name: user-service-config + - name: logs-volume + emptyDir: {} + restartPolicy: Always + terminationGracePeriodSeconds: 30 + dnsPolicy: ClusterFirst + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 2000 +``` + +**HorizontalPodAutoscaler для автоматического масштабирования:** + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: user-service-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: user-service-deployment + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 10 + periodSeconds: 60 + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Percent + value: 50 + periodSeconds: 60 +``` + +#### 3. Service +**Service** — обеспечивает сетевой доступ к подам. + +**Технический пример - Service для микросервиса:** + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: user-service + labels: + app: user-service + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/actuator/prometheus" +spec: + selector: + app: user-service + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 8080 + - name: metrics + protocol: TCP + port: 9090 + targetPort: 9090 + type: ClusterIP + sessionAffinity: None +``` + +**Ingress для внешнего доступа:** + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: microservices-ingress + annotations: + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" +spec: + tls: + - hosts: + - api.example.com + secretName: api-tls-secret + rules: + - host: api.example.com + http: + paths: + - path: /api/users + pathType: Prefix + backend: + service: + name: user-service + port: + number: 80 + - path: /api/orders + pathType: Prefix + backend: + service: + name: order-service + port: + number: 80 + - path: /api/payments + pathType: Prefix + backend: + service: + name: payment-service + port: + number: 80 +``` + +**ConfigMap для конфигурации:** + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: user-service-config + labels: + app: user-service +data: + application.yml: | + server: + port: 8080 + spring: + application: + name: user-service + datasource: + driver-class-name: org.postgresql.Driver + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 20000 + idle-timeout: 300000 + max-lifetime: 1200000 + jpa: + hibernate: + ddl-auto: validate + show-sql: false + properties: + hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect + format_sql: true + rabbitmq: + host: rabbitmq-service + port: 5672 + username: admin + password: admin + management: + endpoints: + web: + exposure: + include: health,info,prometheus,metrics + endpoint: + health: + show-details: always + metrics: + export: + prometheus: + enabled: true + logback-spring.xml: | + + + + + + + /app/logs/user-service.log + + /app/logs/user-service.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + +``` + +**Secret для чувствительных данных:** + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: user-db-secret + labels: + app: user-service +type: Opaque +data: + url: cG9zdGdyZXNxbDovL3VzZXItZGItc2VydmljZToyNTQzMi91c2VyZGI= # base64 encoded + username: dXNlcg== # base64 encoded + password: cGFzc3dvcmQ= # base64 encoded +``` + +**PersistentVolumeClaim для базы данных:** + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: user-db-pvc + labels: + app: user-db +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: fast-ssd +``` + +**StatefulSet для базы данных:** + +```yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: user-db + labels: + app: user-db +spec: + serviceName: user-db + replicas: 1 + selector: + matchLabels: + app: user-db + template: + metadata: + labels: + app: user-db + spec: + containers: + - name: postgres + image: postgres:15-alpine + ports: + - containerPort: 5432 + name: postgres + env: + - name: POSTGRES_DB + value: userdb + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: user-db-secret + key: username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: user-db-secret + key: password + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + livenessProbe: + exec: + command: + - pg_isready + - -U + - user + - -d + - userdb + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + exec: + command: + - pg_isready + - -U + - user + - -d + - userdb + initialDelaySeconds: 5 + periodSeconds: 5 + volumeClaimTemplates: + - metadata: + name: postgres-storage + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 10Gi + storageClassName: fast-ssd +``` + +#### 4. ConfigMap +**ConfigMap** — хранит конфигурационные данные. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config +data: + database_url: "postgresql://localhost:5432/mydb" + api_key: "secret-key" +``` + +#### 5. Secret +**Secret** — хранит чувствительные данные. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: my-secret +type: Opaque +data: + username: YWRtaW4= + password: cGFzc3dvcmQ= +``` + +### Команды Kubernetes + +#### Основные команды: +```bash +# Применение манифеста +kubectl apply -f deployment.yaml + +# Просмотр подов +kubectl get pods + +# Просмотр сервисов +kubectl get services + +# Просмотр деплойментов +kubectl get deployments + +# Описание ресурса +kubectl describe pod + +# Логи пода +kubectl logs + +# Подключение к поду +kubectl exec -it -- /bin/bash + +# Масштабирование деплоймента +kubectl scale deployment my-deployment --replicas=5 +``` + +### Сетевая модель Kubernetes + +#### 1. Cluster Networking +- Каждый под имеет уникальный IP +- Поды могут общаться друг с другом +- Сервисы обеспечивают стабильные IP + +#### 2. Service Types +- **ClusterIP** — внутренний IP кластера +- **NodePort** — доступ через порт узла +- **LoadBalancer** — внешний балансировщик нагрузки +- **ExternalName** — внешний DNS + +#### 3. Ingress +**Ingress** — управляет внешним доступом к сервисам. + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: my-ingress +spec: + rules: + - host: myapp.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: my-service + port: + number: 80 +``` + +### Хранилище в Kubernetes + +#### 1. Volumes +```yaml +apiVersion: v1 +kind: Pod +spec: + containers: + - name: my-container + image: nginx + volumeMounts: + - name: my-volume + mountPath: /data + volumes: + - name: my-volume + persistentVolumeClaim: + claimName: my-pvc +``` + +#### 2. PersistentVolume +```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: my-pv +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: fast-ssd +``` + +#### 3. PersistentVolumeClaim +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: my-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: fast-ssd +``` + +--- + +## Infrastructure as Code + +### Что такое IaC + +**Infrastructure as Code (IaC)** — подход к управлению инфраструктурой через код. + +### Преимущества IaC + +#### 1. Версионирование +- История изменений инфраструктуры +- Откат к предыдущим версиям +- Code review для инфраструктуры + +#### 2. Повторяемость +- Одинаковая инфраструктура в разных средах +- Автоматизация развертывания +- Устранение дрейфа конфигурации + +#### 3. Документация +- Код как документация +- Понятная структура инфраструктуры +- Автоматическая генерация диаграмм + +### Terraform + +**Terraform** — инструмент для управления инфраструктурой через код. + +#### Основные концепции: + +##### 1. Provider +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.0" + } + } +} + +provider "aws" { + region = "us-west-2" +} +``` + +##### 2. Resource +```hcl +resource "aws_instance" "web" { + ami = "ami-0c02fb55956c7d316" + instance_type = "t2.micro" + + tags = { + Name = "HelloWorld" + } +} +``` + +##### 3. Data Source +```hcl +data "aws_ami" "ubuntu" { + most_recent = true + owners = ["099720109477"] + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] + } +} +``` + +##### 4. Variables +```hcl +variable "instance_type" { + description = "EC2 instance type" + type = string + default = "t2.micro" +} + +variable "environment" { + description = "Environment name" + type = string + validation { + condition = contains(["dev", "staging", "prod"], var.environment) + error_message = "Environment must be dev, staging, or prod." + } +} +``` + +##### 5. Outputs +```hcl +output "instance_ip" { + description = "Public IP of the instance" + value = aws_instance.web.public_ip +} +``` + +#### Команды Terraform + +```bash +# Инициализация +terraform init + +# Планирование изменений +terraform plan + +# Применение изменений +terraform apply + +# Уничтожение инфраструктуры +terraform destroy + +# Просмотр состояния +terraform show + +# Форматирование кода +terraform fmt + +# Валидация конфигурации +terraform validate +``` + +#### Модули Terraform + +```hcl +# main.tf +module "vpc" { + source = "./modules/vpc" + + environment = var.environment + cidr_block = "10.0.0.0/16" +} + +module "ec2" { + source = "./modules/ec2" + + vpc_id = module.vpc.vpc_id + subnet_id = module.vpc.public_subnet_id +} +``` + +### Ansible + +**Ansible** — инструмент автоматизации конфигурации и развертывания. + +#### Пример Playbook: +```yaml +--- +- name: Deploy web application + hosts: web_servers + become: yes + vars: + app_name: myapp + app_port: 3000 + + tasks: + - name: Install Node.js + package: + name: nodejs + state: present + + - name: Install npm dependencies + npm: + path: /opt/{{ app_name }} + state: present + + - name: Start application + systemd: + name: "{{ app_name }}" + state: started + enabled: yes +``` + +--- + +## CI/CD с контейнерами + +### CI/CD Pipeline + +```yaml +# .github/workflows/deploy.yml +name: Deploy to Kubernetes + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Build Docker image + run: docker build -t ${{ secrets.REGISTRY }}/myapp:${{ github.sha }} . + + - name: Push to registry + run: docker push ${{ secrets.REGISTRY }}/myapp:${{ github.sha }} + + - name: Deploy to Kubernetes + run: | + kubectl set image deployment/myapp myapp=${{ secrets.REGISTRY }}/myapp:${{ github.sha }} + kubectl rollout status deployment/myapp +``` + +### GitOps + +**GitOps** — подход к управлению инфраструктурой через Git. + +#### Пример ArgoCD: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: myapp + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/myorg/myapp + targetRevision: HEAD + path: k8s + destination: + server: https://kubernetes.default.svc + namespace: default + syncPolicy: + automated: + prune: true + selfHeal: true +``` + +--- + +## Мониторинг контейнеров + +### Prometheus + Grafana + +#### Конфигурация Prometheus: +```yaml +# prometheus.yml +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'kubernetes-pods' + kubernetes_sd_configs: + - role: pod +``` + +#### Дашборд Grafana: +```json +{ + "dashboard": { + "title": "Kubernetes Cluster", + "panels": [ + { + "title": "CPU Usage", + "type": "graph", + "targets": [ + { + "expr": "rate(container_cpu_usage_seconds_total[5m])" + } + ] + } + ] + } +} +``` + +### ELK Stack + +#### Elasticsearch + Logstash + Kibana: +```yaml +# docker-compose.yml +version: '3.8' +services: + elasticsearch: + image: elasticsearch:7.14.0 + environment: + - discovery.type=single-node + ports: + - "9200:9200" + + logstash: + image: logstash:7.14.0 + volumes: + - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf + ports: + - "5044:5044" + + kibana: + image: kibana:7.14.0 + ports: + - "5601:5601" +``` + +--- + +## Практические задания + +### Задание 1: Docker +1. Создайте Dockerfile для Node.js приложения +2. Соберите образ и запустите контейнер +3. Создайте docker-compose.yml для многоконтейнерного приложения + +### Задание 2: Kubernetes +1. Создайте Deployment для веб-приложения +2. Создайте Service для доступа к приложению +3. Настройте ConfigMap и Secret +4. Разверните приложение в кластере + +### Задание 3: Terraform +1. Создайте инфраструктуру в AWS с помощью Terraform +2. Используйте модули для организации кода +3. Настройте переменные и outputs + +--- + +## Дополнительные ресурсы + +### Документация +- [Docker Documentation](https://docs.docker.com/) +- [Kubernetes Documentation](https://kubernetes.io/docs/) +- [Terraform Documentation](https://www.terraform.io/docs/) + +### Книги +- "Kubernetes in Action" by Marko Lukša +- "Terraform: Up & Running" by Yevgeniy Brikman + +--- + +## Контрольные вопросы + +1. Что такое контейнеризация и каковы её преимущества? +2. Как работает Docker и какие основные команды вы знаете? +3. Что такое Kubernetes и какова его архитектура? +4. Какие основные объекты Kubernetes вы знаете? +5. Что такое Infrastructure as Code и зачем это нужно? +6. Как настроить CI/CD для контейнеризованных приложений? +7. Как мониторить контейнеры и кластеры? +8. Какие best practices для Docker и Kubernetes? diff --git a/modules/module4/README.md b/modules/module4/README.md new file mode 100644 index 0000000..35d0d48 --- /dev/null +++ b/modules/module4/README.md @@ -0,0 +1,122 @@ +# Модуль 4: Масштабируемость и отказоустойчивость + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля + +Этот модуль посвящен изучению принципов масштабируемости и отказоустойчивости в современных распределенных системах. Студенты изучат различные стратегии масштабирования, паттерны отказоустойчивости, управление данными и кэширование. + +## Содержание модуля + +### 1. [Основы масштабируемости](sections/01-scalability-basics.md) +- Типы масштабирования (вертикальное и горизонтальное) +- Метрики масштабируемости +- Закон Амдала +- Принципы проектирования масштабируемых систем + +### 2. [Паттерны масштабирования](sections/02-scaling-patterns.md) +- Stateless Services +- Database Sharding +- Read Replicas +- Caching Strategies +- Event-Driven Architecture + +### 3. [Отказоустойчивость](sections/03-fault-tolerance.md) +- Circuit Breaker Pattern +- Retry Pattern +- Bulkhead Pattern +- Timeout Pattern +- Graceful Degradation + +### 4. [Управление данными](sections/04-data-management.md) +- Event Sourcing +- CQRS (Command Query Responsibility Segregation) +- Saga Pattern +- Distributed Transactions + +### 5. [Кэширование](sections/05-caching.md) +- Стратегии кэширования +- Cache-Aside, Write-Through, Write-Behind +- CDN (Content Delivery Network) +- Кэширование на разных уровнях + +### 6. [Load Balancing](sections/06-load-balancing.md) +- Алгоритмы балансировки нагрузки +- Health Checks +- Session Affinity +- Geographic Load Balancing + +### 7. [Мониторинг производительности](sections/07-performance-monitoring.md) +- Ключевые метрики производительности +- APM (Application Performance Monitoring) +- Distributed Tracing +- Алерты и уведомления + +## Ключевые компетенции + +После изучения модуля студенты будут уметь: + +- Проектировать масштабируемые системы +- Выбирать подходящие стратегии масштабирования +- Реализовывать паттерны отказоустойчивости +- Управлять данными в распределенных системах +- Настраивать эффективное кэширование +- Конфигурировать балансировку нагрузки +- Мониторить производительность системы + +## Практические задания + +1. **Проектирование масштабируемой архитектуры** + - Анализ требований к масштабированию + - Выбор стратегии масштабирования + - Проектирование компонентов системы + +2. **Реализация паттернов отказоустойчивости** + - Circuit Breaker + - Retry с экспоненциальной задержкой + - Bulkhead изоляция + - Graceful degradation + +3. **Настройка кэширования** + - Многоуровневое кэширование + - Стратегии инвалидации кэша + - CDN конфигурация + - Кэширование на уровне приложения + +4. **Конфигурация Load Balancing** + - Настройка алгоритмов балансировки + - Health checks + - Session affinity + - Geographic distribution + +5. **Мониторинг и метрики** + - Настройка APM + - Distributed tracing + - Алерты и уведомления + - Дашборды производительности + +6. **Управление данными** + - Event Sourcing + - CQRS implementation + - Saga pattern + - Distributed transactions + +## Дополнительные ресурсы + +- [High Scalability](http://highscalability.com/) - примеры масштабируемых архитектур +- [Microservices Patterns](https://microservices.io/) - паттерны микросервисов +- [Resilience4j](https://resilience4j.readme.io/) - библиотека отказоустойчивости для Java +- [Hystrix](https://github.com/Netflix/Hystrix) - Circuit Breaker для Java +- [Prometheus](https://prometheus.io/) - мониторинг и алертинг + +## Критерии оценки + +- Понимание принципов масштабируемости (20%) +- Навыки проектирования отказоустойчивых систем (25%) +- Опыт работы с паттернами масштабирования (20%) +- Знание стратегий кэширования (15%) +- Навыки настройки Load Balancing (10%) +- Понимание мониторинга производительности (10%) diff --git a/modules/module4/sections/01-scalability-basics.md b/modules/module4/sections/01-scalability-basics.md new file mode 100644 index 0000000..0ccbca1 --- /dev/null +++ b/modules/module4/sections/01-scalability-basics.md @@ -0,0 +1,439 @@ +# Основы масштабируемости + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 4](../README.md) + +--- + +## Что такое масштабируемость + +**Масштабируемость** — способность системы обрабатывать увеличение нагрузки путем добавления ресурсов. + +## Типы масштабирования + +### 1. Вертикальное масштабирование (Scale Up) +**Определение:** Увеличение мощности существующих серверов. + +**Преимущества:** +- Простота реализации +- Нет изменений в архитектуре +- Быстрое внедрение + +**Недостатки:** +- Ограниченность ресурсов +- Высокая стоимость +- Точка отказа + +**Пример:** +``` +Сервер: 4 CPU, 8GB RAM → 8 CPU, 16GB RAM +``` + +### 2. Горизонтальное масштабирование (Scale Out) +**Определение:** Добавление новых серверов в систему. + +**Преимущества:** +- Теоретически неограниченное масштабирование +- Отказоустойчивость +- Экономическая эффективность + +**Недостатки:** +- Сложность архитектуры +- Проблемы с данными +- Сетевые задержки + +**Пример:** +``` +1 сервер → 2 сервера → 4 сервера → 8 серверов +``` + +## Метрики масштабируемости + +### 1. Throughput (Пропускная способность) +- Запросов в секунду (RPS) +- Транзакций в секунду (TPS) +- Байт в секунду + +### 2. Latency (Задержка) +- Время отклика +- Время обработки +- Время ожидания + +### 3. Resource Utilization (Использование ресурсов) +- CPU utilization +- Memory usage +- Disk I/O +- Network bandwidth + +## Закон Амдала + +**Формула:** `Speedup = 1 / (P + (1-P)/N)` + +Где: +- P — доля последовательного кода +- N — количество процессоров + +**Вывод:** Параллелизация эффективна только при малой доле последовательного кода. + +## Принципы проектирования масштабируемых систем + +### 1. Stateless Services (Бессостоятельные сервисы) + +**Принцип:** Сервисы не должны хранить состояние между запросами. + +**Преимущества:** +- Легкое горизонтальное масштабирование +- Отказоустойчивость +- Простота балансировки нагрузки + +**Реализация:** + +```java +// Плохо: Stateful сервис +@Service +public class StatefulUserService { + private Map userCache = new HashMap<>(); + + public User getUser(String id) { + // Состояние хранится в памяти сервиса + return userCache.get(id); + } +} + +// Хорошо: Stateless сервис +@Service +public class StatelessUserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + public User getUser(String id) { + // Состояние хранится во внешнем хранилище + User cached = redisTemplate.opsForValue().get("user:" + id); + if (cached != null) { + return cached; + } + + User user = userRepository.findById(id); + redisTemplate.opsForValue().set("user:" + id, user, Duration.ofMinutes(30)); + return user; + } +} +``` + +### 2. Database Sharding (Шардирование базы данных) + +**Принцип:** Разделение данных на несколько независимых баз данных. + +**Стратегии шардирования:** + +```java +// Стратегия шардирования по пользователю +@Service +public class ShardedUserService { + + @Autowired + private List shardDataSources; + + public DataSource getShardDataSource(String userId) { + int shardIndex = Math.abs(userId.hashCode()) % shardDataSources.size(); + return shardDataSources.get(shardIndex); + } + + public User saveUser(User user) { + DataSource shard = getShardDataSource(user.getId()); + JdbcTemplate template = new JdbcTemplate(shard); + + String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; + template.update(sql, user.getId(), user.getName(), user.getEmail()); + + return user; + } + + public User getUser(String userId) { + DataSource shard = getShardDataSource(userId); + JdbcTemplate template = new JdbcTemplate(shard); + + String sql = "SELECT * FROM users WHERE id = ?"; + return template.queryForObject(sql, new Object[]{userId}, new UserRowMapper()); + } +} +``` + +### 3. Read Replicas (Реплики для чтения) + +**Принцип:** Использование отдельных реплик для операций чтения. + +```java +@Configuration +public class DatabaseConfig { + + @Bean + @Primary + public DataSource masterDataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://master-db:5432/mydb"); + config.setUsername("master_user"); + config.setPassword("master_password"); + return new HikariDataSource(config); + } + + @Bean + public DataSource readOnlyDataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://replica-db:5432/mydb"); + config.setUsername("readonly_user"); + config.setPassword("readonly_password"); + return new HikariDataSource(config); + } +} + +@Service +public class ReadWriteUserService { + + @Autowired + @Qualifier("masterDataSource") + private DataSource masterDataSource; + + @Autowired + @Qualifier("readOnlyDataSource") + private DataSource readOnlyDataSource; + + public User saveUser(User user) { + // Запись в master + JdbcTemplate masterTemplate = new JdbcTemplate(masterDataSource); + String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; + masterTemplate.update(sql, user.getId(), user.getName(), user.getEmail()); + return user; + } + + public User getUser(String userId) { + // Чтение из replica + JdbcTemplate replicaTemplate = new JdbcTemplate(readOnlyDataSource); + String sql = "SELECT * FROM users WHERE id = ?"; + return replicaTemplate.queryForObject(sql, new Object[]{userId}, new UserRowMapper()); + } +} +``` + +### 4. Caching Strategies (Стратегии кэширования) + +**Cache-Aside Pattern:** + +```java +@Service +public class CacheAsideUserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + public User getUser(String userId) { + // 1. Проверка кэша + User cached = redisTemplate.opsForValue().get("user:" + userId); + if (cached != null) { + return cached; + } + + // 2. Загрузка из БД + User user = userRepository.findById(userId); + + // 3. Сохранение в кэш + if (user != null) { + redisTemplate.opsForValue().set("user:" + userId, user, Duration.ofMinutes(30)); + } + + return user; + } + + public User updateUser(User user) { + // 1. Обновление в БД + User updated = userRepository.save(user); + + // 2. Обновление кэша + redisTemplate.opsForValue().set("user:" + user.getId(), updated, Duration.ofMinutes(30)); + + return updated; + } + + public void deleteUser(String userId) { + // 1. Удаление из БД + userRepository.deleteById(userId); + + // 2. Удаление из кэша + redisTemplate.delete("user:" + userId); + } +} +``` + +## Архитектурные принципы масштабируемости + +### 1. Microservices Architecture + +**Принципы:** +- Разделение по бизнес-доменам +- Независимое развертывание +- Слабая связанность +- Высокая когезия + +```java +// Пример микросервиса пользователей +@RestController +@RequestMapping("/api/users") +public class UserController { + + @Autowired + private UserService userService; + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable String id) { + User user = userService.getUser(id); + return ResponseEntity.ok(user); + } + + @PostMapping + public ResponseEntity createUser(@RequestBody CreateUserRequest request) { + User user = userService.createUser(request); + return ResponseEntity.status(HttpStatus.CREATED).body(user); + } +} +``` + +### 2. Event-Driven Architecture + +**Принципы:** +- События как основной способ коммуникации +- Асинхронная обработка +- Слабая связанность сервисов + +```java +// Event Publisher +@Component +public class UserEventPublisher { + + @Autowired + private ApplicationEventPublisher eventPublisher; + + public void publishUserCreated(User user) { + UserCreatedEvent event = new UserCreatedEvent(user); + eventPublisher.publishEvent(event); + } +} + +// Event Listener +@Component +public class UserEventListener { + + @Autowired + private NotificationService notificationService; + + @EventListener + @Async + public void handleUserCreated(UserCreatedEvent event) { + User user = event.getUser(); + + // Отправка приветственного email + notificationService.sendWelcomeEmail(user); + } +} +``` + +### 3. API Gateway Pattern + +**Функции API Gateway:** +- Маршрутизация запросов +- Аутентификация и авторизация +- Rate limiting +- Мониторинг и логирование +- Кэширование ответов + +```java +// Spring Cloud Gateway конфигурация +@Configuration +public class GatewayConfig { + + @Bean + public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("user-service", r -> r.path("/api/users/**") + .filters(f -> f + .stripPrefix(1) + .addRequestHeader("X-Service", "user-service") + .circuitBreaker(config -> config + .setName("user-service-cb") + .setFallbackUri("forward:/fallback/user-service"))) + .uri("lb://user-service")) + .build(); + } +} +``` + +## Метрики и мониторинг масштабируемости + +### 1. Ключевые метрики + +```java +@Component +public class ScalabilityMetrics { + + private final MeterRegistry meterRegistry; + private final Counter requestCounter; + private final Timer responseTimer; + + public ScalabilityMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.requestCounter = Counter.builder("requests.total") + .description("Total number of requests") + .register(meterRegistry); + + this.responseTimer = Timer.builder("response.time") + .description("Response time") + .register(meterRegistry); + } + + public void recordRequest(String service, String method) { + requestCounter.increment( + Tags.of("service", service, "method", method) + ); + } + + public void recordResponseTime(String service, Duration duration) { + responseTimer.record(duration, TimeUnit.MILLISECONDS); + } +} +``` + +### 2. Автоматическое масштабирование + +```yaml +# Kubernetes HPA конфигурация +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: user-service-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: user-service + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 +``` diff --git a/modules/module4/sections/08-practical-tasks.md b/modules/module4/sections/08-practical-tasks.md new file mode 100644 index 0000000..68599be --- /dev/null +++ b/modules/module4/sections/08-practical-tasks.md @@ -0,0 +1,18 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 4](../README.md) + +--- + +### Задание 1: Circuit Breaker +Реализуйте Circuit Breaker для внешнего сервиса с экспоненциальным backoff. + +### Задание 2: Кэширование +Настройте многоуровневое кэширование (L1: local, L2: Redis). + +### Задание 3: Load Balancer +Создайте простой load balancer с health checks. + +--- \ No newline at end of file diff --git a/modules/module4/theory.md b/modules/module4/theory.md new file mode 100644 index 0000000..07ac3be --- /dev/null +++ b/modules/module4/theory.md @@ -0,0 +1,3838 @@ +# Модуль 4: Масштабируемость и отказоустойчивость + +## Содержание модуля +1. [Основы масштабируемости](#основы-масштабируемости) +2. [Паттерны масштабирования](#паттерны-масштабирования) +3. [Отказоустойчивость](#отказоустойчивость) +4. [Управление данными](#управление-данными) +5. [Кэширование](#кэширование) +6. [Load Balancing](#load-balancing) +7. [Мониторинг производительности](#мониторинг-производительности) + +--- + +## Основы масштабируемости + +### Что такое масштабируемость + +**Масштабируемость** — способность системы обрабатывать увеличение нагрузки путем добавления ресурсов. + +### Типы масштабирования + +#### 1. Вертикальное масштабирование (Scale Up) +**Определение:** Увеличение мощности существующих серверов. + +**Преимущества:** +- Простота реализации +- Нет изменений в архитектуре +- Быстрое внедрение + +**Недостатки:** +- Ограниченность ресурсов +- Высокая стоимость +- Точка отказа + +**Пример:** +``` +Сервер: 4 CPU, 8GB RAM → 8 CPU, 16GB RAM +``` + +#### 2. Горизонтальное масштабирование (Scale Out) +**Определение:** Добавление новых серверов в систему. + +**Преимущества:** +- Теоретически неограниченное масштабирование +- Отказоустойчивость +- Экономическая эффективность + +**Недостатки:** +- Сложность архитектуры +- Проблемы с данными +- Сетевые задержки + +**Пример:** +``` +1 сервер → 2 сервера → 4 сервера → 8 серверов +``` + +### Метрики масштабируемости + +#### 1. Throughput (Пропускная способность) +- Запросов в секунду (RPS) +- Транзакций в секунду (TPS) +- Байт в секунду + +#### 2. Latency (Задержка) +- Время отклика +- Время обработки +- Время ожидания + +#### 3. Resource Utilization (Использование ресурсов) +- CPU utilization +- Memory usage +- Disk I/O +- Network bandwidth + +### Закон Амдала + +**Формула:** `Speedup = 1 / (P + (1-P)/N)` + +Где: +- P — доля последовательного кода +- N — количество процессоров + +**Вывод:** Параллелизация эффективна только при малой доле последовательного кода. + +### Принципы проектирования масштабируемых систем + +#### 1. Stateless Services (Бессостоятельные сервисы) + +**Принцип:** Сервисы не должны хранить состояние между запросами. + +**Преимущества:** +- Легкое горизонтальное масштабирование +- Отказоустойчивость +- Простота балансировки нагрузки + +**Реализация:** + +```java +// Плохо: Stateful сервис +@Service +public class StatefulUserService { + private Map userCache = new HashMap<>(); + + public User getUser(String id) { + // Состояние хранится в памяти сервиса + return userCache.get(id); + } +} + +// Хорошо: Stateless сервис +@Service +public class StatelessUserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + public User getUser(String id) { + // Состояние хранится во внешнем хранилище + User cached = redisTemplate.opsForValue().get("user:" + id); + if (cached != null) { + return cached; + } + + User user = userRepository.findById(id); + redisTemplate.opsForValue().set("user:" + id, user, Duration.ofMinutes(30)); + return user; + } +} +``` + +**Rust примеры - Stateless сервисы:** + +```rust +use std::sync::Arc; +use tokio::sync::RwLock; +use redis::Client; + +pub struct StatelessUserService { + user_repository: Arc, + redis_client: Arc, +} + +impl StatelessUserService { + pub fn new(user_repository: Arc, redis_client: Arc) -> Self { + Self { + user_repository, + redis_client, + } + } + + pub async fn get_user(&self, id: String) -> Result, ServiceError> { + // Проверка кэша + if let Ok(user) = self.get_user_from_cache(&id).await { + return Ok(Some(user)); + } + + // Получение из базы данных + let user = self.user_repository.find_by_id(&id).await?; + + if let Some(ref user) = user { + // Сохранение в кэш + self.save_user_to_cache(user).await?; + } + + Ok(user) + } + + async fn get_user_from_cache(&self, id: &str) -> Result { + let mut conn = self.redis_client.get_async_connection().await?; + let key = format!("user:{}", id); + + let user_json: String = redis::cmd("GET") + .arg(&key) + .query_async(&mut conn) + .await?; + + let user: User = serde_json::from_str(&user_json)?; + Ok(user) + } + + async fn save_user_to_cache(&self, user: &User) -> Result<(), ServiceError> { + let mut conn = self.redis_client.get_async_connection().await?; + let key = format!("user:{}", user.id); + let user_json = serde_json::to_string(user)?; + + redis::cmd("SETEX") + .arg(&key) + .arg(1800) // 30 минут + .arg(&user_json) + .query_async(&mut conn) + .await?; + + Ok(()) + } +} +``` + +**Python примеры - Stateless сервисы:** + +```python +import asyncio +from typing import Optional +import redis.asyncio as redis +from dataclasses import dataclass +import json + +@dataclass +class User: + id: str + name: str + email: str + +class StatelessUserService: + def __init__(self, user_repository, redis_client: redis.Redis): + self.user_repository = user_repository + self.redis_client = redis_client + + async def get_user(self, user_id: str) -> Optional[User]: + # Проверка кэша + cached_user = await self._get_user_from_cache(user_id) + if cached_user: + return cached_user + + # Получение из базы данных + user = await self.user_repository.find_by_id(user_id) + + if user: + # Сохранение в кэш + await self._save_user_to_cache(user) + + return user + + async def _get_user_from_cache(self, user_id: str) -> Optional[User]: + try: + key = f"user:{user_id}" + user_json = await self.redis_client.get(key) + + if user_json: + user_data = json.loads(user_json) + return User(**user_data) + except Exception as e: + print(f"Cache error: {e}") + + return None + + async def _save_user_to_cache(self, user: User) -> None: + try: + key = f"user:{user.id}" + user_json = json.dumps({ + 'id': user.id, + 'name': user.name, + 'email': user.email + }) + + await self.redis_client.setex(key, 1800, user_json) # 30 минут + except Exception as e: + print(f"Cache save error: {e}") +``` + +**Node.js примеры - Stateless сервисы:** + +```typescript +import { Redis } from 'ioredis'; +import { UserRepository } from './user-repository'; + +interface User { + id: string; + name: string; + email: string; +} + +export class StatelessUserService { + constructor( + private userRepository: UserRepository, + private redisClient: Redis + ) {} + + async getUser(userId: string): Promise { + // Проверка кэша + const cachedUser = await this.getUserFromCache(userId); + if (cachedUser) { + return cachedUser; + } + + // Получение из базы данных + const user = await this.userRepository.findById(userId); + + if (user) { + // Сохранение в кэш + await this.saveUserToCache(user); + } + + return user; + } + + private async getUserFromCache(userId: string): Promise { + try { + const key = `user:${userId}`; + const userJson = await this.redisClient.get(key); + + if (userJson) { + return JSON.parse(userJson); + } + } catch (error) { + console.error('Cache error:', error); + } + + return null; + } + + private async saveUserToCache(user: User): Promise { + try { + const key = `user:${user.id}`; + const userJson = JSON.stringify(user); + + await this.redisClient.setex(key, 1800, userJson); // 30 минут + } catch (error) { + console.error('Cache save error:', error); + } + } +} +``` + +#### 2. Database Sharding (Шардирование базы данных) + +**Принцип:** Разделение данных на несколько независимых баз данных. + +**Стратегии шардирования:** + +```java +// Стратегия шардирования по пользователю +@Service +public class ShardedUserService { + + @Autowired + private List shardDataSources; + + public DataSource getShardDataSource(String userId) { + int shardIndex = Math.abs(userId.hashCode()) % shardDataSources.size(); + return shardDataSources.get(shardIndex); + } + + public User saveUser(User user) { + DataSource shard = getShardDataSource(user.getId()); + JdbcTemplate template = new JdbcTemplate(shard); + + String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; + template.update(sql, user.getId(), user.getName(), user.getEmail()); + + return user; + } + + public User getUser(String userId) { + DataSource shard = getShardDataSource(userId); + JdbcTemplate template = new JdbcTemplate(shard); + + String sql = "SELECT * FROM users WHERE id = ?"; + return template.queryForObject(sql, new Object[]{userId}, new UserRowMapper()); + } +} + +// Стратегия шардирования по диапазону +@Service +public class RangeShardedUserService { + + private final Map shardMap = Map.of( + "shard1", createDataSource("db_shard1"), + "shard2", createDataSource("db_shard2"), + "shard3", createDataSource("db_shard3") + ); + + public String getShardName(String userId) { + int hash = userId.hashCode(); + if (hash < -1000000000) return "shard1"; + if (hash < 0) return "shard2"; + return "shard3"; + } + + public DataSource getShardDataSource(String userId) { + String shardName = getShardName(userId); + return shardMap.get(shardName); + } +} +``` + +#### 3. Read Replicas (Реплики для чтения) + +**Принцип:** Использование отдельных реплик для операций чтения. + +```java +@Configuration +public class DatabaseConfig { + + @Bean + @Primary + public DataSource masterDataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://master-db:5432/mydb"); + config.setUsername("master_user"); + config.setPassword("master_password"); + return new HikariDataSource(config); + } + + @Bean + public DataSource readOnlyDataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://replica-db:5432/mydb"); + config.setUsername("readonly_user"); + config.setPassword("readonly_password"); + return new HikariDataSource(config); + } +} + +@Service +public class ReadWriteUserService { + + @Autowired + @Qualifier("masterDataSource") + private DataSource masterDataSource; + + @Autowired + @Qualifier("readOnlyDataSource") + private DataSource readOnlyDataSource; + + public User saveUser(User user) { + // Запись в master + JdbcTemplate masterTemplate = new JdbcTemplate(masterDataSource); + String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; + masterTemplate.update(sql, user.getId(), user.getName(), user.getEmail()); + return user; + } + + public User getUser(String userId) { + // Чтение из replica + JdbcTemplate replicaTemplate = new JdbcTemplate(readOnlyDataSource); + String sql = "SELECT * FROM users WHERE id = ?"; + return replicaTemplate.queryForObject(sql, new Object[]{userId}, new UserRowMapper()); + } + + public List getAllUsers() { + // Чтение из replica + JdbcTemplate replicaTemplate = new JdbcTemplate(readOnlyDataSource); + String sql = "SELECT * FROM users"; + return replicaTemplate.query(sql, new UserRowMapper()); + } +} +``` + +#### 4. Caching Strategies (Стратегии кэширования) + +**Cache-Aside Pattern:** + +```java +@Service +public class CacheAsideUserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + public User getUser(String userId) { + // 1. Проверка кэша + User cached = redisTemplate.opsForValue().get("user:" + userId); + if (cached != null) { + return cached; + } + + // 2. Загрузка из БД + User user = userRepository.findById(userId); + + // 3. Сохранение в кэш + if (user != null) { + redisTemplate.opsForValue().set("user:" + userId, user, Duration.ofMinutes(30)); + } + + return user; + } + + public User updateUser(User user) { + // 1. Обновление в БД + User updated = userRepository.save(user); + + // 2. Обновление кэша + redisTemplate.opsForValue().set("user:" + user.getId(), updated, Duration.ofMinutes(30)); + + return updated; + } + + public void deleteUser(String userId) { + // 1. Удаление из БД + userRepository.deleteById(userId); + + // 2. Удаление из кэша + redisTemplate.delete("user:" + userId); + } +} +``` + +**Write-Through Pattern:** + +```java +@Service +public class WriteThroughUserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + public User saveUser(User user) { + // 1. Запись в БД + User saved = userRepository.save(user); + + // 2. Запись в кэш + redisTemplate.opsForValue().set("user:" + saved.getId(), saved, Duration.ofMinutes(30)); + + return saved; + } + + public User getUser(String userId) { + // Чтение из кэша (данные всегда актуальны) + return redisTemplate.opsForValue().get("user:" + userId); + } +} +``` + +**Write-Behind Pattern:** + +```java +@Service +public class WriteBehindUserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private RedisTemplate redisTemplate; + + @Async + public CompletableFuture saveUser(User user) { + // 1. Быстрая запись в кэш + redisTemplate.opsForValue().set("user:" + user.getId(), user, Duration.ofMinutes(30)); + + // 2. Асинхронная запись в БД + return CompletableFuture.supplyAsync(() -> { + return userRepository.save(user); + }); + } + + public User getUser(String userId) { + // Чтение из кэша + return redisTemplate.opsForValue().get("user:" + userId); + } + + @Scheduled(fixedDelay = 30000) // Каждые 30 секунд + public void flushCacheToDatabase() { + // Периодическая синхронизация кэша с БД + Set keys = redisTemplate.keys("user:*"); + for (String key : keys) { + User user = redisTemplate.opsForValue().get(key); + if (user != null) { + userRepository.save(user); + } + } + } +} +``` + +### Архитектурные принципы масштабируемости + +#### 1. Microservices Architecture + +**Принципы:** +- Разделение по бизнес-доменам +- Независимое развертывание +- Слабая связанность +- Высокая когезия + +```java +// Пример микросервиса пользователей +@RestController +@RequestMapping("/api/users") +public class UserController { + + @Autowired + private UserService userService; + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable String id) { + User user = userService.getUser(id); + return ResponseEntity.ok(user); + } + + @PostMapping + public ResponseEntity createUser(@RequestBody CreateUserRequest request) { + User user = userService.createUser(request); + return ResponseEntity.status(HttpStatus.CREATED).body(user); + } +} + +@Service +public class UserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private NotificationService notificationService; + + public User createUser(CreateUserRequest request) { + User user = new User(request.getName(), request.getEmail()); + User saved = userRepository.save(user); + + // Асинхронная отправка уведомления + notificationService.sendWelcomeEmail(saved); + + return saved; + } +} +``` + +#### 2. Event-Driven Architecture + +**Принципы:** +- События как основной способ коммуникации +- Асинхронная обработка +- Слабая связанность сервисов + +```java +// Event Publisher +@Component +public class UserEventPublisher { + + @Autowired + private ApplicationEventPublisher eventPublisher; + + public void publishUserCreated(User user) { + UserCreatedEvent event = new UserCreatedEvent(user); + eventPublisher.publishEvent(event); + } + + public void publishUserUpdated(User user) { + UserUpdatedEvent event = new UserUpdatedEvent(user); + eventPublisher.publishEvent(event); + } +} + +// Event Listener +@Component +public class UserEventListener { + + @Autowired + private NotificationService notificationService; + + @Autowired + private AuditService auditService; + + @EventListener + @Async + public void handleUserCreated(UserCreatedEvent event) { + User user = event.getUser(); + + // Отправка приветственного email + notificationService.sendWelcomeEmail(user); + + // Аудит + auditService.logUserCreation(user); + } + + @EventListener + @Async + public void handleUserUpdated(UserUpdatedEvent event) { + User user = event.getUser(); + + // Аудит изменений + auditService.logUserUpdate(user); + } +} +``` + +#### 3. API Gateway Pattern + +**Функции API Gateway:** +- Маршрутизация запросов +- Аутентификация и авторизация +- Rate limiting +- Мониторинг и логирование +- Кэширование ответов + +```java +// Spring Cloud Gateway конфигурация +@Configuration +public class GatewayConfig { + + @Bean + public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("user-service", r -> r.path("/api/users/**") + .filters(f -> f + .stripPrefix(1) + .addRequestHeader("X-Service", "user-service") + .circuitBreaker(config -> config + .setName("user-service-cb") + .setFallbackUri("forward:/fallback/user-service"))) + .uri("lb://user-service")) + + .route("order-service", r -> r.path("/api/orders/**") + .filters(f -> f + .stripPrefix(1) + .addRequestHeader("X-Service", "order-service") + .circuitBreaker(config -> config + .setName("order-service-cb") + .setFallbackUri("forward:/fallback/order-service"))) + .uri("lb://order-service")) + + .route("notification-service", r -> r.path("/api/notifications/**") + .filters(f -> f + .stripPrefix(1) + .addRequestHeader("X-Service", "notification-service")) + .uri("lb://notification-service")) + + .build(); + } + + @Bean + public GlobalFilter customGlobalFilter() { + return (exchange, chain) -> { + ServerHttpRequest request = exchange.getRequest(); + + // Добавление correlation ID + String correlationId = UUID.randomUUID().toString(); + ServerHttpRequest mutatedRequest = request.mutate() + .header("X-Correlation-ID", correlationId) + .build(); + + ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().add("X-Correlation-ID", correlationId); + + return chain.filter(exchange.mutate().request(mutatedRequest).build()); + }; + } +} +``` + +### Метрики и мониторинг масштабируемости + +#### 1. Ключевые метрики + +```java +@Component +public class ScalabilityMetrics { + + private final MeterRegistry meterRegistry; + private final Counter requestCounter; + private final Timer responseTimer; + private final Gauge activeConnections; + + public ScalabilityMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.requestCounter = Counter.builder("requests.total") + .description("Total number of requests") + .register(meterRegistry); + + this.responseTimer = Timer.builder("response.time") + .description("Response time") + .register(meterRegistry); + + this.activeConnections = Gauge.builder("connections.active") + .description("Active connections") + .register(meterRegistry, this, ScalabilityMetrics::getActiveConnections); + } + + public void recordRequest(String service, String method) { + requestCounter.increment( + Tags.of("service", service, "method", method) + ); + } + + public void recordResponseTime(String service, Duration duration) { + responseTimer.record(duration, TimeUnit.MILLISECONDS); + } + + private double getActiveConnections() { + // Логика подсчета активных соединений + return 0.0; + } +} +``` + +#### 2. Автоматическое масштабирование + +```yaml +# Kubernetes HPA конфигурация +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: user-service-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: user-service + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + - type: Pods + pods: + metric: + name: requests_per_second + target: + type: AverageValue + averageValue: "100" +``` + +--- + +## Паттерны масштабирования + +### 1. Stateless Services (Безгосударственные сервисы) + +**Принцип:** Сервисы не хранят состояние между запросами. + +**Технический пример - Stateless Service:** + +```java +@RestController +@RequestMapping("/api/users") +public class UserController { + @Autowired + private UserService userService; + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + // Stateless - нет сохранения состояния между запросами + User user = userService.getUser(id); + return ResponseEntity.ok(user); + } + + @PostMapping + public ResponseEntity createUser(@RequestBody CreateUserRequest request) { + // Stateless - каждый запрос независим + User user = userService.createUser(request); + return ResponseEntity.ok(user); + } +} + +@Service +@Transactional +public class UserService { + @Autowired + private UserRepository userRepository; + + public User getUser(Long id) { + // Stateless - данные берутся из внешнего хранилища + return userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found")); + } + + public User createUser(CreateUserRequest request) { + // Stateless - состояние сохраняется в БД, не в памяти + User user = new User(); + user.setName(request.getName()); + user.setEmail(request.getEmail()); + return userRepository.save(user); + } +} +``` + +**Технический пример - Database Sharding:** + +```java +// Sharding Strategy +@Component +public class UserShardingStrategy { + + private static final int SHARD_COUNT = 4; + + public String getShardKey(Long userId) { + // Простая стратегия sharding по остатку от деления + int shardIndex = (int) (userId % SHARD_COUNT); + return "user_shard_" + shardIndex; + } + + public String getShardUrl(Long userId) { + String shardKey = getShardKey(userId); + return getShardUrlByKey(shardKey); + } + + private String getShardUrlByKey(String shardKey) { + Map shardUrls = Map.of( + "user_shard_0", "jdbc:postgresql://shard0:5432/users", + "user_shard_1", "jdbc:postgresql://shard1:5432/users", + "user_shard_2", "jdbc:postgresql://shard2:5432/users", + "user_shard_3", "jdbc:postgresql://shard3:5432/users" + ); + return shardUrls.get(shardKey); + } +} + +// Sharded Repository +@Repository +public class ShardedUserRepository { + + @Autowired + private UserShardingStrategy shardingStrategy; + + @Autowired + private Map shardTemplates; + + public User findById(Long userId) { + String shardKey = shardingStrategy.getShardKey(userId); + JdbcTemplate template = shardTemplates.get(shardKey); + + String sql = "SELECT id, name, email FROM users WHERE id = ?"; + return template.queryForObject(sql, new Object[]{userId}, (rs, rowNum) -> { + User user = new User(); + user.setId(rs.getLong("id")); + user.setName(rs.getString("name")); + user.setEmail(rs.getString("email")); + return user; + }); + } + + public void save(User user) { + String shardKey = shardingStrategy.getShardKey(user.getId()); + JdbcTemplate template = shardTemplates.get(shardKey); + + String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; + template.update(sql, user.getId(), user.getName(), user.getEmail()); + } +} + +// Configuration for multiple datasources +@Configuration +public class ShardingDataSourceConfig { + + @Bean + @Primary + public DataSource shardingDataSource() { + Map dataSources = new HashMap<>(); + + // Shard 0 + dataSources.put("user_shard_0", createDataSource( + "jdbc:postgresql://shard0:5432/users", "user", "password")); + + // Shard 1 + dataSources.put("user_shard_1", createDataSource( + "jdbc:postgresql://shard1:5432/users", "user", "password")); + + // Shard 2 + dataSources.put("user_shard_2", createDataSource( + "jdbc:postgresql://shard2:5432/users", "user", "password")); + + // Shard 3 + dataSources.put("user_shard_3", createDataSource( + "jdbc:postgresql://shard3:5432/users", "user", "password")); + + return new ShardingDataSource(dataSources); + } + + @Bean + public Map shardTemplates() { + Map templates = new HashMap<>(); + Map dataSources = getShardDataSources(); + + for (Map.Entry entry : dataSources.entrySet()) { + templates.put(entry.getKey(), new JdbcTemplate(entry.getValue())); + } + + return templates; + } + + private DataSource createDataSource(String url, String username, String password) { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl(url); + config.setUsername(username); + config.setPassword(password); + config.setMaximumPoolSize(10); + config.setMinimumIdle(5); + return new HikariDataSource(config); + } +} +``` + +**Технический пример - Read Replicas:** + +```java +// Master-Slave Configuration +@Configuration +public class DatabaseConfig { + + @Bean + @Primary + @ConfigurationProperties("spring.datasource.master") + public DataSource masterDataSource() { + return DataSourceBuilder.create().build(); + } + + @Bean + @ConfigurationProperties("spring.datasource.slave") + public DataSource slaveDataSource() { + return DataSourceBuilder.create().build(); + } + + @Bean + public DataSource routingDataSource() { + RoutingDataSource routingDataSource = new RoutingDataSource(); + + Map dataSourceMap = new HashMap<>(); + dataSourceMap.put("master", masterDataSource()); + dataSourceMap.put("slave", slaveDataSource()); + + routingDataSource.setTargetDataSources(dataSourceMap); + routingDataSource.setDefaultTargetDataSource(masterDataSource()); + + return routingDataSource; + } +} + +// Routing DataSource +public class RoutingDataSource extends AbstractRoutingDataSource { + + @Override + protected Object determineCurrentLookupKey() { + return DatabaseContextHolder.getDatabaseType(); + } +} + +// Database Context Holder +public class DatabaseContextHolder { + + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static void setDatabaseType(DatabaseType databaseType) { + contextHolder.set(databaseType); + } + + public static DatabaseType getDatabaseType() { + return contextHolder.get(); + } + + public static void clearDatabaseType() { + contextHolder.remove(); + } + + public enum DatabaseType { + MASTER, SLAVE + } +} + +// Service with Read/Write separation +@Service +public class UserService { + + @Autowired + private UserRepository userRepository; + + @Transactional(readOnly = false) + public User createUser(CreateUserRequest request) { + // Write operations go to master + DatabaseContextHolder.setDatabaseType(DatabaseType.MASTER); + try { + User user = new User(); + user.setName(request.getName()); + user.setEmail(request.getEmail()); + return userRepository.save(user); + } finally { + DatabaseContextHolder.clearDatabaseType(); + } + } + + @Transactional(readOnly = true) + public User getUser(Long id) { + // Read operations can go to slave + DatabaseContextHolder.setDatabaseType(DatabaseType.SLAVE); + try { + return userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found")); + } finally { + DatabaseContextHolder.clearDatabaseType(); + } + } + + @Transactional(readOnly = true) + public List getAllUsers() { + // Read operations can go to slave + DatabaseContextHolder.setDatabaseType(DatabaseType.SLAVE); + try { + return userRepository.findAll(); + } finally { + DatabaseContextHolder.clearDatabaseType(); + } + } +} + +// AOP for automatic routing +@Aspect +@Component +public class DatabaseRoutingAspect { + + @Before("@annotation(readOnly)") + public void setReadOnlyDataSource(ReadOnly readOnly) { + DatabaseContextHolder.setDatabaseType(DatabaseType.SLAVE); + } + + @Before("@annotation(Transactional)") + public void setMasterDataSource(Transactional transactional) { + if (!transactional.readOnly()) { + DatabaseContextHolder.setDatabaseType(DatabaseType.MASTER); + } + } + + @After("@annotation(readOnly) || @annotation(Transactional)") + public void clearDataSource() { + DatabaseContextHolder.clearDatabaseType(); + } +} + +// Annotation for read-only operations +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ReadOnly { +} +``` + public User getUser(@PathVariable Long id) { + // нет локального состояния + return userService.findById(id); + } +} +``` + +**Преимущества:** +- Легкое масштабирование +- Отказоустойчивость +- Простота развертывания + +### 2. Database Sharding (Шардирование БД) + +**Определение:** Разделение данных по нескольким базам данных. + +#### Стратегии шардирования: + +##### 1. Horizontal Sharding +```sql +-- Shard 1: users 1-1000 +CREATE TABLE users_1 ( + id INT PRIMARY KEY, + name VARCHAR(100) +); + +-- Shard 2: users 1001-2000 +CREATE TABLE users_2 ( + id INT PRIMARY KEY, + name VARCHAR(100) +); +``` + +##### 2. Vertical Sharding +```sql +-- Shard 1: User data +CREATE TABLE users ( + id INT PRIMARY KEY, + name VARCHAR(100), + email VARCHAR(100) +); + +-- Shard 2: User preferences +CREATE TABLE user_preferences ( + user_id INT, + theme VARCHAR(50), + language VARCHAR(10) +); +``` + +#### Алгоритмы шардирования: + +##### 1. Range-based +```java +public class RangeSharding { + public String getShard(int userId) { + if (userId >= 1 && userId <= 1000) { + return "shard_1"; + } else if (userId >= 1001 && userId <= 2000) { + return "shard_2"; + } + return "shard_3"; + } +} +``` + +##### 2. Hash-based +```java +public class HashSharding { + public String getShard(int userId) { + int hash = userId.hashCode(); + int shardNumber = Math.abs(hash) % 3; + return "shard_" + (shardNumber + 1); + } +} +``` + +### 3. Read Replicas (Реплики для чтения) + +**Принцип:** Разделение операций чтения и записи. + +```yaml +# Master-Slave конфигурация +Master DB (Write) → Slave DB 1 (Read) + → Slave DB 2 (Read) + → Slave DB 3 (Read) +``` + +```java +@Service +public class UserService { + @Autowired + @Qualifier("masterDataSource") + private DataSource masterDataSource; + + @Autowired + @Qualifier("slaveDataSource") + private DataSource slaveDataSource; + + public void createUser(User user) { + // запись в master + JdbcTemplate masterTemplate = new JdbcTemplate(masterDataSource); + masterTemplate.update("INSERT INTO users ...", user.getName()); + } + + public User findById(Long id) { + // чтение из slave + JdbcTemplate slaveTemplate = new JdbcTemplate(slaveDataSource); + return slaveTemplate.queryForObject("SELECT * FROM users WHERE id = ?", + new Object[]{id}, User.class); + } +} +``` + +### 4. Caching Patterns (Паттерны кэширования) + +#### Cache-Aside (Кэш в стороне) +```java +@Service +public class UserService { + @Autowired + private RedisTemplate redisTemplate; + + public User findById(Long id) { + String key = "user:" + id; + + // проверяем кэш + User user = redisTemplate.opsForValue().get(key); + if (user != null) { + return user; + } + + // загружаем из БД + user = userRepository.findById(id); + if (user != null) { + // сохраняем в кэш + redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30)); + } + + return user; + } +} +``` + +#### Write-Through (Сквозная запись) +```java +@Service +public class UserService { + public void updateUser(User user) { + // обновляем БД + userRepository.save(user); + + // обновляем кэш + String key = "user:" + user.getId(); + redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30)); + } +} +``` + +#### Write-Behind (Запись с задержкой) +```java +@Service +public class UserService { + @Async + public void updateUser(User user) { + // обновляем кэш сразу + String key = "user:" + user.getId(); + redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30)); + + // обновляем БД асинхронно + CompletableFuture.runAsync(() -> { + userRepository.save(user); + }); + } +} +``` + +### Продвинутые паттерны масштабирования + +#### 1. Event Sourcing (Событийное программирование) + +**Принцип:** Хранение изменений состояния как последовательности событий. + +**Преимущества:** +- Полная история изменений +- Возможность воспроизведения состояния +- Аудит и отладка +- Временные запросы + +**Реализация:** + +```java +// Event Store +@Component +public class EventStore { + + @Autowired + private EventRepository eventRepository; + + public void saveEvents(String aggregateId, List events, int expectedVersion) { + List storedEvents = events.stream() + .map(event -> new StoredEvent(aggregateId, event, expectedVersion)) + .collect(Collectors.toList()); + + eventRepository.saveAll(storedEvents); + } + + public List getEvents(String aggregateId) { + return eventRepository.findByAggregateIdOrderByVersion(aggregateId) + .stream() + .map(StoredEvent::getEvent) + .collect(Collectors.toList()); + } + + public List getEventsFromVersion(String aggregateId, int fromVersion) { + return eventRepository.findByAggregateIdAndVersionGreaterThanOrderByVersion(aggregateId, fromVersion) + .stream() + .map(StoredEvent::getEvent) + .collect(Collectors.toList()); + } +} + +// Aggregate Root +public abstract class AggregateRoot { + private String id; + private int version; + private List uncommittedEvents = new ArrayList<>(); + + protected void apply(DomainEvent event) { + uncommittedEvents.add(event); + handle(event); + } + + protected abstract void handle(DomainEvent event); + + public List getUncommittedEvents() { + return new ArrayList<>(uncommittedEvents); + } + + public void markEventsAsCommitted() { + uncommittedEvents.clear(); + } +} + +// User Aggregate +public class User extends AggregateRoot { + private String name; + private String email; + private UserStatus status; + + public static User create(String name, String email) { + User user = new User(); + user.apply(new UserCreatedEvent(name, email)); + return user; + } + + public void changeEmail(String newEmail) { + if (status == UserStatus.ACTIVE) { + apply(new UserEmailChangedEvent(newEmail)); + } + } + + public void deactivate() { + if (status == UserStatus.ACTIVE) { + apply(new UserDeactivatedEvent()); + } + } + + @Override + protected void handle(DomainEvent event) { + if (event instanceof UserCreatedEvent) { + UserCreatedEvent e = (UserCreatedEvent) event; + this.name = e.getName(); + this.email = e.getEmail(); + this.status = UserStatus.ACTIVE; + } else if (event instanceof UserEmailChangedEvent) { + UserEmailChangedEvent e = (UserEmailChangedEvent) event; + this.email = e.getNewEmail(); + } else if (event instanceof UserDeactivatedEvent) { + this.status = UserStatus.INACTIVE; + } + } +} + +// Event Sourced Repository +@Repository +public class EventSourcedUserRepository { + + @Autowired + private EventStore eventStore; + + public User findById(String id) { + List events = eventStore.getEvents(id); + if (events.isEmpty()) { + return null; + } + + User user = new User(); + for (DomainEvent event : events) { + user.handle(event); + } + + return user; + } + + public void save(User user) { + List uncommittedEvents = user.getUncommittedEvents(); + if (!uncommittedEvents.isEmpty()) { + eventStore.saveEvents(user.getId(), uncommittedEvents, user.getVersion()); + user.markEventsAsCommitted(); + } + } +} +``` + +**Rust примеры - Event Sourcing:** + +```rust +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum UserEvent { + Created { name: String, email: String }, + EmailChanged { new_email: String }, + Deactivated, +} + +#[derive(Debug, Clone)] +pub struct User { + id: String, + name: String, + email: String, + status: UserStatus, + version: u32, + uncommitted_events: Vec, +} + +#[derive(Debug, Clone)] +pub enum UserStatus { + Active, + Inactive, +} + +impl User { + pub fn create(id: String, name: String, email: String) -> Self { + let mut user = Self { + id, + name: String::new(), + email: String::new(), + status: UserStatus::Active, + version: 0, + uncommitted_events: Vec::new(), + }; + + user.apply(UserEvent::Created { name, email }); + user + } + + pub fn change_email(&mut self, new_email: String) { + if matches!(self.status, UserStatus::Active) { + self.apply(UserEvent::EmailChanged { new_email }); + } + } + + pub fn deactivate(&mut self) { + if matches!(self.status, UserStatus::Active) { + self.apply(UserEvent::Deactivated); + } + } + + fn apply(&mut self, event: UserEvent) { + self.uncommitted_events.push(event.clone()); + self.handle(event); + } + + fn handle(&mut self, event: UserEvent) { + match event { + UserEvent::Created { name, email } => { + self.name = name; + self.email = email; + self.status = UserStatus::Active; + } + UserEvent::EmailChanged { new_email } => { + self.email = new_email; + } + UserEvent::Deactivated => { + self.status = UserStatus::Inactive; + } + } + } + + pub fn get_uncommitted_events(&self) -> Vec { + self.uncommitted_events.clone() + } + + pub fn mark_events_as_committed(&mut self) { + self.uncommitted_events.clear(); + self.version += 1; + } +} + +pub struct EventStore { + events: HashMap>, +} + +impl EventStore { + pub fn new() -> Self { + Self { + events: HashMap::new(), + } + } + + pub fn save_events(&mut self, aggregate_id: &str, events: Vec) { + let stored_events = self.events.entry(aggregate_id.to_string()).or_insert_with(Vec::new); + stored_events.extend(events); + } + + pub fn get_events(&self, aggregate_id: &str) -> Vec { + self.events.get(aggregate_id).cloned().unwrap_or_default() + } +} +``` + +#### 2. CQRS (Command Query Responsibility Segregation) + +**Принцип:** Разделение операций чтения и записи на разные модели. + +**Преимущества:** +- Оптимизация для чтения и записи +- Независимое масштабирование +- Различные модели данных +- Упрощение сложных доменов + +**Реализация:** + +```java +// Command Side +@RestController +@RequestMapping("/api/users/commands") +public class UserCommandController { + + @Autowired + private UserCommandService userCommandService; + + @PostMapping + public ResponseEntity createUser(@RequestBody CreateUserCommand command) { + String userId = userCommandService.createUser(command); + return ResponseEntity.ok(userId); + } + + @PutMapping("/{id}/email") + public ResponseEntity changeEmail(@PathVariable String id, @RequestBody ChangeEmailCommand command) { + userCommandService.changeEmail(id, command); + return ResponseEntity.ok().build(); + } +} + +@Service +public class UserCommandService { + + @Autowired + private UserCommandRepository userCommandRepository; + + @Autowired + private UserEventPublisher eventPublisher; + + @Transactional + public String createUser(CreateUserCommand command) { + User user = new User(command.getName(), command.getEmail()); + user = userCommandRepository.save(user); + + // Публикация события для обновления read model + eventPublisher.publishUserCreated(user); + + return user.getId(); + } + + @Transactional + public void changeEmail(String userId, ChangeEmailCommand command) { + User user = userCommandRepository.findById(userId) + .orElseThrow(() -> new UserNotFoundException("User not found")); + + user.changeEmail(command.getNewEmail()); + userCommandRepository.save(user); + + // Публикация события для обновления read model + eventPublisher.publishUserEmailChanged(user); + } +} + +// Query Side +@RestController +@RequestMapping("/api/users/queries") +public class UserQueryController { + + @Autowired + private UserQueryService userQueryService; + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable String id) { + UserView user = userQueryService.getUser(id); + return ResponseEntity.ok(user); + } + + @GetMapping + public ResponseEntity> getUsers(@RequestParam(required = false) String status) { + List users = userQueryService.getUsers(status); + return ResponseEntity.ok(users); + } +} + +@Service +public class UserQueryService { + + @Autowired + private UserViewRepository userViewRepository; + + public UserView getUser(String id) { + return userViewRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found")); + } + + public List getUsers(String status) { + if (status != null) { + return userViewRepository.findByStatus(status); + } + return userViewRepository.findAll(); + } +} + +// Event Handler для синхронизации read model +@Component +public class UserEventHandler { + + @Autowired + private UserViewRepository userViewRepository; + + @EventListener + @Async + public void handleUserCreated(UserCreatedEvent event) { + UserView userView = new UserView(); + userView.setId(event.getUserId()); + userView.setName(event.getName()); + userView.setEmail(event.getEmail()); + userView.setStatus("ACTIVE"); + userView.setCreatedAt(event.getTimestamp()); + + userViewRepository.save(userView); + } + + @EventListener + @Async + public void handleUserEmailChanged(UserEmailChangedEvent event) { + UserView userView = userViewRepository.findById(event.getUserId()) + .orElseThrow(() -> new UserNotFoundException("User not found")); + + userView.setEmail(event.getNewEmail()); + userView.setUpdatedAt(event.getTimestamp()); + + userViewRepository.save(userView); + } +} +``` + +**Python примеры - CQRS:** + +```python +from dataclasses import dataclass +from typing import List, Optional +from datetime import datetime +import asyncio + +@dataclass +class CreateUserCommand: + name: str + email: str + +@dataclass +class ChangeEmailCommand: + new_email: str + +@dataclass +class UserView: + id: str + name: str + email: str + status: str + created_at: datetime + updated_at: Optional[datetime] = None + +class UserCommandService: + def __init__(self, user_command_repository, event_publisher): + self.user_command_repository = user_command_repository + self.event_publisher = event_publisher + + async def create_user(self, command: CreateUserCommand) -> str: + user = User(command.name, command.email) + user = await self.user_command_repository.save(user) + + # Публикация события для обновления read model + await self.event_publisher.publish_user_created(user) + + return user.id + + async def change_email(self, user_id: str, command: ChangeEmailCommand): + user = await self.user_command_repository.find_by_id(user_id) + if not user: + raise UserNotFoundException("User not found") + + user.change_email(command.new_email) + await self.user_command_repository.save(user) + + # Публикация события для обновления read model + await self.event_publisher.publish_user_email_changed(user) + +class UserQueryService: + def __init__(self, user_view_repository): + self.user_view_repository = user_view_repository + + async def get_user(self, user_id: str) -> UserView: + user_view = await self.user_view_repository.find_by_id(user_id) + if not user_view: + raise UserNotFoundException("User not found") + return user_view + + async def get_users(self, status: Optional[str] = None) -> List[UserView]: + if status: + return await self.user_view_repository.find_by_status(status) + return await self.user_view_repository.find_all() + +class UserEventHandler: + def __init__(self, user_view_repository): + self.user_view_repository = user_view_repository + + async def handle_user_created(self, event: UserCreatedEvent): + user_view = UserView( + id=event.user_id, + name=event.name, + email=event.email, + status="ACTIVE", + created_at=event.timestamp + ) + + await self.user_view_repository.save(user_view) + + async def handle_user_email_changed(self, event: UserEmailChangedEvent): + user_view = await self.user_view_repository.find_by_id(event.user_id) + if not user_view: + raise UserNotFoundException("User not found") + + user_view.email = event.new_email + user_view.updated_at = event.timestamp + + await self.user_view_repository.save(user_view) +``` + +#### 3. Saga Pattern (Паттерн Сага) + +**Принцип:** Управление распределенными транзакциями через последовательность локальных транзакций. + +**Типы Saga:** +- **Choreography Saga** - каждый сервис знает о следующих шагах +- **Orchestration Saga** - центральный оркестратор управляет процессом + +**Choreography Saga:** + +```java +// Order Saga Events +public class OrderCreatedEvent { + private String orderId; + private String customerId; + private BigDecimal amount; + private List items; + // getters/setters +} + +public class PaymentProcessedEvent { + private String orderId; + private String paymentId; + private boolean success; + // getters/setters +} + +public class InventoryReservedEvent { + private String orderId; + private List reservedItems; + private boolean success; + // getters/setters +} + +// Order Service +@Service +public class OrderService { + + @Autowired + private OrderRepository orderRepository; + + @Autowired + private ApplicationEventPublisher eventPublisher; + + @Transactional + public String createOrder(CreateOrderRequest request) { + Order order = new Order(request.getCustomerId(), request.getItems()); + order = orderRepository.save(order); + + // Публикация события для начала саги + eventPublisher.publishEvent(new OrderCreatedEvent(order.getId(), order.getCustomerId(), order.getAmount(), order.getItems())); + + return order.getId(); + } + + @EventListener + @Async + public void handlePaymentProcessed(PaymentProcessedEvent event) { + if (event.isSuccess()) { + // Платеж успешен - резервируем товары + eventPublisher.publishEvent(new ReserveInventoryCommand(event.getOrderId(), event.getOrderItems())); + } else { + // Платеж не прошел - отменяем заказ + cancelOrder(event.getOrderId()); + } + } + + @EventListener + @Async + public void handleInventoryReserved(InventoryReservedEvent event) { + if (event.isSuccess()) { + // Товары зарезервированы - подтверждаем заказ + confirmOrder(event.getOrderId()); + } else { + // Не удалось зарезервировать - отменяем платеж + eventPublisher.publishEvent(new CancelPaymentCommand(event.getOrderId())); + } + } +} + +// Payment Service +@Service +public class PaymentService { + + @Autowired + private ApplicationEventPublisher eventPublisher; + + @EventListener + @Async + public void handleOrderCreated(OrderCreatedEvent event) { + try { + // Обработка платежа + String paymentId = processPayment(event.getCustomerId(), event.getAmount()); + + // Публикация успешного результата + eventPublisher.publishEvent(new PaymentProcessedEvent(event.getOrderId(), paymentId, true)); + + } catch (PaymentException e) { + // Публикация неуспешного результата + eventPublisher.publishEvent(new PaymentProcessedEvent(event.getOrderId(), null, false)); + } + } + + @EventListener + @Async + public void handleCancelPayment(CancelPaymentCommand command) { + // Отмена платежа + cancelPayment(command.getOrderId()); + } +} + +// Inventory Service +@Service +public class InventoryService { + + @Autowired + private ApplicationEventPublisher eventPublisher; + + @EventListener + @Async + public void handleReserveInventory(ReserveInventoryCommand command) { + try { + // Резервирование товаров + List reservedItems = reserveItems(command.getOrderItems()); + + // Публикация успешного результата + eventPublisher.publishEvent(new InventoryReservedEvent(command.getOrderId(), reservedItems, true)); + + } catch (InsufficientInventoryException e) { + // Публикация неуспешного результата + eventPublisher.publishEvent(new InventoryReservedEvent(command.getOrderId(), null, false)); + } + } +} +``` + +**Orchestration Saga:** + +```java +// Saga Orchestrator +@Component +public class OrderSagaOrchestrator { + + @Autowired + private PaymentService paymentService; + + @Autowired + private InventoryService inventoryService; + + @Autowired + private OrderService orderService; + + @Autowired + private SagaRepository sagaRepository; + + public void startOrderSaga(CreateOrderRequest request) { + OrderSaga saga = new OrderSaga(request); + sagaRepository.save(saga); + + // Шаг 1: Создание заказа + String orderId = orderService.createOrder(request); + saga.setOrderId(orderId); + saga.setCurrentStep(SagaStep.CREATE_ORDER); + sagaRepository.save(saga); + + // Шаг 2: Обработка платежа + processPayment(saga); + } + + private void processPayment(OrderSaga saga) { + try { + String paymentId = paymentService.processPayment(saga.getCustomerId(), saga.getAmount()); + saga.setPaymentId(paymentId); + saga.setCurrentStep(SagaStep.PROCESS_PAYMENT); + sagaRepository.save(saga); + + // Шаг 3: Резервирование товаров + reserveInventory(saga); + + } catch (PaymentException e) { + // Компенсация: отмена заказа + compensateOrderCreation(saga); + } + } + + private void reserveInventory(OrderSaga saga) { + try { + List reservedItems = inventoryService.reserveItems(saga.getOrderItems()); + saga.setReservedItems(reservedItems); + saga.setCurrentStep(SagaStep.RESERVE_INVENTORY); + sagaRepository.save(saga); + + // Шаг 4: Подтверждение заказа + confirmOrder(saga); + + } catch (InsufficientInventoryException e) { + // Компенсация: отмена платежа + compensatePayment(saga); + } + } + + private void confirmOrder(OrderSaga saga) { + orderService.confirmOrder(saga.getOrderId()); + saga.setCurrentStep(SagaStep.CONFIRM_ORDER); + saga.setStatus(SagaStatus.COMPLETED); + sagaRepository.save(saga); + } + + private void compensatePayment(OrderSaga saga) { + paymentService.cancelPayment(saga.getPaymentId()); + compensateOrderCreation(saga); + } + + private void compensateOrderCreation(OrderSaga saga) { + orderService.cancelOrder(saga.getOrderId()); + saga.setStatus(SagaStatus.COMPENSATED); + sagaRepository.save(saga); + } +} +``` + +#### 4. Bulkhead Pattern (Переборка) + +**Принцип:** Изоляция критически важных ресурсов от отказа других ресурсов. + +```java +// Thread Pool Isolation +@Configuration +public class ThreadPoolConfig { + + @Bean("userServiceExecutor") + public ThreadPoolTaskExecutor userServiceExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); + executor.setMaxPoolSize(20); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("user-service-"); + executor.initialize(); + return executor; + } + + @Bean("paymentServiceExecutor") + public ThreadPoolTaskExecutor paymentServiceExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(50); + executor.setThreadNamePrefix("payment-service-"); + executor.initialize(); + return executor; + } + + @Bean("notificationServiceExecutor") + public ThreadPoolTaskExecutor notificationServiceExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(3); + executor.setMaxPoolSize(5); + executor.setQueueCapacity(25); + executor.setThreadNamePrefix("notification-service-"); + executor.initialize(); + return executor; + } +} + +// Service с изолированными thread pools +@Service +public class BulkheadUserService { + + @Autowired + @Qualifier("userServiceExecutor") + private ThreadPoolTaskExecutor userServiceExecutor; + + @Autowired + @Qualifier("paymentServiceExecutor") + private ThreadPoolTaskExecutor paymentServiceExecutor; + + @Autowired + @Qualifier("notificationServiceExecutor") + private ThreadPoolTaskExecutor notificationServiceExecutor; + + public CompletableFuture createUser(CreateUserRequest request) { + return CompletableFuture.supplyAsync(() -> { + // Обработка пользователя в изолированном пуле + return processUserCreation(request); + }, userServiceExecutor); + } + + public CompletableFuture processPayment(PaymentRequest request) { + return CompletableFuture.supplyAsync(() -> { + // Обработка платежа в изолированном пуле + return processPaymentInternal(request); + }, paymentServiceExecutor); + } + + public CompletableFuture sendNotification(NotificationRequest request) { + return CompletableFuture.runAsync(() -> { + // Отправка уведомления в изолированном пуле + sendNotificationInternal(request); + }, notificationServiceExecutor); + } +} +``` + +#### 5. Database Per Service Pattern + +**Принцип:** Каждый микросервис имеет собственную базу данных. + +```java +// User Service Database +@Configuration +@EnableJpaRepositories(basePackages = "com.example.userservice.repository") +public class UserServiceDatabaseConfig { + + @Bean + @Primary + public DataSource userDataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://user-db:5432/user_service"); + config.setUsername("user_service_user"); + config.setPassword("user_service_password"); + config.setMaximumPoolSize(20); + return new HikariDataSource(config); + } + + @Bean + @Primary + public LocalContainerEntityManagerFactoryBean userEntityManagerFactory() { + LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(userDataSource()); + em.setPackagesToScan("com.example.userservice.entity"); + + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + em.setJpaVendorAdapter(vendorAdapter); + + Properties properties = new Properties(); + properties.setProperty("hibernate.hbm2ddl.auto", "update"); + properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect"); + em.setJpaProperties(properties); + + return em; + } +} + +// Order Service Database +@Configuration +@EnableJpaRepositories(basePackages = "com.example.orderservice.repository") +public class OrderServiceDatabaseConfig { + + @Bean + public DataSource orderDataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://order-db:5432/order_service"); + config.setUsername("order_service_user"); + config.setPassword("order_service_password"); + config.setMaximumPoolSize(15); + return new HikariDataSource(config); + } + + @Bean + public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory() { + LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(orderDataSource()); + em.setPackagesToScan("com.example.orderservice.entity"); + + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + em.setJpaVendorAdapter(vendorAdapter); + + Properties properties = new Properties(); + properties.setProperty("hibernate.hbm2ddl.auto", "update"); + properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect"); + em.setJpaProperties(properties); + + return em; + } +} +``` + +--- + +## Отказоустойчивость + +### Что такое отказоустойчивость + +**Отказоустойчивость** — способность системы продолжать работу при отказе отдельных компонентов. + +### Типы отказов + +#### 1. Hardware Failures +- Отказ диска +- Отказ памяти +- Отказ сети +- Отказ процессора + +#### 2. Software Failures +- Ошибки в коде +- Утечки памяти +- Deadlocks +- Infinite loops + +#### 3. Network Failures +- Потеря пакетов +- Высокая задержка +- Разрыв соединения +- DDoS атаки + +### Паттерны отказоустойчивости + +#### 1. Circuit Breaker (Автоматический выключатель) + +**Технический пример - Circuit Breaker с Spring Cloud:** + +```java +// Circuit Breaker Configuration +@Configuration +@EnableCircuitBreaker +public class CircuitBreakerConfig { + + @Bean + public CircuitBreakerFactory circuitBreakerFactory() { + return new DefaultCircuitBreakerFactory(); + } +} + +// Service with Circuit Breaker +@Service +public class PaymentService { + + @Autowired + private RestTemplate restTemplate; + + @CircuitBreaker(name = "payment-service", fallbackMethod = "processPaymentFallback") + @Retry(name = "payment-service") + @TimeLimiter(name = "payment-service") + public CompletableFuture processPayment(PaymentRequest request) { + return CompletableFuture.supplyAsync(() -> { + try { + ResponseEntity response = restTemplate.postForEntity( + "http://payment-service/api/payments/process", + request, + PaymentResult.class + ); + + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + throw new PaymentServiceException("Payment service returned error"); + } + + } catch (Exception e) { + throw new PaymentServiceException("Payment processing failed", e); + } + }); + } + + // Fallback method + public CompletableFuture processPaymentFallback(PaymentRequest request, Exception ex) { + log.warn("Payment service is down, using fallback for request: {}", request, ex); + + // Fallback logic - save to queue for later processing + return CompletableFuture.completedFuture( + new PaymentResult( + null, + PaymentStatus.PENDING, + "Payment queued for later processing", + "Service temporarily unavailable" + ) + ); + } +} + +// Advanced Circuit Breaker Implementation +@Component +public class AdvancedCircuitBreaker { + + private final Map circuitStates = new ConcurrentHashMap<>(); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + public T execute(String serviceName, Supplier operation, Supplier fallback) { + CircuitBreakerState state = circuitStates.computeIfAbsent(serviceName, k -> new CircuitBreakerState()); + + if (state.isOpen()) { + if (state.shouldAttemptReset()) { + state.setState(CircuitState.HALF_OPEN); + } else { + return fallback.get(); + } + } + + try { + T result = operation.get(); + state.onSuccess(); + return result; + } catch (Exception e) { + state.onFailure(); + return fallback.get(); + } + } + + private static class CircuitBreakerState { + private volatile CircuitState state = CircuitState.CLOSED; + private final AtomicInteger failureCount = new AtomicInteger(0); + private final AtomicLong lastFailureTime = new AtomicLong(0); + private final AtomicInteger successCount = new AtomicInteger(0); + + private static final int FAILURE_THRESHOLD = 5; + private static final long TIMEOUT_DURATION = 60000; // 1 minute + private static final int SUCCESS_THRESHOLD = 3; + + public boolean isOpen() { + return state == CircuitState.OPEN; + } + + public boolean shouldAttemptReset() { + return System.currentTimeMillis() - lastFailureTime.get() > TIMEOUT_DURATION; + } + + public void onSuccess() { + if (state == CircuitState.HALF_OPEN) { + int successes = successCount.incrementAndGet(); + if (successes >= SUCCESS_THRESHOLD) { + state = CircuitState.CLOSED; + failureCount.set(0); + successCount.set(0); + } + } else { + failureCount.set(0); + } + } + + public void onFailure() { + int failures = failureCount.incrementAndGet(); + lastFailureTime.set(System.currentTimeMillis()); + + if (failures >= FAILURE_THRESHOLD) { + state = CircuitState.OPEN; + successCount.set(0); + } + } + + public void setState(CircuitState newState) { + this.state = newState; + } + } + + private enum CircuitState { + CLOSED, OPEN, HALF_OPEN + } +} +``` + +**Node.js примеры - Circuit Breaker:** + +**Circuit Breaker с библиотекой opossum:** +```typescript +import CircuitBreaker from 'opossum'; +import axios from 'axios'; + +// Circuit Breaker Configuration +const circuitBreakerOptions = { + timeout: 3000, // 3 seconds + errorThresholdPercentage: 50, // 50% error rate + resetTimeout: 30000, // 30 seconds + rollingCountTimeout: 10000, // 10 seconds + rollingCountBuckets: 10, + name: 'payment-service', + group: 'payment' +}; + +// Service with Circuit Breaker +class PaymentService { + private circuitBreaker: CircuitBreaker; + + constructor() { + this.circuitBreaker = new CircuitBreaker(this.callPaymentService.bind(this), circuitBreakerOptions); + + // Event listeners + this.circuitBreaker.on('open', () => { + console.log('Circuit breaker opened for payment service'); + }); + + this.circuitBreaker.on('halfOpen', () => { + console.log('Circuit breaker half-open for payment service'); + }); + + this.circuitBreaker.on('close', () => { + console.log('Circuit breaker closed for payment service'); + }); + + this.circuitBreaker.on('fallback', (error: Error) => { + console.log('Circuit breaker fallback triggered:', error.message); + }); + } + + async processPayment(request: PaymentRequest): Promise { + try { + return await this.circuitBreaker.fire(request); + } catch (error) { + console.error('Payment processing failed:', error); + throw error; + } + } + + private async callPaymentService(request: PaymentRequest): Promise { + const response = await axios.post('http://payment-service/api/payments/process', request, { + timeout: 5000 + }); + + if (response.status === 200) { + return response.data; + } else { + throw new Error(`Payment service returned error: ${response.status}`); + } + } + + // Fallback method + private async processPaymentFallback(request: PaymentRequest, error: Error): Promise { + console.warn('Payment service is down, using fallback for request:', request, error); + + // Fallback logic - save to queue for later processing + return { + id: null, + status: 'PENDING', + message: 'Payment queued for later processing', + error: 'Service temporarily unavailable' + }; + } +} + +// Advanced Circuit Breaker Implementation +class AdvancedCircuitBreaker { + private circuitStates: Map = new Map(); + private scheduler: NodeJS.Timeout; + + constructor() { + this.scheduler = setInterval(() => { + this.checkCircuitStates(); + }, 1000); + } + + async execute( + serviceName: string, + operation: () => Promise, + fallback: () => Promise + ): Promise { + const state = this.getOrCreateState(serviceName); + + if (state.isOpen()) { + if (state.shouldAttemptReset()) { + state.setState(CircuitState.HALF_OPEN); + } else { + return await fallback(); + } + } + + try { + const result = await operation(); + state.onSuccess(); + return result; + } catch (error) { + state.onFailure(); + return await fallback(); + } + } + + private getOrCreateState(serviceName: string): CircuitBreakerState { + if (!this.circuitStates.has(serviceName)) { + this.circuitStates.set(serviceName, new CircuitBreakerState()); + } + return this.circuitStates.get(serviceName)!; + } + + private checkCircuitStates(): void { + for (const [serviceName, state] of this.circuitStates) { + if (state.isOpen() && state.shouldAttemptReset()) { + state.setState(CircuitState.HALF_OPEN); + console.log(`Circuit breaker for ${serviceName} moved to HALF_OPEN`); + } + } + } + + destroy(): void { + if (this.scheduler) { + clearInterval(this.scheduler); + } + } +} + +class CircuitBreakerState { + private state: CircuitState = CircuitState.CLOSED; + private failureCount: number = 0; + private lastFailureTime: number = 0; + private successCount: number = 0; + + private static readonly FAILURE_THRESHOLD = 5; + private static readonly TIMEOUT_DURATION = 60000; // 1 minute + private static readonly SUCCESS_THRESHOLD = 3; + + isOpen(): boolean { + return this.state === CircuitState.OPEN; + } + + shouldAttemptReset(): boolean { + return Date.now() - this.lastFailureTime > CircuitBreakerState.TIMEOUT_DURATION; + } + + onSuccess(): void { + if (this.state === CircuitState.HALF_OPEN) { + this.successCount++; + if (this.successCount >= CircuitBreakerState.SUCCESS_THRESHOLD) { + this.state = CircuitState.CLOSED; + this.failureCount = 0; + this.successCount = 0; + console.log('Circuit breaker moved to CLOSED'); + } + } else { + this.failureCount = 0; + } + } + + onFailure(): void { + this.failureCount++; + this.lastFailureTime = Date.now(); + + if (this.failureCount >= CircuitBreakerState.FAILURE_THRESHOLD) { + this.state = CircuitState.OPEN; + this.successCount = 0; + console.log('Circuit breaker moved to OPEN'); + } + } + + setState(newState: CircuitState): void { + this.state = newState; + } +} + +enum CircuitState { + CLOSED = 'CLOSED', + OPEN = 'OPEN', + HALF_OPEN = 'HALF_OPEN' +} + +// Usage example +const paymentService = new PaymentService(); +const advancedCircuitBreaker = new AdvancedCircuitBreaker(); + +// Using opossum circuit breaker +async function processPaymentWithOpossum(request: PaymentRequest): Promise { + return await paymentService.processPayment(request); +} + +// Using custom circuit breaker +async function processPaymentWithCustom(request: PaymentRequest): Promise { + return await advancedCircuitBreaker.execute( + 'payment-service', + async () => { + const response = await axios.post('http://payment-service/api/payments/process', request); + return response.data; + }, + async () => { + console.log('Using fallback for payment processing'); + return { + id: null, + status: 'PENDING', + message: 'Payment queued for later processing', + error: 'Service temporarily unavailable' + }; + } + ); +} + +// Cleanup +process.on('SIGINT', () => { + advancedCircuitBreaker.destroy(); + process.exit(0); +}); +``` + +**Circuit Breaker с библиотекой hystrix-js:** +```typescript +import HystrixCommand from 'hystrix-js'; + +class PaymentHystrixCommand extends HystrixCommand { + constructor(private request: PaymentRequest) { + super({ + name: 'PaymentService', + timeout: 3000, + errorThresholdPercentage: 50, + requestVolumeThreshold: 10, + sleepWindowInMilliseconds: 30000 + }); + } + + run(): Promise { + return axios.post('http://payment-service/api/payments/process', this.request) + .then(response => response.data); + } + + getFallback(): PaymentResult { + console.log('Payment service fallback triggered'); + return { + id: null, + status: 'PENDING', + message: 'Payment queued for later processing', + error: 'Service temporarily unavailable' + }; + } +} + +// Usage +async function processPaymentWithHystrix(request: PaymentRequest): Promise { + const command = new PaymentHystrixCommand(request); + return await command.execute(); +} +``` + +**Rust примеры - Circuit Breaker:** + +```rust +// Circuit Breaker с tokio +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::sync::{Mutex, RwLock}; +use tokio::time::sleep; + +#[derive(Debug, Clone)] +enum CircuitState { + Closed, + Open, + HalfOpen, +} + +#[derive(Debug)] +struct CircuitBreakerConfig { + failure_threshold: usize, + timeout_duration: Duration, + success_threshold: usize, +} + +impl Default for CircuitBreakerConfig { + fn default() -> Self { + Self { + failure_threshold: 5, + timeout_duration: Duration::from_secs(60), + success_threshold: 3, + } + } +} + +#[derive(Debug)] +struct CircuitBreakerState { + state: CircuitState, + failure_count: usize, + success_count: usize, + last_failure_time: Option, +} + +impl Default for CircuitBreakerState { + fn default() -> Self { + Self { + state: CircuitState::Closed, + failure_count: 0, + success_count: 0, + last_failure_time: None, + } + } +} + +pub struct CircuitBreaker { + config: CircuitBreakerConfig, + state: Arc>, +} + +impl CircuitBreaker { + pub fn new(config: CircuitBreakerConfig) -> Self { + Self { + config, + state: Arc::new(RwLock::new(CircuitBreakerState::default())), + } + } + + pub async fn execute(&self, operation: F) -> Result + where + F: FnOnce() -> Result, + E: std::fmt::Display, + { + let current_state = self.get_state().await; + + match current_state { + CircuitState::Open => { + if self.should_attempt_reset().await { + self.set_state(CircuitState::HalfOpen).await; + } else { + return Err(CircuitBreakerError::CircuitOpen); + } + } + CircuitState::HalfOpen | CircuitState::Closed => {} + } + + match operation() { + Ok(result) => { + self.on_success().await; + Ok(result) + } + Err(error) => { + self.on_failure().await; + Err(CircuitBreakerError::OperationFailed(error.to_string())) + } + } + } + + async fn get_state(&self) -> CircuitState { + let state = self.state.read().await; + state.state.clone() + } + + async fn set_state(&self, new_state: CircuitState) { + let mut state = self.state.write().await; + state.state = new_state; + } + + async fn should_attempt_reset(&self) -> bool { + let state = self.state.read().await; + if let Some(last_failure) = state.last_failure_time { + last_failure.elapsed() > self.config.timeout_duration + } else { + false + } + } + + async fn on_success(&self) { + let mut state = self.state.write().await; + + match state.state { + CircuitState::HalfOpen => { + state.success_count += 1; + if state.success_count >= self.config.success_threshold { + state.state = CircuitState::Closed; + state.failure_count = 0; + state.success_count = 0; + } + } + CircuitState::Closed => { + state.failure_count = 0; + } + CircuitState::Open => {} + } + } + + async fn on_failure(&self) { + let mut state = self.state.write().await; + state.failure_count += 1; + state.last_failure_time = Some(Instant::now()); + + if state.failure_count >= self.config.failure_threshold { + state.state = CircuitState::Open; + state.success_count = 0; + } + } +} + +#[derive(Debug)] +pub enum CircuitBreakerError { + CircuitOpen, + OperationFailed(String), +} + +impl std::fmt::Display for CircuitBreakerError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CircuitBreakerError::CircuitOpen => write!(f, "Circuit breaker is open"), + CircuitBreakerError::OperationFailed(msg) => write!(f, "Operation failed: {}", msg), + } + } +} + +impl std::error::Error for CircuitBreakerError {} + +// Payment Service с Circuit Breaker +pub struct PaymentService { + circuit_breaker: CircuitBreaker, + http_client: reqwest::Client, +} + +impl PaymentService { + pub fn new() -> Self { + let config = CircuitBreakerConfig { + failure_threshold: 5, + timeout_duration: Duration::from_secs(30), + success_threshold: 3, + }; + + Self { + circuit_breaker: CircuitBreaker::new(config), + http_client: reqwest::Client::new(), + } + } + + pub async fn process_payment(&self, request: PaymentRequest) -> Result { + self.circuit_breaker.execute(|| async { + let response = self.http_client + .post("http://payment-service/api/payments/process") + .json(&request) + .timeout(Duration::from_secs(5)) + .send() + .await?; + + if response.status().is_success() { + let result: PaymentResult = response.json().await?; + Ok(result) + } else { + Err(format!("Payment service returned error: {}", response.status())) + } + }).await + } + + pub async fn process_payment_with_fallback(&self, request: PaymentRequest) -> PaymentResult { + match self.process_payment(request.clone()).await { + Ok(result) => result, + Err(_) => { + // Fallback logic + PaymentResult { + id: None, + status: "PENDING".to_string(), + message: "Payment queued for later processing".to_string(), + error: Some("Service temporarily unavailable".to_string()), + } + } + } + } +} + +#[derive(Debug, serde::Serialize)] +struct PaymentRequest { + amount: f64, + currency: String, + payment_method: String, + order_id: u64, + customer_id: u64, +} + +#[derive(Debug, serde::Deserialize)] +struct PaymentResult { + id: Option, + status: String, + message: String, + error: Option, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let payment_service = PaymentService::new(); + + let request = PaymentRequest { + amount: 100.0, + currency: "USD".to_string(), + payment_method: "credit_card".to_string(), + order_id: 12345, + customer_id: 67890, + }; + + let result = payment_service.process_payment_with_fallback(request).await; + println!("Payment result: {:?}", result); + + Ok(()) +} +``` + +**Python примеры - Circuit Breaker:** + +```python +# Circuit Breaker с asyncio +import asyncio +import time +from enum import Enum +from typing import Callable, Any, Optional +from dataclasses import dataclass +import aiohttp +import json + +class CircuitState(Enum): + CLOSED = "CLOSED" + OPEN = "OPEN" + HALF_OPEN = "HALF_OPEN" + +@dataclass +class CircuitBreakerConfig: + failure_threshold: int = 5 + timeout_duration: float = 60.0 # seconds + success_threshold: int = 3 + +class CircuitBreakerError(Exception): + def __init__(self, message: str): + self.message = message + super().__init__(self.message) + +class CircuitBreaker: + def __init__(self, config: CircuitBreakerConfig = None): + self.config = config or CircuitBreakerConfig() + self.state = CircuitState.CLOSED + self.failure_count = 0 + self.success_count = 0 + self.last_failure_time: Optional[float] = None + self._lock = asyncio.Lock() + + async def execute(self, operation: Callable[[], Any]) -> Any: + async with self._lock: + if self.state == CircuitState.OPEN: + if self._should_attempt_reset(): + self.state = CircuitState.HALF_OPEN + else: + raise CircuitBreakerError("Circuit breaker is open") + + try: + result = await operation() + await self._on_success() + return result + except Exception as e: + await self._on_failure() + raise CircuitBreakerError(f"Operation failed: {str(e)}") + + def _should_attempt_reset(self) -> bool: + if self.last_failure_time is None: + return False + return time.time() - self.last_failure_time > self.config.timeout_duration + + async def _on_success(self): + async with self._lock: + if self.state == CircuitState.HALF_OPEN: + self.success_count += 1 + if self.success_count >= self.config.success_threshold: + self.state = CircuitState.CLOSED + self.failure_count = 0 + self.success_count = 0 + elif self.state == CircuitState.CLOSED: + self.failure_count = 0 + + async def _on_failure(self): + async with self._lock: + self.failure_count += 1 + self.last_failure_time = time.time() + + if self.failure_count >= self.config.failure_threshold: + self.state = CircuitState.OPEN + self.success_count = 0 + +# Payment Service с Circuit Breaker +class PaymentService: + def __init__(self): + config = CircuitBreakerConfig( + failure_threshold=5, + timeout_duration=30.0, + success_threshold=3 + ) + self.circuit_breaker = CircuitBreaker(config) + self.session = aiohttp.ClientSession() + + async def process_payment(self, request: dict) -> dict: + async def _make_request(): + async with self.session.post( + "http://payment-service/api/payments/process", + json=request, + timeout=aiohttp.ClientTimeout(total=5) + ) as response: + if response.status == 200: + return await response.json() + else: + raise Exception(f"Payment service returned error: {response.status}") + + try: + return await self.circuit_breaker.execute(_make_request) + except CircuitBreakerError: + # Fallback logic + return { + "id": None, + "status": "PENDING", + "message": "Payment queued for later processing", + "error": "Service temporarily unavailable" + } + + async def process_payment_with_fallback(self, request: dict) -> dict: + try: + return await self.process_payment(request) + except Exception as e: + print(f"Payment processing failed: {e}") + return { + "id": None, + "status": "PENDING", + "message": "Payment queued for later processing", + "error": "Service temporarily unavailable" + } + + async def close(self): + await self.session.close() + +# Usage example +async def main(): + payment_service = PaymentService() + + request = { + "amount": 100.0, + "currency": "USD", + "payment_method": "credit_card", + "order_id": 12345, + "customer_id": 67890 + } + + try: + result = await payment_service.process_payment_with_fallback(request) + print(f"Payment result: {result}") + finally: + await payment_service.close() + +if __name__ == "__main__": + asyncio.run(main()) +``` + +**Технический пример - Bulkhead Pattern:** + +```java +// Bulkhead Configuration +@Configuration +public class BulkheadConfig { + + @Bean + public ThreadPoolTaskExecutor paymentExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("payment-"); + executor.initialize(); + return executor; + } + + @Bean + public ThreadPoolTaskExecutor notificationExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(3); + executor.setMaxPoolSize(5); + executor.setQueueCapacity(50); + executor.setThreadNamePrefix("notification-"); + executor.initialize(); + return executor; + } +} + +// Service with Bulkhead Pattern +@Service +public class OrderService { + + @Autowired + @Qualifier("paymentExecutor") + private ThreadPoolTaskExecutor paymentExecutor; + + @Autowired + @Qualifier("notificationExecutor") + private ThreadPoolTaskExecutor notificationExecutor; + + @Autowired + private PaymentService paymentService; + + @Autowired + private NotificationService notificationService; + + public CompletableFuture processOrder(Order order) { + // Payment processing in dedicated thread pool + CompletableFuture paymentFuture = CompletableFuture + .supplyAsync(() -> paymentService.processPayment(order.getPaymentRequest()), paymentExecutor) + .exceptionally(ex -> { + log.error("Payment processing failed for order: {}", order.getId(), ex); + return new PaymentResult(null, PaymentStatus.FAILED, null, ex.getMessage()); + }); + + // Notification processing in separate thread pool + CompletableFuture notificationFuture = CompletableFuture + .runAsync(() -> notificationService.sendOrderConfirmation(order), notificationExecutor) + .exceptionally(ex -> { + log.error("Notification failed for order: {}", order.getId(), ex); + return null; + }); + + // Combine results + return paymentFuture.thenCompose(paymentResult -> { + if (paymentResult.isSuccess()) { + order.setStatus(OrderStatus.PROCESSED); + order.setPaymentId(paymentResult.getTransactionId()); + } else { + order.setStatus(OrderStatus.FAILED); + } + + return CompletableFuture.completedFuture(order); + }); + } +} + +// Timeout Pattern +@Component +public class TimeoutService { + + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5); + + public CompletableFuture executeWithTimeout(Supplier operation, Duration timeout) { + CompletableFuture future = CompletableFuture.supplyAsync(operation); + + CompletableFuture timeoutFuture = new CompletableFuture<>(); + scheduler.schedule(() -> { + timeoutFuture.completeExceptionally(new TimeoutException("Operation timed out")); + }, timeout.toMillis(), TimeUnit.MILLISECONDS); + + return CompletableFuture.anyOf(future, timeoutFuture) + .thenApply(result -> (T) result); + } + + public T executeWithTimeoutSync(Supplier operation, Duration timeout) { + try { + return executeWithTimeout(operation, timeout).get(); + } catch (Exception e) { + throw new RuntimeException("Operation failed or timed out", e); + } + } +} +``` + +#### 2. Retry Pattern (Паттерн повторов) + +```java +@Component +public class RetryService { + public T executeWithRetry(Supplier operation, int maxAttempts) { + Exception lastException = null; + + for (int attempt = 1; attempt <= maxAttempts; attempt++) { + try { + return operation.get(); + } catch (Exception e) { + lastException = e; + + if (attempt < maxAttempts) { + long delay = calculateDelay(attempt); + try { + Thread.sleep(delay); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new RuntimeException(ie); + } + } + } + } + + throw new RuntimeException("Operation failed after " + maxAttempts + " attempts", lastException); + } + + private long calculateDelay(int attempt) { + // Exponential backoff + return (long) Math.pow(2, attempt) * 1000; + } +} +``` + +#### 3. Bulkhead Pattern (Переборка) + +```java +@Configuration +public class BulkheadConfig { + @Bean + public ThreadPoolExecutor paymentExecutor() { + return new ThreadPoolExecutor( + 10, 10, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(100), + new ThreadFactoryBuilder().setNameFormat("payment-%d").build() + ); + } + + @Bean + public ThreadPoolExecutor notificationExecutor() { + return new ThreadPoolExecutor( + 5, 5, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(50), + new ThreadFactoryBuilder().setNameFormat("notification-%d").build() + ); + } +} + +@Service +public class OrderService { + @Autowired + @Qualifier("paymentExecutor") + private Executor paymentExecutor; + + @Autowired + @Qualifier("notificationExecutor") + private Executor notificationExecutor; + + public void processOrder(Order order) { + // обработка платежа в отдельном пуле + paymentExecutor.execute(() -> { + paymentService.processPayment(order.getPayment()); + }); + + // отправка уведомления в отдельном пуле + notificationExecutor.execute(() -> { + notificationService.sendConfirmation(order.getCustomerId()); + }); + } +} +``` + +#### 4. Timeout Pattern (Паттерн таймаута) + +```java +@Service +public class ExternalServiceClient { + private final RestTemplate restTemplate; + + public ExternalServiceClient() { + HttpComponentsClientHttpRequestFactory factory = + new HttpComponentsClientHttpRequestFactory(); + factory.setConnectTimeout(5000); // 5 секунд + factory.setReadTimeout(10000); // 10 секунд + + this.restTemplate = new RestTemplate(factory); + } + + public String callExternalService(String data) { + try { + return restTemplate.postForObject( + "http://external-service/api/process", + data, + String.class + ); + } catch (ResourceAccessException e) { + throw new ServiceTimeoutException("External service timeout", e); + } + } +} +``` + +### Плавная деградация + +```java +@Service +public class RecommendationService { + @Autowired + private MLRecommendationService mlService; + + @Autowired + private RuleBasedRecommendationService ruleService; + + public List getRecommendations(Long userId) { + try { + // пытаемся получить ML рекомендации + return mlService.getRecommendations(userId); + } catch (Exception e) { + log.warn("ML service failed, falling back to rule-based", e); + + try { + // fallback на правило-основанные рекомендации + return ruleService.getRecommendations(userId); + } catch (Exception e2) { + log.error("All recommendation services failed", e2); + // возвращаем популярные товары + return getPopularProducts(); + } + } + } + + private List getPopularProducts() { + return Arrays.asList( + new Product(1L, "Popular Product 1"), + new Product(2L, "Popular Product 2"), + new Product(3L, "Popular Product 3") + ); + } +} +``` + +--- + +## Управление данными + +### Теорема CAP + +**CAP Theorem** утверждает, что распределенная система не может одновременно обеспечить: +- **Consistency** (Консистентность) +- **Availability** (Доступность) +- **Partition Tolerance** (Устойчивость к разделению) + +### Типы консистентности + +#### 1. Strong Consistency +- Все узлы видят одинаковые данные +- Сложно в распределенных системах +- Высокая задержка + +#### 2. Eventual Consistency +- Данные в итоге станут консистентными +- Подходит для большинства случаев +- Низкая задержка + +#### 3. Weak Consistency +- Нет гарантий консистентности +- Используется редко + +### Паттерны управления данными + +#### 1. Event Sourcing + +```java +@Entity +public class OrderEvent { + private Long id; + private String aggregateId; + private String eventType; + private String eventData; + private LocalDateTime occurredAt; +} + +@Service +public class OrderEventStore { + public void saveEvent(OrderEvent event) { + eventRepository.save(event); + } + + public List getEvents(String aggregateId) { + return eventRepository.findByAggregateIdOrderByOccurredAt(aggregateId); + } +} + +@Service +public class OrderProjectionService { + public OrderView buildOrderView(String orderId) { + List events = eventStore.getEvents(orderId); + OrderView view = new OrderView(); + + for (OrderEvent event : events) { + applyEvent(view, event); + } + + return view; + } + + private void applyEvent(OrderView view, OrderEvent event) { + switch (event.getEventType()) { + case "OrderCreated": + view.setId(event.getAggregateId()); + view.setStatus("CREATED"); + break; + case "OrderConfirmed": + view.setStatus("CONFIRMED"); + break; + case "OrderShipped": + view.setStatus("SHIPPED"); + break; + } + } +} +``` + +#### 2. CQRS (Command Query Responsibility Segregation) + +```java +// Command Side +@Service +public class OrderCommandService { + public void createOrder(CreateOrderCommand command) { + Order order = new Order(command); + orderRepository.save(order); + + // обновляем read model + orderReadModelService.updateReadModel(order); + } +} + +// Query Side +@Service +public class OrderQueryService { + public OrderView getOrderView(OrderId orderId) { + return orderReadModelRepository.findById(orderId); + } + + public List getOrdersByCustomer(CustomerId customerId) { + return orderReadModelRepository.findByCustomerId(customerId); + } +} +``` + +#### 3. Saga Pattern + +```java +@Service +public class OrderSaga { + public void processOrder(CreateOrderRequest request) { + Order order = orderService.createOrder(request); + + try { + // шаг 1: резервирование товаров + inventoryService.reserveItems(order.getId(), request.getItems()); + + // шаг 2: обработка платежа + paymentService.processPayment(order.getId(), request.getPayment()); + + // шаг 3: подтверждение заказа + orderService.confirmOrder(order.getId()); + + } catch (Exception e) { + // компенсирующие транзакции + compensateOrder(order.getId()); + } + } + + private void compensateOrder(OrderId orderId) { + try { + inventoryService.releaseItems(orderId); + paymentService.refundPayment(orderId); + orderService.cancelOrder(orderId); + } catch (Exception e) { + log.error("Compensation failed for order: " + orderId, e); + } + } +} +``` + +--- + +## Кэширование + +### Стратегии кэширования + +#### 1. Cache-Aside (Lazy Loading) + +**Технический пример - Advanced Cache-Aside Pattern:** + +```java +@Service +public class UserService { + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private UserRepository userRepository; + + private static final String USER_CACHE_KEY_PREFIX = "user:"; + private static final Duration CACHE_TTL = Duration.ofMinutes(30); + + public User findById(Long id) { + String cacheKey = USER_CACHE_KEY_PREFIX + id; + + try { + // Проверяем кэш + User cachedUser = redisTemplate.opsForValue().get(cacheKey); + if (cachedUser != null) { + log.debug("Cache hit for user: {}", id); + return cachedUser; + } + + log.debug("Cache miss for user: {}", id); + + // Загружаем из БД + User user = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found: " + id)); + + // Сохраняем в кэш асинхронно + CompletableFuture.runAsync(() -> { + try { + redisTemplate.opsForValue().set(cacheKey, user, CACHE_TTL); + log.debug("User cached: {}", id); + } catch (Exception e) { + log.error("Failed to cache user: {}", id, e); + } + }); + + return user; + + } catch (Exception e) { + log.error("Error retrieving user: {}", id, e); + throw new UserServiceException("Failed to retrieve user", e); + } + } + + public List findByIds(List ids) { + List cacheKeys = ids.stream() + .map(id -> USER_CACHE_KEY_PREFIX + id) + .collect(Collectors.toList()); + + // Batch get from cache + List cachedUsers = redisTemplate.opsForValue().multiGet(cacheKeys); + + List result = new ArrayList<>(); + List missingIds = new ArrayList<>(); + + for (int i = 0; i < ids.size(); i++) { + if (cachedUsers.get(i) != null) { + result.add(cachedUsers.get(i)); + } else { + missingIds.add(ids.get(i)); + } + } + + // Load missing users from DB + if (!missingIds.isEmpty()) { + List dbUsers = userRepository.findByIdIn(missingIds); + + // Cache the loaded users + Map usersToCache = new HashMap<>(); + for (User user : dbUsers) { + usersToCache.put(USER_CACHE_KEY_PREFIX + user.getId(), user); + } + + if (!usersToCache.isEmpty()) { + redisTemplate.opsForValue().multiSet(usersToCache); + redisTemplate.expire(usersToCache.keySet(), CACHE_TTL); + } + + result.addAll(dbUsers); + } + + return result; + } + + public void invalidateUserCache(Long userId) { + String cacheKey = USER_CACHE_KEY_PREFIX + userId; + redisTemplate.delete(cacheKey); + log.debug("Cache invalidated for user: {}", userId); + } +} +``` + +#### 2. Write-Through + +```java +@Service +public class UserService { + public void updateUser(User user) { + // обновляем БД + userRepository.save(user); + + // обновляем кэш + String key = "user:" + user.getId(); + redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30)); + } +} +``` + +#### 3. Write-Behind (Write-Back) + +```java +@Service +public class UserService { + @Async + public void updateUser(User user) { + // обновляем кэш сразу + String key = "user:" + user.getId(); + redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30)); + + // обновляем БД асинхронно + CompletableFuture.runAsync(() -> { + userRepository.save(user); + }); + } +} +``` + +### Распределенное кэширование + +#### Кластер Redis + +```java +@Configuration +public class RedisConfig { + @Bean + public LettuceConnectionFactory redisConnectionFactory() { + RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration(); + clusterConfig.addClusterNode(new RedisNode("127.0.0.1", 7000)); + clusterConfig.addClusterNode(new RedisNode("127.0.0.1", 7001)); + clusterConfig.addClusterNode(new RedisNode("127.0.0.1", 7002)); + + return new LettuceConnectionFactory(clusterConfig); + } +} +``` + +#### Memcached + +```java +@Service +public class MemcachedService { + @Autowired + private MemcachedClient memcachedClient; + + public void set(String key, Object value, int expiration) { + try { + memcachedClient.set(key, expiration, value); + } catch (Exception e) { + log.error("Failed to set cache", e); + } + } + + public Object get(String key) { + try { + return memcachedClient.get(key); + } catch (Exception e) { + log.error("Failed to get cache", e); + return null; + } + } +} +``` + +--- + +## Load Balancing + +### Алгоритмы балансировки нагрузки + +#### 1. Round Robin +```java +public class RoundRobinLoadBalancer { + private final List servers; + private int currentIndex = 0; + + public String getServer() { + String server = servers.get(currentIndex); + currentIndex = (currentIndex + 1) % servers.size(); + return server; + } +} +``` + +#### 2. Weighted Round Robin +```java +public class WeightedRoundRobinLoadBalancer { + private final List servers; + private int currentWeight = 0; + private int currentIndex = -1; + + public String getServer() { + int totalWeight = servers.stream().mapToInt(Server::getWeight).sum(); + + while (true) { + currentIndex = (currentIndex + 1) % servers.size(); + + if (currentIndex == 0) { + currentWeight = currentWeight - 1; + if (currentWeight <= 0) { + currentWeight = totalWeight; + } + } + + if (servers.get(currentIndex).getWeight() >= currentWeight) { + return servers.get(currentIndex).getAddress(); + } + } + } +} +``` + +#### 3. Least Connections +```java +public class LeastConnectionsLoadBalancer { + private final Map connectionCounts = new ConcurrentHashMap<>(); + + public String getServer() { + return connectionCounts.entrySet().stream() + .min(Map.Entry.comparingByValue()) + .map(Map.Entry::getKey) + .orElse(null); + } + + public void incrementConnections(String server) { + connectionCounts.computeIfAbsent(server, k -> new AtomicInteger(0)).incrementAndGet(); + } + + public void decrementConnections(String server) { + connectionCounts.computeIfPresent(server, (k, v) -> { + v.decrementAndGet(); + return v.get() > 0 ? v : null; + }); + } +} +``` + +### Проверки здоровья + +```java +@Component +public class HealthChecker { + private final RestTemplate restTemplate; + + public HealthChecker() { + HttpComponentsClientHttpRequestFactory factory = + new HttpComponentsClientHttpRequestFactory(); + factory.setConnectTimeout(5000); + factory.setReadTimeout(5000); + this.restTemplate = new RestTemplate(factory); + } + + public boolean isHealthy(String serverUrl) { + try { + ResponseEntity response = restTemplate.getForEntity( + serverUrl + "/health", String.class); + return response.getStatusCode().is2xxSuccessful(); + } catch (Exception e) { + return false; + } + } + + @Scheduled(fixedRate = 30000) // каждые 30 секунд + public void checkHealth() { + servers.forEach(server -> { + boolean healthy = isHealthy(server.getUrl()); + server.setHealthy(healthy); + + if (!healthy) { + log.warn("Server {} is unhealthy", server.getUrl()); + } + }); + } +} +``` + +--- + +## Мониторинг производительности + +### Метрики производительности + +#### 1. Application Metrics +```java +@Component +public class MetricsCollector { + private final MeterRegistry meterRegistry; + + public MetricsCollector(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public void recordRequestDuration(String endpoint, Duration duration) { + Timer.Sample sample = Timer.start(meterRegistry); + sample.stop(Timer.builder("http.request.duration") + .tag("endpoint", endpoint) + .register(meterRegistry)); + } + + public void recordError(String endpoint, String errorType) { + Counter.builder("http.request.errors") + .tag("endpoint", endpoint) + .tag("error_type", errorType) + .register(meterRegistry) + .increment(); + } +} +``` + +#### 2. Business Metrics +```java +@Service +public class OrderMetrics { + private final MeterRegistry meterRegistry; + + public void recordOrderCreated() { + Counter.builder("orders.created") + .register(meterRegistry) + .increment(); + } + + public void recordOrderValue(BigDecimal value) { + DistributionSummary.builder("orders.value") + .register(meterRegistry) + .record(value.doubleValue()); + } +} +``` + +### APM (Мониторинг производительности приложений) + +#### Пример New Relic +```java +@RestController +public class OrderController { + @Autowired + private NewRelic newRelic; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + newRelic.addCustomParameter("customer_id", request.getCustomerId()); + newRelic.addCustomParameter("order_value", request.getTotalValue()); + + try { + Order order = orderService.createOrder(request); + newRelic.recordMetric("Orders/Created", 1); + return ResponseEntity.ok(order); + } catch (Exception e) { + newRelic.recordMetric("Orders/Errors", 1); + throw e; + } + } +} +``` + +--- + +## Практические задания + +### Задание 1: Circuit Breaker +Реализуйте Circuit Breaker для внешнего сервиса с экспоненциальным backoff. + +### Задание 2: Кэширование +Настройте многоуровневое кэширование (L1: local, L2: Redis). + +### Задание 3: Load Balancer +Создайте простой load balancer с health checks. + +--- + +## Дополнительные ресурсы + +### Книги +- "Designing Data-Intensive Applications" by Martin Kleppmann +- "Release It!" by Michael Nygard +- "Site Reliability Engineering" by Google + +### Инструменты +- **Monitoring:** Prometheus, Grafana, New Relic +- **Caching:** Redis, Memcached, Hazelcast +- **Load Balancing:** HAProxy, NGINX, AWS ALB + +--- + +## Контрольные вопросы + +1. Какие типы масштабирования вы знаете? +2. Что такое CAP Theorem и как оно влияет на архитектуру? +3. Какие паттерны отказоустойчивости вы знаете? +4. Как работает Circuit Breaker? +5. Какие стратегии кэширования существуют? +6. Как реализовать health checks? +7. Какие метрики важны для мониторинга производительности? +8. Как обеспечить graceful degradation? diff --git a/modules/module5/README.md b/modules/module5/README.md new file mode 100644 index 0000000..c7e4c71 --- /dev/null +++ b/modules/module5/README.md @@ -0,0 +1,113 @@ +# Модуль 5: Мониторинг и наблюдаемость + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля + +Этот модуль посвящен изучению современных подходов к мониторингу и наблюдаемости в распределенных системах. Студенты изучат принципы сбора метрик, логирования, трассировки и создания эффективных систем мониторинга. + +## Содержание модуля + +### 1. [Основы мониторинга](sections/01-monitoring-basics.md) +- Цели и типы мониторинга +- Метрики мониторинга (Counters, Gauges, Histograms, Summaries) +- Принципы проектирования систем мониторинга + +### 2. [Prometheus и Grafana](sections/02-prometheus-grafana.md) +- Архитектура Prometheus +- PromQL (язык запросов Prometheus) +- Создание дашбордов в Grafana +- Настройка алертов + +### 3. [Логирование](sections/03-logging.md) +- Structured Logging +- Log Levels и Correlation ID +- ELK Stack (Elasticsearch, Logstash, Kibana) +- Fluentd для сбора логов + +### 4. [Distributed Tracing](sections/04-distributed-tracing.md) +- Принципы распределенной трассировки +- OpenTelemetry +- Jaeger для анализа трассировок +- Интеграция с микросервисами + +### 5. [Observability](sections/05-observability.md) +- Три столпа наблюдаемости +- Health Checks +- Custom Metrics +- Паттерны наблюдаемости + +### 6. [Alerting](sections/06-alerting.md) +- Принципы алертинга +- Правила оповещений Prometheus +- Конфигурация Alertmanager +- Предотвращение усталости от алертов + +### 7. [SLI/SLO/SLA](sections/07-sli-slo-sla.md) +- SLI (Service Level Indicators) +- SLO (Service Level Objectives) +- SLA (Service Level Agreements) +- Мониторинг соответствия SLO + +## Ключевые компетенции + +После изучения модуля студенты будут уметь: + +- Проектировать системы мониторинга +- Настраивать Prometheus и Grafana +- Реализовывать структурированное логирование +- Настраивать распределенную трассировку +- Создавать эффективные алерты +- Определять и мониторить SLI/SLO/SLA + +## Практические задания + +1. **Настройка мониторинга** + - Установка и конфигурация Prometheus + - Создание дашбордов в Grafana + - Настройка сбора метрик из приложений + +2. **Структурированное логирование** + - Настройка ELK Stack + - Реализация Correlation ID + - Создание шаблонов логов + +3. **Distributed Tracing** + - Настройка OpenTelemetry + - Интеграция с Jaeger + - Анализ трассировок + +4. **Система алертинга** + - Создание правил алертов + - Настройка Alertmanager + - Интеграция с уведомлениями + +5. **SLI/SLO/SLA** + - Определение ключевых метрик + - Создание SLO + - Мониторинг соответствия целям + +6. **Observability Dashboard** + - Создание комплексного дашборда + - Интеграция метрик, логов и трассировок + - Настройка автоматических проверок + +## Дополнительные ресурсы + +- [Prometheus Documentation](https://prometheus.io/docs/) - официальная документация Prometheus +- [Grafana Documentation](https://grafana.com/docs/) - документация Grafana +- [OpenTelemetry](https://opentelemetry.io/) - стандарт для наблюдаемости +- [Jaeger Documentation](https://www.jaegertracing.io/docs/) - документация Jaeger +- [ELK Stack](https://www.elastic.co/elastic-stack/) - стек для логирования + +## Критерии оценки + +- Понимание принципов мониторинга (20%) +- Навыки работы с Prometheus и Grafana (25%) +- Опыт настройки логирования (20%) +- Знание Distributed Tracing (15%) +- Навыки создания алертов (10%) +- Понимание SLI/SLO/SLA (10%) diff --git a/modules/module5/sections/01-monitoring-basics.md b/modules/module5/sections/01-monitoring-basics.md new file mode 100644 index 0000000..cc1178c --- /dev/null +++ b/modules/module5/sections/01-monitoring-basics.md @@ -0,0 +1,187 @@ +# Основы мониторинга + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 5](../README.md) + +--- + +## Что такое мониторинг + +**Мониторинг** — процесс сбора, анализа и использования информации о работе системы для обеспечения её стабильности и производительности. + +## Цели мониторинга + +### 1. Проактивное обнаружение проблем +- Выявление проблем до их влияния на пользователей +- Предотвращение каскадных отказов +- Оптимизация производительности + +### 2. Реактивное решение проблем +- Быстрая диагностика инцидентов +- Сокращение времени восстановления (MTTR) +- Анализ причин отказов + +### 3. Бизнес-аналитика +- Понимание поведения пользователей +- Анализ производительности бизнес-процессов +- Планирование ресурсов + +## Типы мониторинга + +### 1. Infrastructure Monitoring +- CPU, Memory, Disk, Network +- Контейнеры и виртуальные машины +- Базы данных и кэши + +### 2. Application Monitoring +- Метрики приложения +- Производительность кода +- Ошибки и исключения + +### 3. Business Monitoring +- Ключевые бизнес-метрики +- Пользовательская активность +- Конверсии и доходы + +## Метрики мониторинга + +### 1. Counters (Счетчики) +**Определение:** Метрики, которые только увеличиваются. + +**Технический пример - Advanced Metrics Collection:** + +```java +@Component +public class RequestCounter { + private final Counter requestCounter; + private final Counter errorCounter; + private final Timer requestTimer; + private final MeterRegistry meterRegistry; + + public RequestCounter(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + + this.requestCounter = Counter.builder("http.requests.total") + .description("Total number of HTTP requests") + .tag("service", "user-service") + .register(meterRegistry); + + this.errorCounter = Counter.builder("http.errors.total") + .description("Total number of HTTP errors") + .tag("service", "user-service") + .register(meterRegistry); + + this.requestTimer = Timer.builder("http.request.duration") + .description("HTTP request duration") + .tag("service", "user-service") + .register(meterRegistry); + } + + public void increment(String method, String endpoint, String status) { + requestCounter.increment( + Tags.of( + "method", method, + "endpoint", endpoint, + "status", status + ) + ); + + if (Integer.parseInt(status) >= 400) { + errorCounter.increment( + Tags.of( + "method", method, + "endpoint", endpoint, + "error_type", getErrorType(status) + ) + ); + } + } + + public Timer.Sample startTimer() { + return Timer.start(meterRegistry); + } + + public void recordDuration(Timer.Sample sample, String method, String endpoint) { + sample.stop(requestTimer.tags( + "method", method, + "endpoint", endpoint + )); + } + + private String getErrorType(String status) { + int statusCode = Integer.parseInt(status); + if (statusCode >= 400 && statusCode < 500) { + return "client_error"; + } else if (statusCode >= 500) { + return "server_error"; + } + return "unknown"; + } +} +``` + +### 2. Gauges (Измерители) +**Определение:** Метрики, которые могут увеличиваться и уменьшаться. + +```java +@Component +public class ActiveConnectionsGauge { + private final AtomicInteger activeConnections = new AtomicInteger(0); + private final Gauge activeConnectionsGauge; + + public ActiveConnectionsGauge(MeterRegistry meterRegistry) { + this.activeConnectionsGauge = Gauge.builder("active.connections") + .description("Number of active connections") + .register(meterRegistry, this, obj -> obj.activeConnections.get()); + } + + public void incrementConnections() { + activeConnections.incrementAndGet(); + } + + public void decrementConnections() { + activeConnections.decrementAndGet(); + } +} +``` + +### 3. Histograms (Гистограммы) +**Определение:** Метрики для измерения распределения значений. + +```java +@Component +public class ResponseTimeHistogram { + private final Timer responseTimeTimer; + + public ResponseTimeHistogram(MeterRegistry meterRegistry) { + this.responseTimeTimer = Timer.builder("http.request.duration") + .description("HTTP request duration") + .register(meterRegistry); + } + + public void recordResponseTime(String endpoint, Duration duration) { + responseTimeTimer.record(duration); + } +} +``` + +### 4. Summaries (Сумматоры) +**Определение:** Метрики для измерения квантилей. + +```java +@Component +public class ResponseTimeSummary { + private final DistributionSummary responseTimeSummary; + + public ResponseTimeSummary(MeterRegistry meterRegistry) { + this.responseTimeSummary = DistributionSummary.builder("http.request.size") + .description("HTTP request size in bytes") + .register(meterRegistry); + } + + public void recordRequestSize(long size) { + responseTimeSummary.record(size); + } +} +``` diff --git a/modules/module5/sections/02-prometheus-grafana.md b/modules/module5/sections/02-prometheus-grafana.md new file mode 100644 index 0000000..4be0a84 --- /dev/null +++ b/modules/module5/sections/02-prometheus-grafana.md @@ -0,0 +1,155 @@ +# Prometheus и Grafana + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 5](../README.md) + +--- + +## Prometheus + +**Prometheus** — система мониторинга и база данных временных рядов. + +### Архитектура Prometheus + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Application │ │ Application │ │ Application │ +│ (Metrics) │ │ (Metrics) │ │ (Metrics) │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ │ + └───────────────────────┼───────────────────────┘ + │ + ┌─────────────────┐ + │ Prometheus │ + │ Server │ + └─────────────────┘ + │ + ┌─────────────────┐ + │ Grafana │ + │ (Dashboard) │ + └─────────────────┘ +``` + +### PromQL (Язык запросов Prometheus) + +#### Основные запросы: +```promql +# Простая метрика +http_requests_total + +# Метрика с фильтрами +http_requests_total{method="GET", status="200"} + +# Агрегация +sum(http_requests_total) by (method) + +# Rate (скорость изменения) +rate(http_requests_total[5m]) + +# Histogram quantiles +histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) +``` + +#### Примеры запросов: +```promql +# Количество запросов в секунду +rate(http_requests_total[5m]) + +# Среднее время ответа +rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]) + +# 95-й перцентиль времени ответа +histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) + +# Ошибки в процентах +rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100 +``` + +### Spring Boot + Prometheus + +```java +@Configuration +public class PrometheusConfig { + @Bean + public MeterRegistry meterRegistry() { + return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + } +} + +@RestController +public class MetricsController { + private final MeterRegistry meterRegistry; + + public MetricsController(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @GetMapping("/metrics") + public String metrics() { + return ((PrometheusMeterRegistry) meterRegistry).scrape(); + } +} +``` + +## Grafana + +**Grafana** — платформа для визуализации метрик и создания дашбордов. + +### Создание дашборда + +```json +{ + "dashboard": { + "title": "Application Metrics", + "panels": [ + { + "title": "Request Rate", + "type": "graph", + "targets": [ + { + "expr": "rate(http_requests_total[5m])", + "legendFormat": "{{method}} {{endpoint}}" + } + ] + }, + { + "title": "Response Time", + "type": "graph", + "targets": [ + { + "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))", + "legendFormat": "95th percentile" + } + ] + } + ] + } +} +``` + +### Алерты в Grafana + +```json +{ + "alert": { + "name": "High Error Rate", + "message": "Error rate is above 5%", + "conditions": [ + { + "query": { + "params": ["A", "5m", "now"] + }, + "reducer": { + "type": "last", + "params": [] + }, + "evaluator": { + "params": [0.05], + "type": "gt" + } + } + ] + } +} +``` diff --git a/modules/module5/sections/03-logging.md b/modules/module5/sections/03-logging.md new file mode 100644 index 0000000..66f4e88 --- /dev/null +++ b/modules/module5/sections/03-logging.md @@ -0,0 +1,238 @@ +# Логирование + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 5](../README.md) + +--- + +## Принципы логирования + +### 1. Structured Logging +**Определение:** Логирование в структурированном формате (JSON). + +**Технический пример - Advanced Structured Logging:** + +```java +@Slf4j +@RestController +public class OrderController { + + @Autowired + private OrderService orderService; + + @Autowired + private MetricsService metricsService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + String correlationId = MDC.get("correlationId"); + String userId = MDC.get("userId"); + + // Structured logging with business context + log.info("Order creation started", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .customerId(request.getCustomerId()) + .orderValue(request.getTotalValue()) + .itemsCount(request.getItems().size()) + .currency(request.getCurrency()) + .paymentMethod(request.getPaymentMethod()) + .build() + ); + + Timer.Sample sample = Timer.start(); + + try { + Order order = orderService.createOrder(request); + + // Success logging with performance metrics + Duration duration = Duration.ofNanos(sample.stop()); + log.info("Order created successfully", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .orderId(order.getId()) + .customerId(request.getCustomerId()) + .status(order.getStatus()) + .processingTimeMs(duration.toMillis()) + .build() + ); + + // Record business metrics + metricsService.recordBusinessEvent("order_created", userId); + metricsService.recordOrderValue(request.getTotalValue(), request.getCurrency()); + + return ResponseEntity.ok(order); + + } catch (ValidationException e) { + log.warn("Order validation failed", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .customerId(request.getCustomerId()) + .errorType("validation_error") + .errorMessage(e.getMessage()) + .validationErrors(e.getValidationErrors()) + .build() + ); + return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); + + } catch (PaymentException e) { + log.error("Payment processing failed", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .customerId(request.getCustomerId()) + .errorType("payment_error") + .errorMessage(e.getMessage()) + .paymentMethod(request.getPaymentMethod()) + .orderValue(request.getTotalValue()) + .build() + ); + return ResponseEntity.status(HttpStatus.PAYMENT_REQUIRED) + .body(new ErrorResponse("Payment processing failed")); + + } catch (Exception e) { + log.error("Unexpected error during order creation", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .customerId(request.getCustomerId()) + .errorType("system_error") + .errorMessage(e.getMessage()) + .stackTrace(getStackTrace(e)) + .build() + ); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse("Internal server error")); + } + } +} +``` + +### 2. Log Levels +- **ERROR** — критические ошибки +- **WARN** — предупреждения +- **INFO** — информационные сообщения +- **DEBUG** — отладочная информация +- **TRACE** — детальная трассировка + +### 3. Correlation ID +```java +@Component +public class CorrelationIdFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + String correlationId = httpRequest.getHeader("X-Correlation-ID"); + + if (correlationId == null) { + correlationId = UUID.randomUUID().toString(); + } + + MDC.put("correlationId", correlationId); + + try { + chain.doFilter(request, response); + } finally { + MDC.clear(); + } + } +} +``` + +## ELK Stack (Elasticsearch, Logstash, Kibana) + +### Конфигурация Logstash +```ruby +# logstash.conf +input { + beats { + port => 5044 + } +} + +filter { + if [fields][service] == "order-service" { + grok { + match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}" } + } + + date { + match => [ "timestamp", "ISO8601" ] + } + } +} + +output { + elasticsearch { + hosts => ["elasticsearch:9200"] + index => "logs-%{+YYYY.MM.dd}" + } +} +``` + +### Шаблон индекса Elasticsearch +```json +{ + "index_patterns": ["logs-*"], + "template": { + "settings": { + "number_of_shards": 1, + "number_of_replicas": 1 + }, + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + }, + "level": { + "type": "keyword" + }, + "message": { + "type": "text" + }, + "correlationId": { + "type": "keyword" + }, + "service": { + "type": "keyword" + } + } + } + } +} +``` + +## Fluentd + +```ruby +# fluent.conf + + @type tail + path /var/log/application.log + pos_file /var/log/fluentd.log.pos + tag application.logs + format json + + + + @type record_transformer + + service "order-service" + environment "production" + + + + + @type elasticsearch + host elasticsearch + port 9200 + index_name logs + type_name _doc + +``` diff --git a/modules/module5/sections/04-distributed-tracing.md b/modules/module5/sections/04-distributed-tracing.md new file mode 100644 index 0000000..b59356a --- /dev/null +++ b/modules/module5/sections/04-distributed-tracing.md @@ -0,0 +1,102 @@ +# Distributed Tracing + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 5](../README.md) + +--- + +## Что такое Distributed Tracing + +**Distributed Tracing** — метод отслеживания запросов через распределенную систему. + +## OpenTelemetry + +```java +@Configuration +public class TracingConfig { + @Bean + public OpenTelemetry openTelemetry() { + return OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(BatchSpanProcessor.builder( + JaegerGrpcSpanExporter.builder() + .setEndpoint("http://jaeger:14250") + .build() + ).build()) + .build() + ) + .build(); + } +} + +@Service +public class OrderService { + private final Tracer tracer; + + public OrderService(OpenTelemetry openTelemetry) { + this.tracer = openTelemetry.getTracer("order-service"); + } + + public Order createOrder(CreateOrderRequest request) { + Span span = tracer.spanBuilder("create-order") + .setAttribute("customer.id", request.getCustomerId()) + .setAttribute("order.value", request.getTotalValue().doubleValue()) + .startSpan(); + + try (Scope scope = span.makeCurrent()) { + // бизнес-логика + Order order = processOrder(request); + + span.setStatus(StatusCode.OK); + span.setAttribute("order.id", order.getId()); + + return order; + } catch (Exception e) { + span.setStatus(StatusCode.ERROR, e.getMessage()); + span.recordException(e); + throw e; + } finally { + span.end(); + } + } +} +``` + +## Jaeger + +### Конфигурация Jaeger +```yaml +# docker-compose.yml +version: '3.8' +services: + jaeger: + image: jaegertracing/all-in-one:latest + ports: + - "16686:16686" + - "14268:14268" + environment: + - COLLECTOR_OTLP_ENABLED=true +``` + +### Анализ трассировок +```java +@Component +public class TraceAnalyzer { + public void analyzeTrace(String traceId) { + // анализ трассировки + List spans = getSpansByTraceId(traceId); + + // поиск узких мест + Span slowestSpan = spans.stream() + .max(Comparator.comparing(Span::getDuration)) + .orElse(null); + + // анализ ошибок + List errorSpans = spans.stream() + .filter(span -> span.getStatus().getStatusCode() == StatusCode.ERROR) + .collect(Collectors.toList()); + } +} +``` diff --git a/modules/module5/sections/05-observability.md b/modules/module5/sections/05-observability.md new file mode 100644 index 0000000..ebdb174 --- /dev/null +++ b/modules/module5/sections/05-observability.md @@ -0,0 +1,92 @@ +# Observability + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 5](../README.md) + +--- + +## Три столпа Observability + +### 1. Metrics (Метрики) +- Количественные измерения +- Агрегированные данные +- Хорошо для мониторинга трендов + +### 2. Logs (Логи) +- События с временными метками +- Детальная информация +- Хорошо для отладки + +### 3. Traces (Трассировки) +- Путь запроса через систему +- Временные зависимости +- Хорошо для анализа производительности + +## Паттерны наблюдаемости + +### 1. Health Checks +```java +@Component +public class HealthIndicator implements HealthIndicator { + @Override + public Health health() { + try { + // проверка зависимостей + boolean dbHealthy = checkDatabase(); + boolean cacheHealthy = checkCache(); + + if (dbHealthy && cacheHealthy) { + return Health.up() + .withDetail("database", "UP") + .withDetail("cache", "UP") + .build(); + } else { + return Health.down() + .withDetail("database", dbHealthy ? "UP" : "DOWN") + .withDetail("cache", cacheHealthy ? "UP" : "DOWN") + .build(); + } + } catch (Exception e) { + return Health.down(e).build(); + } + } +} +``` + +### 2. Custom Metrics +```java +@Component +public class BusinessMetrics { + private final Counter ordersCreated; + private final Timer orderProcessingTime; + private final Gauge activeUsers; + + public BusinessMetrics(MeterRegistry meterRegistry) { + this.ordersCreated = Counter.builder("orders.created") + .description("Total orders created") + .register(meterRegistry); + + this.orderProcessingTime = Timer.builder("orders.processing.time") + .description("Order processing time") + .register(meterRegistry); + + this.activeUsers = Gauge.builder("users.active") + .description("Number of active users") + .register(meterRegistry, this, obj -> obj.getActiveUsersCount()); + } + + public void recordOrderCreated() { + ordersCreated.increment(); + } + + public void recordOrderProcessingTime(Duration duration) { + orderProcessingTime.record(duration); + } + + private int getActiveUsersCount() { + // логика подсчета активных пользователей + return 0; + } +} +``` diff --git a/modules/module5/sections/06-alerting.md b/modules/module5/sections/06-alerting.md new file mode 100644 index 0000000..99311ac --- /dev/null +++ b/modules/module5/sections/06-alerting.md @@ -0,0 +1,76 @@ +# Alerting + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 5](../README.md) + +--- + +## Принципы алертинга + +### 1. Alert Fatigue Prevention +- Только важные алерты +- Группировка похожих алертов +- Эскалация по времени + +### 2. Alert Classification +- **Critical** — система недоступна +- **Warning** — деградация производительности +- **Info** — информационные уведомления + +## Правила оповещений Prometheus + +```yaml +# alerts.yml +groups: +- name: application + rules: + - alert: HighErrorRate + expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05 + for: 2m + labels: + severity: critical + annotations: + summary: "High error rate detected" + description: "Error rate is {{ $value }} for {{ $labels.instance }}" + + - alert: HighResponseTime + expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1 + for: 5m + labels: + severity: warning + annotations: + summary: "High response time" + description: "95th percentile response time is {{ $value }}s" +``` + +## Конфигурация Alertmanager + +```yaml +# alertmanager.yml +global: + smtp_smarthost: 'localhost:587' + smtp_from: 'alerts@example.com' + +route: + group_by: ['alertname'] + group_wait: 10s + group_interval: 10s + repeat_interval: 1h + receiver: 'web.hook' + +receivers: +- name: 'web.hook' + webhook_configs: + - url: 'http://alert-webhook:5001/alerts' + +- name: 'email' + email_configs: + - to: 'admin@example.com' + subject: 'Alert: {{ .GroupLabels.alertname }}' + body: | + {{ range .Alerts }} + Alert: {{ .Annotations.summary }} + Description: {{ .Annotations.description }} + {{ end }} +``` diff --git a/modules/module5/sections/07-sli-slo-sla.md b/modules/module5/sections/07-sli-slo-sla.md new file mode 100644 index 0000000..ef80c2d --- /dev/null +++ b/modules/module5/sections/07-sli-slo-sla.md @@ -0,0 +1,80 @@ +# SLI/SLO/SLA + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 5](../README.md) + +--- + +## SLI (Индикатор уровня сервиса) + +**SLI** — метрика, которая измеряет качество сервиса. + +### Примеры SLI: +- **Availability**: `successful_requests / total_requests` +- **Latency**: `requests_completed_within_threshold / total_requests` +- **Throughput**: `successful_requests_per_second` + +```java +@Component +public class SLICalculator { + public double calculateAvailability(String service, Duration window) { + Counter successfulRequests = Counter.builder("requests.successful") + .tag("service", service) + .register(meterRegistry); + + Counter totalRequests = Counter.builder("requests.total") + .tag("service", service) + .register(meterRegistry); + + double successful = successfulRequests.count(); + double total = totalRequests.count(); + + return total > 0 ? successful / total : 0.0; + } + + public double calculateLatency(String service, Duration threshold) { + Timer requestTimer = Timer.builder("request.duration") + .tag("service", service) + .register(meterRegistry); + + // логика расчета латентности + return 0.95; // пример + } +} +``` + +## SLO (Цель уровня сервиса) + +**SLO** — целевое значение для SLI. + +### Примеры SLO: +- **Availability**: 99.9% uptime +- **Latency**: 95% of requests < 200ms +- **Error Rate**: < 0.1% errors + +```java +@Component +public class SLOMonitor { + private final Map slos = Map.of( + "availability", new SLO("availability", 0.999, "99.9% uptime"), + "latency", new SLO("latency", 0.95, "95% < 200ms"), + "error_rate", new SLO("error_rate", 0.001, "< 0.1% errors") + ); + + public boolean checkSLO(String sloName, double currentValue) { + SLO slo = slos.get(sloName); + return currentValue >= slo.getTarget(); + } +} +``` + +## SLA (Соглашение об уровне сервиса) + +**SLA** — соглашение с клиентами о качестве сервиса. + +### Компоненты SLA: +- **Service Description** — описание сервиса +- **Performance Metrics** — метрики производительности +- **Availability Targets** — цели доступности +- **Remediation** — меры по устранению нарушений diff --git a/modules/module5/sections/08-practical-tasks.md b/modules/module5/sections/08-practical-tasks.md new file mode 100644 index 0000000..f34191e --- /dev/null +++ b/modules/module5/sections/08-practical-tasks.md @@ -0,0 +1,24 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 5](../README.md) + +--- + +### Задание 1: Prometheus + Grafana +1. Настройте Prometheus для сбора метрик +2. Создайте дашборд в Grafana +3. Настройте алерты + +### Задание 2: Distributed Tracing +1. Интегрируйте OpenTelemetry в приложение +2. Настройте Jaeger для анализа трассировок +3. Создайте анализ производительности + +### Задание 3: ELK Stack +1. Настройте централизованное логирование +2. Создайте индексы в Elasticsearch +3. Настройте дашборды в Kibana + +--- \ No newline at end of file diff --git a/modules/module5/theory.md b/modules/module5/theory.md new file mode 100644 index 0000000..79a5676 --- /dev/null +++ b/modules/module5/theory.md @@ -0,0 +1,2009 @@ +# Модуль 5: Мониторинг и наблюдаемость + +## Содержание модуля +1. [Основы мониторинга](#основы-мониторинга) +2. [Prometheus и Grafana](#prometheus-и-grafana) +3. [Логирование](#логирование) +4. [Distributed Tracing](#distributed-tracing) +5. [Observability](#observability) +6. [Alerting](#alerting) +7. [SLI/SLO/SLA](#slislosla) + +--- + +## Основы мониторинга + +### Что такое мониторинг + +**Мониторинг** — процесс сбора, анализа и использования информации о работе системы для обеспечения её стабильности и производительности. + +### Цели мониторинга + +#### 1. Проактивное обнаружение проблем +- Выявление проблем до их влияния на пользователей +- Предотвращение каскадных отказов +- Оптимизация производительности + +#### 2. Реактивное решение проблем +- Быстрая диагностика инцидентов +- Сокращение времени восстановления (MTTR) +- Анализ причин отказов + +#### 3. Бизнес-аналитика +- Понимание поведения пользователей +- Анализ производительности бизнес-процессов +- Планирование ресурсов + +### Типы мониторинга + +#### 1. Infrastructure Monitoring +- CPU, Memory, Disk, Network +- Контейнеры и виртуальные машины +- Базы данных и кэши + +#### 2. Application Monitoring +- Метрики приложения +- Производительность кода +- Ошибки и исключения + +#### 3. Business Monitoring +- Ключевые бизнес-метрики +- Пользовательская активность +- Конверсии и доходы + +### Метрики мониторинга + +#### 1. Counters (Счетчики) +**Определение:** Метрики, которые только увеличиваются. + +**Технический пример - Advanced Metrics Collection:** + +```java +@Component +public class RequestCounter { + private final Counter requestCounter; + private final Counter errorCounter; + private final Timer requestTimer; + private final MeterRegistry meterRegistry; + + public RequestCounter(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + + this.requestCounter = Counter.builder("http.requests.total") + .description("Total number of HTTP requests") + .tag("service", "user-service") + .register(meterRegistry); + + this.errorCounter = Counter.builder("http.errors.total") + .description("Total number of HTTP errors") + .tag("service", "user-service") + .register(meterRegistry); + + this.requestTimer = Timer.builder("http.request.duration") + .description("HTTP request duration") + .tag("service", "user-service") + .register(meterRegistry); + } + + public void increment(String method, String endpoint, String status) { + requestCounter.increment( + Tags.of( + "method", method, + "endpoint", endpoint, + "status", status + ) + ); + + if (Integer.parseInt(status) >= 400) { + errorCounter.increment( + Tags.of( + "method", method, + "endpoint", endpoint, + "error_type", getErrorType(status) + ) + ); + } + } + + public Timer.Sample startTimer() { + return Timer.start(meterRegistry); + } + + public void recordDuration(Timer.Sample sample, String method, String endpoint) { + sample.stop(requestTimer.tags( + "method", method, + "endpoint", endpoint + )); + } + + private String getErrorType(String status) { + int statusCode = Integer.parseInt(status); + if (statusCode >= 400 && statusCode < 500) { + return "client_error"; + } else if (statusCode >= 500) { + return "server_error"; + } + return "unknown"; + } +} + +// Custom Metrics Service +@Service +public class MetricsService { + + private final MeterRegistry meterRegistry; + private final Counter businessCounter; + private final Gauge activeUsersGauge; + private final DistributionSummary orderValueSummary; + + public MetricsService(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + + this.businessCounter = Counter.builder("business.events.total") + .description("Business events counter") + .register(meterRegistry); + + this.activeUsersGauge = Gauge.builder("active.users") + .description("Number of active users") + .register(meterRegistry, this, MetricsService::getActiveUsersCount); + + this.orderValueSummary = DistributionSummary.builder("order.value") + .description("Order value distribution") + .baseUnit("currency") + .register(meterRegistry); + } + + public void recordBusinessEvent(String eventType, String userId) { + businessCounter.increment( + Tags.of( + "event_type", eventType, + "user_id", userId + ) + ); + } + + public void recordOrderValue(BigDecimal value, String currency) { + orderValueSummary.record(value.doubleValue()); + + // Record currency-specific metric + Counter.builder("order.value.by.currency") + .tag("currency", currency) + .register(meterRegistry) + .increment(value.doubleValue()); + } + + private double getActiveUsersCount() { + // Implementation to get active users count + return 0.0; // Placeholder + } +} + +// Metrics Configuration +@Configuration +@EnableConfigurationProperties(MetricsProperties.class) +public class MetricsConfig { + + @Bean + public MeterRegistry meterRegistry() { + return new SimpleMeterRegistry(); + } + + @Bean + public PrometheusMeterRegistry prometheusMeterRegistry() { + return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + } + + @Bean + public CompositeMeterRegistry compositeMeterRegistry( + MeterRegistry simpleRegistry, + PrometheusMeterRegistry prometheusRegistry) { + + CompositeMeterRegistry composite = new CompositeMeterRegistry(); + composite.add(simpleRegistry); + composite.add(prometheusRegistry); + + return composite; + } + + @Bean + public TimedAspect timedAspect(MeterRegistry meterRegistry) { + return new TimedAspect(meterRegistry); + } + + @Bean + public CountedAspect countedAspect(MeterRegistry meterRegistry) { + return new CountedAspect(meterRegistry); + } +} + +// AOP for automatic metrics +@Aspect +@Component +public class MetricsAspect { + + private final MeterRegistry meterRegistry; + private final Timer.Sample sample; + + public MetricsAspect(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @Around("@annotation(org.springframework.web.bind.annotation.GetMapping)") + public Object timeGetRequests(ProceedingJoinPoint joinPoint) throws Throwable { + Timer.Sample sample = Timer.start(meterRegistry); + try { + Object result = joinPoint.proceed(); + sample.stop(Timer.builder("http.get.requests") + .tag("endpoint", getEndpointName(joinPoint)) + .register(meterRegistry)); + return result; + } catch (Exception e) { + sample.stop(Timer.builder("http.get.requests") + .tag("endpoint", getEndpointName(joinPoint)) + .tag("error", "true") + .register(meterRegistry)); + throw e; + } + } + + private String getEndpointName(ProceedingJoinPoint joinPoint) { + Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); + return method.getDeclaringClass().getSimpleName() + "." + method.getName(); + } +} +``` + +**Node.js примеры - метрики с prom-client:** + +```typescript +import express, { Request, Response, NextFunction } from 'express'; +import client from 'prom-client'; +import { PrismaClient } from '@prisma/client'; + +const app = express(); +const prisma = new PrismaClient(); + +// Регистр метрик +const register = new client.Registry(); + +// Добавление стандартных метрик Node.js +client.collectDefaultMetrics({ register }); + +// Кастомные метрики +const httpRequestDuration = new client.Histogram({ + name: 'http_request_duration_seconds', + help: 'Duration of HTTP requests in seconds', + labelNames: ['method', 'route', 'status_code'], + buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10] +}); + +const httpRequestTotal = new client.Counter({ + name: 'http_requests_total', + help: 'Total number of HTTP requests', + labelNames: ['method', 'route', 'status_code'] +}); + +const activeConnections = new client.Gauge({ + name: 'active_connections', + help: 'Number of active connections' +}); + +const businessEvents = new client.Counter({ + name: 'business_events_total', + help: 'Total number of business events', + labelNames: ['event_type', 'status'] +}); + +const orderValue = new client.Histogram({ + name: 'order_value_dollars', + help: 'Order value in dollars', + buckets: [10, 50, 100, 500, 1000, 5000, 10000] +}); + +// Регистрация метрик +register.registerMetric(httpRequestDuration); +register.registerMetric(httpRequestTotal); +register.registerMetric(activeConnections); +register.registerMetric(businessEvents); +register.registerMetric(orderValue); + +// Middleware для автоматического сбора метрик +app.use((req: Request, res: Response, next: NextFunction) => { + const start = Date.now(); + + res.on('finish', () => { + const duration = (Date.now() - start) / 1000; + const route = req.route?.path || req.path; + + httpRequestDuration + .labels(req.method, route, res.statusCode.toString()) + .observe(duration); + + httpRequestTotal + .labels(req.method, route, res.statusCode.toString()) + .inc(); + }); + + next(); +}); + +// Service для работы с метриками +class MetricsService { + private activeConnectionsCount = 0; + + incrementActiveConnections(): void { + this.activeConnectionsCount++; + activeConnections.set(this.activeConnectionsCount); + } + + decrementActiveConnections(): void { + this.activeConnectionsCount--; + activeConnections.set(this.activeConnectionsCount); + } + + recordBusinessEvent(eventType: string, status: string): void { + businessEvents.labels(eventType, status).inc(); + } + + recordOrderValue(value: number): void { + orderValue.observe(value); + } +} + +// Order Service с метриками +class OrderService { + constructor( + private orderRepository: OrderRepository, + private metricsService: MetricsService + ) {} + + async createOrder(request: CreateOrderRequest): Promise { + const timer = httpRequestDuration.startTimer(); + + try { + // Бизнес-логика + const order = await this.orderRepository.save({ + userId: request.userId, + amount: request.amount, + status: 'CREATED', + createdAt: new Date() + }); + + // Запись метрик + this.metricsService.recordBusinessEvent('order_created', 'success'); + this.metricsService.recordOrderValue(request.amount); + + return order; + } catch (error) { + this.metricsService.recordBusinessEvent('order_created', 'error'); + throw error; + } finally { + timer({ method: 'POST', route: '/api/orders', status_code: '200' }); + } + } + + async getOrder(id: number): Promise { + const timer = httpRequestDuration.startTimer(); + + try { + const order = await this.orderRepository.findById(id); + return order; + } finally { + timer({ method: 'GET', route: '/api/orders/:id', status_code: '200' }); + } + } +} + +// Repository +interface OrderRepository { + save(order: Omit): Promise; + findById(id: number): Promise; +} + +class PrismaOrderRepository implements OrderRepository { + async save(order: Omit): Promise { + return await prisma.order.create({ data: order }); + } + + async findById(id: number): Promise { + return await prisma.order.findUnique({ where: { id } }); + } +} + +// Dependency injection +const metricsService = new MetricsService(); +const orderRepository = new PrismaOrderRepository(); +const orderService = new OrderService(orderRepository, metricsService); + +// API endpoints +app.post('/api/orders', async (req: Request, res: Response, next: NextFunction) => { + try { + const order = await orderService.createOrder(req.body); + res.json(order); + } catch (error) { + next(error); + } +}); + +app.get('/api/orders/:id', async (req: Request, res: Response, next: NextFunction) => { + try { + const id = parseInt(req.params.id); + const order = await orderService.getOrder(id); + + if (!order) { + res.status(404).json({ error: 'Order not found' }); + return; + } + + res.json(order); + } catch (error) { + next(error); + } +}); + +// Endpoint для метрик Prometheus +app.get('/metrics', async (req: Request, res: Response) => { + res.set('Content-Type', register.contentType); + res.end(await register.metrics()); +}); + +// Health check endpoint +app.get('/health', (req: Request, res: Response) => { + res.json({ status: 'healthy', timestamp: new Date().toISOString() }); +}); + +// Middleware для отслеживания активных соединений +app.use((req: Request, res: Response, next: NextFunction) => { + metricsService.incrementActiveConnections(); + + res.on('finish', () => { + metricsService.decrementActiveConnections(); + }); + + next(); +}); + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +app.listen(3000, () => { + console.log('Server running on port 3000'); + console.log('Metrics available at http://localhost:3000/metrics'); +}); +``` + +**Node.js примеры с библиотекой prom-client для NestJS:** + +```typescript +import { Module } from '@nestjs/common'; +import { PrometheusModule } from '@willsoto/nestjs-prometheus'; +import { Controller, Get, Post, Body, Param } from '@nestjs/common'; +import { Counter, Histogram, Gauge, register } from 'prom-client'; + +@Module({ + imports: [ + PrometheusModule.register({ + defaultMetrics: { + enabled: true, + }, + }), + ], +}) +export class AppModule {} + +// Кастомные метрики +const httpRequestDuration = new Histogram({ + name: 'http_request_duration_seconds', + help: 'Duration of HTTP requests in seconds', + labelNames: ['method', 'route', 'status_code'], + buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10], +}); + +const businessEvents = new Counter({ + name: 'business_events_total', + help: 'Total number of business events', + labelNames: ['event_type', 'status'], +}); + +const activeUsers = new Gauge({ + name: 'active_users', + help: 'Number of active users', +}); + +// Регистрация метрик +register.registerMetric(httpRequestDuration); +register.registerMetric(businessEvents); +register.registerMetric(activeUsers); + +@Controller('orders') +export class OrderController { + constructor(private orderService: OrderService) {} + + @Post() + async createOrder(@Body() createOrderDto: CreateOrderDto) { + const timer = httpRequestDuration.startTimer(); + + try { + const order = await this.orderService.createOrder(createOrderDto); + + // Запись бизнес-метрик + businessEvents.labels('order_created', 'success').inc(); + + return order; + } catch (error) { + businessEvents.labels('order_created', 'error').inc(); + throw error; + } finally { + timer({ method: 'POST', route: '/orders', status_code: '200' }); + } + } + + @Get(':id') + async getOrder(@Param('id') id: string) { + const timer = httpRequestDuration.startTimer(); + + try { + const order = await this.orderService.getOrder(+id); + return order; + } finally { + timer({ method: 'GET', route: '/orders/:id', status_code: '200' }); + } + } +} + +@Controller('metrics') +export class MetricsController { + @Get() + async getMetrics() { + return register.metrics(); + } +} +``` + +**Rust примеры - Метрики с prometheus:** + +```rust +// Метрики с prometheus для Actix Web +use actix_web::{web, App, HttpServer, middleware::Logger}; +use prometheus::{Counter, Histogram, Gauge, Registry, Encoder, TextEncoder}; +use std::sync::Arc; +use std::time::Instant; + +// Глобальные метрики +lazy_static::lazy_static! { + static ref HTTP_REQUESTS_TOTAL: Counter = Counter::new( + "http_requests_total", + "Total number of HTTP requests" + ).unwrap(); + + static ref HTTP_REQUEST_DURATION: Histogram = Histogram::new( + "http_request_duration_seconds", + "Duration of HTTP requests in seconds" + ).unwrap(); + + static ref ACTIVE_CONNECTIONS: Gauge = Gauge::new( + "active_connections", + "Number of active connections" + ).unwrap(); + + static ref BUSINESS_EVENTS_TOTAL: Counter = Counter::new( + "business_events_total", + "Total number of business events" + ).unwrap(); + + static ref ORDER_VALUE: Histogram = Histogram::new( + "order_value_dollars", + "Order value in dollars" + ).unwrap(); +} + +// Метрики сервис +pub struct MetricsService { + registry: Registry, + active_connections_count: Arc, +} + +impl MetricsService { + pub fn new() -> Self { + let registry = Registry::new(); + + // Регистрация метрик + registry.register(Box::new(HTTP_REQUESTS_TOTAL.clone())).unwrap(); + registry.register(Box::new(HTTP_REQUEST_DURATION.clone())).unwrap(); + registry.register(Box::new(ACTIVE_CONNECTIONS.clone())).unwrap(); + registry.register(Box::new(BUSINESS_EVENTS_TOTAL.clone())).unwrap(); + registry.register(Box::new(ORDER_VALUE.clone())).unwrap(); + + Self { + registry, + active_connections_count: Arc::new(std::sync::atomic::AtomicUsize::new(0)), + } + } + + pub fn increment_active_connections(&self) { + let count = self.active_connections_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + ACTIVE_CONNECTIONS.set(count as f64 + 1.0); + } + + pub fn decrement_active_connections(&self) { + let count = self.active_connections_count.fetch_sub(1, std::sync::atomic::Ordering::SeqCst); + ACTIVE_CONNECTIONS.set(count as f64 - 1.0); + } + + pub fn record_business_event(&self, event_type: &str, status: &str) { + BUSINESS_EVENTS_TOTAL.with_label_values(&[event_type, status]).inc(); + } + + pub fn record_order_value(&self, value: f64) { + ORDER_VALUE.observe(value); + } + + pub fn get_metrics(&self) -> String { + let mut buffer = Vec::new(); + let encoder = TextEncoder::new(); + let metric_families = self.registry.gather(); + encoder.encode(&metric_families, &mut buffer).unwrap(); + String::from_utf8(buffer).unwrap() + } +} + +// Order Service с метриками +pub struct OrderService { + metrics_service: Arc, +} + +impl OrderService { + pub fn new(metrics_service: Arc) -> Self { + Self { metrics_service } + } + + pub async fn create_order(&self, request: CreateOrderRequest) -> Result { + let timer = HTTP_REQUEST_DURATION.start_timer(); + + // Бизнес-логика + let order = Order { + id: uuid::Uuid::new_v4(), + user_id: request.user_id, + amount: request.amount, + status: "CREATED".to_string(), + created_at: chrono::Utc::now(), + }; + + // Запись метрик + self.metrics_service.record_business_event("order_created", "success"); + self.metrics_service.record_order_value(request.amount); + + timer.observe_duration(std::time::Duration::from_millis(100)); + + Ok(order) + } + + pub async fn get_order(&self, id: uuid::Uuid) -> Result, String> { + let timer = HTTP_REQUEST_DURATION.start_timer(); + + // Бизнес-логика + let order = Some(Order { + id, + user_id: 1, + amount: 100.0, + status: "CREATED".to_string(), + created_at: chrono::Utc::now(), + }); + + timer.observe_duration(std::time::Duration::from_millis(50)); + + Ok(order) + } +} + +// Структуры данных +#[derive(serde::Deserialize)] +pub struct CreateOrderRequest { + pub user_id: u64, + pub amount: f64, +} + +#[derive(serde::Serialize)] +pub struct Order { + pub id: uuid::Uuid, + pub user_id: u64, + pub amount: f64, + pub status: String, + pub created_at: chrono::DateTime, +} + +// Handlers +async fn create_order( + order_service: web::Data>, + request: web::Json, +) -> actix_web::Result { + HTTP_REQUESTS_TOTAL.with_label_values(&["POST", "/api/orders", "200"]).inc(); + + match order_service.create_order(request.into_inner()).await { + Ok(order) => Ok(actix_web::HttpResponse::Ok().json(order)), + Err(e) => Ok(actix_web::HttpResponse::BadRequest().json(serde_json::json!({ + "error": e + }))), + } +} + +async fn get_order( + order_service: web::Data>, + path: web::Path, +) -> actix_web::Result { + let id = path.into_inner(); + HTTP_REQUESTS_TOTAL.with_label_values(&["GET", "/api/orders/:id", "200"]).inc(); + + match order_service.get_order(id).await { + Ok(Some(order)) => Ok(actix_web::HttpResponse::Ok().json(order)), + Ok(None) => Ok(actix_web::HttpResponse::NotFound().json(serde_json::json!({ + "error": "Order not found" + }))), + Err(e) => Ok(actix_web::HttpResponse::BadRequest().json(serde_json::json!({ + "error": e + }))), + } +} + +async fn metrics(metrics_service: web::Data>) -> actix_web::Result { + let metrics_data = metrics_service.get_metrics(); + Ok(actix_web::HttpResponse::Ok() + .content_type("text/plain") + .body(metrics_data)) +} + +async fn health() -> actix_web::Result { + Ok(actix_web::HttpResponse::Ok().json(serde_json::json!({ + "status": "healthy", + "timestamp": chrono::Utc::now().to_rfc3339() + }))) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let metrics_service = Arc::new(MetricsService::new()); + let order_service = Arc::new(OrderService::new(metrics_service.clone())); + + HttpServer::new(move || { + App::new() + .app_data(web::Data::new(order_service.clone())) + .app_data(web::Data::new(metrics_service.clone())) + .wrap(Logger::default()) + .route("/api/orders", web::post().to(create_order)) + .route("/api/orders/{id}", web::get().to(get_order)) + .route("/metrics", web::get().to(metrics)) + .route("/health", web::get().to(health)) + }) + .bind("127.0.0.1:3000")? + .run() + .await +} +``` + +**Python примеры - Метрики с prometheus:** + +```python +# Метрики с prometheus для FastAPI +from fastapi import FastAPI, Request, Response +from prometheus_client import Counter, Histogram, Gauge, generate_latest, CONTENT_TYPE_LATEST +import time +import asyncio +from typing import Dict, Any +import uuid +from datetime import datetime + +app = FastAPI(title="Metrics Service", version="1.0.0") + +# Метрики +HTTP_REQUESTS_TOTAL = Counter( + 'http_requests_total', + 'Total number of HTTP requests', + ['method', 'route', 'status_code'] +) + +HTTP_REQUEST_DURATION = Histogram( + 'http_request_duration_seconds', + 'Duration of HTTP requests in seconds', + ['method', 'route', 'status_code'], + buckets=[0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10] +) + +ACTIVE_CONNECTIONS = Gauge( + 'active_connections', + 'Number of active connections' +) + +BUSINESS_EVENTS_TOTAL = Counter( + 'business_events_total', + 'Total number of business events', + ['event_type', 'status'] +) + +ORDER_VALUE = Histogram( + 'order_value_dollars', + 'Order value in dollars', + buckets=[10, 50, 100, 500, 1000, 5000, 10000] +) + +# Метрики сервис +class MetricsService: + def __init__(self): + self.active_connections_count = 0 + + def increment_active_connections(self): + self.active_connections_count += 1 + ACTIVE_CONNECTIONS.set(self.active_connections_count) + + def decrement_active_connections(self): + self.active_connections_count -= 1 + ACTIVE_CONNECTIONS.set(self.active_connections_count) + + def record_business_event(self, event_type: str, status: str): + BUSINESS_EVENTS_TOTAL.labels(event_type=event_type, status=status).inc() + + def record_order_value(self, value: float): + ORDER_VALUE.observe(value) + +# Order Service с метриками +class OrderService: + def __init__(self, metrics_service: MetricsService): + self.metrics_service = metrics_service + + async def create_order(self, request: Dict[str, Any]) -> Dict[str, Any]: + # Бизнес-логика + order = { + "id": str(uuid.uuid4()), + "user_id": request["user_id"], + "amount": request["amount"], + "status": "CREATED", + "created_at": datetime.utcnow().isoformat() + } + + # Запись метрик + self.metrics_service.record_business_event("order_created", "success") + self.metrics_service.record_order_value(request["amount"]) + + return order + + async def get_order(self, order_id: str) -> Dict[str, Any]: + # Бизнес-логика + order = { + "id": order_id, + "user_id": 1, + "amount": 100.0, + "status": "CREATED", + "created_at": datetime.utcnow().isoformat() + } + + return order + +# Dependency injection +metrics_service = MetricsService() +order_service = OrderService(metrics_service) + +# Middleware для автоматического сбора метрик +@app.middleware("http") +async def metrics_middleware(request: Request, call_next): + start_time = time.time() + + response = await call_next(request) + + duration = time.time() - start_time + route = request.url.path + method = request.method + status_code = str(response.status_code) + + HTTP_REQUEST_DURATION.labels( + method=method, + route=route, + status_code=status_code + ).observe(duration) + + HTTP_REQUESTS_TOTAL.labels( + method=method, + route=route, + status_code=status_code + ).inc() + + return response + +# API endpoints +@app.post("/api/orders") +async def create_order(request: Dict[str, Any]): + order = await order_service.create_order(request) + return order + +@app.get("/api/orders/{order_id}") +async def get_order(order_id: str): + order = await order_service.get_order(order_id) + return order + +@app.get("/metrics") +async def metrics(): + return Response( + generate_latest(), + media_type=CONTENT_TYPE_LATEST + ) + +@app.get("/health") +async def health(): + return { + "status": "healthy", + "timestamp": datetime.utcnow().isoformat() + } + +# Middleware для отслеживания активных соединений +@app.middleware("http") +async def active_connections_middleware(request: Request, call_next): + metrics_service.increment_active_connections() + + try: + response = await call_next(request) + return response + finally: + metrics_service.decrement_active_connections() + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=3000) +``` + +#### 2. Gauges (Измерители) +**Определение:** Метрики, которые могут увеличиваться и уменьшаться. + +```java +@Component +public class ActiveConnectionsGauge { + private final AtomicInteger activeConnections = new AtomicInteger(0); + private final Gauge activeConnectionsGauge; + + public ActiveConnectionsGauge(MeterRegistry meterRegistry) { + this.activeConnectionsGauge = Gauge.builder("active.connections") + .description("Number of active connections") + .register(meterRegistry, this, obj -> obj.activeConnections.get()); + } + + public void incrementConnections() { + activeConnections.incrementAndGet(); + } + + public void decrementConnections() { + activeConnections.decrementAndGet(); + } +} +``` + +#### 3. Histograms (Гистограммы) +**Определение:** Метрики для измерения распределения значений. + +```java +@Component +public class ResponseTimeHistogram { + private final Timer responseTimeTimer; + + public ResponseTimeHistogram(MeterRegistry meterRegistry) { + this.responseTimeTimer = Timer.builder("http.request.duration") + .description("HTTP request duration") + .register(meterRegistry); + } + + public void recordResponseTime(String endpoint, Duration duration) { + responseTimeTimer.record(duration); + } +} +``` + +#### 4. Summaries (Сумматоры) +**Определение:** Метрики для измерения квантилей. + +```java +@Component +public class ResponseTimeSummary { + private final DistributionSummary responseTimeSummary; + + public ResponseTimeSummary(MeterRegistry meterRegistry) { + this.responseTimeSummary = DistributionSummary.builder("http.request.size") + .description("HTTP request size in bytes") + .register(meterRegistry); + } + + public void recordRequestSize(long size) { + responseTimeSummary.record(size); + } +} +``` + +--- + +## Prometheus и Grafana + +### Prometheus + +**Prometheus** — система мониторинга и база данных временных рядов. + +#### Архитектура Prometheus + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Application │ │ Application │ │ Application │ +│ (Metrics) │ │ (Metrics) │ │ (Metrics) │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ │ + └───────────────────────┼───────────────────────┘ + │ + ┌─────────────────┐ + │ Prometheus │ + │ Server │ + └─────────────────┘ + │ + ┌─────────────────┐ + │ Grafana │ + │ (Dashboard) │ + └─────────────────┘ +``` + +#### PromQL (Язык запросов Prometheus) + +##### Основные запросы: +```promql +# Простая метрика +http_requests_total + +# Метрика с фильтрами +http_requests_total{method="GET", status="200"} + +# Агрегация +sum(http_requests_total) by (method) + +# Rate (скорость изменения) +rate(http_requests_total[5m]) + +# Histogram quantiles +histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) +``` + +##### Примеры запросов: +```promql +# Количество запросов в секунду +rate(http_requests_total[5m]) + +# Среднее время ответа +rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]) + +# 95-й перцентиль времени ответа +histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) + +# Ошибки в процентах +rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100 +``` + +#### Spring Boot + Prometheus + +```java +@Configuration +public class PrometheusConfig { + @Bean + public MeterRegistry meterRegistry() { + return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + } +} + +@RestController +public class MetricsController { + private final MeterRegistry meterRegistry; + + public MetricsController(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @GetMapping("/metrics") + public String metrics() { + return ((PrometheusMeterRegistry) meterRegistry).scrape(); + } +} +``` + +### Grafana + +**Grafana** — платформа для визуализации метрик и создания дашбордов. + +#### Создание дашборда + +```json +{ + "dashboard": { + "title": "Application Metrics", + "panels": [ + { + "title": "Request Rate", + "type": "graph", + "targets": [ + { + "expr": "rate(http_requests_total[5m])", + "legendFormat": "{{method}} {{endpoint}}" + } + ] + }, + { + "title": "Response Time", + "type": "graph", + "targets": [ + { + "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))", + "legendFormat": "95th percentile" + } + ] + } + ] + } +} +``` + +#### Алерты в Grafana + +```json +{ + "alert": { + "name": "High Error Rate", + "message": "Error rate is above 5%", + "conditions": [ + { + "query": { + "params": ["A", "5m", "now"] + }, + "reducer": { + "type": "last", + "params": [] + }, + "evaluator": { + "params": [0.05], + "type": "gt" + } + } + ] + } +} +``` + +--- + +## Логирование + +### Принципы логирования + +#### 1. Structured Logging +**Определение:** Логирование в структурированном формате (JSON). + +**Технический пример - Advanced Structured Logging:** + +```java +@Slf4j +@RestController +public class OrderController { + + @Autowired + private OrderService orderService; + + @Autowired + private MetricsService metricsService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) { + String correlationId = MDC.get("correlationId"); + String userId = MDC.get("userId"); + + // Structured logging with business context + log.info("Order creation started", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .customerId(request.getCustomerId()) + .orderValue(request.getTotalValue()) + .itemsCount(request.getItems().size()) + .currency(request.getCurrency()) + .paymentMethod(request.getPaymentMethod()) + .build() + ); + + Timer.Sample sample = Timer.start(); + + try { + Order order = orderService.createOrder(request); + + // Success logging with performance metrics + Duration duration = Duration.ofNanos(sample.stop()); + log.info("Order created successfully", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .orderId(order.getId()) + .customerId(request.getCustomerId()) + .status(order.getStatus()) + .processingTimeMs(duration.toMillis()) + .build() + ); + + // Record business metrics + metricsService.recordBusinessEvent("order_created", userId); + metricsService.recordOrderValue(request.getTotalValue(), request.getCurrency()); + + return ResponseEntity.ok(order); + + } catch (ValidationException e) { + log.warn("Order validation failed", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .customerId(request.getCustomerId()) + .errorType("validation_error") + .errorMessage(e.getMessage()) + .validationErrors(e.getValidationErrors()) + .build() + ); + return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); + + } catch (PaymentException e) { + log.error("Payment processing failed", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .customerId(request.getCustomerId()) + .errorType("payment_error") + .errorMessage(e.getMessage()) + .paymentMethod(request.getPaymentMethod()) + .orderValue(request.getTotalValue()) + .build() + ); + return ResponseEntity.status(HttpStatus.PAYMENT_REQUIRED) + .body(new ErrorResponse("Payment processing failed")); + + } catch (Exception e) { + log.error("Unexpected error during order creation", + StructuredLogging.builder() + .correlationId(correlationId) + .userId(userId) + .customerId(request.getCustomerId()) + .errorType("system_error") + .errorMessage(e.getMessage()) + .stackTrace(getStackTrace(e)) + .build() + ); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse("Internal server error")); + } + } +} + +// Structured Logging Builder +public class StructuredLogging { + private final Map fields = new HashMap<>(); + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private final StructuredLogging logging = new StructuredLogging(); + + public Builder correlationId(String correlationId) { + logging.fields.put("correlationId", correlationId); + return this; + } + + public Builder userId(String userId) { + logging.fields.put("userId", userId); + return this; + } + + public Builder customerId(Long customerId) { + logging.fields.put("customerId", customerId); + return this; + } + + public Builder orderId(Long orderId) { + logging.fields.put("orderId", orderId); + return this; + } + + public Builder orderValue(BigDecimal value) { + logging.fields.put("orderValue", value); + return this; + } + + public Builder itemsCount(Integer count) { + logging.fields.put("itemsCount", count); + return this; + } + + public Builder currency(String currency) { + logging.fields.put("currency", currency); + return this; + } + + public Builder paymentMethod(String method) { + logging.fields.put("paymentMethod", method); + return this; + } + + public Builder status(String status) { + logging.fields.put("status", status); + return this; + } + + public Builder processingTimeMs(Long timeMs) { + logging.fields.put("processingTimeMs", timeMs); + return this; + } + + public Builder errorType(String errorType) { + logging.fields.put("errorType", errorType); + return this; + } + + public Builder errorMessage(String message) { + logging.fields.put("errorMessage", message); + return this; + } + + public Builder validationErrors(List errors) { + logging.fields.put("validationErrors", errors); + return this; + } + + public Builder stackTrace(String stackTrace) { + logging.fields.put("stackTrace", stackTrace); + return this; + } + + public StructuredLogging build() { + return logging; + } + } + + public Map getFields() { + return fields; + } + + @Override + public String toString() { + try { + return new ObjectMapper().writeValueAsString(fields); + } catch (Exception e) { + return fields.toString(); + } + } +} + +// Advanced Correlation ID Filter +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class CorrelationIdFilter implements Filter { + + private static final String CORRELATION_ID_HEADER = "X-Correlation-ID"; + private static final String USER_ID_HEADER = "X-User-ID"; + private static final String REQUEST_ID_HEADER = "X-Request-ID"; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + // Extract or generate correlation ID + String correlationId = httpRequest.getHeader(CORRELATION_ID_HEADER); + if (correlationId == null || correlationId.isEmpty()) { + correlationId = UUID.randomUUID().toString(); + } + + // Extract user ID + String userId = httpRequest.getHeader(USER_ID_HEADER); + + // Generate request ID + String requestId = UUID.randomUUID().toString(); + + // Set MDC + MDC.put("correlationId", correlationId); + MDC.put("userId", userId); + MDC.put("requestId", requestId); + MDC.put("service", "order-service"); + MDC.put("version", "1.0.0"); + + // Add headers to response + httpResponse.setHeader(CORRELATION_ID_HEADER, correlationId); + httpResponse.setHeader(REQUEST_ID_HEADER, requestId); + + try { + chain.doFilter(request, response); + } finally { + MDC.clear(); + } + } +} + +// Logback Configuration for Structured Logging +// logback-spring.xml + + + + + + + + + + + + + + + + + + + + + /app/logs/order-service.json + + /app/logs/order-service.%d{yyyy-MM-dd}.%i.json + 100MB + 30 + 3GB + + + + + + + + + + + + + + + + /app/logs/order-service-errors.json + + ERROR + ACCEPT + DENY + + + /app/logs/order-service-errors.%d{yyyy-MM-dd}.%i.json + 50MB + 90 + + + + + + + + + + + + + + + + + + + +``` + +#### 2. Log Levels +- **ERROR** — критические ошибки +- **WARN** — предупреждения +- **INFO** — информационные сообщения +- **DEBUG** — отладочная информация +- **TRACE** — детальная трассировка + +#### 3. Correlation ID +```java +@Component +public class CorrelationIdFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + String correlationId = httpRequest.getHeader("X-Correlation-ID"); + + if (correlationId == null) { + correlationId = UUID.randomUUID().toString(); + } + + MDC.put("correlationId", correlationId); + + try { + chain.doFilter(request, response); + } finally { + MDC.clear(); + } + } +} +``` + +### ELK Stack (Elasticsearch, Logstash, Kibana) + +#### Конфигурация Logstash +```ruby +# logstash.conf +input { + beats { + port => 5044 + } +} + +filter { + if [fields][service] == "order-service" { + grok { + match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}" } + } + + date { + match => [ "timestamp", "ISO8601" ] + } + } +} + +output { + elasticsearch { + hosts => ["elasticsearch:9200"] + index => "logs-%{+YYYY.MM.dd}" + } +} +``` + +#### Шаблон индекса Elasticsearch +```json +{ + "index_patterns": ["logs-*"], + "template": { + "settings": { + "number_of_shards": 1, + "number_of_replicas": 1 + }, + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + }, + "level": { + "type": "keyword" + }, + "message": { + "type": "text" + }, + "correlationId": { + "type": "keyword" + }, + "service": { + "type": "keyword" + } + } + } + } +} +``` + +### Fluentd + +```ruby +# fluent.conf + + @type tail + path /var/log/application.log + pos_file /var/log/fluentd.log.pos + tag application.logs + format json + + + + @type record_transformer + + service "order-service" + environment "production" + + + + + @type elasticsearch + host elasticsearch + port 9200 + index_name logs + type_name _doc + +``` + +--- + +## Distributed Tracing + +### Что такое Distributed Tracing + +**Distributed Tracing** — метод отслеживания запросов через распределенную систему. + +### OpenTelemetry + +```java +@Configuration +public class TracingConfig { + @Bean + public OpenTelemetry openTelemetry() { + return OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(BatchSpanProcessor.builder( + JaegerGrpcSpanExporter.builder() + .setEndpoint("http://jaeger:14250") + .build() + ).build()) + .build() + ) + .build(); + } +} + +@Service +public class OrderService { + private final Tracer tracer; + + public OrderService(OpenTelemetry openTelemetry) { + this.tracer = openTelemetry.getTracer("order-service"); + } + + public Order createOrder(CreateOrderRequest request) { + Span span = tracer.spanBuilder("create-order") + .setAttribute("customer.id", request.getCustomerId()) + .setAttribute("order.value", request.getTotalValue().doubleValue()) + .startSpan(); + + try (Scope scope = span.makeCurrent()) { + // бизнес-логика + Order order = processOrder(request); + + span.setStatus(StatusCode.OK); + span.setAttribute("order.id", order.getId()); + + return order; + } catch (Exception e) { + span.setStatus(StatusCode.ERROR, e.getMessage()); + span.recordException(e); + throw e; + } finally { + span.end(); + } + } +} +``` + +### Jaeger + +#### Конфигурация Jaeger +```yaml +# docker-compose.yml +version: '3.8' +services: + jaeger: + image: jaegertracing/all-in-one:latest + ports: + - "16686:16686" + - "14268:14268" + environment: + - COLLECTOR_OTLP_ENABLED=true +``` + +#### Анализ трассировок +```java +@Component +public class TraceAnalyzer { + public void analyzeTrace(String traceId) { + // анализ трассировки + List spans = getSpansByTraceId(traceId); + + // поиск узких мест + Span slowestSpan = spans.stream() + .max(Comparator.comparing(Span::getDuration)) + .orElse(null); + + // анализ ошибок + List errorSpans = spans.stream() + .filter(span -> span.getStatus().getStatusCode() == StatusCode.ERROR) + .collect(Collectors.toList()); + } +} +``` + +--- + +## Observability + +### Три столпа Observability + +#### 1. Metrics (Метрики) +- Количественные измерения +- Агрегированные данные +- Хорошо для мониторинга трендов + +#### 2. Logs (Логи) +- События с временными метками +- Детальная информация +- Хорошо для отладки + +#### 3. Traces (Трассировки) +- Путь запроса через систему +- Временные зависимости +- Хорошо для анализа производительности + +### Паттерны наблюдаемости + +#### 1. Health Checks +```java +@Component +public class HealthIndicator implements HealthIndicator { + @Override + public Health health() { + try { + // проверка зависимостей + boolean dbHealthy = checkDatabase(); + boolean cacheHealthy = checkCache(); + + if (dbHealthy && cacheHealthy) { + return Health.up() + .withDetail("database", "UP") + .withDetail("cache", "UP") + .build(); + } else { + return Health.down() + .withDetail("database", dbHealthy ? "UP" : "DOWN") + .withDetail("cache", cacheHealthy ? "UP" : "DOWN") + .build(); + } + } catch (Exception e) { + return Health.down(e).build(); + } + } +} +``` + +#### 2. Custom Metrics +```java +@Component +public class BusinessMetrics { + private final Counter ordersCreated; + private final Timer orderProcessingTime; + private final Gauge activeUsers; + + public BusinessMetrics(MeterRegistry meterRegistry) { + this.ordersCreated = Counter.builder("orders.created") + .description("Total orders created") + .register(meterRegistry); + + this.orderProcessingTime = Timer.builder("orders.processing.time") + .description("Order processing time") + .register(meterRegistry); + + this.activeUsers = Gauge.builder("users.active") + .description("Number of active users") + .register(meterRegistry, this, obj -> obj.getActiveUsersCount()); + } + + public void recordOrderCreated() { + ordersCreated.increment(); + } + + public void recordOrderProcessingTime(Duration duration) { + orderProcessingTime.record(duration); + } + + private int getActiveUsersCount() { + // логика подсчета активных пользователей + return 0; + } +} +``` + +--- + +## Alerting + +### Принципы алертинга + +#### 1. Alert Fatigue Prevention +- Только важные алерты +- Группировка похожих алертов +- Эскалация по времени + +#### 2. Alert Classification +- **Critical** — система недоступна +- **Warning** — деградация производительности +- **Info** — информационные уведомления + +### Правила оповещений Prometheus + +```yaml +# alerts.yml +groups: +- name: application + rules: + - alert: HighErrorRate + expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05 + for: 2m + labels: + severity: critical + annotations: + summary: "High error rate detected" + description: "Error rate is {{ $value }} for {{ $labels.instance }}" + + - alert: HighResponseTime + expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1 + for: 5m + labels: + severity: warning + annotations: + summary: "High response time" + description: "95th percentile response time is {{ $value }}s" +``` + +### Конфигурация Alertmanager + +```yaml +# alertmanager.yml +global: + smtp_smarthost: 'localhost:587' + smtp_from: 'alerts@example.com' + +route: + group_by: ['alertname'] + group_wait: 10s + group_interval: 10s + repeat_interval: 1h + receiver: 'web.hook' + +receivers: +- name: 'web.hook' + webhook_configs: + - url: 'http://alert-webhook:5001/alerts' + +- name: 'email' + email_configs: + - to: 'admin@example.com' + subject: 'Alert: {{ .GroupLabels.alertname }}' + body: | + {{ range .Alerts }} + Alert: {{ .Annotations.summary }} + Description: {{ .Annotations.description }} + {{ end }} +``` + +--- + +## SLI/SLO/SLA + +### SLI (Индикатор уровня сервиса) + +**SLI** — метрика, которая измеряет качество сервиса. + +#### Примеры SLI: +- **Availability**: `successful_requests / total_requests` +- **Latency**: `requests_completed_within_threshold / total_requests` +- **Throughput**: `successful_requests_per_second` + +```java +@Component +public class SLICalculator { + public double calculateAvailability(String service, Duration window) { + Counter successfulRequests = Counter.builder("requests.successful") + .tag("service", service) + .register(meterRegistry); + + Counter totalRequests = Counter.builder("requests.total") + .tag("service", service) + .register(meterRegistry); + + double successful = successfulRequests.count(); + double total = totalRequests.count(); + + return total > 0 ? successful / total : 0.0; + } + + public double calculateLatency(String service, Duration threshold) { + Timer requestTimer = Timer.builder("request.duration") + .tag("service", service) + .register(meterRegistry); + + // логика расчета латентности + return 0.95; // пример + } +} +``` + +### SLO (Цель уровня сервиса) + +**SLO** — целевое значение для SLI. + +#### Примеры SLO: +- **Availability**: 99.9% uptime +- **Latency**: 95% of requests < 200ms +- **Error Rate**: < 0.1% errors + +```java +@Component +public class SLOMonitor { + private final Map slos = Map.of( + "availability", new SLO("availability", 0.999, "99.9% uptime"), + "latency", new SLO("latency", 0.95, "95% < 200ms"), + "error_rate", new SLO("error_rate", 0.001, "< 0.1% errors") + ); + + public boolean checkSLO(String sloName, double currentValue) { + SLO slo = slos.get(sloName); + return currentValue >= slo.getTarget(); + } +} +``` + +### SLA (Соглашение об уровне сервиса) + +**SLA** — соглашение с клиентами о качестве сервиса. + +#### Компоненты SLA: +- **Service Description** — описание сервиса +- **Performance Metrics** — метрики производительности +- **Availability Targets** — цели доступности +- **Remediation** — меры по устранению нарушений + +--- + +## Практические задания + +### Задание 1: Prometheus + Grafana +1. Настройте Prometheus для сбора метрик +2. Создайте дашборд в Grafana +3. Настройте алерты + +### Задание 2: Distributed Tracing +1. Интегрируйте OpenTelemetry в приложение +2. Настройте Jaeger для анализа трассировок +3. Создайте анализ производительности + +### Задание 3: ELK Stack +1. Настройте централизованное логирование +2. Создайте индексы в Elasticsearch +3. Настройте дашборды в Kibana + +--- + +## Дополнительные ресурсы + +### Инструменты +- **Metrics**: Prometheus, InfluxDB, TimescaleDB +- **Logging**: ELK Stack, Fluentd, Splunk +- **Tracing**: Jaeger, Zipkin, OpenTelemetry +- **Dashboards**: Grafana, Kibana, DataDog + +### Книги +- "Observability Engineering" by Charity Majors +- "Site Reliability Engineering" by Google +- "Distributed Systems Observability" by Cindy Sridharan + +--- + +## Контрольные вопросы + +1. Что такое мониторинг и зачем он нужен? +2. Какие типы метрик вы знаете? +3. Как работает Prometheus и PromQL? +4. Что такое structured logging? +5. Как работает distributed tracing? +6. Что такое три столпа observability? +7. Как настроить алертинг? +8. Что такое SLI/SLO/SLA? diff --git a/modules/module6/README.md b/modules/module6/README.md new file mode 100644 index 0000000..6b8620c --- /dev/null +++ b/modules/module6/README.md @@ -0,0 +1,134 @@ +# Модуль 6: Безопасность + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля + +Модуль посвящен комплексному изучению безопасности в современной разработке программного обеспечения. Студенты изучат принципы безопасности, методы защиты от уязвимостей, шифрование данных, управление ключами, обфускацию кода и соответствие требованиям безопасности. + +## Содержание модуля + +### 1. [Основы безопасности](sections/01-security-basics.md) +- Принципы безопасности (CIA Triad) +- Defense in Depth +- Least Privilege +- Угрозы безопасности (OWASP Top 10) +- Методы устранения уязвимостей + +### 2. [Уязвимости и межсервисное взаимодействие](sections/02-vulnerabilities-inter-service.md) +- Типы уязвимостей в микросервисной архитектуре +- TVM (Token Validation Middleware) +- Mutual TLS (mTLS) +- Service Mesh Security +- API Gateway Security +- Security Headers +- Rate Limiting +- Audit Logging + +### 3. [Шифрование и управление ключами](sections/03-encryption-key-management.md) +- Симметричное шифрование (AES) +- Асимметричное шифрование (RSA) +- Хеширование (BCrypt, PBKDF2) +- Управление ключами +- Key Rotation +- Hardware Security Module (HSM) +- Key Vault Integration + +### 4. [Обфускация и защита кода](sections/04-code-obfuscation-protection.md) +- Обфускация кода +- Минификация +- Защита от реверс-инжиниринга +- Source Maps и их безопасность +- Защита конфигурационных файлов +- Защита от анализа трафика + +### 5. [Security by Design](sections/05-security-by-design.md) +- Принципы Security by Design +- Моделирование угроз (STRIDE) +- Zero Trust Architecture +- Microsegmentation +- Аутентификация и авторизация +- Безопасность API +- Безопасность данных +- Соответствие требованиям (GDPR, PCI DSS, SOX) + +## Ключевые компетенции + +По завершении модуля студенты будут уметь: + +1. **Анализировать угрозы безопасности** + - Выявлять уязвимости в коде + - Применять STRIDE модель для анализа угроз + - Оценивать риски безопасности + +2. **Реализовывать защитные механизмы** + - Настраивать аутентификацию и авторизацию + - Внедрять шифрование данных + - Конфигурировать безопасность API + +3. **Управлять ключами шифрования** + - Реализовывать ротацию ключей + - Интегрироваться с HSM и Key Vault + - Обеспечивать безопасное хранение ключей + +4. **Защищать исходный код** + - Применять обфускацию и минификацию + - Настраивать защиту от реверс-инжиниринга + - Управлять Source Maps + +5. **Обеспечивать соответствие требованиям** + - Реализовывать GDPR compliance + - Настраивать PCI DSS для платежей + - Внедрять SOX controls + +## Практические задания + +### Задание 1: Security Implementation +1. Реализуйте аутентификацию с JWT +2. Настройте авторизацию с RBAC +3. Добавьте валидацию входных данных + +### Задание 2: Data Protection +1. Реализуйте шифрование данных +2. Настройте маскирование данных +3. Добавьте аудит доступа к данным + +### Задание 3: Compliance +1. Реализуйте GDPR compliance +2. Настройте PCI DSS для платежей +3. Добавьте SOX controls + +## Дополнительные ресурсы + +### Стандарты и фреймворки +- **OWASP** — Open Web Application Security Project +- **NIST** — National Institute of Standards and Technology +- **ISO 27001** — Information Security Management +- **SOC 2** — Service Organization Control + +### Инструменты +- **Security Scanning**: OWASP ZAP, Burp Suite, Nessus +- **Code Analysis**: SonarQube, Checkmarx, Veracode +- **Secrets Management**: HashiCorp Vault, AWS Secrets Manager +- **Identity Management**: Keycloak, Auth0, Okta + +## Контрольные вопросы + +1. Что такое CIA Triad и как он применяется? +2. Какие принципы Security by Design вы знаете? +3. Как работает OAuth 2.0 и OpenID Connect? +4. Какие типы шифрования существуют? +5. Как обеспечить безопасность API? +6. Что такое GDPR и как его соблюдать? +7. Какие требования PCI DSS вы знаете? +8. Как реализовать Zero Trust Architecture? + +## Критерии оценки + +- **Теоретические знания** (30%) — понимание принципов безопасности +- **Практические навыки** (40%) — реализация защитных механизмов +- **Анализ безопасности** (20%) — выявление и устранение уязвимостей +- **Соответствие стандартам** (10%) — соблюдение требований безопасности diff --git a/modules/module6/sections/02-vulnerabilities-inter-service.md b/modules/module6/sections/02-vulnerabilities-inter-service.md new file mode 100644 index 0000000..f34ddf9 --- /dev/null +++ b/modules/module6/sections/02-vulnerabilities-inter-service.md @@ -0,0 +1,723 @@ +# Уязвимости и межсервисное взаимодействие + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 6](../README.md) + +--- + +## Типы уязвимостей в микросервисной архитектуре + +### 1. Уязвимости межсервисного взаимодействия + +**Проблемы:** +- Отсутствие аутентификации между сервисами +- Передача чувствительных данных в открытом виде +- Отсутствие валидации токенов +- Man-in-the-middle атаки +- Replay атаки + +**Решения:** +- Mutual TLS (mTLS) +- Service-to-Service токены +- API Gateway с аутентификацией +- Token Validation Middleware (TVM) + +### 2. TVM (Token Validation Middleware) + +**TVM** — промежуточное ПО для валидации токенов между микросервисами. + +**Принципы работы:** +1. Сервис получает токен от клиента +2. TVM валидирует токен +3. TVM извлекает информацию о пользователе/сервисе +4. TVM передает валидные данные в целевой сервис + +**Технический пример - TVM Implementation:** + +```java +@Component +public class TokenValidationMiddleware { + + @Autowired + private JwtTokenService jwtTokenService; + + @Autowired + private ServiceRegistry serviceRegistry; + + public ServiceTokenValidationResult validateServiceToken(String token, String targetService) { + try { + // Валидация токена + Claims claims = jwtTokenService.validateToken(token); + + // Проверка прав доступа к сервису + if (!hasAccessToService(claims, targetService)) { + return ServiceTokenValidationResult.denied("No access to service: " + targetService); + } + + // Проверка срока действия токена + if (isTokenExpired(claims)) { + return ServiceTokenValidationResult.denied("Token expired"); + } + + // Извлечение информации о сервисе + ServiceInfo serviceInfo = extractServiceInfo(claims); + + return ServiceTokenValidationResult.success(serviceInfo); + + } catch (JwtException e) { + return ServiceTokenValidationResult.denied("Invalid token: " + e.getMessage()); + } + } + + private boolean hasAccessToService(Claims claims, String targetService) { + List allowedServices = claims.get("allowed_services", List.class); + return allowedServices != null && allowedServices.contains(targetService); + } + + private boolean isTokenExpired(Claims claims) { + Date expiration = claims.getExpiration(); + return expiration.before(new Date()); + } + + private ServiceInfo extractServiceInfo(Claims claims) { + return ServiceInfo.builder() + .serviceId(claims.getSubject()) + .serviceName(claims.get("service_name", String.class)) + .permissions(claims.get("permissions", List.class)) + .issuedAt(claims.getIssuedAt()) + .expiresAt(claims.getExpiration()) + .build(); + } +} + +// Service Token Validation Result +@Data +@Builder +public class ServiceTokenValidationResult { + private boolean valid; + private String errorMessage; + private ServiceInfo serviceInfo; + + public static ServiceTokenValidationResult success(ServiceInfo serviceInfo) { + return ServiceTokenValidationResult.builder() + .valid(true) + .serviceInfo(serviceInfo) + .build(); + } + + public static ServiceTokenValidationResult denied(String errorMessage) { + return ServiceTokenValidationResult.builder() + .valid(false) + .errorMessage(errorMessage) + .build(); + } +} + +// Service Info +@Data +@Builder +public class ServiceInfo { + private String serviceId; + private String serviceName; + private List permissions; + private Date issuedAt; + private Date expiresAt; +} +``` + +**Rust примеры - TVM с Actix Web:** + +```rust +// Token Validation Middleware для Actix Web +use actix_web::{web, HttpRequest, HttpResponse, Error, middleware::Next}; +use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm}; +use serde::{Deserialize, Serialize}; +use std::collections::HashSet; + +#[derive(Debug, Serialize, Deserialize)] +struct ServiceClaims { + sub: String, // service_id + service_name: String, + allowed_services: Vec, + permissions: Vec, + iat: u64, // issued_at + exp: u64, // expires_at +} + +#[derive(Debug, Clone)] +pub struct ServiceInfo { + pub service_id: String, + pub service_name: String, + pub permissions: Vec, +} + +pub struct TokenValidationMiddleware { + decoding_key: DecodingKey, + validation: Validation, +} + +impl TokenValidationMiddleware { + pub fn new(secret: &str) -> Self { + let mut validation = Validation::new(Algorithm::HS256); + validation.set_issuer(&["microservices-platform"]); + + Self { + decoding_key: DecodingKey::from_secret(secret.as_ref()), + validation, + } + } + + pub fn validate_service_token( + &self, + token: &str, + target_service: &str, + ) -> Result { + // Декодирование токена + let token_data = decode::(token, &self.decoding_key, &self.validation) + .map_err(|e| format!("Token validation failed: {}", e))?; + + let claims = token_data.claims; + + // Проверка срока действия + let now = chrono::Utc::now().timestamp() as u64; + if claims.exp < now { + return Err("Token expired".to_string()); + } + + // Проверка прав доступа к сервису + if !claims.allowed_services.contains(&target_service.to_string()) { + return Err(format!("No access to service: {}", target_service)); + } + + Ok(ServiceInfo { + service_id: claims.sub, + service_name: claims.service_name, + permissions: claims.permissions, + }) + } +} + +// Middleware для Actix Web +pub async fn token_validation_middleware( + req: HttpRequest, + next: Next, +) -> Result { + // Извлечение токена из заголовка + let token = req.headers() + .get("Authorization") + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.strip_prefix("Bearer ")); + + match token { + Some(token) => { + // Валидация токена + let middleware = TokenValidationMiddleware::new("your-secret-key"); + let target_service = req.path().split('/').nth(1).unwrap_or("unknown"); + + match middleware.validate_service_token(token, target_service) { + Ok(service_info) => { + // Добавление информации о сервисе в контекст запроса + req.extensions_mut().insert(service_info); + next.run(req).await + } + Err(e) => Ok(HttpResponse::Unauthorized() + .json(serde_json::json!({ + "error": "Token validation failed", + "message": e + }))) + } + } + None => Ok(HttpResponse::Unauthorized() + .json(serde_json::json!({ + "error": "Missing authorization token" + }))) + } +} +``` + +**Python примеры - TVM с FastAPI:** + +```python +# Token Validation Middleware для FastAPI +from fastapi import FastAPI, HTTPException, Depends, Request +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from jose import JWTError, jwt +from datetime import datetime, timedelta +from typing import Optional, List +from pydantic import BaseModel + +class ServiceClaims(BaseModel): + sub: str + service_name: str + allowed_services: List[str] + permissions: List[str] + iat: int + exp: int + +class ServiceInfo(BaseModel): + service_id: str + service_name: str + permissions: List[str] + +class TokenValidationMiddleware: + def __init__(self, secret_key: str): + self.secret_key = secret_key + self.algorithm = "HS256" + + def validate_service_token(self, token: str, target_service: str) -> ServiceInfo: + try: + # Декодирование токена + payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm]) + + # Проверка срока действия + if datetime.utcnow().timestamp() > payload.get("exp", 0): + raise HTTPException(status_code=401, detail="Token expired") + + # Проверка прав доступа к сервису + allowed_services = payload.get("allowed_services", []) + if target_service not in allowed_services: + raise HTTPException( + status_code=403, + detail=f"No access to service: {target_service}" + ) + + return ServiceInfo( + service_id=payload.get("sub"), + service_name=payload.get("service_name"), + permissions=payload.get("permissions", []) + ) + + except JWTError as e: + raise HTTPException(status_code=401, detail=f"Invalid token: {str(e)}") + +# Dependency для FastAPI +security = HTTPBearer() +middleware = TokenValidationMiddleware("your-secret-key") + +async def get_service_info( + credentials: HTTPAuthorizationCredentials = Depends(security), + request: Request = None +) -> ServiceInfo: + token = credentials.credentials + target_service = request.url.path.split('/')[1] if request else "unknown" + + return middleware.validate_service_token(token, target_service) + +# Использование в FastAPI +app = FastAPI() + +@app.get("/api/orders") +async def get_orders(service_info: ServiceInfo = Depends(get_service_info)): + # Проверка прав доступа + if "read:orders" not in service_info.permissions: + raise HTTPException(status_code=403, detail="Insufficient permissions") + + return {"message": "Orders retrieved", "service": service_info.service_name} +``` + +**Node.js примеры - TVM с Express:** + +```typescript +// Token Validation Middleware для Express +import express, { Request, Response, NextFunction } from 'express'; +import jwt from 'jsonwebtoken'; +import { Request as JWTRequest } from 'express-jwt'; + +interface ServiceClaims { + sub: string; + service_name: string; + allowed_services: string[]; + permissions: string[]; + iat: number; + exp: number; +} + +interface ServiceInfo { + service_id: string; + service_name: string; + permissions: string[]; +} + +class TokenValidationMiddleware { + private secretKey: string; + private algorithm: string = 'HS256'; + + constructor(secretKey: string) { + this.secretKey = secretKey; + } + + validateServiceToken(token: string, targetService: string): ServiceInfo { + try { + // Декодирование токена + const payload = jwt.verify(token, this.secretKey, { + algorithms: [this.algorithm] + }) as ServiceClaims; + + // Проверка срока действия + if (Date.now() / 1000 > payload.exp) { + throw new Error('Token expired'); + } + + // Проверка прав доступа к сервису + if (!payload.allowed_services.includes(targetService)) { + throw new Error(`No access to service: ${targetService}`); + } + + return { + service_id: payload.sub, + service_name: payload.service_name, + permissions: payload.permissions + }; + + } catch (error) { + throw new Error(`Token validation failed: ${error.message}`); + } + } +} + +// Middleware для Express +const middleware = new TokenValidationMiddleware('your-secret-key'); + +export const tokenValidationMiddleware = (req: Request, res: Response, next: NextFunction) => { + try { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'Missing authorization token' }); + } + + const token = authHeader.substring(7); + const targetService = req.path.split('/')[2] || 'unknown'; + + const serviceInfo = middleware.validateServiceToken(token, targetService); + + // Добавление информации о сервисе в контекст запроса + (req as any).serviceInfo = serviceInfo; + + next(); + } catch (error) { + res.status(401).json({ + error: 'Token validation failed', + message: error.message + }); + } +}; + +// Использование в Express +app.use('/api', tokenValidationMiddleware); + +app.get('/api/orders', (req: Request, res: Response) => { + const serviceInfo = (req as any).serviceInfo; + + // Проверка прав доступа + if (!serviceInfo.permissions.includes('read:orders')) { + return res.status(403).json({ error: 'Insufficient permissions' }); + } + + res.json({ + message: 'Orders retrieved', + service: serviceInfo.service_name + }); +}); +``` + +### 3. Mutual TLS (mTLS) + +**mTLS** обеспечивает взаимную аутентификацию между сервисами. + +**Java пример - Spring Boot с mTLS:** + +```java +@Configuration +@EnableWebSecurity +public class MTlsSecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/public/**").permitAll() + .anyRequest().authenticated() + ) + .x509(x509 -> x509 + .subjectPrincipalRegex("CN=(.*?)(?:,|$)") + .userDetailsService(userDetailsService()) + ) + .build(); + } + + @Bean + public UserDetailsService userDetailsService() { + return username -> { + // Валидация сертификата клиента + if (isValidServiceCertificate(username)) { + return User.builder() + .username(username) + .password("") + .authorities("ROLE_SERVICE") + .build(); + } + throw new UsernameNotFoundException("Invalid service certificate"); + }; + } + + private boolean isValidServiceCertificate(String subject) { + // Логика валидации сертификата + return subject.startsWith("service-"); + } +} +``` + +### 4. Service Mesh Security + +**Istio Security Configuration:** + +```yaml +# PeerAuthentication для mTLS +apiVersion: security.istio.io/v1beta1 +kind: PeerAuthentication +metadata: + name: default + namespace: production +spec: + mtls: + mode: STRICT + +# AuthorizationPolicy для контроля доступа +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: service-access + namespace: production +spec: + rules: + - from: + - source: + principals: ["cluster.local/ns/production/sa/order-service"] + to: + - operation: + methods: ["GET", "POST"] + paths: ["/api/orders/*"] +``` + +### 5. API Gateway Security + +**Kong Gateway Configuration:** + +```yaml +# Kong API Gateway с JWT аутентификацией +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: jwt-auth +config: + key_claim_name: iss + secret_is_base64: false + run_on_preflight: true +--- +apiVersion: configuration.konghq.com/v1 +kind: KongIngress +metadata: + name: api-ingress +upstream: + healthchecks: + active: + healthy: + interval: 30 + successes: 3 + unhealthy: + interval: 10 + http_failures: 3 +``` + +### 6. Security Headers для микросервисов + +**Java пример - Security Headers:** + +```java +@Component +public class SecurityHeadersFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletResponse httpResponse = (HttpServletResponse) response; + + // Security Headers + httpResponse.setHeader("X-Content-Type-Options", "nosniff"); + httpResponse.setHeader("X-Frame-Options", "DENY"); + httpResponse.setHeader("X-XSS-Protection", "1; mode=block"); + httpResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); + httpResponse.setHeader("Content-Security-Policy", "default-src 'self'"); + httpResponse.setHeader("Referrer-Policy", "strict-origin-when-cross-origin"); + httpResponse.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=()"); + + chain.doFilter(request, response); + } +} +``` + +### 7. Rate Limiting для межсервисного взаимодействия + +**Redis-based Rate Limiting:** + +```java +@Service +public class InterServiceRateLimitingService { + + @Autowired + private RedisTemplate redisTemplate; + + public boolean isAllowed(String serviceId, String targetService, int maxRequests, Duration window) { + String key = String.format("rate_limit:%s:%s", serviceId, targetService); + + // Sliding window rate limiting + long now = System.currentTimeMillis(); + long windowStart = now - window.toMillis(); + + // Удаление старых записей + redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart); + + // Подсчет текущих запросов + Long currentCount = redisTemplate.opsForZSet().count(key, windowStart, now); + + if (currentCount >= maxRequests) { + return false; + } + + // Добавление нового запроса + redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now); + redisTemplate.expire(key, window); + + return true; + } +} +``` + +### 8. Audit Logging для межсервисного взаимодействия + +**Structured Audit Logging:** + +```java +@Component +public class InterServiceAuditLogger { + + private static final Logger auditLogger = LoggerFactory.getLogger("AUDIT"); + + public void logServiceCall(String sourceService, String targetService, + String operation, boolean success, String details) { + + Map auditData = Map.of( + "timestamp", Instant.now().toString(), + "source_service", sourceService, + "target_service", targetService, + "operation", operation, + "success", success, + "details", details, + "correlation_id", MDC.get("correlationId") + ); + + auditLogger.info("Inter-service call: {}", auditData); + } +} +``` + +### 9. Circuit Breaker для безопасности + +**Resilience4j Circuit Breaker:** + +```java +@Component +public class SecureServiceClient { + + private final CircuitBreaker circuitBreaker; + private final Retry retry; + + public SecureServiceClient() { + this.circuitBreaker = CircuitBreaker.ofDefaults("secure-service"); + this.retry = Retry.ofDefaults("secure-service"); + } + + @CircuitBreaker(name = "secure-service") + @Retry(name = "secure-service") + public ServiceResponse callSecureService(String serviceUrl, String token) { + try { + // Вызов защищенного сервиса + return restTemplate.exchange( + serviceUrl, + HttpMethod.GET, + createSecureRequest(token), + ServiceResponse.class + ).getBody(); + } catch (Exception e) { + // Логирование безопасности + securityLogger.warn("Secure service call failed: {}", e.getMessage()); + throw e; + } + } + + private HttpEntity createSecureRequest(String token) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(token); + headers.set("X-Service-Name", "order-service"); + headers.set("X-Request-ID", UUID.randomUUID().toString()); + + return new HttpEntity<>(headers); + } +} +``` + +### 10. Security Monitoring и Alerting + +**Security Metrics:** + +```java +@Component +public class SecurityMetrics { + + private final MeterRegistry meterRegistry; + private final Counter authenticationFailures; + private final Counter authorizationFailures; + private final Timer serviceCallDuration; + + public SecurityMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.authenticationFailures = Counter.builder("security.auth.failures") + .description("Authentication failures") + .register(meterRegistry); + this.authorizationFailures = Counter.builder("security.authz.failures") + .description("Authorization failures") + .register(meterRegistry); + this.serviceCallDuration = Timer.builder("security.service.call.duration") + .description("Secure service call duration") + .register(meterRegistry); + } + + public void recordAuthenticationFailure(String reason) { + authenticationFailures.increment(Tags.of("reason", reason)); + } + + public void recordAuthorizationFailure(String resource) { + authorizationFailures.increment(Tags.of("resource", resource)); + } + + public Timer.Sample startServiceCallTimer() { + return Timer.start(meterRegistry); + } +} +``` + +## Заключение + +Безопасность межсервисного взаимодействия требует комплексного подхода: + +1. **Аутентификация** - проверка подлинности сервисов +2. **Авторизация** - контроль доступа к ресурсам +3. **Шифрование** - защита данных в транзите +4. **Мониторинг** - отслеживание подозрительной активности +5. **Аудит** - логирование всех операций +6. **Rate Limiting** - защита от злоупотреблений +7. **Circuit Breaker** - устойчивость к сбоям + +TVM и mTLS являются ключевыми технологиями для обеспечения безопасности в микросервисной архитектуре. diff --git a/modules/module6/sections/03-encryption-key-management.md b/modules/module6/sections/03-encryption-key-management.md new file mode 100644 index 0000000..3bee5fa --- /dev/null +++ b/modules/module6/sections/03-encryption-key-management.md @@ -0,0 +1,766 @@ +# Шифрование и управление ключами + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 6](../README.md) + +--- + +## Типы шифрования + +### 1. Symmetric Encryption (Симметричное шифрование) + +**Симметричное шифрование** использует один ключ для шифрования и расшифровки данных. + +**Преимущества:** +- Быстрое выполнение +- Подходит для больших объемов данных +- Простота реализации + +**Недостатки:** +- Проблема распределения ключей +- Сложность масштабирования + +**Технический пример - Advanced Encryption Service:** + +```java +@Service +public class SymmetricEncryptionService { + + private final SecretKey secretKey; + private final String algorithm = "AES/GCM/NoPadding"; + private final int GCM_IV_LENGTH = 12; + private final int GCM_TAG_LENGTH = 16; + + @Value("${encryption.key}") + private String base64Key; + + public SymmetricEncryptionService() throws Exception { + // Генерируем ключ из конфигурации или создаем новый + if (base64Key != null && !base64Key.isEmpty()) { + byte[] keyBytes = Base64.getDecoder().decode(base64Key); + this.secretKey = new SecretKeySpec(keyBytes, "AES"); + } else { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(256); + this.secretKey = keyGenerator.generateKey(); + } + } + + public EncryptedData encrypt(String plainText) throws Exception { + if (plainText == null || plainText.isEmpty()) { + throw new IllegalArgumentException("Plain text cannot be null or empty"); + } + + // Генерируем случайный IV + byte[] iv = new byte[GCM_IV_LENGTH]; + SecureRandom.getInstanceStrong().nextBytes(iv); + + // Настраиваем шифр + Cipher cipher = Cipher.getInstance(algorithm); + GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); + + // Шифруем данные + byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + + // Объединяем IV и зашифрованные данные + byte[] encryptedWithIv = new byte[GCM_IV_LENGTH + encryptedBytes.length]; + System.arraycopy(iv, 0, encryptedWithIv, 0, GCM_IV_LENGTH); + System.arraycopy(encryptedBytes, 0, encryptedWithIv, GCM_IV_LENGTH, encryptedBytes.length); + + return new EncryptedData( + Base64.getEncoder().encodeToString(encryptedWithIv), + System.currentTimeMillis() + ); + } + + public String decrypt(EncryptedData encryptedData) throws Exception { + if (encryptedData == null || encryptedData.getData() == null) { + throw new IllegalArgumentException("Encrypted data cannot be null"); + } + + byte[] encryptedWithIv = Base64.getDecoder().decode(encryptedData.getData()); + + if (encryptedWithIv.length < GCM_IV_LENGTH) { + throw new IllegalArgumentException("Invalid encrypted data length"); + } + + // Извлекаем IV + byte[] iv = new byte[GCM_IV_LENGTH]; + System.arraycopy(encryptedWithIv, 0, iv, 0, GCM_IV_LENGTH); + + // Извлекаем зашифрованные данные + byte[] encryptedBytes = new byte[encryptedWithIv.length - GCM_IV_LENGTH]; + System.arraycopy(encryptedWithIv, GCM_IV_LENGTH, encryptedBytes, 0, encryptedBytes.length); + + // Настраиваем шифр для расшифровки + Cipher cipher = Cipher.getInstance(algorithm); + GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec); + + // Расшифровываем данные + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + public String encryptSensitiveField(String value) throws Exception { + if (value == null) { + return null; + } + return encrypt(value).getData(); + } + + public String decryptSensitiveField(String encryptedValue) throws Exception { + if (encryptedValue == null) { + return null; + } + return decrypt(new EncryptedData(encryptedValue, 0L)); + } +} + +// Encrypted Data Wrapper +public class EncryptedData { + private final String data; + private final long timestamp; + + public EncryptedData(String data, long timestamp) { + this.data = data; + this.timestamp = timestamp; + } + + public String getData() { + return data; + } + + public long getTimestamp() { + return timestamp; + } +} + +// Field-level Encryption +@Entity +public class User { + @Id + private Long id; + + private String name; + + @Convert(converter = EncryptedStringConverter.class) + private String email; + + @Convert(converter = EncryptedStringConverter.class) + private String phoneNumber; + + @Convert(converter = EncryptedStringConverter.class) + private String ssn; + + // Getters and setters +} + +// Custom Converter for JPA +@Converter +public class EncryptedStringConverter implements AttributeConverter { + + @Autowired + private SymmetricEncryptionService encryptionService; + + @Override + public String convertToDatabaseColumn(String attribute) { + if (attribute == null) { + return null; + } + try { + return encryptionService.encryptSensitiveField(attribute); + } catch (Exception e) { + throw new RuntimeException("Failed to encrypt field", e); + } + } + + @Override + public String convertToEntityAttribute(String dbData) { + if (dbData == null) { + return null; + } + try { + return encryptionService.decryptSensitiveField(dbData); + } catch (Exception e) { + throw new RuntimeException("Failed to decrypt field", e); + } + } +} +``` + +**Rust примеры - Симметричное шифрование:** + +```rust +use aes_gcm::{Aes256Gcm, Key, Nonce}; +use aes_gcm::aead::{Aead, NewAead}; +use rand::Rng; + +pub struct SymmetricEncryptionService { + cipher: Aes256Gcm, +} + +impl SymmetricEncryptionService { + pub fn new(key: &[u8]) -> Result> { + let key = Key::from_slice(key); + let cipher = Aes256Gcm::new(key); + + Ok(Self { cipher }) + } + + pub fn encrypt(&self, plaintext: &str) -> Result> { + let nonce = self.generate_nonce(); + let ciphertext = self.cipher.encrypt(&nonce, plaintext.as_bytes())?; + + Ok(EncryptedData { + data: base64::encode(ciphertext), + nonce: base64::encode(nonce), + timestamp: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_secs(), + }) + } + + pub fn decrypt(&self, encrypted_data: &EncryptedData) -> Result> { + let nonce = base64::decode(&encrypted_data.nonce)?; + let ciphertext = base64::decode(&encrypted_data.data)?; + + let plaintext = self.cipher.decrypt(&nonce.into(), ciphertext.as_ref())?; + Ok(String::from_utf8(plaintext)?) + } + + fn generate_nonce(&self) -> Nonce { + let mut nonce = [0u8; 12]; + rand::thread_rng().fill(&mut nonce); + nonce.into() + } +} + +#[derive(Debug, Clone)] +pub struct EncryptedData { + pub data: String, + pub nonce: String, + pub timestamp: u64, +} +``` + +**Python примеры - Симметричное шифрование:** + +```python +from cryptography.fernet import Fernet +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC +import base64 +import os + +class SymmetricEncryptionService: + def __init__(self, password: str): + # Генерируем ключ из пароля + salt = os.urandom(16) + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=32, + salt=salt, + iterations=100000, + ) + key = base64.urlsafe_b64encode(kdf.derive(password.encode())) + self.cipher = Fernet(key) + + def encrypt(self, plaintext: str) -> EncryptedData: + if not plaintext: + raise ValueError("Plaintext cannot be empty") + + encrypted_data = self.cipher.encrypt(plaintext.encode()) + + return EncryptedData( + data=base64.b64encode(encrypted_data).decode(), + timestamp=int(time.time()) + ) + + def decrypt(self, encrypted_data: EncryptedData) -> str: + if not encrypted_data.data: + raise ValueError("Encrypted data cannot be empty") + + encrypted_bytes = base64.b64decode(encrypted_data.data) + decrypted_bytes = self.cipher.decrypt(encrypted_bytes) + + return decrypted_bytes.decode() + +@dataclass +class EncryptedData: + data: str + timestamp: int +``` + +**Node.js примеры - Симметричное шифрование:** + +```typescript +import crypto from 'crypto'; + +interface EncryptedData { + data: string; + iv: string; + timestamp: number; +} + +class SymmetricEncryptionService { + private algorithm = 'aes-256-gcm'; + private keyLength = 32; + private ivLength = 16; + private tagLength = 16; + + constructor(private key: Buffer) { + if (key.length !== this.keyLength) { + throw new Error(`Key must be ${this.keyLength} bytes long`); + } + } + + encrypt(plaintext: string): EncryptedData { + if (!plaintext) { + throw new Error('Plaintext cannot be empty'); + } + + const iv = crypto.randomBytes(this.ivLength); + const cipher = crypto.createCipher(this.algorithm, this.key); + cipher.setAAD(iv); + + let encrypted = cipher.update(plaintext, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + + const tag = cipher.getAuthTag(); + + return { + data: encrypted, + iv: iv.toString('hex'), + timestamp: Date.now() + }; + } + + decrypt(encryptedData: EncryptedData): string { + if (!encryptedData.data || !encryptedData.iv) { + throw new Error('Invalid encrypted data'); + } + + const iv = Buffer.from(encryptedData.iv, 'hex'); + const decipher = crypto.createDecipher(this.algorithm, this.key); + decipher.setAAD(iv); + + let decrypted = decipher.update(encryptedData.data, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + + return decrypted; + } +} +``` + +### 2. Asymmetric Encryption (Асимметричное шифрование) + +**Асимметричное шифрование** использует пару ключей: публичный для шифрования и приватный для расшифровки. + +**Преимущества:** +- Безопасное распределение ключей +- Цифровые подписи +- Масштабируемость + +**Недостатки:** +- Медленнее симметричного шифрования +- Сложность реализации + +```java +@Service +public class AsymmetricEncryptionService { + private final KeyPair keyPair; + + public AsymmetricEncryptionService() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + this.keyPair = keyPairGenerator.generateKeyPair(); + } + + public String encrypt(String plainText, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + + byte[] encryptedBytes = cipher.doFinal(plainText.getBytes()); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + public String decrypt(String encryptedText, PrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText)); + return new String(decryptedBytes); + } + + public String sign(String data, PrivateKey privateKey) throws Exception { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(privateKey); + signature.update(data.getBytes()); + + byte[] signatureBytes = signature.sign(); + return Base64.getEncoder().encodeToString(signatureBytes); + } + + public boolean verify(String data, String signature, PublicKey publicKey) throws Exception { + Signature sig = Signature.getInstance("SHA256withRSA"); + sig.initVerify(publicKey); + sig.update(data.getBytes()); + + return sig.verify(Base64.getDecoder().decode(signature)); + } +} +``` + +### 3. Hashing (Хеширование) + +**Хеширование** — одностороннее преобразование данных в фиксированную длину. + +**Применения:** +- Хранение паролей +- Проверка целостности данных +- Цифровые подписи + +```java +@Service +public class HashingService { + + private final int BCRYPT_ROUNDS = 12; + private final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256"; + private final int PBKDF2_ITERATIONS = 100000; + private final int PBKDF2_KEY_LENGTH = 256; + + public String hashPassword(String password) { + if (password == null || password.isEmpty()) { + throw new IllegalArgumentException("Password cannot be null or empty"); + } + return BCrypt.hashpw(password, BCrypt.gensalt(BCRYPT_ROUNDS)); + } + + public boolean verifyPassword(String password, String hash) { + if (password == null || hash == null) { + return false; + } + return BCrypt.checkpw(password, hash); + } + + public String generatePBKDF2Hash(String password, String salt) throws Exception { + if (password == null || salt == null) { + throw new IllegalArgumentException("Password and salt cannot be null"); + } + + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), + PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH); + SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); + byte[] hash = skf.generateSecret(spec).getEncoded(); + + return Base64.getEncoder().encodeToString(hash); + } + + public String generateSalt() { + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[16]; + random.nextBytes(salt); + return Base64.getEncoder().encodeToString(salt); + } + + public String generateHash(String input, String algorithm) throws Exception { + if (input == null) { + throw new IllegalArgumentException("Input cannot be null"); + } + + MessageDigest digest = MessageDigest.getInstance(algorithm); + byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(hash); + } + + public String generateHMAC(String data, String key, String algorithm) throws Exception { + if (data == null || key == null) { + throw new IllegalArgumentException("Data and key cannot be null"); + } + + Mac mac = Mac.getInstance(algorithm); + SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm); + mac.init(secretKeySpec); + + byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(hmacBytes); + } +} +``` + +## Управление ключами + +### 1. Key Rotation (Ротация ключей) + +**Ротация ключей** — регулярная смена ключей шифрования для повышения безопасности. + +```java +@Service +public class KeyManagementService { + + @Value("${encryption.key.rotation.enabled:true}") + private boolean keyRotationEnabled; + + @Value("${encryption.key.rotation.interval:30}") + private int rotationIntervalDays; + + private final Map keyVersions = new ConcurrentHashMap<>(); + private final AtomicInteger currentKeyVersion = new AtomicInteger(1); + + @PostConstruct + public void initializeKeys() throws Exception { + // Создаем начальный ключ + SecretKey initialKey = generateAESKey(); + keyVersions.put("v1", initialKey); + + if (keyRotationEnabled) { + scheduleKeyRotation(); + } + } + + @Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00 + public void rotateKeys() throws Exception { + if (!keyRotationEnabled) { + return; + } + + log.info("Starting key rotation process"); + + // Генерируем новый ключ + int newVersion = currentKeyVersion.incrementAndGet(); + SecretKey newKey = generateAESKey(); + keyVersions.put("v" + newVersion, newKey); + + // Перешифровываем критически важные данные + reencryptCriticalData(newKey); + + // Удаляем старые ключи (оставляем последние 3 версии) + cleanupOldKeys(); + + log.info("Key rotation completed. New version: v{}", newVersion); + } + + public SecretKey getCurrentKey() { + return keyVersions.get("v" + currentKeyVersion.get()); + } + + public SecretKey getKeyByVersion(int version) { + return keyVersions.get("v" + version); + } + + private SecretKey generateAESKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(256); + return keyGenerator.generateKey(); + } + + private void reencryptCriticalData(SecretKey newKey) { + // Логика перешифровки критически важных данных + // Это может включать перешифровку данных в БД + } + + private void cleanupOldKeys() { + int currentVersion = currentKeyVersion.get(); + int keepVersions = 3; + + for (int i = 1; i <= currentVersion - keepVersions; i++) { + keyVersions.remove("v" + i); + } + } +} +``` + +### 2. Hardware Security Module (HSM) + +**HSM** — аппаратный модуль безопасности для безопасного хранения и управления ключами. + +```java +@Service +public class HSMService { + private final HSMClient hsmClient; + + public String encryptWithHSM(String data) { + return hsmClient.encrypt(data); + } + + public String decryptWithHSM(String encryptedData) { + return hsmClient.decrypt(encryptedData); + } + + public String signWithHSM(String data) { + return hsmClient.sign(data); + } + + public boolean verifyWithHSM(String data, String signature) { + return hsmClient.verify(data, signature); + } + + public String generateKeyInHSM(String keyId) { + return hsmClient.generateKey(keyId); + } + + public void deleteKeyFromHSM(String keyId) { + hsmClient.deleteKey(keyId); + } +} +``` + +### 3. Key Vault Integration + +**Интеграция с хранилищем ключей** для централизованного управления ключами. + +```java +@Service +public class KeyVaultService { + + @Value("${azure.keyvault.url}") + private String keyVaultUrl; + + @Value("${azure.keyvault.client-id}") + private String clientId; + + @Value("${azure.keyvault.client-secret}") + private String clientSecret; + + private KeyVaultClient keyVaultClient; + + @PostConstruct + public void initialize() { + // Инициализация клиента Key Vault + this.keyVaultClient = new KeyVaultClient(keyVaultUrl, clientId, clientSecret); + } + + public String getSecret(String secretName) { + return keyVaultClient.getSecret(secretName); + } + + public void setSecret(String secretName, String secretValue) { + keyVaultClient.setSecret(secretName, secretValue); + } + + public void deleteSecret(String secretName) { + keyVaultClient.deleteSecret(secretName); + } + + public String getKey(String keyName) { + return keyVaultClient.getKey(keyName); + } + + public void createKey(String keyName, String keyType) { + keyVaultClient.createKey(keyName, keyType); + } +} +``` + +## Безопасность ключей + +### 1. Key Storage (Хранение ключей) + +**Принципы безопасного хранения ключей:** +- Никогда не храните ключи в коде +- Используйте переменные окружения +- Шифруйте ключи при хранении +- Разделяйте ключи по окружениям + +```java +@Configuration +public class KeyStorageConfig { + + @Bean + public SecretKey encryptionKey() { + String keyString = System.getenv("ENCRYPTION_KEY"); + if (keyString == null) { + throw new IllegalStateException("ENCRYPTION_KEY environment variable not set"); + } + + byte[] keyBytes = Base64.getDecoder().decode(keyString); + return new SecretKeySpec(keyBytes, "AES"); + } + + @Bean + public String jwtSecret() { + String secret = System.getenv("JWT_SECRET"); + if (secret == null) { + throw new IllegalStateException("JWT_SECRET environment variable not set"); + } + return secret; + } +} +``` + +### 2. Key Access Control (Контроль доступа к ключам) + +```java +@Service +public class KeyAccessControlService { + + @Autowired + private AuditService auditService; + + public SecretKey getKeyWithAccessControl(String keyId, String userId, String operation) { + // Проверяем права доступа + if (!hasKeyAccess(userId, keyId, operation)) { + auditService.logUnauthorizedKeyAccess(userId, keyId, operation); + throw new AccessDeniedException("Access denied to key: " + keyId); + } + + // Логируем доступ к ключу + auditService.logKeyAccess(userId, keyId, operation); + + // Возвращаем ключ + return keyManagementService.getKey(keyId); + } + + private boolean hasKeyAccess(String userId, String keyId, String operation) { + // Логика проверки прав доступа к ключу + return userService.hasPermission(userId, "key:" + keyId + ":" + operation); + } +} +``` + +### 3. Key Monitoring (Мониторинг ключей) + +```java +@Service +public class KeyMonitoringService { + + @Autowired + private MetricsService metricsService; + + @Autowired + private AlertService alertService; + + public void monitorKeyUsage(String keyId, String operation) { + // Увеличиваем счетчик использования ключа + metricsService.incrementCounter("key.usage", + Tags.of("key_id", keyId, "operation", operation)); + + // Проверяем на подозрительную активность + if (isSuspiciousActivity(keyId, operation)) { + alertService.sendKeySecurityAlert(keyId, operation); + } + } + + private boolean isSuspiciousActivity(String keyId, String operation) { + // Проверка на необычное использование ключа + long usageCount = metricsService.getCounterValue("key.usage", + Tags.of("key_id", keyId, "operation", operation)); + + return usageCount > 1000; // Подозрительно много использований + } +} +``` + +## Заключение + +Шифрование и управление ключами являются критически важными компонентами безопасности: + +1. **Выбор алгоритма** — AES для симметричного, RSA/ECC для асимметричного +2. **Управление ключами** — ротация, безопасное хранение, контроль доступа +3. **Мониторинг** — отслеживание использования и подозрительной активности +4. **Соответствие стандартам** — PCI DSS, GDPR, SOX + +Правильная реализация этих компонентов обеспечивает защиту данных на всех уровнях приложения. diff --git a/modules/module6/sections/04-code-obfuscation-protection.md b/modules/module6/sections/04-code-obfuscation-protection.md new file mode 100644 index 0000000..894d473 --- /dev/null +++ b/modules/module6/sections/04-code-obfuscation-protection.md @@ -0,0 +1,555 @@ +# Обфускация и защита кода + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 6](../README.md) + +--- + +## Принципы защиты исходного кода + +### 1. Обфускация кода (Code Obfuscation) + +**Обфускация** — процесс преобразования исходного кода в трудночитаемую форму для защиты интеллектуальной собственности и усложнения реверс-инжиниринга. + +**Цели обфускации:** +- Защита алгоритмов и бизнес-логики +- Усложнение реверс-инжиниринга +- Защита от кражи интеллектуальной собственности +- Предотвращение модификации кода + +**Методы обфускации:** + +```java +// Исходный код +public class PaymentProcessor { + private static final String API_KEY = "sk_live_123456789"; + private static final String SECRET_KEY = "secret_abc123"; + + public PaymentResult processPayment(PaymentRequest request) { + if (request.getAmount() > 10000) { + return validateHighAmountPayment(request); + } + return processStandardPayment(request); + } + + private PaymentResult validateHighAmountPayment(PaymentRequest request) { + // Сложная логика валидации + boolean isValid = performAdvancedValidation(request); + if (isValid) { + return executePayment(request); + } + return PaymentResult.failed("Validation failed"); + } +} + +// Обфускированный код +public class a { + private static final String b = "sk_live_123456789"; + private static final String c = "secret_abc123"; + + public d e(f g) { + if (g.h() > 10000) { + return i(g); + } + return j(g); + } + + private d i(f g) { + boolean h = k(g); + if (h) { + return l(g); + } + return d.m("Validation failed"); + } +} +``` + +**Rust примеры - Обфускация:** + +```rust +// Исходный код +pub struct PaymentProcessor { + api_key: String, + secret_key: String, +} + +impl PaymentProcessor { + pub fn new(api_key: String, secret_key: String) -> Self { + Self { api_key, secret_key } + } + + pub fn process_payment(&self, request: PaymentRequest) -> PaymentResult { + if request.amount > 10000 { + return self.validate_high_amount_payment(request); + } + self.process_standard_payment(request) + } + + fn validate_high_amount_payment(&self, request: PaymentRequest) -> PaymentResult { + let is_valid = self.perform_advanced_validation(&request); + if is_valid { + self.execute_payment(request) + } else { + PaymentResult::failed("Validation failed") + } + } +} + +// Обфускированный код (с использованием макросов) +macro_rules! obfuscate_string { + ($s:expr) => { + base64::decode($s).unwrap_or_else(|_| $s.as_bytes().to_vec()) + }; +} + +pub struct a { + b: Vec, + c: Vec, +} + +impl a { + pub fn d(e: Vec, f: Vec) -> Self { + Self { b: e, c: f } + } + + pub fn g(&self, h: i) -> j { + if h.k > 10000 { + return self.l(h); + } + self.m(h) + } + + fn l(&self, h: i) -> j { + let n = self.o(&h); + if n { + self.p(h) + } else { + j::q("Validation failed") + } + } +} +``` + +**Python примеры - Обфускация:** + +```python +# Исходный код +class PaymentProcessor: + def __init__(self, api_key, secret_key): + self.api_key = api_key + self.secret_key = secret_key + + def process_payment(self, request): + if request.amount > 10000: + return self.validate_high_amount_payment(request) + return self.process_standard_payment(request) + + def validate_high_amount_payment(self, request): + is_valid = self.perform_advanced_validation(request) + if is_valid: + return self.execute_payment(request) + return PaymentResult.failed("Validation failed") + +# Обфускированный код +import base64 +import zlib + +class a: + def __init__(self, b, c): + self.b = b + self.c = c + + def d(self, e): + if e.f > 10000: + return self.g(e) + return self.h(e) + + def g(self, e): + i = self.j(e) + if i: + return self.k(e) + return l.m("Validation failed") + +# Дополнительная обфускация строк +def obfuscate_string(s): + return base64.b64encode(zlib.compress(s.encode())).decode() + +# Использование +API_KEY = obfuscate_string("sk_live_123456789") +SECRET_KEY = obfuscate_string("secret_abc123") +``` + +**Node.js примеры - Обфускация:** + +```typescript +// Исходный код +class PaymentProcessor { + private apiKey: string; + private secretKey: string; + + constructor(apiKey: string, secretKey: string) { + this.apiKey = apiKey; + this.secretKey = secretKey; + } + + processPayment(request: PaymentRequest): PaymentResult { + if (request.amount > 10000) { + return this.validateHighAmountPayment(request); + } + return this.processStandardPayment(request); + } + + private validateHighAmountPayment(request: PaymentRequest): PaymentResult { + const isValid = this.performAdvancedValidation(request); + if (isValid) { + return this.executePayment(request); + } + return PaymentResult.failed("Validation failed"); + } +} + +// Обфускированный код +class a { + private b: string; + private c: string; + + constructor(d: string, e: string) { + this.b = d; + this.c = e; + } + + f(g: h): i { + if (g.j > 10000) { + return this.k(g); + } + return this.l(g); + } + + private k(g: h): i { + const m = this.n(g); + if (m) { + return this.o(g); + } + return i.p("Validation failed"); + } +} + +// Webpack конфигурация для обфускации +module.exports = { + mode: 'production', + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + mangle: { + properties: { + regex: /^_/ + } + }, + compress: { + drop_console: true, + drop_debugger: true, + pure_funcs: ['console.log'] + } + } + }) + ] + } +}; +``` + +### 2. Минификация (Minification) + +**Минификация** — процесс удаления ненужных символов из кода без изменения его функциональности. + +**Методы минификации:** + +```javascript +// Исходный JavaScript код +function calculateTotal(items) { + let total = 0; + for (let i = 0; i < items.length; i++) { + if (items[i].price > 0) { + total += items[i].price * items[i].quantity; + } + } + return total; +} + +// Минифицированный код +function calculateTotal(items){let total=0;for(let i=0;i0){total+=items[i].price*items[i].quantity}}return total} + +// CSS минификация +/* Исходный CSS */ +.header { + background-color: #ffffff; + padding: 20px; + margin-bottom: 10px; +} + +/* Минифицированный CSS */ +.header{background-color:#fff;padding:20px;margin-bottom:10px} +``` + +### 3. Защита от реверс-инжиниринга + +**Методы защиты:** + +```java +// Анти-отладка +public class AntiDebugProtection { + + public static boolean isDebuggerPresent() { + try { + // Проверка наличия отладчика + System.getProperty("java.vm.info"); + return System.getProperty("java.vm.info").contains("debug"); + } catch (Exception e) { + return true; // Предполагаем наличие отладчика при ошибке + } + } + + public static void checkDebugger() { + if (isDebuggerPresent()) { + System.exit(1); // Завершение при обнаружении отладчика + } + } + + // Проверка целостности кода + public static boolean verifyCodeIntegrity() { + try { + String expectedHash = "a1b2c3d4e5f6"; + String actualHash = calculateClassHash(); + return expectedHash.equals(actualHash); + } catch (Exception e) { + return false; + } + } + + private static String calculateClassHash() { + // Расчет хеша класса для проверки целостности + return "calculated_hash"; + } +} + +// Защита от модификации +@Component +public class CodeProtectionService { + + @PostConstruct + public void initializeProtection() { + // Проверка целостности при запуске + if (!verifyCodeIntegrity()) { + throw new SecurityException("Code integrity check failed"); + } + + // Проверка на отладчик + checkDebugger(); + + // Запуск фоновой проверки + startBackgroundProtection(); + } + + @Scheduled(fixedRate = 30000) // Каждые 30 секунд + public void backgroundProtection() { + if (!verifyCodeIntegrity()) { + System.exit(1); + } + + if (isDebuggerPresent()) { + System.exit(1); + } + } +} +``` + +### 4. Source Maps и их безопасность + +**Проблемы Source Maps:** + +```javascript +// webpack.config.js - НЕБЕЗОПАСНО +module.exports = { + devtool: 'source-map', // Раскрывает весь исходный код + + // БЕЗОПАСНО - только для разработки + devtool: process.env.NODE_ENV === 'development' ? 'source-map' : false, + + // БЕЗОПАСНО - скрытые source maps + devtool: 'hidden-source-map', + + // БЕЗОПАСНО - только для внутреннего использования + devtool: 'nosources-source-map' +}; + +// Безопасная конфигурация +module.exports = { + mode: 'production', + devtool: false, // Отключение source maps в продакшене + + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + sourceMap: false, // Отключение source maps + mangle: true, + compress: { + drop_console: true, + drop_debugger: true + } + } + }) + ] + }, + + // Условное включение source maps только для разработки + ...(process.env.NODE_ENV === 'development' && { + devtool: 'eval-source-map' + }) +}; +``` + +### 5. Защита конфигурационных файлов + +```java +// Защита конфигурации +@Configuration +@EnableEncryption +public class SecureConfiguration { + + @Value("${encrypted.database.password}") + private String encryptedDbPassword; + + @Value("${encrypted.api.key}") + private String encryptedApiKey; + + @Bean + public DataSource dataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb"); + config.setUsername("user"); + config.setPassword(decrypt(encryptedDbPassword)); // Расшифровка пароля + return new HikariDataSource(config); + } + + @Bean + public RestTemplate restTemplate() { + RestTemplate template = new RestTemplate(); + template.getInterceptors().add(new ApiKeyInterceptor(decrypt(encryptedApiKey))); + return template; + } + + private String decrypt(String encryptedValue) { + // Логика расшифровки + return encryptionService.decrypt(encryptedValue); + } +} + +// Шифрование конфигурации +@Service +public class ConfigurationEncryptionService { + + public String encryptConfiguration(String value) { + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec( + getEncryptionKey(), "AES"); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + + byte[] encrypted = cipher.doFinal(value.getBytes()); + return Base64.getEncoder().encodeToString(encrypted); + + } catch (Exception e) { + throw new ConfigurationException("Failed to encrypt configuration", e); + } + } + + private byte[] getEncryptionKey() { + // Получение ключа шифрования из безопасного хранилища + return keyManagementService.getEncryptionKey(); + } +} +``` + +### 6. Защита от анализа трафика + +```java +// Обфускация сетевого трафика +@Service +public class NetworkObfuscationService { + + public String obfuscateRequest(String originalRequest) { + // Обфускация параметров запроса + String obfuscated = base64Encode(originalRequest); + obfuscated = addRandomPadding(obfuscated); + obfuscated = xorWithKey(obfuscated, getObfuscationKey()); + + return obfuscated; + } + + public String deobfuscateResponse(String obfuscatedResponse) { + // Деобфускация ответа + String deobfuscated = xorWithKey(obfuscatedResponse, getObfuscationKey()); + deobfuscated = removeRandomPadding(deobfuscated); + deobfuscated = base64Decode(deobfuscated); + + return deobfuscated; + } + + private String addRandomPadding(String data) { + // Добавление случайных данных для усложнения анализа + Random random = new Random(); + int paddingLength = random.nextInt(100) + 50; + StringBuilder padding = new StringBuilder(); + + for (int i = 0; i < paddingLength; i++) { + padding.append((char) (random.nextInt(26) + 'a')); + } + + return padding.toString() + data + padding.toString(); + } + + private String removeRandomPadding(String data) { + // Удаление случайных данных + // Логика удаления padding + return data.substring(50, data.length() - 50); + } +} +``` + +## Инструменты обфускации + +### Java +- **ProGuard** - обфускация и минификация +- **Allatori** - коммерческая обфускация +- **DashO** - профессиональная обфускация + +### JavaScript/TypeScript +- **Terser** - минификация и обфускация +- **UglifyJS** - минификация JavaScript +- **Webpack** - бандлинг и обфускация + +### Python +- **PyArmor** - обфускация Python кода +- **Nuitka** - компиляция в нативный код +- **cx_Freeze** - создание исполняемых файлов + +### Rust +- **rustc** с флагами оптимизации +- **strip** - удаление отладочной информации +- **upx** - сжатие исполняемых файлов + +## Заключение + +Обфускация и защита кода являются важными мерами безопасности: + +1. **Обфускация** — усложнение понимания кода +2. **Минификация** — уменьшение размера и удаление отладочной информации +3. **Анти-отладка** — защита от анализа во время выполнения +4. **Защита конфигурации** — шифрование чувствительных настроек +5. **Обфускация трафика** — усложнение анализа сетевого взаимодействия + +Правильное применение этих техник значительно повышает безопасность приложения и защищает интеллектуальную собственность. diff --git a/modules/module6/sections/05-security-by-design.md b/modules/module6/sections/05-security-by-design.md new file mode 100644 index 0000000..e43c950 --- /dev/null +++ b/modules/module6/sections/05-security-by-design.md @@ -0,0 +1,745 @@ +# Security by Design + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 6](../README.md) + +--- + +## Принципы Security by Design + +### 1. Secure by Default +- Безопасные настройки по умолчанию +- Минимальные права доступа +- Защита от случайных ошибок + +### 2. Fail Secure +- При отказе система переходит в безопасное состояние +- Блокировка доступа при ошибках +- Логирование всех событий безопасности + +### 3. Security as Code +- Безопасность как часть кода +- Автоматизированные проверки безопасности +- Версионирование политик безопасности + +## Моделирование угроз + +### STRIDE Model +- **S**poofing (Подмена) +- **T**ampering (Изменение) +- **R**epudiation (Отказ) +- **I**nformation Disclosure (Раскрытие информации) +- **D**enial of Service (Отказ в обслуживании) +- **E**levation of Privilege (Повышение привилегий) + +### Пример Threat Model: +```yaml +# threat-model.yml +application: "E-commerce Platform" +components: + - name: "User Authentication" + threats: + - type: "Spoofing" + description: "User impersonation" + mitigations: + - "Strong password policy" + - "Multi-factor authentication" + + - name: "Payment Processing" + threats: + - type: "Tampering" + description: "Payment data modification" + mitigations: + - "Data encryption" + - "Digital signatures" +``` + +## Архитектура безопасности + +### 1. Zero Trust Architecture +**Принцип:** "Никогда не доверяй, всегда проверяй" + +```java +@Component +public class ZeroTrustFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + + // проверяем каждую аутентификацию + if (!isAuthenticated(httpRequest)) { + throw new AuthenticationException("Authentication required"); + } + + // проверяем каждую авторизацию + if (!isAuthorized(httpRequest)) { + throw new AuthorizationException("Access denied"); + } + + // проверяем устройство + if (!isDeviceTrusted(httpRequest)) { + throw new SecurityException("Untrusted device"); + } + + chain.doFilter(request, response); + } +} +``` + +### 2. Microsegmentation +**Принцип:** Разделение сети на мелкие сегменты + +```yaml +# network-policy.yml +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: order-service-policy +spec: + podSelector: + matchLabels: + app: order-service + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: + matchLabels: + app: api-gateway + ports: + - protocol: TCP + port: 8080 + egress: + - to: + - podSelector: + matchLabels: + app: payment-service + ports: + - protocol: TCP + port: 8080 +``` + +## Аутентификация и авторизация + +### Аутентификация + +#### 1. Password-based Authentication +```java +@Service +public class AuthenticationService { + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private UserRepository userRepository; + + public AuthenticationResult authenticate(String username, String password) { + User user = userRepository.findByUsername(username); + + if (user == null) { + return AuthenticationResult.failure("User not found"); + } + + if (!passwordEncoder.matches(password, user.getPasswordHash())) { + return AuthenticationResult.failure("Invalid password"); + } + + if (!user.isActive()) { + return AuthenticationResult.failure("User account is disabled"); + } + + return AuthenticationResult.success(user); + } +} +``` + +#### 2. Multi-Factor Authentication (MFA) +```java +@Service +public class MFAService { + @Autowired + private TOTPGenerator totpGenerator; + + @Autowired + private SMSService smsService; + + public void enableMFA(Long userId, String phoneNumber) { + String secret = totpGenerator.generateSecret(); + userRepository.updateMFASecret(userId, secret); + userRepository.updatePhoneNumber(userId, phoneNumber); + } + + public boolean verifyMFA(Long userId, String code) { + User user = userRepository.findById(userId); + String secret = user.getMFASecret(); + + return totpGenerator.verifyCode(secret, code); + } + + public void sendSMS(Long userId, String code) { + User user = userRepository.findById(userId); + smsService.sendSMS(user.getPhoneNumber(), "Your verification code: " + code); + } +} +``` + +#### 3. OAuth 2.0 / OpenID Connect +```java +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .oauth2Login(oauth2 -> oauth2 + .userInfoEndpoint(userInfo -> userInfo + .userService(customOAuth2UserService) + ) + ) + .authorizeHttpRequests(authz -> authz + .requestMatchers("/public/**").permitAll() + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + + return http.build(); + } +} +``` + +### Авторизация + +#### 1. Role-Based Access Control (RBAC) +```java +@Entity +public class User { + @Id + private Long id; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable( + name = "user_roles", + joinColumns = @JoinColumn(name = "user_id"), + inverseJoinColumns = @JoinColumn(name = "role_id") + ) + private Set roles = new HashSet<>(); +} + +@Entity +public class Role { + @Id + private Long id; + + private String name; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable( + name = "role_permissions", + joinColumns = @JoinColumn(name = "role_id"), + inverseJoinColumns = @JoinColumn(name = "permission_id") + ) + private Set permissions = new HashSet<>(); +} +``` + +#### 2. Attribute-Based Access Control (ABAC) +```java +@Component +public class ABACPolicyEngine { + public boolean evaluate(Policy policy, AccessRequest request) { + // проверяем субъект + if (!policy.getSubject().matches(request.getSubject())) { + return false; + } + + // проверяем ресурс + if (!policy.getResource().matches(request.getResource())) { + return false; + } + + // проверяем действие + if (!policy.getAction().matches(request.getAction())) { + return false; + } + + // проверяем контекст + if (!policy.getContext().matches(request.getContext())) { + return false; + } + + return true; + } +} + +// Пример политики +Policy policy = Policy.builder() + .subject("role:manager") + .resource("department:finance") + .action("read") + .context("time:business_hours") + .build(); +``` + +#### 3. JWT Tokens +```java +@Service +public class JWTService { + private final String secretKey = "mySecretKey"; + private final int jwtExpiration = 86400000; // 24 hours + + public String generateToken(UserDetails userDetails) { + Map claims = new HashMap<>(); + claims.put("roles", userDetails.getAuthorities()); + claims.put("userId", userDetails.getUsername()); + + return createToken(claims, userDetails.getUsername()); + } + + private String createToken(Map claims, String subject) { + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + jwtExpiration)) + .signWith(SignatureAlgorithm.HS512, secretKey) + .compact(); + } + + public Boolean validateToken(String token, UserDetails userDetails) { + final String username = extractUsername(token); + return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); + } +} +``` + +## Безопасность API + +### Лучшие практики безопасности API + +#### 1. Input Validation +```java +@RestController +@Validated +public class UserController { + @PostMapping("/users") + public ResponseEntity createUser(@Valid @RequestBody CreateUserRequest request) { + // валидация выполняется автоматически через @Valid + User user = userService.createUser(request); + return ResponseEntity.ok(user); + } +} + +public class CreateUserRequest { + @NotBlank(message = "Username is required") + @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters") + private String username; + + @NotBlank(message = "Email is required") + @Email(message = "Email must be valid") + private String email; + + @NotBlank(message = "Password is required") + @Size(min = 8, message = "Password must be at least 8 characters") + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$", + message = "Password must contain at least one uppercase letter, one lowercase letter, one number and one special character") + private String password; +} +``` + +#### 2. Rate Limiting +```java +@Component +public class RateLimitingFilter implements Filter { + private final Map rateLimiters = new ConcurrentHashMap<>(); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + String clientId = getClientId(httpRequest); + + RateLimiter rateLimiter = rateLimiters.computeIfAbsent(clientId, + k -> RateLimiter.create(10.0)); // 10 запросов в секунду + + if (!rateLimiter.tryAcquire()) { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setStatus(429); // Too Many Requests + httpResponse.getWriter().write("Rate limit exceeded"); + return; + } + + chain.doFilter(request, response); + } +} +``` + +#### 3. API Versioning +```java +@RestController +@RequestMapping("/api/v1/users") +public class UserControllerV1 { + @GetMapping("/{id}") + public UserV1 getUser(@PathVariable Long id) { + return userService.getUserV1(id); + } +} + +@RestController +@RequestMapping("/api/v2/users") +public class UserControllerV2 { + @GetMapping("/{id}") + public UserV2 getUser(@PathVariable Long id) { + return userService.getUserV2(id); + } +} +``` + +### Безопасность API Gateway + +```yaml +# api-gateway-security.yml +apiVersion: networking.istio.io/v1alpha3 +kind: AuthorizationPolicy +metadata: + name: api-gateway-policy +spec: + selector: + matchLabels: + app: api-gateway + rules: + - from: + - source: + principals: ["cluster.local/ns/default/sa/api-gateway"] + to: + - operation: + methods: ["GET", "POST"] + paths: ["/api/v1/*"] + - from: + - source: + principals: ["cluster.local/ns/default/sa/admin-service"] + to: + - operation: + methods: ["*"] + paths: ["/admin/*"] +``` + +## Безопасность данных + +### Классификация данных + +#### 1. Data Sensitivity Levels +- **Public** — общедоступные данные +- **Internal** — внутренние данные +- **Confidential** — конфиденциальные данные +- **Restricted** — ограниченные данные + +```java +@Entity +public class User { + @Id + private Long id; + + @Column(name = "username") + @DataClassification(level = DataClassification.Level.INTERNAL) + private String username; + + @Column(name = "email") + @DataClassification(level = DataClassification.Level.CONFIDENTIAL) + private String email; + + @Column(name = "ssn") + @DataClassification(level = DataClassification.Level.RESTRICTED) + @Encrypted + private String ssn; +} +``` + +#### 2. Data Masking +```java +@Service +public class DataMaskingService { + public User maskUserData(User user, UserRole requesterRole) { + User maskedUser = new User(); + maskedUser.setId(user.getId()); + + switch (requesterRole) { + case ADMIN: + // админы видят все данные + maskedUser.setEmail(user.getEmail()); + maskedUser.setSsn(user.getSsn()); + break; + case MANAGER: + // менеджеры видят частично замаскированные данные + maskedUser.setEmail(maskEmail(user.getEmail())); + maskedUser.setSsn(maskSSN(user.getSsn())); + break; + case USER: + // обычные пользователи видят только базовую информацию + maskedUser.setEmail(maskEmail(user.getEmail())); + maskedUser.setSsn("***-**-****"); + break; + } + + return maskedUser; + } + + private String maskEmail(String email) { + String[] parts = email.split("@"); + String username = parts[0]; + String domain = parts[1]; + + if (username.length() <= 2) { + return "*@" + domain; + } + + return username.substring(0, 2) + "*".repeat(username.length() - 2) + "@" + domain; + } + + private String maskSSN(String ssn) { + return "***-**-" + ssn.substring(ssn.length() - 4); + } +} +``` + +### Шифрование данных + +#### 1. Encryption at Rest +```java +@Entity +public class PaymentCard { + @Id + private Long id; + + @Column(name = "card_number") + @Convert(converter = EncryptedStringConverter.class) + private String cardNumber; + + @Column(name = "cvv") + @Convert(converter = EncryptedStringConverter.class) + private String cvv; +} + +@Converter +public class EncryptedStringConverter implements AttributeConverter { + @Autowired + private EncryptionService encryptionService; + + @Override + public String convertToDatabaseColumn(String attribute) { + return encryptionService.encrypt(attribute); + } + + @Override + public String convertToEntityAttribute(String dbData) { + return encryptionService.decrypt(dbData); + } +} +``` + +#### 2. Encryption in Transit +```java +@Configuration +public class SSLConfig { + @Bean + public RestTemplate restTemplate() throws Exception { + SSLContext sslContext = SSLContextBuilder.create() + .loadTrustMaterial(null, (certificate, authType) -> true) + .build(); + + HttpClient httpClient = HttpClients.custom() + .setSSLContext(sslContext) + .build(); + + HttpComponentsClientHttpRequestFactory factory = + new HttpComponentsClientHttpRequestFactory(httpClient); + + return new RestTemplate(factory); + } +} +``` + +## Соответствие требованиям + +### GDPR (Общий регламент по защите данных) + +#### 1. Data Subject Rights +```java +@Service +public class GDPRService { + public void handleDataSubjectRequest(DataSubjectRequest request) { + switch (request.getType()) { + case ACCESS: + provideDataAccess(request.getSubjectId()); + break; + case RECTIFICATION: + rectifyData(request.getSubjectId(), request.getData()); + break; + case ERASURE: + eraseData(request.getSubjectId()); + break; + case PORTABILITY: + provideDataPortability(request.getSubjectId()); + break; + } + } + + private void eraseData(Long subjectId) { + // удаляем персональные данные + userRepository.deletePersonalData(subjectId); + + // логируем удаление + auditService.logDataErasure(subjectId); + + // уведомляем заинтересованные системы + notificationService.notifyDataErasure(subjectId); + } +} +``` + +#### 2. Consent Management +```java +@Entity +public class Consent { + @Id + private Long id; + + private Long userId; + private String purpose; + private ConsentStatus status; + private LocalDateTime givenAt; + private LocalDateTime withdrawnAt; + + public boolean isValid() { + return status == ConsentStatus.GIVEN && withdrawnAt == null; + } +} + +@Service +public class ConsentService { + public void giveConsent(Long userId, String purpose) { + Consent consent = new Consent(); + consent.setUserId(userId); + consent.setPurpose(purpose); + consent.setStatus(ConsentStatus.GIVEN); + consent.setGivenAt(LocalDateTime.now()); + + consentRepository.save(consent); + } + + public void withdrawConsent(Long userId, String purpose) { + Consent consent = consentRepository.findByUserIdAndPurpose(userId, purpose); + if (consent != null) { + consent.setStatus(ConsentStatus.WITHDRAWN); + consent.setWithdrawnAt(LocalDateTime.now()); + consentRepository.save(consent); + } + } +} +``` + +### PCI DSS (Стандарт безопасности данных индустрии платежных карт) + +#### 1. Card Data Protection +```java +@Service +public class PCIService { + public String tokenizeCard(String cardNumber) { + // генерируем токен вместо хранения номера карты + String token = UUID.randomUUID().toString(); + + // сохраняем токен в безопасном хранилище + secureStorage.storeToken(token, cardNumber); + + return token; + } + + public String detokenizeCard(String token) { + // получаем номер карты по токену + return secureStorage.getCardByToken(token); + } + + public boolean validateCard(String cardNumber) { + // проверяем номер карты по алгоритму Луна + return luhnAlgorithm.validate(cardNumber); + } +} +``` + +#### 2. Secure Payment Processing +```java +@Service +public class PaymentService { + @Transactional + public PaymentResult processPayment(PaymentRequest request) { + // валидируем данные карты + if (!validateCardData(request.getCardData())) { + return PaymentResult.failure("Invalid card data"); + } + + // токенизируем данные карты + String token = tokenizeCard(request.getCardData().getNumber()); + + // обрабатываем платеж + PaymentResult result = paymentProcessor.processPayment(request); + + // логируем транзакцию + auditService.logPaymentTransaction(request, result); + + return result; + } +} +``` + +### SOX (Закон Сарбейнса-Оксли) + +#### 1. Financial Data Controls +```java +@Service +public class FinancialControlService { + public void validateFinancialTransaction(FinancialTransaction transaction) { + // проверяем авторизацию + if (!isAuthorized(transaction.getAmount())) { + throw new AuthorizationException("Transaction amount exceeds authorization limit"); + } + + // проверяем дублирование + if (isDuplicateTransaction(transaction)) { + throw new DuplicateTransactionException("Duplicate transaction detected"); + } + + // проверяем целостность данных + if (!validateDataIntegrity(transaction)) { + throw new DataIntegrityException("Data integrity check failed"); + } + } + + public void auditFinancialData() { + // аудит финансовых данных + List transactions = getTransactionsForAudit(); + + for (FinancialTransaction transaction : transactions) { + validateTransaction(transaction); + logAuditTrail(transaction); + } + } +} +``` + +## Заключение + +Security by Design обеспечивает встроенную безопасность на всех уровнях приложения: + +1. **Принципы безопасности** — Secure by Default, Fail Secure, Security as Code +2. **Моделирование угроз** — STRIDE модель для выявления рисков +3. **Архитектура безопасности** — Zero Trust, Microsegmentation +4. **Аутентификация и авторизация** — MFA, RBAC, ABAC, JWT +5. **Безопасность API** — Валидация, Rate Limiting, Versioning +6. **Безопасность данных** — Классификация, Маскирование, Шифрование +7. **Соответствие требованиям** — GDPR, PCI DSS, SOX + +Интеграция этих принципов в процесс разработки обеспечивает надежную защиту приложения от угроз безопасности. diff --git a/modules/module6/sections/08-practical-tasks.md b/modules/module6/sections/08-practical-tasks.md new file mode 100644 index 0000000..8cad159 --- /dev/null +++ b/modules/module6/sections/08-practical-tasks.md @@ -0,0 +1,24 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 6](../README.md) + +--- + +### Задание 1: Security Implementation +1. Реализуйте аутентификацию с JWT +2. Настройте авторизацию с RBAC +3. Добавьте валидацию входных данных + +### Задание 2: Data Protection +1. Реализуйте шифрование данных +2. Настройте маскирование данных +3. Добавьте аудит доступа к данным + +### Задание 3: Compliance +1. Реализуйте GDPR compliance +2. Настройте PCI DSS для платежей +3. Добавьте SOX controls + +--- \ No newline at end of file diff --git a/modules/module6/theory.md b/modules/module6/theory.md new file mode 100644 index 0000000..9c5ae32 --- /dev/null +++ b/modules/module6/theory.md @@ -0,0 +1,4009 @@ +# Модуль 6: Безопасность и соответствие требованиям + +## Содержание модуля +1. [Основы безопасности](#основы-безопасности) +2. [Уязвимости и межсервисное взаимодействие](#уязвимости-и-межсервисное-взаимодействие) +3. [Полный каталог уязвимостей информационной безопасности](#полный-каталог-уязвимостей-информационной-безопасности) +4. [Обфускация и защита кода](#обфускация-и-защита-кода) +5. [Security by Design](#security-by-design) +6. [Аутентификация и авторизация](#аутентификация-и-авторизация) +7. [Шифрование](#шифрование) +8. [Безопасность API](#безопасность-api) +9. [Безопасность данных](#безопасность-данных) +10. [Соответствие требованиям](#соответствие-требованиям) + +--- + +## Основы безопасности + +### Принципы информационной безопасности + +#### 1. CIA Triad +- **Confidentiality (Конфиденциальность)** — доступ только авторизованным пользователям +- **Integrity (Целостность)** — данные не изменяются несанкционированно +- **Availability (Доступность)** — система доступна когда нужно + +#### 2. Defense in Depth +**Принцип:** Многоуровневая защита системы. + +``` +┌─────────────────────────────────────┐ +│ Application │ +├─────────────────────────────────────┤ +│ Network Security │ +├─────────────────────────────────────┤ +│ Infrastructure │ +├─────────────────────────────────────┤ +│ Physical Security │ +└─────────────────────────────────────┘ +``` + +#### 3. Least Privilege +**Принцип:** Предоставление минимально необходимых прав доступа. + +**Технический пример - Advanced Security Implementation:** + +```java +@Service +@Transactional +public class UserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private AuditService auditService; + + @PreAuthorize("hasRole('ADMIN')") + @Audit(action = "DELETE_USER", resource = "USER") + public void deleteUser(Long userId) { + // Только админы могут удалять пользователей + User user = userRepository.findById(userId) + .orElseThrow(() -> new UserNotFoundException("User not found")); + + // Проверяем, что админ не удаляет сам себя + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth.getName().equals(user.getEmail())) { + throw new SecurityException("Cannot delete your own account"); + } + + // Логируем действие + auditService.logAction("DELETE_USER", userId, auth.getName()); + + userRepository.deleteById(userId); + } + + @PreAuthorize("hasRole('USER') and #userId == authentication.principal.id") + public User getUserProfile(Long userId) { + // Пользователи могут видеть только свой профиль + return userRepository.findById(userId) + .orElseThrow(() -> new UserNotFoundException("User not found")); + } + + @PreAuthorize("hasRole('ADMIN') or hasRole('MANAGER')") + public List getAllUsers() { + // Админы и менеджеры могут видеть всех пользователей + return userRepository.findAll(); + } + + @PreAuthorize("hasRole('ADMIN')") + @Audit(action = "UPDATE_USER_ROLE", resource = "USER") + public void updateUserRole(Long userId, String newRole) { + // Только админы могут изменять роли + User user = userRepository.findById(userId) + .orElseThrow(() -> new UserNotFoundException("User not found")); + + // Проверяем валидность роли + if (!isValidRole(newRole)) { + throw new IllegalArgumentException("Invalid role: " + newRole); + } + + String oldRole = user.getRole(); + user.setRole(newRole); + userRepository.save(user); + + // Логируем изменение роли + auditService.logAction("UPDATE_USER_ROLE", userId, + SecurityContextHolder.getContext().getAuthentication().getName(), + Map.of("oldRole", oldRole, "newRole", newRole)); + } + + private boolean isValidRole(String role) { + return Arrays.asList("USER", "MANAGER", "ADMIN").contains(role); + } +} + +// Security Configuration +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class SecurityConfig { + + @Autowired + private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + + @Autowired + private JwtRequestFilter jwtRequestFilter; + + @Autowired + private UserDetailsService userDetailsService; + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(12); + } + + @Bean + public AuthenticationManager authenticationManager( + AuthenticationConfiguration authConfig) throws Exception { + return authConfig.getAuthenticationManager(); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf().disable() + .authorizeHttpRequests(authz -> authz + .requestMatchers("/api/auth/**").permitAll() + .requestMatchers("/api/public/**").permitAll() + .requestMatchers("/actuator/health").permitAll() + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .requestMatchers("/api/users/**").hasAnyRole("USER", "ADMIN", "MANAGER") + .anyRequest().authenticated() + ) + .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) + .and() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class) + .headers(headers -> headers + .frameOptions().deny() + .contentTypeOptions().and() + .httpStrictTransportSecurity(hstsConfig -> hstsConfig + .maxAgeInSeconds(31536000) + .includeSubdomains(true) + ) + ); + + return http.build(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOriginPatterns(Arrays.asList("https://*.example.com")); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} + +// JWT Token Service +@Service +public class JwtTokenService { + + private static final String SECRET_KEY = "mySecretKey"; + private static final int JWT_TOKEN_VALIDITY = 5 * 60 * 60; // 5 hours + + @Value("${jwt.secret}") + private String jwtSecret; + + public String generateToken(UserDetails userDetails) { + Map claims = new HashMap<>(); + claims.put("roles", userDetails.getAuthorities()); + claims.put("username", userDetails.getUsername()); + + return createToken(claims, userDetails.getUsername()); + } + + private String createToken(Map claims, String subject) { + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) + .signWith(SignatureAlgorithm.HS512, jwtSecret) + .compact(); + } + + public Boolean validateToken(String token, UserDetails userDetails) { + final String username = getUsernameFromToken(token); + return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); + } + + public String getUsernameFromToken(String token) { + return getClaimFromToken(token, Claims::getSubject); + } + + public Date getExpirationDateFromToken(String token) { + return getClaimFromToken(token, Claims::getExpiration); + } + + public T getClaimFromToken(String token, Function claimsResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + + private Claims getAllClaimsFromToken(String token) { + return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody(); + } + + private Boolean isTokenExpired(String token) { + final Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } +} +``` + +**Node.js примеры - безопасность:** + +**JWT аутентификация с Express.js:** +```typescript +import express, { Request, Response, NextFunction } from 'express'; +import jwt from 'jsonwebtoken'; +import bcrypt from 'bcrypt'; +import rateLimit from 'express-rate-limit'; +import helmet from 'helmet'; +import cors from 'cors'; +import { PrismaClient } from '@prisma/client'; + +const app = express(); +const prisma = new PrismaClient(); + +// Middleware для безопасности +app.use(helmet()); +app.use(cors({ + origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'], + credentials: true +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs + message: 'Too many requests from this IP, please try again later.', + standardHeaders: true, + legacyHeaders: false, +}); + +app.use('/api/', limiter); + +// JWT Token Service +class JwtTokenService { + private readonly secretKey: string; + private readonly tokenValidity: number = 5 * 60 * 60; // 5 hours + + constructor() { + this.secretKey = process.env.JWT_SECRET || 'mySecretKey'; + } + + generateToken(userDetails: UserDetails): string { + const claims = { + roles: userDetails.roles, + username: userDetails.username, + userId: userDetails.id + }; + + return jwt.sign( + claims, + this.secretKey, + { + subject: userDetails.username, + expiresIn: this.tokenValidity, + issuer: 'my-app', + audience: 'my-app-users' + } + ); + } + + validateToken(token: string, userDetails: UserDetails): boolean { + try { + const decoded = jwt.verify(token, this.secretKey) as any; + return decoded.username === userDetails.username && !this.isTokenExpired(decoded); + } catch (error) { + return false; + } + } + + getUsernameFromToken(token: string): string | null { + try { + const decoded = jwt.verify(token, this.secretKey) as any; + return decoded.username; + } catch (error) { + return null; + } + } + + private isTokenExpired(decoded: any): boolean { + return Date.now() >= decoded.exp * 1000; + } +} + +// Security Filter +class SecurityFilter { + constructor(private jwtService: JwtTokenService) {} + + async authenticate(req: Request, res: Response, next: NextFunction): Promise { + try { + const token = this.extractToken(req); + + if (!token) { + res.status(401).json({ error: 'Access token required' }); + return; + } + + const username = this.jwtService.getUsernameFromToken(token); + if (!username) { + res.status(401).json({ error: 'Invalid token' }); + return; + } + + req.user = { username }; + next(); + } catch (error) { + res.status(401).json({ error: 'Authentication failed' }); + } + } + + addSecurityHeaders(req: Request, res: Response, next: NextFunction): void { + res.setHeader('X-Content-Type-Options', 'nosniff'); + res.setHeader('X-Frame-Options', 'DENY'); + res.setHeader('X-XSS-Protection', '1; mode=block'); + res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); + next(); + } + + private extractToken(req: Request): string | null { + const authHeader = req.headers.authorization; + if (authHeader && authHeader.startsWith('Bearer ')) { + return authHeader.substring(7); + } + return null; + } +} + +// API endpoints +app.post('/api/auth/login', async (req: Request, res: Response) => { + const { username, password } = req.body; + + // Mock authentication + if (username === 'admin' && password === 'password') { + const jwtService = new JwtTokenService(); + const token = jwtService.generateToken({ + id: 1, + username: 'admin', + roles: ['ADMIN'] + }); + + res.json({ token }); + } else { + res.status(401).json({ error: 'Invalid credentials' }); + } +}); + +app.get('/api/profile', + new SecurityFilter(new JwtTokenService()).authenticate.bind(new SecurityFilter(new JwtTokenService())), + (req: Request, res: Response) => { + res.json({ user: req.user }); + } +); + +app.listen(3000, () => { + console.log('Server running on port 3000'); +}); +``` + +// Rate Limiting +@Component +public class RateLimitingService { + + private final Map limiters = new ConcurrentHashMap<>(); + + public boolean isAllowed(String key, int maxRequests, Duration window) { + RateLimiter limiter = limiters.computeIfAbsent(key, k -> + RateLimiter.create(maxRequests / window.getSeconds())); + + return limiter.tryAcquire(); + } + + public boolean isAllowedForUser(String userId, int maxRequests, Duration window) { + return isAllowed("user:" + userId, maxRequests, window); + } + + public boolean isAllowedForIP(String ip, int maxRequests, Duration window) { + return isAllowed("ip:" + ip, maxRequests, window); + } +} + +// Security Filter +@Component +public class SecurityFilter implements Filter { + + @Autowired + private RateLimitingService rateLimitingService; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + String clientIP = getClientIP(httpRequest); + String userId = getCurrentUserId(); + + // Rate limiting + if (!rateLimitingService.isAllowedForIP(clientIP, 100, Duration.ofMinutes(1))) { + httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); + return; + } + + if (userId != null && !rateLimitingService.isAllowedForUser(userId, 1000, Duration.ofMinutes(1))) { + httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); + return; + } + + // Security headers + httpResponse.setHeader("X-Content-Type-Options", "nosniff"); + httpResponse.setHeader("X-Frame-Options", "DENY"); + httpResponse.setHeader("X-XSS-Protection", "1; mode=block"); + httpResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); + + chain.doFilter(request, response); + } + + private String getClientIP(HttpServletRequest request) { + String xForwardedFor = request.getHeader("X-Forwarded-For"); + if (xForwardedFor != null && !xForwardedFor.isEmpty()) { + return xForwardedFor.split(",")[0].trim(); + } + return request.getRemoteAddr(); + } + + private String getCurrentUserId() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null && auth.isAuthenticated()) { + return auth.getName(); + } + return null; + } +} +``` + +### Угрозы безопасности + +#### 1. OWASP Top 10 (2021) + +##### A01: Broken Access Control +```java +// Плохо: нет проверки прав +@GetMapping("/users/{id}") +public User getUser(@PathVariable Long id) { + return userRepository.findById(id); +} + +// Хорошо: проверка прав +@GetMapping("/users/{id}") +@PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id") +public User getUser(@PathVariable Long id) { + return userRepository.findById(id); +} +``` + +##### A02: Cryptographic Failures +```java +// Плохо: слабое шифрование +@Value("${app.encryption.key:123456}") +private String encryptionKey; + +// Хорошо: сильное шифрование +@Bean +public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(12); +} +``` + +##### A03: Injection +```java +// Плохо: SQL injection +String query = "SELECT * FROM users WHERE name = '" + userName + "'"; + +// Хорошо: параметризованные запросы +@Query("SELECT u FROM User u WHERE u.name = :name") +User findByName(@Param("name") String name); +``` + +##### A04: Insecure Design +```java +// Плохо: небезопасный дизайн +public class User { + private String password; // пароль в открытом виде + + public String getPassword() { + return password; // возвращаем пароль + } +} + +// Хорошо: безопасный дизайн +public class User { + private String passwordHash; // хэш пароля + + public void setPassword(String password) { + this.passwordHash = passwordEncoder.encode(password); + } + + // нет геттера для пароля +} +``` + +##### A05: Security Misconfiguration +```yaml +# Плохо: небезопасная конфигурация +spring: + security: + user: + name: admin + password: admin123 + +# Хорошо: безопасная конфигурация +spring: + security: + user: + name: ${ADMIN_USERNAME:admin} + password: ${ADMIN_PASSWORD:} +``` + +--- + +## Уязвимости и межсервисное взаимодействие + +### Типы уязвимостей в микросервисной архитектуре + +#### 1. Уязвимости межсервисного взаимодействия + +**Проблемы:** +- Отсутствие аутентификации между сервисами +- Передача чувствительных данных в открытом виде +- Отсутствие валидации токенов +- Man-in-the-middle атаки +- Replay атаки + +**Решения:** +- Mutual TLS (mTLS) +- Service-to-Service токены +- API Gateway с аутентификацией +- Token Validation Middleware (TVM) + +#### 2. TVM (Token Validation Middleware) + +**TVM** — промежуточное ПО для валидации токенов между микросервисами. + +**Принципы работы:** +1. Сервис получает токен от клиента +2. TVM валидирует токен +3. TVM извлекает информацию о пользователе/сервисе +4. TVM передает валидные данные в целевой сервис + +**Технический пример - TVM Implementation:** + +```java +@Component +public class TokenValidationMiddleware { + + @Autowired + private JwtTokenService jwtTokenService; + + @Autowired + private ServiceRegistry serviceRegistry; + + public ServiceTokenValidationResult validateServiceToken(String token, String targetService) { + try { + // Валидация токена + Claims claims = jwtTokenService.validateToken(token); + + // Проверка прав доступа к сервису + if (!hasAccessToService(claims, targetService)) { + return ServiceTokenValidationResult.denied("No access to service: " + targetService); + } + + // Проверка срока действия токена + if (isTokenExpired(claims)) { + return ServiceTokenValidationResult.denied("Token expired"); + } + + // Извлечение информации о сервисе + ServiceInfo serviceInfo = extractServiceInfo(claims); + + return ServiceTokenValidationResult.success(serviceInfo); + + } catch (JwtException e) { + return ServiceTokenValidationResult.denied("Invalid token: " + e.getMessage()); + } + } + + private boolean hasAccessToService(Claims claims, String targetService) { + List allowedServices = claims.get("allowed_services", List.class); + return allowedServices != null && allowedServices.contains(targetService); + } + + private boolean isTokenExpired(Claims claims) { + Date expiration = claims.getExpiration(); + return expiration.before(new Date()); + } + + private ServiceInfo extractServiceInfo(Claims claims) { + return ServiceInfo.builder() + .serviceId(claims.getSubject()) + .serviceName(claims.get("service_name", String.class)) + .permissions(claims.get("permissions", List.class)) + .issuedAt(claims.getIssuedAt()) + .expiresAt(claims.getExpiration()) + .build(); + } +} + +// Service Token Validation Result +@Data +@Builder +public class ServiceTokenValidationResult { + private boolean valid; + private String errorMessage; + private ServiceInfo serviceInfo; + + public static ServiceTokenValidationResult success(ServiceInfo serviceInfo) { + return ServiceTokenValidationResult.builder() + .valid(true) + .serviceInfo(serviceInfo) + .build(); + } + + public static ServiceTokenValidationResult denied(String errorMessage) { + return ServiceTokenValidationResult.builder() + .valid(false) + .errorMessage(errorMessage) + .build(); + } +} + +// Service Info +@Data +@Builder +public class ServiceInfo { + private String serviceId; + private String serviceName; + private List permissions; + private Date issuedAt; + private Date expiresAt; +} +``` + +**Rust примеры - TVM с Actix Web:** + +```rust +// Token Validation Middleware для Actix Web +use actix_web::{web, HttpRequest, HttpResponse, Error, middleware::Next}; +use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm}; +use serde::{Deserialize, Serialize}; +use std::collections::HashSet; + +#[derive(Debug, Serialize, Deserialize)] +struct ServiceClaims { + sub: String, // service_id + service_name: String, + allowed_services: Vec, + permissions: Vec, + iat: u64, // issued_at + exp: u64, // expires_at +} + +#[derive(Debug, Clone)] +pub struct ServiceInfo { + pub service_id: String, + pub service_name: String, + pub permissions: Vec, +} + +pub struct TokenValidationMiddleware { + decoding_key: DecodingKey, + validation: Validation, +} + +impl TokenValidationMiddleware { + pub fn new(secret: &str) -> Self { + let mut validation = Validation::new(Algorithm::HS256); + validation.set_issuer(&["microservices-platform"]); + + Self { + decoding_key: DecodingKey::from_secret(secret.as_ref()), + validation, + } + } + + pub fn validate_service_token( + &self, + token: &str, + target_service: &str, + ) -> Result { + // Декодирование токена + let token_data = decode::(token, &self.decoding_key, &self.validation) + .map_err(|e| format!("Token validation failed: {}", e))?; + + let claims = token_data.claims; + + // Проверка срока действия + let now = chrono::Utc::now().timestamp() as u64; + if claims.exp < now { + return Err("Token expired".to_string()); + } + + // Проверка прав доступа к сервису + if !claims.allowed_services.contains(&target_service.to_string()) { + return Err(format!("No access to service: {}", target_service)); + } + + Ok(ServiceInfo { + service_id: claims.sub, + service_name: claims.service_name, + permissions: claims.permissions, + }) + } +} + +// Middleware для Actix Web +pub async fn token_validation_middleware( + req: HttpRequest, + next: Next, +) -> Result { + // Извлечение токена из заголовка + let token = req.headers() + .get("Authorization") + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.strip_prefix("Bearer ")) + .ok_or_else(|| actix_web::error::ErrorUnauthorized("Missing or invalid token"))?; + + // Получение целевого сервиса из пути + let target_service = req.path().split('/').nth(1) + .ok_or_else(|| actix_web::error::ErrorBadRequest("Invalid service path"))?; + + // Валидация токена + let middleware = TokenValidationMiddleware::new("your-secret-key"); + let service_info = middleware.validate_service_token(token, target_service) + .map_err(|e| actix_web::error::ErrorUnauthorized(e))?; + + // Добавление информации о сервисе в запрос + req.extensions_mut().insert(service_info); + + next.run(req).await +} + +// Handler с валидацией токена +async fn protected_endpoint( + req: HttpRequest, + service_info: web::Data, +) -> Result { + Ok(HttpResponse::Ok().json(serde_json::json!({ + "message": "Access granted", + "service": service_info.service_name, + "permissions": service_info.permissions + }))) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .wrap(token_validation_middleware) + .route("/api/protected", web::get().to(protected_endpoint)) + }) + .bind("127.0.0.1:8080")? + .run() + .await +} +``` + +**Python примеры - TVM с FastAPI:** + +```python +# Token Validation Middleware для FastAPI +from fastapi import FastAPI, HTTPException, Depends, Request +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +import jwt +from datetime import datetime, timedelta +from typing import List, Optional +import asyncio + +app = FastAPI(title="TVM Service", version="1.0.0") +security = HTTPBearer() + +class ServiceClaims: + def __init__(self, service_id: str, service_name: str, + allowed_services: List[str], permissions: List[str], + issued_at: datetime, expires_at: datetime): + self.service_id = service_id + self.service_name = service_name + self.allowed_services = allowed_services + self.permissions = permissions + self.issued_at = issued_at + self.expires_at = expires_at + +class TokenValidationMiddleware: + def __init__(self, secret_key: str): + self.secret_key = secret_key + self.algorithm = "HS256" + + def validate_service_token(self, token: str, target_service: str) -> ServiceClaims: + try: + # Декодирование токена + payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm]) + + # Проверка срока действия + exp_timestamp = payload.get('exp') + if exp_timestamp and datetime.utcnow().timestamp() > exp_timestamp: + raise HTTPException(status_code=401, detail="Token expired") + + # Проверка прав доступа к сервису + allowed_services = payload.get('allowed_services', []) + if target_service not in allowed_services: + raise HTTPException( + status_code=403, + detail=f"No access to service: {target_service}" + ) + + # Создание объекта ServiceClaims + return ServiceClaims( + service_id=payload.get('sub'), + service_name=payload.get('service_name'), + allowed_services=allowed_services, + permissions=payload.get('permissions', []), + issued_at=datetime.fromtimestamp(payload.get('iat', 0)), + expires_at=datetime.fromtimestamp(exp_timestamp) + ) + + except jwt.ExpiredSignatureError: + raise HTTPException(status_code=401, detail="Token expired") + except jwt.InvalidTokenError: + raise HTTPException(status_code=401, detail="Invalid token") + +# Dependency для валидации токена +async def validate_token( + request: Request, + credentials: HTTPAuthorizationCredentials = Depends(security) +) -> ServiceClaims: + token = credentials.credentials + + # Получение целевого сервиса из пути + path_parts = request.url.path.split('/') + target_service = path_parts[1] if len(path_parts) > 1 else "default" + + # Валидация токена + middleware = TokenValidationMiddleware("your-secret-key") + return middleware.validate_service_token(token, target_service) + +# API endpoints +@app.get("/api/protected") +async def protected_endpoint(service_info: ServiceClaims = Depends(validate_token)): + return { + "message": "Access granted", + "service": service_info.service_name, + "permissions": service_info.permissions, + "service_id": service_info.service_id + } + +@app.get("/api/services/{service_name}/data") +async def get_service_data( + service_name: str, + service_info: ServiceClaims = Depends(validate_token) +): + # Проверка дополнительных прав + if "read_data" not in service_info.permissions: + raise HTTPException(status_code=403, detail="Insufficient permissions") + + return { + "service": service_name, + "data": f"Data for {service_name}", + "accessed_by": service_info.service_name + } + +# Utility для создания токенов (для тестирования) +def create_service_token(service_id: str, service_name: str, + allowed_services: List[str], permissions: List[str]) -> str: + payload = { + "sub": service_id, + "service_name": service_name, + "allowed_services": allowed_services, + "permissions": permissions, + "iat": datetime.utcnow(), + "exp": datetime.utcnow() + timedelta(hours=1) + } + + return jwt.encode(payload, "your-secret-key", algorithm="HS256") + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) +``` + +**Node.js примеры - TVM с Express.js:** + +```typescript +// Token Validation Middleware для Express.js +import express, { Request, Response, NextFunction } from 'express'; +import jwt from 'jsonwebtoken'; +import { z } from 'zod'; + +const app = express(); + +// Interfaces +interface ServiceClaims { + sub: string; // service_id + service_name: string; + allowed_services: string[]; + permissions: string[]; + iat: number; // issued_at + exp: number; // expires_at +} + +interface ServiceInfo { + serviceId: string; + serviceName: string; + permissions: string[]; +} + +// Token Validation Middleware +class TokenValidationMiddleware { + private secretKey: string; + private algorithm: jwt.Algorithm = 'HS256'; + + constructor(secretKey: string) { + this.secretKey = secretKey; + } + + validateServiceToken(token: string, targetService: string): ServiceInfo { + try { + // Декодирование токена + const decoded = jwt.verify(token, this.secretKey, { + algorithms: [this.algorithm] + }) as ServiceClaims; + + // Проверка срока действия + const now = Math.floor(Date.now() / 1000); + if (decoded.exp < now) { + throw new Error('Token expired'); + } + + // Проверка прав доступа к сервису + if (!decoded.allowed_services.includes(targetService)) { + throw new Error(`No access to service: ${targetService}`); + } + + return { + serviceId: decoded.sub, + serviceName: decoded.service_name, + permissions: decoded.permissions + }; + + } catch (error) { + if (error instanceof jwt.JsonWebTokenError) { + throw new Error('Invalid token'); + } + throw error; + } + } +} + +// Middleware для Express.js +const tokenValidationMiddleware = (req: Request, res: Response, next: NextFunction) => { + try { + // Извлечение токена из заголовка + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'Missing or invalid token' }); + } + + const token = authHeader.substring(7); + + // Получение целевого сервиса из пути + const pathParts = req.path.split('/'); + const targetService = pathParts[2] || 'default'; // /api/services/{service} + + // Валидация токена + const middleware = new TokenValidationMiddleware('your-secret-key'); + const serviceInfo = middleware.validateServiceToken(token, targetService); + + // Добавление информации о сервисе в запрос + (req as any).serviceInfo = serviceInfo; + + next(); + } catch (error) { + res.status(401).json({ error: error.message }); + } +}; + +// API endpoints +app.get('/api/protected', tokenValidationMiddleware, (req: Request, res: Response) => { + const serviceInfo = (req as any).serviceInfo; + + res.json({ + message: 'Access granted', + service: serviceInfo.serviceName, + permissions: serviceInfo.permissions, + serviceId: serviceInfo.serviceId + }); +}); + +app.get('/api/services/:serviceName/data', tokenValidationMiddleware, (req: Request, res: Response) => { + const serviceInfo = (req as any).serviceInfo; + const { serviceName } = req.params; + + // Проверка дополнительных прав + if (!serviceInfo.permissions.includes('read_data')) { + return res.status(403).json({ error: 'Insufficient permissions' }); + } + + res.json({ + service: serviceName, + data: `Data for ${serviceName}`, + accessedBy: serviceInfo.serviceName + }); +}); + +// Utility для создания токенов (для тестирования) +function createServiceToken( + serviceId: string, + serviceName: string, + allowedServices: string[], + permissions: string[] +): string { + const payload = { + sub: serviceId, + service_name: serviceName, + allowed_services: allowedServices, + permissions: permissions, + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 hour + }; + + return jwt.sign(payload, 'your-secret-key', { algorithm: 'HS256' }); +} + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +app.listen(3000, () => { + console.log('TVM Service running on port 3000'); +}); +``` + +#### 3. Безопасность межсервисного взаимодействия + +**Принципы:** +- **Zero Trust** — не доверяй, проверяй +- **Least Privilege** — минимальные права доступа +- **Defense in Depth** — многоуровневая защита +- **Audit Everything** — логирование всех операций + +**Паттерны безопасности:** +- Service Mesh с mTLS +- API Gateway с аутентификацией +- Token-based аутентификация +- Rate limiting и throttling +- Circuit breaker для защиты от DoS + +--- + +## Полный каталог уязвимостей информационной безопасности + +### OWASP Top 10 (2021) - Детальный анализ + +#### A01: Broken Access Control (Нарушение контроля доступа) + +**Описание:** Неправильная реализация ограничений доступа, позволяющая пользователям действовать вне своих предполагаемых привилегий. + +**Примеры уязвимостей:** +- Обход авторизации путем изменения URL +- Принудительный просмотр, изменение или удаление данных других пользователей +- Повышение привилегий +- Метаданные манипуляции (JWT, cookies) +- CORS неправильная конфигурация + +**Методы устранения:** + +```java +// Правильная реализация контроля доступа +@RestController +@RequestMapping("/api/users") +public class UserController { + + @GetMapping("/{userId}") + @PreAuthorize("hasRole('USER') and #userId == authentication.principal.id") + public ResponseEntity getUser(@PathVariable Long userId) { + // Проверка прав доступа встроена в аннотацию + return ResponseEntity.ok(userService.findById(userId)); + } + + @GetMapping("/admin") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity> getAllUsers() { + return ResponseEntity.ok(userService.findAll()); + } +} + +// Дополнительная проверка на уровне сервиса +@Service +public class UserService { + + public User findById(Long userId) { + User currentUser = getCurrentUser(); + + // Проверка: пользователь может видеть только свои данные + if (!currentUser.getId().equals(userId) && !currentUser.hasRole("ADMIN")) { + throw new AccessDeniedException("Access denied"); + } + + return userRepository.findById(userId) + .orElseThrow(() -> new UserNotFoundException("User not found")); + } +} +``` + +**Rust примеры - Контроль доступа:** + +```rust +// Контроль доступа в Rust +use actix_web::{web, HttpRequest, HttpResponse, Error}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +struct User { + id: u64, + name: String, + role: String, +} + +#[derive(Debug, Serialize, Deserialize)] +struct AccessToken { + user_id: u64, + role: String, + permissions: Vec, +} + +// Middleware для проверки доступа +pub async fn access_control_middleware( + req: HttpRequest, + next: actix_web::middleware::Next, +) -> Result { + // Извлечение токена + let token = extract_token(&req)?; + + // Валидация токена + let access_token = validate_token(token)?; + + // Проверка прав доступа + if !has_permission(&access_token, &req.path()) { + return Ok(HttpResponse::Forbidden().json(serde_json::json!({ + "error": "Access denied" + }))); + } + + // Добавление информации о пользователе в запрос + req.extensions_mut().insert(access_token); + + next.run(req).await +} + +fn extract_token(req: &HttpRequest) -> Result { + req.headers() + .get("Authorization") + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.strip_prefix("Bearer ")) + .ok_or_else(|| actix_web::error::ErrorUnauthorized("Missing token")) + .map(|s| s.to_string()) +} + +fn validate_token(token: &str) -> Result { + // Здесь должна быть реальная валидация JWT + // Для примера возвращаем mock данные + Ok(AccessToken { + user_id: 1, + role: "USER".to_string(), + permissions: vec!["read:own_data".to_string()], + }) +} + +fn has_permission(token: &AccessToken, path: &str) -> bool { + // Проверка прав доступа на основе пути и роли + match path { + path if path.startsWith("/api/users/") => { + // Пользователь может видеть только свои данные + if token.permissions.contains(&"read:own_data".to_string()) { + return true; + } + } + path if path.startsWith("/api/admin/") => { + // Только админы могут получить доступ к админ панели + return token.role == "ADMIN"; + } + _ => return false, + } + false +} +``` + +#### A02: Cryptographic Failures (Криптографические сбои) + +**Описание:** Неправильное использование криптографии или слабые криптографические алгоритмы. + +**Примеры уязвимостей:** +- Передача данных без шифрования +- Использование слабых алгоритмов шифрования +- Неправильное управление ключами +- Использование устаревших алгоритмов (MD5, SHA-1) +- Хранение паролей в открытом виде + +**Методы устранения:** + +```java +// Правильное шифрование данных +@Service +public class EncryptionService { + + private static final String ALGORITHM = "AES/GCM/NoPadding"; + private static final int KEY_LENGTH = 256; + private static final int IV_LENGTH = 12; + private static final int TAG_LENGTH = 16; + + @Value("${encryption.key}") + private String encryptionKey; + + public String encrypt(String plaintext) { + try { + // Генерация случайного IV + byte[] iv = new byte[IV_LENGTH]; + SecureRandom.getInstanceStrong().nextBytes(iv); + + // Создание ключа + SecretKeySpec keySpec = new SecretKeySpec( + encryptionKey.getBytes(StandardCharsets.UTF_8), "AES"); + + // Инициализация шифра + Cipher cipher = Cipher.getInstance(ALGORITHM); + GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec); + + // Шифрование + byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + + // Объединение IV и зашифрованных данных + byte[] encryptedData = new byte[IV_LENGTH + ciphertext.length]; + System.arraycopy(iv, 0, encryptedData, 0, IV_LENGTH); + System.arraycopy(ciphertext, 0, encryptedData, IV_LENGTH, ciphertext.length); + + return Base64.getEncoder().encodeToString(encryptedData); + + } catch (Exception e) { + throw new EncryptionException("Failed to encrypt data", e); + } + } + + public String decrypt(String encryptedData) { + try { + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData); + + // Извлечение IV + byte[] iv = new byte[IV_LENGTH]; + System.arraycopy(encryptedBytes, 0, iv, 0, IV_LENGTH); + + // Извлечение зашифрованных данных + byte[] ciphertext = new byte[encryptedBytes.length - IV_LENGTH]; + System.arraycopy(encryptedBytes, IV_LENGTH, ciphertext, 0, ciphertext.length); + + // Создание ключа + SecretKeySpec keySpec = new SecretKeySpec( + encryptionKey.getBytes(StandardCharsets.UTF_8), "AES"); + + // Инициализация шифра для расшифровки + Cipher cipher = Cipher.getInstance(ALGORITHM); + GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv); + cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec); + + // Расшифровка + byte[] plaintext = cipher.doFinal(ciphertext); + return new String(plaintext, StandardCharsets.UTF_8); + + } catch (Exception e) { + throw new EncryptionException("Failed to decrypt data", e); + } + } +} + +// Правильное хеширование паролей +@Service +public class PasswordService { + + private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12); + + public String hashPassword(String plainPassword) { + return passwordEncoder.encode(plainPassword); + } + + public boolean verifyPassword(String plainPassword, String hashedPassword) { + return passwordEncoder.matches(plainPassword, hashedPassword); + } + + // Проверка силы пароля + public boolean isPasswordStrong(String password) { + if (password.length() < 8) return false; + + boolean hasUpperCase = password.chars().anyMatch(Character::isUpperCase); + boolean hasLowerCase = password.chars().anyMatch(Character::isLowerCase); + boolean hasDigit = password.chars().anyMatch(Character::isDigit); + boolean hasSpecialChar = password.chars().anyMatch(ch -> "!@#$%^&*()_+-=[]{}|;:,.<>?".contains(String.valueOf(ch))); + + return hasUpperCase && hasLowerCase && hasDigit && hasSpecialChar; + } +} +``` + +#### A03: Injection (Инъекции) + +**Описание:** Внедрение вредоносного кода через пользовательский ввод. + +**Типы инъекций:** +- SQL Injection +- NoSQL Injection +- Command Injection +- LDAP Injection +- XPath Injection + +**Методы устранения:** + +```java +// Защита от SQL Injection +@Repository +public class UserRepository { + + @Autowired + private JdbcTemplate jdbcTemplate; + + // НЕПРАВИЛЬНО - уязвимо к SQL Injection + public List findByUsernameUnsafe(String username) { + String sql = "SELECT * FROM users WHERE username = '" + username + "'"; + return jdbcTemplate.query(sql, new UserRowMapper()); + } + + // ПРАВИЛЬНО - использование параметризованных запросов + public List findByUsernameSafe(String username) { + String sql = "SELECT * FROM users WHERE username = ?"; + return jdbcTemplate.query(sql, new Object[]{username}, new UserRowMapper()); + } + + // Использование JPA с именованными параметрами + @Query("SELECT u FROM User u WHERE u.username = :username") + List findByUsername(@Param("username") String username); +} + +// Защита от NoSQL Injection +@Service +public class UserService { + + @Autowired + private MongoTemplate mongoTemplate; + + // НЕПРАВИЛЬНО - уязвимо к NoSQL Injection + public List findByUsernameUnsafe(String username) { + Query query = new Query(); + query.addCriteria(Criteria.where("username").is(username)); + return mongoTemplate.find(query, User.class); + } + + // ПРАВИЛЬНО - валидация и санитизация ввода + public List findByUsernameSafe(String username) { + // Валидация ввода + if (!isValidUsername(username)) { + throw new IllegalArgumentException("Invalid username format"); + } + + // Санитизация ввода + String sanitizedUsername = sanitizeInput(username); + + Query query = new Query(); + query.addCriteria(Criteria.where("username").is(sanitizedUsername)); + return mongoTemplate.find(query, User.class); + } + + private boolean isValidUsername(String username) { + // Проверка на допустимые символы + return username.matches("^[a-zA-Z0-9_]{3,20}$"); + } + + private String sanitizeInput(String input) { + // Удаление потенциально опасных символов + return input.replaceAll("[^a-zA-Z0-9_]", ""); + } +} + +// Защита от Command Injection +@Service +public class SystemService { + + // НЕПРАВИЛЬНО - уязвимо к Command Injection + public String executeCommandUnsafe(String command) { + try { + Process process = Runtime.getRuntime().exec(command); + // обработка результата... + } catch (IOException e) { + throw new RuntimeException("Command execution failed", e); + } + } + + // ПРАВИЛЬНО - использование ProcessBuilder с валидацией + public String executeCommandSafe(String command, String... args) { + // Валидация команды + if (!isAllowedCommand(command)) { + throw new IllegalArgumentException("Command not allowed"); + } + + // Валидация аргументов + for (String arg : args) { + if (!isValidArgument(arg)) { + throw new IllegalArgumentException("Invalid argument: " + arg); + } + } + + try { + ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.command().addAll(Arrays.asList(args)); + + Process process = processBuilder.start(); + // обработка результата... + + } catch (IOException e) { + throw new RuntimeException("Command execution failed", e); + } + } + + private boolean isAllowedCommand(String command) { + Set allowedCommands = Set.of("ls", "ps", "df", "whoami"); + return allowedCommands.contains(command); + } + + private boolean isValidArgument(String arg) { + // Проверка на допустимые символы + return arg.matches("^[a-zA-Z0-9._/-]+$"); + } +} +``` + +#### A04: Insecure Design (Небезопасный дизайн) + +**Описание:** Недостатки в архитектуре и дизайне приложения. + +**Примеры проблем:** +- Отсутствие threat modeling +- Неправильная архитектура безопасности +- Отсутствие принципа "fail secure" +- Недостаточная изоляция компонентов + +**Методы устранения:** + +```java +// Безопасный дизайн архитектуры +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + // Принцип "fail secure" - по умолчанию все заблокировано + .authorizeHttpRequests(authz -> authz + .requestMatchers("/api/public/**").permitAll() + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ) + // Защита от CSRF + .csrf(csrf -> csrf + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) + ) + // Защита от clickjacking + .headers(headers -> headers + .frameOptions().deny() + .contentTypeOptions().and() + .httpStrictTransportSecurity(hstsConfig -> hstsConfig + .maxAgeInSeconds(31536000) + .includeSubdomains(true) + ) + ) + // Сессии + .sessionManagement(session -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .maximumSessions(1) + .maxSessionsPreventsLogin(true) + ) + // Обработка ошибок + .exceptionHandling(exceptions -> exceptions + .authenticationEntryPoint(customAuthenticationEntryPoint()) + .accessDeniedHandler(customAccessDeniedHandler()) + ); + + return http.build(); + } + + @Bean + public AuthenticationEntryPoint customAuthenticationEntryPoint() { + return (request, response, authException) -> { + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + response.setContentType("application/json"); + response.getWriter().write("{\"error\":\"Unauthorized\"}"); + }; + } + + @Bean + public AccessDeniedHandler customAccessDeniedHandler() { + return (request, response, accessDeniedException) -> { + response.setStatus(HttpStatus.FORBIDDEN.value()); + response.setContentType("application/json"); + response.getWriter().write("{\"error\":\"Access denied\"}"); + }; + } +} + +// Threat Modeling в коде +@Component +public class ThreatModelingService { + + // Анализ угроз для каждого endpoint + public ThreatAnalysis analyzeEndpoint(String endpoint, String method) { + ThreatAnalysis analysis = new ThreatAnalysis(); + + // STRIDE анализ + analysis.addThreat(ThreatType.SPOOFING, "User impersonation"); + analysis.addThreat(ThreatType.TAMPERING, "Data modification"); + analysis.addThreat(ThreatType.REPUDIATION, "Action denial"); + analysis.addThreat(ThreatType.INFORMATION_DISCLOSURE, "Data exposure"); + analysis.addThreat(ThreatType.DENIAL_OF_SERVICE, "Service unavailability"); + analysis.addThreat(ThreatType.ELEVATION_OF_PRIVILEGE, "Unauthorized access"); + + return analysis; + } +} +``` + +#### A05: Security Misconfiguration (Неправильная конфигурация безопасности) + +**Описание:** Неправильная настройка компонентов безопасности. + +**Примеры проблем:** +- Отсутствие обновлений безопасности +- Небезопасные настройки по умолчанию +- Открытые ненужные порты +- Неправильная конфигурация CORS +- Отсутствие заголовков безопасности + +**Методы устранения:** + +```java +// Безопасная конфигурация +@Configuration +public class SecurityConfiguration { + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + + // Ограничение источников + configuration.setAllowedOriginPatterns(List.of("https://*.example.com")); + configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); + configuration.setAllowedHeaders(List.of("*")); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/api/**", configuration); + + return source; + } + + @Bean + public FilterRegistrationBean securityHeadersFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new SecurityHeadersFilter()); + registrationBean.addUrlPatterns("/*"); + registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); + return registrationBean; + } +} + +// Фильтр для добавления заголовков безопасности +public class SecurityHeadersFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + HttpServletResponse httpResponse = (HttpServletResponse) response; + + // Заголовки безопасности + httpResponse.setHeader("X-Content-Type-Options", "nosniff"); + httpResponse.setHeader("X-Frame-Options", "DENY"); + httpResponse.setHeader("X-XSS-Protection", "1; mode=block"); + httpResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); + httpResponse.setHeader("Referrer-Policy", "strict-origin-when-cross-origin"); + httpResponse.setHeader("Content-Security-Policy", + "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"); + + chain.doFilter(request, response); + } +} + +// Проверка конфигурации безопасности +@Component +public class SecurityConfigurationValidator { + + @EventListener + public void onApplicationReady(ApplicationReadyEvent event) { + validateSecurityConfiguration(); + } + + private void validateSecurityConfiguration() { + // Проверка переменных окружения + validateEnvironmentVariables(); + + // Проверка конфигурации базы данных + validateDatabaseConfiguration(); + + // Проверка SSL/TLS + validateSSLConfiguration(); + } + + private void validateEnvironmentVariables() { + String[] requiredVars = { + "JWT_SECRET", "ENCRYPTION_KEY", "DB_PASSWORD", "ADMIN_PASSWORD" + }; + + for (String var : requiredVars) { + if (System.getenv(var) == null) { + throw new SecurityConfigurationException("Required environment variable not set: " + var); + } + } + } + + private void validateDatabaseConfiguration() { + // Проверка подключения к БД с SSL + // Проверка прав доступа к БД + // Проверка настроек пула соединений + } + + private void validateSSLConfiguration() { + // Проверка SSL сертификатов + // Проверка TLS версии + // Проверка cipher suites + } +} +``` + +#### A06: Vulnerable and Outdated Components (Уязвимые и устаревшие компоненты) + +**Описание:** Использование компонентов с известными уязвимостями. + +**Методы устранения:** + +```java +// Автоматическая проверка уязвимостей +@Component +public class VulnerabilityScanner { + + @Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00 + public void scanForVulnerabilities() { + scanDependencies(); + scanSystemComponents(); + generateReport(); + } + + private void scanDependencies() { + // Сканирование Maven зависимостей + List dependencies = getMavenDependencies(); + + for (Dependency dependency : dependencies) { + List vulnerabilities = checkVulnerabilityDatabase(dependency); + + if (!vulnerabilities.isEmpty()) { + logVulnerabilities(dependency, vulnerabilities); + notifySecurityTeam(dependency, vulnerabilities); + } + } + } + + private void scanSystemComponents() { + // Проверка версий Java, Spring Boot, и других компонентов + String javaVersion = System.getProperty("java.version"); + String springVersion = SpringBootVersion.getVersion(); + + checkComponentVersions(javaVersion, springVersion); + } + + private void generateReport() { + // Генерация отчета об уязвимостях + VulnerabilityReport report = new VulnerabilityReport(); + report.setScanDate(LocalDateTime.now()); + report.setVulnerabilities(foundVulnerabilities); + + saveReport(report); + } +} + +// Конфигурация для автоматических обновлений +@Configuration +public class DependencyManagementConfig { + + @Bean + public DependencyUpdatesChecker dependencyUpdatesChecker() { + return new DependencyUpdatesChecker(); + } +} + +@Component +public class DependencyUpdatesChecker { + + @Scheduled(cron = "0 0 1 * * ?") // Каждый день в 1:00 + public void checkForUpdates() { + List updates = findAvailableUpdates(); + + for (DependencyUpdate update : updates) { + if (update.isSecurityUpdate()) { + notifySecurityTeam(update); + } + } + } +} +``` + +#### A07: Identification and Authentication Failures (Сбои идентификации и аутентификации) + +**Описание:** Неправильная реализация аутентификации и идентификации. + +**Методы устранения:** + +```java +// Безопасная аутентификация +@Service +public class AuthenticationService { + + @Autowired + private PasswordService passwordService; + + @Autowired + private RateLimitingService rateLimitingService; + + @Autowired + private AuditService auditService; + + public AuthenticationResult authenticate(String username, String password, String ipAddress) { + // Проверка rate limiting + if (!rateLimitingService.isAllowed(ipAddress)) { + auditService.logFailedLogin(username, ipAddress, "Rate limit exceeded"); + throw new AuthenticationException("Too many login attempts"); + } + + // Поиск пользователя + User user = userService.findByUsername(username); + if (user == null) { + auditService.logFailedLogin(username, ipAddress, "User not found"); + throw new AuthenticationException("Invalid credentials"); + } + + // Проверка блокировки аккаунта + if (user.isLocked()) { + auditService.logFailedLogin(username, ipAddress, "Account locked"); + throw new AuthenticationException("Account is locked"); + } + + // Проверка пароля + if (!passwordService.verifyPassword(password, user.getPasswordHash())) { + handleFailedLogin(user, ipAddress); + throw new AuthenticationException("Invalid credentials"); + } + + // Проверка двухфакторной аутентификации + if (user.isTwoFactorEnabled()) { + return AuthenticationResult.twoFactorRequired(user.getId()); + } + + // Успешная аутентификация + auditService.logSuccessfulLogin(username, ipAddress); + userService.resetFailedLoginAttempts(user.getId()); + + return AuthenticationResult.success(user); + } + + private void handleFailedLogin(User user, String ipAddress) { + auditService.logFailedLogin(user.getUsername(), ipAddress, "Invalid password"); + + int failedAttempts = userService.incrementFailedLoginAttempts(user.getId()); + + if (failedAttempts >= 5) { + userService.lockAccount(user.getId()); + auditService.logAccountLocked(user.getUsername(), ipAddress); + } + } +} + +// Rate Limiting Service +@Service +public class RateLimitingService { + + @Autowired + private RedisTemplate redisTemplate; + + public boolean isAllowed(String ipAddress) { + String key = "login_attempts:" + ipAddress; + + // Получение текущего количества попыток + String attemptsStr = redisTemplate.opsForValue().get(key); + int attempts = attemptsStr != null ? Integer.parseInt(attemptsStr) : 0; + + // Проверка лимита + if (attempts >= 5) { + return false; + } + + // Увеличение счетчика + redisTemplate.opsForValue().increment(key); + redisTemplate.expire(key, Duration.ofMinutes(15)); + + return true; + } +} +``` + +#### A08: Software and Data Integrity Failures (Сбои целостности ПО и данных) + +**Описание:** Недостаточная проверка целостности данных и кода. + +**Методы устранения:** + +```java +// Проверка целостности данных +@Service +public class DataIntegrityService { + + public String calculateChecksum(String data) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(hash); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("SHA-256 algorithm not available", e); + } + } + + public boolean verifyDataIntegrity(String data, String expectedChecksum) { + String actualChecksum = calculateChecksum(data); + return actualChecksum.equals(expectedChecksum); + } + + public void validateFileIntegrity(String filePath, String expectedChecksum) { + try { + String fileContent = Files.readString(Paths.get(filePath)); + if (!verifyDataIntegrity(fileContent, expectedChecksum)) { + throw new DataIntegrityException("File integrity check failed"); + } + } catch (IOException e) { + throw new DataIntegrityException("Failed to read file", e); + } + } +} + +// Проверка целостности кода +@Component +public class CodeIntegrityChecker { + + @EventListener + public void onApplicationStart(ApplicationReadyEvent event) { + validateCodeIntegrity(); + } + + private void validateCodeIntegrity() { + // Проверка цифровых подписей + validateDigitalSignatures(); + + // Проверка контрольных сумм + validateChecksums(); + + // Проверка целостности зависимостей + validateDependencies(); + } + + private void validateDigitalSignatures() { + // Проверка подписей JAR файлов + // Проверка подписей DLL/SO файлов + } + + private void validateChecksums() { + // Проверка контрольных сумм критических файлов + } + + private void validateDependencies() { + // Проверка целостности Maven зависимостей + } +} +``` + +#### A09: Security Logging and Monitoring Failures (Сбои логирования и мониторинга безопасности) + +**Описание:** Недостаточное логирование и мониторинг событий безопасности. + +**Методы устранения:** + +```java +// Комплексная система логирования безопасности +@Component +public class SecurityAuditService { + + @Autowired + private AuditRepository auditRepository; + + @Autowired + private AlertService alertService; + + public void logSecurityEvent(SecurityEvent event) { + // Логирование события + AuditLog auditLog = AuditLog.builder() + .eventType(event.getType()) + .userId(event.getUserId()) + .ipAddress(event.getIpAddress()) + .userAgent(event.getUserAgent()) + .timestamp(LocalDateTime.now()) + .details(event.getDetails()) + .severity(event.getSeverity()) + .build(); + + auditRepository.save(auditLog); + + // Отправка алерта при критических событиях + if (event.getSeverity() == SecuritySeverity.CRITICAL) { + alertService.sendSecurityAlert(event); + } + + // Проверка на подозрительную активность + checkForSuspiciousActivity(event); + } + + private void checkForSuspiciousActivity(SecurityEvent event) { + // Проверка на множественные неудачные попытки входа + if (event.getType() == SecurityEventType.FAILED_LOGIN) { + long failedAttempts = auditRepository.countFailedLogins( + event.getUserId(), + LocalDateTime.now().minusHours(1) + ); + + if (failedAttempts > 10) { + alertService.sendBruteForceAlert(event); + } + } + + // Проверка на необычные IP адреса + if (isUnusualIP(event.getIpAddress())) { + alertService.sendUnusualActivityAlert(event); + } + } + + private boolean isUnusualIP(String ipAddress) { + // Проверка на известные VPN/Proxy сервисы + // Проверка на подозрительные географические локации + return false; // Упрощенная реализация + } +} + +// Система мониторинга безопасности +@Component +public class SecurityMonitoringService { + + @Autowired + private MetricsService metricsService; + + @Scheduled(fixedRate = 60000) // Каждую минуту + public void monitorSecurityMetrics() { + // Мониторинг количества неудачных попыток входа + long failedLogins = metricsService.getFailedLoginCount(); + if (failedLogins > 100) { + alertService.sendHighFailedLoginAlert(failedLogins); + } + + // Мониторинг количества активных сессий + long activeSessions = metricsService.getActiveSessionCount(); + if (activeSessions > 10000) { + alertService.sendHighSessionCountAlert(activeSessions); + } + + // Мониторинг использования ресурсов + double cpuUsage = metricsService.getCpuUsage(); + if (cpuUsage > 90) { + alertService.sendHighCpuUsageAlert(cpuUsage); + } + } +} +``` + +#### A10: Server-Side Request Forgery (SSRF) (Подделка запросов на стороне сервера) + +**Описание:** Принуждение сервера к выполнению запросов к внутренним ресурсам. + +**Методы устранения:** + +```java +// Защита от SSRF +@Service +public class SSRFProtectionService { + + private final Set allowedHosts = Set.of( + "api.example.com", "external-service.com" + ); + + private final Set blockedIPs = Set.of( + "127.0.0.1", "0.0.0.0", "localhost", "::1" + ); + + public String makeSafeRequest(String url) { + // Валидация URL + validateUrl(url); + + // Проверка разрешенных хостов + String host = extractHost(url); + if (!allowedHosts.contains(host)) { + throw new SSRFException("Host not allowed: " + host); + } + + // Проверка на заблокированные IP + String ip = resolveIP(host); + if (blockedIPs.contains(ip)) { + throw new SSRFException("IP address blocked: " + ip); + } + + // Выполнение безопасного запроса + return executeSafeRequest(url); + } + + private void validateUrl(String url) { + try { + URI uri = new URI(url); + + // Проверка схемы + if (!uri.getScheme().equals("https")) { + throw new SSRFException("Only HTTPS URLs are allowed"); + } + + // Проверка порта + if (uri.getPort() != -1 && uri.getPort() != 443) { + throw new SSRFException("Non-standard port not allowed"); + } + + } catch (URISyntaxException e) { + throw new SSRFException("Invalid URL format", e); + } + } + + private String extractHost(String url) { + try { + URI uri = new URI(url); + return uri.getHost(); + } catch (URISyntaxException e) { + throw new SSRFException("Invalid URL format", e); + } + } + + private String resolveIP(String host) { + try { + InetAddress address = InetAddress.getByName(host); + return address.getHostAddress(); + } catch (UnknownHostException e) { + throw new SSRFException("Cannot resolve host: " + host, e); + } + } + + private String executeSafeRequest(String url) { + // Выполнение запроса с ограничениями + RestTemplate restTemplate = new RestTemplate(); + + // Настройка таймаутов + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); + factory.setConnectTimeout(5000); + factory.setReadTimeout(10000); + restTemplate.setRequestFactory(factory); + + try { + ResponseEntity response = restTemplate.getForEntity(url, String.class); + return response.getBody(); + } catch (Exception e) { + throw new SSRFException("Request failed", e); + } + } +} +``` + +### Дополнительные уязвимости + +#### Cross-Site Scripting (XSS) + +**Методы устранения:** + +```java +// Защита от XSS +@Component +public class XSSProtectionService { + + public String sanitizeInput(String input) { + if (input == null) return null; + + // HTML экранирование + return input + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'") + .replace("/", "/"); + } + + public boolean containsXSS(String input) { + // Проверка на подозрительные паттерны + String[] xssPatterns = { + "", ">") + .replace("\"", """) + .replace("'", "'"); + } +} +``` + +#### Cross-Site Request Forgery (CSRF) + +**Методы устранения:** + +```java +// Защита от CSRF +@Configuration +public class CSRFProtectionConfig { + + @Bean + public CsrfTokenRepository csrfTokenRepository() { + CookieCsrfTokenRepository repository = CookieCsrfTokenRepository.withHttpOnlyFalse(); + repository.setCookiePath("/"); + repository.setCookieName("XSRF-TOKEN"); + return repository; + } +} + +// Проверка CSRF токена +@Component +public class CSRFValidationService { + + public boolean validateCSRFToken(HttpServletRequest request) { + String tokenFromRequest = request.getHeader("X-XSRF-TOKEN"); + String tokenFromCookie = getCookieValue(request, "XSRF-TOKEN"); + + if (tokenFromRequest == null || tokenFromCookie == null) { + return false; + } + + return tokenFromRequest.equals(tokenFromCookie); + } + + private String getCookieValue(HttpServletRequest request, String cookieName) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(cookieName)) { + return cookie.getValue(); + } + } + } + return null; + } +} +``` + +--- + +## Обфускация и защита кода + +### Принципы защиты исходного кода + +#### 1. Обфускация кода (Code Obfuscation) + +**Обфускация** — процесс преобразования исходного кода в трудночитаемую форму для защиты интеллектуальной собственности и усложнения реверс-инжиниринга. + +**Цели обфускации:** +- Защита алгоритмов и бизнес-логики +- Усложнение реверс-инжиниринга +- Защита от кражи интеллектуальной собственности +- Предотвращение модификации кода + +**Методы обфускации:** + +```java +// Исходный код +public class PaymentProcessor { + private static final String API_KEY = "sk_live_123456789"; + private static final String SECRET_KEY = "secret_abc123"; + + public PaymentResult processPayment(PaymentRequest request) { + if (request.getAmount() > 10000) { + return validateHighAmountPayment(request); + } + return processStandardPayment(request); + } + + private PaymentResult validateHighAmountPayment(PaymentRequest request) { + // Сложная логика валидации + boolean isValid = performAdvancedValidation(request); + if (isValid) { + return executePayment(request); + } + return PaymentResult.failed("Validation failed"); + } +} + +// Обфускированный код +public class a { + private static final String b = "sk_live_123456789"; + private static final String c = "secret_abc123"; + + public d e(f g) { + if (g.h() > 10000) { + return i(g); + } + return j(g); + } + + private d i(f g) { + boolean h = k(g); + if (h) { + return l(g); + } + return d.m("Validation failed"); + } +} +``` + +**Rust примеры - Обфускация:** + +```rust +// Исходный код +pub struct PaymentProcessor { + api_key: String, + secret_key: String, +} + +impl PaymentProcessor { + pub fn new(api_key: String, secret_key: String) -> Self { + Self { api_key, secret_key } + } + + pub fn process_payment(&self, request: PaymentRequest) -> PaymentResult { + if request.amount > 10000 { + return self.validate_high_amount_payment(request); + } + self.process_standard_payment(request) + } + + fn validate_high_amount_payment(&self, request: PaymentRequest) -> PaymentResult { + let is_valid = self.perform_advanced_validation(&request); + if is_valid { + self.execute_payment(request) + } else { + PaymentResult::failed("Validation failed") + } + } +} + +// Обфускированный код (с использованием макросов) +macro_rules! obfuscate_string { + ($s:expr) => { + base64::decode($s).unwrap_or_else(|_| $s.as_bytes().to_vec()) + }; +} + +pub struct a { + b: Vec, + c: Vec, +} + +impl a { + pub fn d(e: Vec, f: Vec) -> Self { + Self { b: e, c: f } + } + + pub fn g(&self, h: i) -> j { + if h.k > 10000 { + return self.l(h); + } + self.m(h) + } + + fn l(&self, h: i) -> j { + let n = self.o(&h); + if n { + self.p(h) + } else { + j::q("Validation failed") + } + } +} +``` + +**Python примеры - Обфускация:** + +```python +# Исходный код +class PaymentProcessor: + def __init__(self, api_key, secret_key): + self.api_key = api_key + self.secret_key = secret_key + + def process_payment(self, request): + if request.amount > 10000: + return self.validate_high_amount_payment(request) + return self.process_standard_payment(request) + + def validate_high_amount_payment(self, request): + is_valid = self.perform_advanced_validation(request) + if is_valid: + return self.execute_payment(request) + return PaymentResult.failed("Validation failed") + +# Обфускированный код +import base64 +import zlib + +class a: + def __init__(self, b, c): + self.b = b + self.c = c + + def d(self, e): + if e.f > 10000: + return self.g(e) + return self.h(e) + + def g(self, e): + i = self.j(e) + if i: + return self.k(e) + return l.m("Validation failed") + +# Дополнительная обфускация строк +def obfuscate_string(s): + return base64.b64encode(zlib.compress(s.encode())).decode() + +# Использование +API_KEY = obfuscate_string("sk_live_123456789") +SECRET_KEY = obfuscate_string("secret_abc123") +``` + +**Node.js примеры - Обфускация:** + +```typescript +// Исходный код +class PaymentProcessor { + private apiKey: string; + private secretKey: string; + + constructor(apiKey: string, secretKey: string) { + this.apiKey = apiKey; + this.secretKey = secretKey; + } + + processPayment(request: PaymentRequest): PaymentResult { + if (request.amount > 10000) { + return this.validateHighAmountPayment(request); + } + return this.processStandardPayment(request); + } + + private validateHighAmountPayment(request: PaymentRequest): PaymentResult { + const isValid = this.performAdvancedValidation(request); + if (isValid) { + return this.executePayment(request); + } + return PaymentResult.failed("Validation failed"); + } +} + +// Обфускированный код +class a { + private b: string; + private c: string; + + constructor(d: string, e: string) { + this.b = d; + this.c = e; + } + + f(g: h): i { + if (g.j > 10000) { + return this.k(g); + } + return this.l(g); + } + + private k(g: h): i { + const m = this.n(g); + if (m) { + return this.o(g); + } + return i.p("Validation failed"); + } +} + +// Webpack конфигурация для обфускации +module.exports = { + mode: 'production', + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + mangle: { + properties: { + regex: /^_/ + } + }, + compress: { + drop_console: true, + drop_debugger: true, + pure_funcs: ['console.log'] + } + } + }) + ] + } +}; +``` + +#### 2. Минификация (Minification) + +**Минификация** — процесс удаления ненужных символов из кода без изменения его функциональности. + +**Методы минификации:** + +```javascript +// Исходный JavaScript код +function calculateTotal(items) { + let total = 0; + for (let i = 0; i < items.length; i++) { + if (items[i].price > 0) { + total += items[i].price * items[i].quantity; + } + } + return total; +} + +// Минифицированный код +function calculateTotal(items){let total=0;for(let i=0;i0){total+=items[i].price*items[i].quantity}}return total} + +// CSS минификация +/* Исходный CSS */ +.header { + background-color: #ffffff; + padding: 20px; + margin-bottom: 10px; +} + +/* Минифицированный CSS */ +.header{background-color:#fff;padding:20px;margin-bottom:10px} +``` + +#### 3. Защита от реверс-инжиниринга + +**Методы защиты:** + +```java +// Анти-отладка +public class AntiDebugProtection { + + public static boolean isDebuggerPresent() { + try { + // Проверка наличия отладчика + System.getProperty("java.vm.info"); + return System.getProperty("java.vm.info").contains("debug"); + } catch (Exception e) { + return true; // Предполагаем наличие отладчика при ошибке + } + } + + public static void checkDebugger() { + if (isDebuggerPresent()) { + System.exit(1); // Завершение при обнаружении отладчика + } + } + + // Проверка целостности кода + public static boolean verifyCodeIntegrity() { + try { + String expectedHash = "a1b2c3d4e5f6"; + String actualHash = calculateClassHash(); + return expectedHash.equals(actualHash); + } catch (Exception e) { + return false; + } + } + + private static String calculateClassHash() { + // Расчет хеша класса для проверки целостности + return "calculated_hash"; + } +} + +// Защита от модификации +@Component +public class CodeProtectionService { + + @PostConstruct + public void initializeProtection() { + // Проверка целостности при запуске + if (!verifyCodeIntegrity()) { + throw new SecurityException("Code integrity check failed"); + } + + // Проверка на отладчик + checkDebugger(); + + // Запуск фоновой проверки + startBackgroundProtection(); + } + + @Scheduled(fixedRate = 30000) // Каждые 30 секунд + public void backgroundProtection() { + if (!verifyCodeIntegrity()) { + System.exit(1); + } + + if (isDebuggerPresent()) { + System.exit(1); + } + } +} +``` + +#### 4. Source Maps и их безопасность + +**Проблемы Source Maps:** + +```javascript +// webpack.config.js - НЕБЕЗОПАСНО +module.exports = { + devtool: 'source-map', // Раскрывает весь исходный код + + // БЕЗОПАСНО - только для разработки + devtool: process.env.NODE_ENV === 'development' ? 'source-map' : false, + + // БЕЗОПАСНО - скрытые source maps + devtool: 'hidden-source-map', + + // БЕЗОПАСНО - только для внутреннего использования + devtool: 'nosources-source-map' +}; + +// Безопасная конфигурация +module.exports = { + mode: 'production', + devtool: false, // Отключение source maps в продакшене + + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + sourceMap: false, // Отключение source maps + mangle: true, + compress: { + drop_console: true, + drop_debugger: true + } + } + }) + ] + }, + + // Условное включение source maps только для разработки + ...(process.env.NODE_ENV === 'development' && { + devtool: 'eval-source-map' + }) +}; +``` + +#### 5. Защита конфигурационных файлов + +```java +// Защита конфигурации +@Configuration +@EnableEncryption +public class SecureConfiguration { + + @Value("${encrypted.database.password}") + private String encryptedDbPassword; + + @Value("${encrypted.api.key}") + private String encryptedApiKey; + + @Bean + public DataSource dataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb"); + config.setUsername("user"); + config.setPassword(decrypt(encryptedDbPassword)); // Расшифровка пароля + return new HikariDataSource(config); + } + + @Bean + public RestTemplate restTemplate() { + RestTemplate template = new RestTemplate(); + template.getInterceptors().add(new ApiKeyInterceptor(decrypt(encryptedApiKey))); + return template; + } + + private String decrypt(String encryptedValue) { + // Логика расшифровки + return encryptionService.decrypt(encryptedValue); + } +} + +// Шифрование конфигурации +@Service +public class ConfigurationEncryptionService { + + public String encryptConfiguration(String value) { + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec( + getEncryptionKey(), "AES"); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + + byte[] encrypted = cipher.doFinal(value.getBytes()); + return Base64.getEncoder().encodeToString(encrypted); + + } catch (Exception e) { + throw new ConfigurationException("Failed to encrypt configuration", e); + } + } + + private byte[] getEncryptionKey() { + // Получение ключа шифрования из безопасного хранилища + return keyManagementService.getEncryptionKey(); + } +} +``` + +#### 6. Защита от анализа трафика + +```java +// Обфускация сетевого трафика +@Service +public class NetworkObfuscationService { + + public String obfuscateRequest(String originalRequest) { + // Обфускация параметров запроса + String obfuscated = base64Encode(originalRequest); + obfuscated = addRandomPadding(obfuscated); + obfuscated = xorWithKey(obfuscated, getObfuscationKey()); + + return obfuscated; + } + + public String deobfuscateResponse(String obfuscatedResponse) { + // Деобфускация ответа + String deobfuscated = xorWithKey(obfuscatedResponse, getObfuscationKey()); + deobfuscated = removeRandomPadding(deobfuscated); + deobfuscated = base64Decode(deobfuscated); + + return deobfuscated; + } + + private String addRandomPadding(String data) { + // Добавление случайных данных для усложнения анализа + Random random = new Random(); + int paddingLength = random.nextInt(100) + 50; + StringBuilder padding = new StringBuilder(); + + for (int i = 0; i < paddingLength; i++) { + padding.append((char) (random.nextInt(26) + 'a')); + } + + return padding.toString() + data + padding.toString(); + } + + private String removeRandomPadding(String data) { + // Удаление случайных данных + // Логика удаления padding + return data.substring(50, data.length() - 50); + } +} +``` + +### Инструменты обфускации + +#### Java +- **ProGuard** - обфускация и минификация +- **Allatori** - коммерческая обфускация +- **DashO** - профессиональная обфускация + +#### JavaScript/TypeScript +- **Terser** - минификация и обфускация +- **UglifyJS** - минификация JavaScript +- **Webpack** - бандлинг и обфускация + +#### Python +- **PyArmor** - обфускация Python кода +- **Nuitka** - компиляция в нативный код +- **cx_Freeze** - создание исполняемых файлов + +#### Rust +- **rustc** с флагами оптимизации +- **strip** - удаление отладочной информации +- **upx** - сжатие исполняемых файлов + +--- + +## Security by Design + +### Принципы Security by Design + +#### 1. Secure by Default +- Безопасные настройки по умолчанию +- Минимальные права доступа +- Защита от случайных ошибок + +#### 2. Fail Secure +- При отказе система переходит в безопасное состояние +- Блокировка доступа при ошибках +- Логирование всех событий безопасности + +#### 3. Security as Code +- Безопасность как часть кода +- Автоматизированные проверки безопасности +- Версионирование политик безопасности + +### Моделирование угроз + +#### STRIDE Model +- **S**poofing (Подмена) +- **T**ampering (Изменение) +- **R**epudiation (Отказ) +- **I**nformation Disclosure (Раскрытие информации) +- **D**enial of Service (Отказ в обслуживании) +- **E**levation of Privilege (Повышение привилегий) + +#### Пример Threat Model: +```yaml +# threat-model.yml +application: "E-commerce Platform" +components: + - name: "User Authentication" + threats: + - type: "Spoofing" + description: "User impersonation" + mitigations: + - "Strong password policy" + - "Multi-factor authentication" + + - name: "Payment Processing" + threats: + - type: "Tampering" + description: "Payment data modification" + mitigations: + - "Data encryption" + - "Digital signatures" +``` + +### Архитектура безопасности + +#### 1. Zero Trust Architecture +**Принцип:** "Никогда не доверяй, всегда проверяй" + +```java +@Component +public class ZeroTrustFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + + // проверяем каждую аутентификацию + if (!isAuthenticated(httpRequest)) { + throw new AuthenticationException("Authentication required"); + } + + // проверяем каждую авторизацию + if (!isAuthorized(httpRequest)) { + throw new AuthorizationException("Access denied"); + } + + // проверяем устройство + if (!isDeviceTrusted(httpRequest)) { + throw new SecurityException("Untrusted device"); + } + + chain.doFilter(request, response); + } +} +``` + +#### 2. Microsegmentation +**Принцип:** Разделение сети на мелкие сегменты + +```yaml +# network-policy.yml +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: order-service-policy +spec: + podSelector: + matchLabels: + app: order-service + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: + matchLabels: + app: api-gateway + ports: + - protocol: TCP + port: 8080 + egress: + - to: + - podSelector: + matchLabels: + app: payment-service + ports: + - protocol: TCP + port: 8080 +``` + +--- + +## Аутентификация и авторизация + +### Аутентификация + +#### 1. Password-based Authentication +```java +@Service +public class AuthenticationService { + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private UserRepository userRepository; + + public AuthenticationResult authenticate(String username, String password) { + User user = userRepository.findByUsername(username); + + if (user == null) { + return AuthenticationResult.failure("User not found"); + } + + if (!passwordEncoder.matches(password, user.getPasswordHash())) { + return AuthenticationResult.failure("Invalid password"); + } + + if (!user.isActive()) { + return AuthenticationResult.failure("User account is disabled"); + } + + return AuthenticationResult.success(user); + } +} +``` + +#### 2. Multi-Factor Authentication (MFA) +```java +@Service +public class MFAService { + @Autowired + private TOTPGenerator totpGenerator; + + @Autowired + private SMSService smsService; + + public void enableMFA(Long userId, String phoneNumber) { + String secret = totpGenerator.generateSecret(); + userRepository.updateMFASecret(userId, secret); + userRepository.updatePhoneNumber(userId, phoneNumber); + } + + public boolean verifyMFA(Long userId, String code) { + User user = userRepository.findById(userId); + String secret = user.getMFASecret(); + + return totpGenerator.verifyCode(secret, code); + } + + public void sendSMS(Long userId, String code) { + User user = userRepository.findById(userId); + smsService.sendSMS(user.getPhoneNumber(), "Your verification code: " + code); + } +} +``` + +#### 3. OAuth 2.0 / OpenID Connect +```java +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .oauth2Login(oauth2 -> oauth2 + .userInfoEndpoint(userInfo -> userInfo + .userService(customOAuth2UserService) + ) + ) + .authorizeHttpRequests(authz -> authz + .requestMatchers("/public/**").permitAll() + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + + return http.build(); + } +} +``` + +### Авторизация + +#### 1. Role-Based Access Control (RBAC) +```java +@Entity +public class User { + @Id + private Long id; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable( + name = "user_roles", + joinColumns = @JoinColumn(name = "user_id"), + inverseJoinColumns = @JoinColumn(name = "role_id") + ) + private Set roles = new HashSet<>(); +} + +@Entity +public class Role { + @Id + private Long id; + + private String name; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable( + name = "role_permissions", + joinColumns = @JoinColumn(name = "role_id"), + inverseJoinColumns = @JoinColumn(name = "permission_id") + ) + private Set permissions = new HashSet<>(); +} +``` + +#### 2. Attribute-Based Access Control (ABAC) +```java +@Component +public class ABACPolicyEngine { + public boolean evaluate(Policy policy, AccessRequest request) { + // проверяем субъект + if (!policy.getSubject().matches(request.getSubject())) { + return false; + } + + // проверяем ресурс + if (!policy.getResource().matches(request.getResource())) { + return false; + } + + // проверяем действие + if (!policy.getAction().matches(request.getAction())) { + return false; + } + + // проверяем контекст + if (!policy.getContext().matches(request.getContext())) { + return false; + } + + return true; + } +} + +// Пример политики +Policy policy = Policy.builder() + .subject("role:manager") + .resource("department:finance") + .action("read") + .context("time:business_hours") + .build(); +``` + +#### 3. JWT Tokens +```java +@Service +public class JWTService { + private final String secretKey = "mySecretKey"; + private final int jwtExpiration = 86400000; // 24 hours + + public String generateToken(UserDetails userDetails) { + Map claims = new HashMap<>(); + claims.put("roles", userDetails.getAuthorities()); + claims.put("userId", userDetails.getUsername()); + + return createToken(claims, userDetails.getUsername()); + } + + private String createToken(Map claims, String subject) { + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + jwtExpiration)) + .signWith(SignatureAlgorithm.HS512, secretKey) + .compact(); + } + + public Boolean validateToken(String token, UserDetails userDetails) { + final String username = extractUsername(token); + return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); + } +} +``` + +--- + +## Шифрование + +### Типы шифрования + +#### 1. Symmetric Encryption +**Технический пример - Advanced Encryption Service:** + +```java +@Service +public class SymmetricEncryptionService { + + private final SecretKey secretKey; + private final String algorithm = "AES/GCM/NoPadding"; + private final int GCM_IV_LENGTH = 12; + private final int GCM_TAG_LENGTH = 16; + + @Value("${encryption.key}") + private String base64Key; + + public SymmetricEncryptionService() throws Exception { + // Генерируем ключ из конфигурации или создаем новый + if (base64Key != null && !base64Key.isEmpty()) { + byte[] keyBytes = Base64.getDecoder().decode(base64Key); + this.secretKey = new SecretKeySpec(keyBytes, "AES"); + } else { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(256); + this.secretKey = keyGenerator.generateKey(); + } + } + + public EncryptedData encrypt(String plainText) throws Exception { + if (plainText == null || plainText.isEmpty()) { + throw new IllegalArgumentException("Plain text cannot be null or empty"); + } + + // Генерируем случайный IV + byte[] iv = new byte[GCM_IV_LENGTH]; + SecureRandom.getInstanceStrong().nextBytes(iv); + + // Настраиваем шифр + Cipher cipher = Cipher.getInstance(algorithm); + GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); + + // Шифруем данные + byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + + // Объединяем IV и зашифрованные данные + byte[] encryptedWithIv = new byte[GCM_IV_LENGTH + encryptedBytes.length]; + System.arraycopy(iv, 0, encryptedWithIv, 0, GCM_IV_LENGTH); + System.arraycopy(encryptedBytes, 0, encryptedWithIv, GCM_IV_LENGTH, encryptedBytes.length); + + return new EncryptedData( + Base64.getEncoder().encodeToString(encryptedWithIv), + System.currentTimeMillis() + ); + } + + public String decrypt(EncryptedData encryptedData) throws Exception { + if (encryptedData == null || encryptedData.getData() == null) { + throw new IllegalArgumentException("Encrypted data cannot be null"); + } + + byte[] encryptedWithIv = Base64.getDecoder().decode(encryptedData.getData()); + + if (encryptedWithIv.length < GCM_IV_LENGTH) { + throw new IllegalArgumentException("Invalid encrypted data length"); + } + + // Извлекаем IV + byte[] iv = new byte[GCM_IV_LENGTH]; + System.arraycopy(encryptedWithIv, 0, iv, 0, GCM_IV_LENGTH); + + // Извлекаем зашифрованные данные + byte[] encryptedBytes = new byte[encryptedWithIv.length - GCM_IV_LENGTH]; + System.arraycopy(encryptedWithIv, GCM_IV_LENGTH, encryptedBytes, 0, encryptedBytes.length); + + // Настраиваем шифр для расшифровки + Cipher cipher = Cipher.getInstance(algorithm); + GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec); + + // Расшифровываем данные + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + public String encryptSensitiveField(String value) throws Exception { + if (value == null) { + return null; + } + return encrypt(value).getData(); + } + + public String decryptSensitiveField(String encryptedValue) throws Exception { + if (encryptedValue == null) { + return null; + } + return decrypt(new EncryptedData(encryptedValue, 0L)); + } +} + +// Encrypted Data Wrapper +public class EncryptedData { + private final String data; + private final long timestamp; + + public EncryptedData(String data, long timestamp) { + this.data = data; + this.timestamp = timestamp; + } + + public String getData() { + return data; + } + + public long getTimestamp() { + return timestamp; + } +} + +// Field-level Encryption +@Entity +public class User { + @Id + private Long id; + + private String name; + + @Convert(converter = EncryptedStringConverter.class) + private String email; + + @Convert(converter = EncryptedStringConverter.class) + private String phoneNumber; + + @Convert(converter = EncryptedStringConverter.class) + private String ssn; + + // Getters and setters +} + +// Custom Converter for JPA +@Converter +public class EncryptedStringConverter implements AttributeConverter { + + @Autowired + private SymmetricEncryptionService encryptionService; + + @Override + public String convertToDatabaseColumn(String attribute) { + if (attribute == null) { + return null; + } + try { + return encryptionService.encryptSensitiveField(attribute); + } catch (Exception e) { + throw new RuntimeException("Failed to encrypt field", e); + } + } + + @Override + public String convertToEntityAttribute(String dbData) { + if (dbData == null) { + return null; + } + try { + return encryptionService.decryptSensitiveField(dbData); + } catch (Exception e) { + throw new RuntimeException("Failed to decrypt field", e); + } + } +} + +// Advanced Hashing Service +@Service +public class HashingService { + + private final int BCRYPT_ROUNDS = 12; + private final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256"; + private final int PBKDF2_ITERATIONS = 100000; + private final int PBKDF2_KEY_LENGTH = 256; + + public String hashPassword(String password) { + if (password == null || password.isEmpty()) { + throw new IllegalArgumentException("Password cannot be null or empty"); + } + return BCrypt.hashpw(password, BCrypt.gensalt(BCRYPT_ROUNDS)); + } + + public boolean verifyPassword(String password, String hash) { + if (password == null || hash == null) { + return false; + } + return BCrypt.checkpw(password, hash); + } + + public String generatePBKDF2Hash(String password, String salt) throws Exception { + if (password == null || salt == null) { + throw new IllegalArgumentException("Password and salt cannot be null"); + } + + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), + PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH); + SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); + byte[] hash = skf.generateSecret(spec).getEncoded(); + + return Base64.getEncoder().encodeToString(hash); + } + + public String generateSalt() { + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[16]; + random.nextBytes(salt); + return Base64.getEncoder().encodeToString(salt); + } + + public String generateHash(String input, String algorithm) throws Exception { + if (input == null) { + throw new IllegalArgumentException("Input cannot be null"); + } + + MessageDigest digest = MessageDigest.getInstance(algorithm); + byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(hash); + } + + public String generateHMAC(String data, String key, String algorithm) throws Exception { + if (data == null || key == null) { + throw new IllegalArgumentException("Data and key cannot be null"); + } + + Mac mac = Mac.getInstance(algorithm); + SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm); + mac.init(secretKeySpec); + + byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(hmacBytes); + } +} + +// Key Management Service +@Service +public class KeyManagementService { + + @Value("${encryption.key.rotation.enabled:true}") + private boolean keyRotationEnabled; + + @Value("${encryption.key.rotation.interval:30}") + private int rotationIntervalDays; + + private final Map keyVersions = new ConcurrentHashMap<>(); + private final AtomicInteger currentKeyVersion = new AtomicInteger(1); + + @PostConstruct + public void initializeKeys() throws Exception { + // Создаем начальный ключ + SecretKey initialKey = generateAESKey(); + keyVersions.put("v1", initialKey); + + if (keyRotationEnabled) { + scheduleKeyRotation(); + } + } + + @Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00 + public void rotateKeys() throws Exception { + if (!keyRotationEnabled) { + return; + } + + log.info("Starting key rotation process"); + + // Генерируем новый ключ + int newVersion = currentKeyVersion.incrementAndGet(); + SecretKey newKey = generateAESKey(); + keyVersions.put("v" + newVersion, newKey); + + // Перешифровываем критически важные данные + reencryptCriticalData(newKey); + + // Удаляем старые ключи (оставляем последние 3 версии) + cleanupOldKeys(); + + log.info("Key rotation completed. New version: v{}", newVersion); + } + + public SecretKey getCurrentKey() { + return keyVersions.get("v" + currentKeyVersion.get()); + } + + public SecretKey getKeyByVersion(int version) { + return keyVersions.get("v" + version); + } + + private SecretKey generateAESKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(256); + return keyGenerator.generateKey(); + } + + private void reencryptCriticalData(SecretKey newKey) { + // Логика перешифровки критически важных данных + // Это может включать перешифровку данных в БД + } + + private void cleanupOldKeys() { + int currentVersion = currentKeyVersion.get(); + int keepVersions = 3; + + for (int i = 1; i <= currentVersion - keepVersions; i++) { + keyVersions.remove("v" + i); + } + } + + private void scheduleKeyRotation() { + // Дополнительная логика планирования ротации ключей + } +} +``` + +#### 2. Asymmetric Encryption +```java +@Service +public class AsymmetricEncryptionService { + private final KeyPair keyPair; + + public AsymmetricEncryptionService() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + this.keyPair = keyPairGenerator.generateKeyPair(); + } + + public String encrypt(String plainText, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + + byte[] encryptedBytes = cipher.doFinal(plainText.getBytes()); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + public String decrypt(String encryptedText, PrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText)); + return new String(decryptedBytes); + } +} +``` + +#### 3. Hashing +```java +@Service +public class HashingService { + public String hashPassword(String password) { + return BCrypt.hashpw(password, BCrypt.gensalt(12)); + } + + public boolean verifyPassword(String password, String hash) { + return BCrypt.checkpw(password, hash); + } + + public String generateHash(String input) throws Exception { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(input.getBytes()); + return Base64.getEncoder().encodeToString(hash); + } +} +``` + +### Управление ключами + +#### 1. Key Rotation +```java +@Service +public class KeyRotationService { + @Scheduled(cron = "0 0 0 1 * ?") // каждый месяц + public void rotateKeys() { + // генерируем новые ключи + KeyPair newKeyPair = generateNewKeyPair(); + + // обновляем ключи в системе + updateSystemKeys(newKeyPair); + + // перешифровываем существующие данные + reencryptExistingData(newKeyPair); + + // удаляем старые ключи + deleteOldKeys(); + } +} +``` + +#### 2. Hardware Security Module (HSM) +```java +@Service +public class HSMService { + private final HSMClient hsmClient; + + public String encryptWithHSM(String data) { + return hsmClient.encrypt(data); + } + + public String decryptWithHSM(String encryptedData) { + return hsmClient.decrypt(encryptedData); + } + + public String signWithHSM(String data) { + return hsmClient.sign(data); + } +} +``` + +--- + +## Безопасность API + +### Лучшие практики безопасности API + +#### 1. Input Validation +```java +@RestController +@Validated +public class UserController { + @PostMapping("/users") + public ResponseEntity createUser(@Valid @RequestBody CreateUserRequest request) { + // валидация выполняется автоматически через @Valid + User user = userService.createUser(request); + return ResponseEntity.ok(user); + } +} + +public class CreateUserRequest { + @NotBlank(message = "Username is required") + @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters") + private String username; + + @NotBlank(message = "Email is required") + @Email(message = "Email must be valid") + private String email; + + @NotBlank(message = "Password is required") + @Size(min = 8, message = "Password must be at least 8 characters") + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$", + message = "Password must contain at least one uppercase letter, one lowercase letter, one number and one special character") + private String password; +} +``` + +#### 2. Rate Limiting +```java +@Component +public class RateLimitingFilter implements Filter { + private final Map rateLimiters = new ConcurrentHashMap<>(); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + String clientId = getClientId(httpRequest); + + RateLimiter rateLimiter = rateLimiters.computeIfAbsent(clientId, + k -> RateLimiter.create(10.0)); // 10 запросов в секунду + + if (!rateLimiter.tryAcquire()) { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setStatus(429); // Too Many Requests + httpResponse.getWriter().write("Rate limit exceeded"); + return; + } + + chain.doFilter(request, response); + } +} +``` + +#### 3. API Versioning +```java +@RestController +@RequestMapping("/api/v1/users") +public class UserControllerV1 { + @GetMapping("/{id}") + public UserV1 getUser(@PathVariable Long id) { + return userService.getUserV1(id); + } +} + +@RestController +@RequestMapping("/api/v2/users") +public class UserControllerV2 { + @GetMapping("/{id}") + public UserV2 getUser(@PathVariable Long id) { + return userService.getUserV2(id); + } +} +``` + +### Безопасность API Gateway + +```yaml +# api-gateway-security.yml +apiVersion: networking.istio.io/v1alpha3 +kind: AuthorizationPolicy +metadata: + name: api-gateway-policy +spec: + selector: + matchLabels: + app: api-gateway + rules: + - from: + - source: + principals: ["cluster.local/ns/default/sa/api-gateway"] + to: + - operation: + methods: ["GET", "POST"] + paths: ["/api/v1/*"] + - from: + - source: + principals: ["cluster.local/ns/default/sa/admin-service"] + to: + - operation: + methods: ["*"] + paths: ["/admin/*"] +``` + +--- + +## Безопасность данных + +### Классификация данных + +#### 1. Data Sensitivity Levels +- **Public** — общедоступные данные +- **Internal** — внутренние данные +- **Confidential** — конфиденциальные данные +- **Restricted** — ограниченные данные + +```java +@Entity +public class User { + @Id + private Long id; + + @Column(name = "username") + @DataClassification(level = DataClassification.Level.INTERNAL) + private String username; + + @Column(name = "email") + @DataClassification(level = DataClassification.Level.CONFIDENTIAL) + private String email; + + @Column(name = "ssn") + @DataClassification(level = DataClassification.Level.RESTRICTED) + @Encrypted + private String ssn; +} +``` + +#### 2. Data Masking +```java +@Service +public class DataMaskingService { + public User maskUserData(User user, UserRole requesterRole) { + User maskedUser = new User(); + maskedUser.setId(user.getId()); + + switch (requesterRole) { + case ADMIN: + // админы видят все данные + maskedUser.setEmail(user.getEmail()); + maskedUser.setSsn(user.getSsn()); + break; + case MANAGER: + // менеджеры видят частично замаскированные данные + maskedUser.setEmail(maskEmail(user.getEmail())); + maskedUser.setSsn(maskSSN(user.getSsn())); + break; + case USER: + // обычные пользователи видят только базовую информацию + maskedUser.setEmail(maskEmail(user.getEmail())); + maskedUser.setSsn("***-**-****"); + break; + } + + return maskedUser; + } + + private String maskEmail(String email) { + String[] parts = email.split("@"); + String username = parts[0]; + String domain = parts[1]; + + if (username.length() <= 2) { + return "*@" + domain; + } + + return username.substring(0, 2) + "*".repeat(username.length() - 2) + "@" + domain; + } + + private String maskSSN(String ssn) { + return "***-**-" + ssn.substring(ssn.length() - 4); + } +} +``` + +### Шифрование данных + +#### 1. Encryption at Rest +```java +@Entity +public class PaymentCard { + @Id + private Long id; + + @Column(name = "card_number") + @Convert(converter = EncryptedStringConverter.class) + private String cardNumber; + + @Column(name = "cvv") + @Convert(converter = EncryptedStringConverter.class) + private String cvv; +} + +@Converter +public class EncryptedStringConverter implements AttributeConverter { + @Autowired + private EncryptionService encryptionService; + + @Override + public String convertToDatabaseColumn(String attribute) { + return encryptionService.encrypt(attribute); + } + + @Override + public String convertToEntityAttribute(String dbData) { + return encryptionService.decrypt(dbData); + } +} +``` + +#### 2. Encryption in Transit +```java +@Configuration +public class SSLConfig { + @Bean + public RestTemplate restTemplate() throws Exception { + SSLContext sslContext = SSLContextBuilder.create() + .loadTrustMaterial(null, (certificate, authType) -> true) + .build(); + + HttpClient httpClient = HttpClients.custom() + .setSSLContext(sslContext) + .build(); + + HttpComponentsClientHttpRequestFactory factory = + new HttpComponentsClientHttpRequestFactory(httpClient); + + return new RestTemplate(factory); + } +} +``` + +--- + +## Соответствие требованиям + +### GDPR (Общий регламент по защите данных) + +#### 1. Data Subject Rights +```java +@Service +public class GDPRService { + public void handleDataSubjectRequest(DataSubjectRequest request) { + switch (request.getType()) { + case ACCESS: + provideDataAccess(request.getSubjectId()); + break; + case RECTIFICATION: + rectifyData(request.getSubjectId(), request.getData()); + break; + case ERASURE: + eraseData(request.getSubjectId()); + break; + case PORTABILITY: + provideDataPortability(request.getSubjectId()); + break; + } + } + + private void eraseData(Long subjectId) { + // удаляем персональные данные + userRepository.deletePersonalData(subjectId); + + // логируем удаление + auditService.logDataErasure(subjectId); + + // уведомляем заинтересованные системы + notificationService.notifyDataErasure(subjectId); + } +} +``` + +#### 2. Consent Management +```java +@Entity +public class Consent { + @Id + private Long id; + + private Long userId; + private String purpose; + private ConsentStatus status; + private LocalDateTime givenAt; + private LocalDateTime withdrawnAt; + + public boolean isValid() { + return status == ConsentStatus.GIVEN && withdrawnAt == null; + } +} + +@Service +public class ConsentService { + public void giveConsent(Long userId, String purpose) { + Consent consent = new Consent(); + consent.setUserId(userId); + consent.setPurpose(purpose); + consent.setStatus(ConsentStatus.GIVEN); + consent.setGivenAt(LocalDateTime.now()); + + consentRepository.save(consent); + } + + public void withdrawConsent(Long userId, String purpose) { + Consent consent = consentRepository.findByUserIdAndPurpose(userId, purpose); + if (consent != null) { + consent.setStatus(ConsentStatus.WITHDRAWN); + consent.setWithdrawnAt(LocalDateTime.now()); + consentRepository.save(consent); + } + } +} +``` + +### PCI DSS (Стандарт безопасности данных индустрии платежных карт) + +#### 1. Card Data Protection +```java +@Service +public class PCIService { + public String tokenizeCard(String cardNumber) { + // генерируем токен вместо хранения номера карты + String token = UUID.randomUUID().toString(); + + // сохраняем токен в безопасном хранилище + secureStorage.storeToken(token, cardNumber); + + return token; + } + + public String detokenizeCard(String token) { + // получаем номер карты по токену + return secureStorage.getCardByToken(token); + } + + public boolean validateCard(String cardNumber) { + // проверяем номер карты по алгоритму Луна + return luhnAlgorithm.validate(cardNumber); + } +} +``` + +#### 2. Secure Payment Processing +```java +@Service +public class PaymentService { + @Transactional + public PaymentResult processPayment(PaymentRequest request) { + // валидируем данные карты + if (!validateCardData(request.getCardData())) { + return PaymentResult.failure("Invalid card data"); + } + + // токенизируем данные карты + String token = tokenizeCard(request.getCardData().getNumber()); + + // обрабатываем платеж + PaymentResult result = paymentProcessor.processPayment(request); + + // логируем транзакцию + auditService.logPaymentTransaction(request, result); + + return result; + } +} +``` + +### SOX (Закон Сарбейнса-Оксли) + +#### 1. Financial Data Controls +```java +@Service +public class FinancialControlService { + public void validateFinancialTransaction(FinancialTransaction transaction) { + // проверяем авторизацию + if (!isAuthorized(transaction.getAmount())) { + throw new AuthorizationException("Transaction amount exceeds authorization limit"); + } + + // проверяем дублирование + if (isDuplicateTransaction(transaction)) { + throw new DuplicateTransactionException("Duplicate transaction detected"); + } + + // проверяем целостность данных + if (!validateDataIntegrity(transaction)) { + throw new DataIntegrityException("Data integrity check failed"); + } + } + + public void auditFinancialData() { + // аудит финансовых данных + List transactions = getTransactionsForAudit(); + + for (FinancialTransaction transaction : transactions) { + validateTransaction(transaction); + logAuditTrail(transaction); + } + } +} +``` + +--- + +## Практические задания + +### Задание 1: Security Implementation +1. Реализуйте аутентификацию с JWT +2. Настройте авторизацию с RBAC +3. Добавьте валидацию входных данных + +### Задание 2: Data Protection +1. Реализуйте шифрование данных +2. Настройте маскирование данных +3. Добавьте аудит доступа к данным + +### Задание 3: Compliance +1. Реализуйте GDPR compliance +2. Настройте PCI DSS для платежей +3. Добавьте SOX controls + +--- + +## Дополнительные ресурсы + +### Стандарты и фреймворки +- **OWASP** — Open Web Application Security Project +- **NIST** — National Institute of Standards and Technology +- **ISO 27001** — Information Security Management +- **SOC 2** — Service Organization Control + +### Инструменты +- **Security Scanning**: OWASP ZAP, Burp Suite, Nessus +- **Code Analysis**: SonarQube, Checkmarx, Veracode +- **Secrets Management**: HashiCorp Vault, AWS Secrets Manager +- **Identity Management**: Keycloak, Auth0, Okta + +--- + +## Контрольные вопросы + +1. Что такое CIA Triad и как он применяется? +2. Какие принципы Security by Design вы знаете? +3. Как работает OAuth 2.0 и OpenID Connect? +4. Какие типы шифрования существуют? +5. Как обеспечить безопасность API? +6. Что такое GDPR и как его соблюдать? +7. Какие требования PCI DSS вы знаете? +8. Как реализовать Zero Trust Architecture? diff --git a/modules/module7/README.md b/modules/module7/README.md new file mode 100644 index 0000000..b40038a --- /dev/null +++ b/modules/module7/README.md @@ -0,0 +1,102 @@ +# Модуль 7: Документирование архитектуры + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля + +Модуль посвящен созданию эффективной архитектурной документации, которая служит основой для коммуникации между различными стейкхолдерами, принятия архитектурных решений и поддержки системы на протяжении всего жизненного цикла. + +## Содержание модуля + +### 1. [Основы архитектурной документации](sections/01-architecture-documentation-basics.md) +- Назначение и принципы архитектурной документации +- Типы документации и их применение +- Living Documentation и Just Enough Documentation +- Аудитория и адаптация контента + +### 2. [C4 Model](sections/02-c4-model.md) +- Концепция C4 Model +- Уровни абстракции: Context, Container, Component, Code +- Практические примеры диаграмм +- Инструменты и best practices + +### 3. [PlantUML](sections/03-plantuml.md) +- Создание диаграмм с помощью PlantUML +- Sequence, Class, Activity, Deployment диаграммы +- Интеграция с документацией +- Автоматизация генерации диаграмм + +### 4. [Architecture Decision Records (ADR)](sections/04-architecture-decision-records.md) +- Структура и формат ADR +- Процесс создания и ведения ADR +- Шаблоны и примеры +- Интеграция с процессом разработки + +### 5. [Техническая документация](sections/05-technical-documentation.md) +- API документация (OpenAPI) +- Database документация (ERD, Data Dictionary) +- Инструменты и автоматизация +- Версионирование и поддержка актуальности + +### 6. [Коммуникация архитектуры](sections/06-architecture-communication.md) +- Взаимодействие со стейкхолдерами +- Стратегии коммуникации +- Инструменты презентации +- Управление конфликтами и компромиссами + +## Ключевые компетенции + +После изучения модуля вы сможете: + +- **Создавать эффективную архитектурную документацию** для разных аудиторий +- **Использовать C4 Model** для описания архитектуры на разных уровнях +- **Создавать диаграммы** с помощью PlantUML и других инструментов +- **Вести ADR** для документирования архитектурных решений +- **Автоматизировать генерацию документации** и поддержание её актуальности +- **Эффективно коммуницировать** архитектурные решения со стейкхолдерами +- **Управлять конфликтами** и находить компромиссы в архитектурных решениях + +## Практические задания + +### Задание 1: C4 Model +1. Создайте Context диаграмму для системы интернет-магазина +2. Создайте Container диаграмму +3. Создайте Component диаграмму для одного сервиса + +### Задание 2: PlantUML +1. Создайте Sequence диаграмму для процесса заказа +2. Создайте Class диаграмму для доменной модели +3. Создайте Deployment диаграмму + +### Задание 3: ADR +1. Создайте ADR для выбора базы данных +2. Создайте ADR для выбора архитектурного стиля +3. Создайте ADR для стратегии кэширования + +### Задание 4: Коммуникация +1. Подготовьте презентацию для C-Level +2. Создайте техническую документацию для разработчиков +3. Проведите архитектурное ревью + +## Дополнительные ресурсы + +### Инструменты +- **C4 Model**: C4-PlantUML, Structurizr +- **PlantUML**: PlantUML Online, VS Code Extension +- **Documentation**: GitBook, Confluence, Notion +- **API Docs**: Swagger, Postman, Insomnia + +### Книги +- "Documenting Software Architectures" by Paul Clements +- "Architecture Documentation" by Rozanski & Woods +- "The C4 Model" by Simon Brown + +## Критерии оценки + +- **Качество документации**: Четкость, полнота, актуальность +- **Использование инструментов**: Правильное применение C4 Model, PlantUML +- **Коммуникативные навыки**: Эффективная презентация архитектуры +- **Управление решениями**: Качество ADR и процесс принятия решений diff --git a/modules/module7/sections/01-architecture-documentation-basics.md b/modules/module7/sections/01-architecture-documentation-basics.md new file mode 100644 index 0000000..02c6fa3 --- /dev/null +++ b/modules/module7/sections/01-architecture-documentation-basics.md @@ -0,0 +1,428 @@ +# Основы архитектурной документации + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 7](../README.md) + +--- + +## Зачем нужна архитектурная документация + +### 1. Коммуникация +- Общий язык для команды +- Понимание системы новыми участниками +- Обсуждение архитектурных решений + +### 2. Планирование +- Основа для принятия решений +- Анализ влияния изменений +- Планирование миграций + +### 3. Поддержка +- Быстрое понимание системы +- Отладка проблем +- Обучение команды + +## Принципы архитектурной документации + +### 1. Living Documentation +**Принцип:** Документация должна быть живой и актуальной. + +**Технический пример - Living Documentation с автоматизацией:** + +```markdown +# Архитектура системы 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:** + +```java +// 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 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 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 getAllServices() { + List services = new ArrayList<>(); + + List serviceNames = discoveryClient.getServices(); + + for (String serviceName : serviceNames) { + List 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 healthResponse = restTemplate.getForEntity(healthUrl, Map.class); + + String infoUrl = String.format("http://%s:%d/actuator/info", + instance.getHost(), instance.getPort()); + + ResponseEntity 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 info) { + Map build = (Map) info.get("build"); + return build != null ? build.get("version").toString() : "unknown"; + } + + private List getDependenciesFromInfo(Map info) { + // Логика извлечения зависимостей из actuator/info + return new ArrayList<>(); + } +} + +// Database Schema Analyzer +@Component +public class DatabaseSchemaAnalyzer { + + @Autowired + private JdbcTemplate jdbcTemplate; + + public DatabaseSchema analyzeSchema() { + List tables = new ArrayList<>(); + + // Получаем список таблиц + List tableNames = getTableNames(); + + for (String tableName : tableNames) { + TableInfo table = analyzeTable(tableName); + tables.add(table); + } + + return new DatabaseSchema(tables); + } + + private List 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 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 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 generateSpecifications() { + List specifications = new ArrayList<>(); + + List 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 response = restTemplate.getForEntity(openApiUrl, Map.class); + Map openApiSpec = response.getBody(); + + return ApiSpecification.builder() + .serviceName(service.getName()) + .version(service.getVersion()) + .openApiSpec(openApiSpec) + .endpoints(extractEndpoints(openApiSpec)) + .build(); + } + + private List extractEndpoints(Map 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. **Живая документация** — должна обновляться вместе с системой + +Правильная организация и ведение архитектурной документации значительно повышает эффективность команды разработки. diff --git a/modules/module7/sections/02-c4-model.md b/modules/module7/sections/02-c4-model.md new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/modules/module7/sections/02-c4-model.md @@ -0,0 +1,152 @@ +# C4 Model + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 7](../README.md) + +--- + +## Что такое C4 Model + +**C4 Model** — методология для документирования архитектуры программного обеспечения через четыре уровня абстракции. + +## Уровни C4 Model + +### 1. Context (Контекст) +**Цель:** Показать систему в контексте её окружения. + +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml + +title System Context diagram for Internet Banking System + +Person(customer, "Customer", "A customer of the bank, with personal bank accounts.") +Person(customerService, "Customer Service", "Customer service representative of the bank.") + +System(bankingSystem, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.") + +System_Ext(emailSystem, "E-mail System", "The internal Microsoft Exchange e-mail system.") +System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + +Rel(customer, bankingSystem, "Uses", "HTTPS") +Rel(customerService, bankingSystem, "Uses", "HTTPS") +Rel(bankingSystem, emailSystem, "Sends e-mails using", "SMTP") +Rel(bankingSystem, mainframeBankingSystem, "Gets account information from, and makes payments using", "XML/HTTPS") + +@enduml +``` + +### 2. Container (Контейнеры) +**Цель:** Показать высокоуровневую форму системы. + +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml + +title Container diagram for Internet Banking System + +Person(customer, "Customer", "A customer of the bank, with personal bank accounts.") +Person(customerService, "Customer Service", "Customer service representative of the bank.") + +System_Boundary(c1, "Internet Banking System") { + Container(webApp, "Web Application", "Java, Spring MVC", "Delivers the static content and the Internet banking single page application.") + Container(api, "API Application", "Java, Spring Boot", "Provides Internet banking functionality via a JSON/HTTPS API.") + ContainerDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.") +} + +System_Ext(emailSystem, "E-mail System", "The internal Microsoft Exchange e-mail system.") +System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + +Rel(customer, webApp, "Uses", "HTTPS") +Rel(customerService, webApp, "Uses", "HTTPS") +Rel(webApp, api, "Makes API calls to", "JSON/HTTPS") +Rel(api, database, "Reads from and writes to", "JDBC") +Rel(api, emailSystem, "Sends e-mails using", "SMTP") +Rel(api, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS") + +@enduml +``` + +### 3. Component (Компоненты) +**Цель:** Показать как контейнер состоит из компонентов. + +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml + +title Component diagram for Internet Banking System - API Application + +Container_Boundary(api, "API Application") { + Component(security, "Security Component", "Spring Security", "Provides authentication and authorisation functionality.") + Component(controllers, "Controllers", "Spring MVC", "Handles HTTP requests and responses.") + Component(services, "Services", "Spring", "Contains the business logic.") + Component(repositories, "Repositories", "Spring Data", "Handles data access.") +} + +ContainerDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.") +System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + +Rel(controllers, security, "Uses") +Rel(controllers, services, "Uses") +Rel(services, repositories, "Uses") +Rel(repositories, database, "Reads from and writes to", "JDBC") +Rel(services, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS") + +@enduml +``` + +### 4. Code (Код) +**Цель:** Показать детали реализации компонентов. + +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml + +title Component diagram for Internet Banking System - Services + +Component_Boundary(services, "Services") { + Component(accountService, "AccountService", "Java Class", "Provides account related operations.") + Component(securityService, "SecurityService", "Java Class", "Provides security related operations.") + Component(emailService, "EmailService", "Java Class", "Provides email related operations.") +} + +ComponentDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.") +System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + +Rel(accountService, database, "Reads from and writes to", "JDBC") +Rel(accountService, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS") +Rel(securityService, database, "Reads from and writes to", "JDBC") +Rel(emailService, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS") + +@enduml +``` + +## Лучшие практики C4 Model + +### 1. Используйте правильный уровень детализации +- Context: для стейкхолдеров +- Container: для разработчиков +- Component: для архитекторов +- Code: для разработчиков + +### 2. Фокусируйтесь на взаимодействиях +- Показывайте потоки данных +- Указывайте протоколы +- Описывайте интерфейсы + +### 3. Обновляйте документацию +- Синхронизируйте с кодом +- Версионируйте изменения +- Отслеживайте устаревшую документацию + +## Заключение + +C4 Model предоставляет структурированный подход к документированию архитектуры: + +1. **Четыре уровня абстракции** — от контекста до кода +2. **Аудитория-ориентированность** — каждый уровень для своей аудитории +3. **Фокус на взаимодействиях** — показывает как компоненты взаимодействуют +4. **Масштабируемость** — подходит для систем любого размера + +Правильное использование C4 Model значительно улучшает понимание архитектуры системы всеми участниками проекта. diff --git a/modules/module7/sections/03-plantuml.md b/modules/module7/sections/03-plantuml.md new file mode 100644 index 0000000..e3665b9 --- /dev/null +++ b/modules/module7/sections/03-plantuml.md @@ -0,0 +1,201 @@ +# PlantUML + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 7](../README.md) + +--- + +## Что такое PlantUML + +**PlantUML** — инструмент для создания диаграмм из текстового описания. + +## Типы диаграмм + +### 1. Sequence Diagrams (Диаграммы последовательности) +```plantuml +@startuml +title Order Processing Sequence + +actor Customer +participant "Web App" as Web +participant "API Gateway" as Gateway +participant "Order Service" as Order +participant "Payment Service" as Payment +participant "Inventory Service" as Inventory +database "Database" as DB + +Customer -> Web: Create Order +Web -> Gateway: POST /api/orders +Gateway -> Order: Create Order Request +Order -> Inventory: Check Availability +Inventory --> Order: Availability Status +Order -> Payment: Process Payment +Payment --> Order: Payment Result +Order -> DB: Save Order +DB --> Order: Order Saved +Order --> Gateway: Order Created +Gateway --> Web: Order Response +Web --> Customer: Order Confirmation +@enduml +``` + +### 2. Class Diagrams (Диаграммы классов) +```plantuml +@startuml +title Order Domain Model + +class Order { + - id: OrderId + - customerId: CustomerId + - items: List + - status: OrderStatus + - totalAmount: Money + - createdAt: LocalDateTime + + addItem(item: OrderItem): void + + removeItem(itemId: ItemId): void + + confirm(): void + + cancel(): void +} + +class OrderItem { + - id: ItemId + - productId: ProductId + - quantity: int + - unitPrice: Money + + getTotalPrice(): Money +} + +class Money { + - amount: BigDecimal + - currency: Currency + + add(other: Money): Money + + multiply(factor: BigDecimal): Money +} + +Order ||--o{ OrderItem : contains +OrderItem ||--|| Money : unitPrice +Order ||--|| Money : totalAmount + +@enduml +``` + +### 3. Activity Diagrams (Диаграммы активности) +```plantuml +@startuml +title Order Processing Flow + +start +:Receive Order Request; +:Validate Order Data; +if (Data Valid?) then (yes) + :Check Inventory; + if (Items Available?) then (yes) + :Reserve Items; + :Process Payment; + if (Payment Successful?) then (yes) + :Create Order; + :Send Confirmation; + stop + else (no) + :Release Reserved Items; + :Return Payment Error; + stop + endif + else (no) + :Return Inventory Error; + stop + endif +else (no) + :Return Validation Error; + stop +endif +@enduml +``` + +### 4. Deployment Diagrams (Диаграммы развертывания) +```plantuml +@startuml +title System Deployment + +node "Load Balancer" { + component "NGINX" as nginx +} + +node "Application Servers" { + component "API Gateway" as gateway + component "Order Service" as order + component "Payment Service" as payment + component "User Service" as user +} + +node "Database Servers" { + database "PostgreSQL" as db + database "Redis" as cache +} + +node "External Services" { + component "Payment Gateway" as ext_payment + component "Email Service" as ext_email +} + +nginx --> gateway +gateway --> order +gateway --> payment +gateway --> user +order --> db +payment --> db +user --> db +order --> cache +payment --> cache +user --> cache +payment --> ext_payment +user --> ext_email + +@enduml +``` + +## Лучшие практики PlantUML + +### 1. Используйте семантические имена +```plantuml +# Хорошо +participant "Order Service" as OrderService +participant "Payment Service" as PaymentService + +# Плохо +participant "Service1" as S1 +participant "Service2" as S2 +``` + +### 2. Группируйте связанные элементы +```plantuml +box "Order Processing" #LightBlue + participant "Order Service" as Order + participant "Inventory Service" as Inventory +end box + +box "Payment Processing" #LightGreen + participant "Payment Service" as Payment + participant "Payment Gateway" as Gateway +end box +``` + +### 3. Используйте цвета для выделения +```plantuml +actor Customer #LightBlue +participant "Web App" as Web #LightGreen +participant "API Gateway" as Gateway #LightYellow +database "Database" as DB #LightCoral +``` + +## Заключение + +PlantUML предоставляет мощный инструмент для создания диаграмм: + +1. **Текстовое описание** — легко версионировать и изменять +2. **Множество типов диаграмм** — от последовательностей до развертывания +3. **Автоматизация** — интеграция с CI/CD +4. **Читаемость** — семантические имена и группировка + +Правильное использование PlantUML значительно улучшает качество архитектурной документации. diff --git a/modules/module7/sections/04-architecture-decision-records.md b/modules/module7/sections/04-architecture-decision-records.md new file mode 100644 index 0000000..650664d --- /dev/null +++ b/modules/module7/sections/04-architecture-decision-records.md @@ -0,0 +1,280 @@ +# Architecture Decision Records (ADR) + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 7](../README.md) + +--- + +## Что такое ADR + +**Architecture Decision Record (ADR)** — документ, который фиксирует важное архитектурное решение, принятое в проекте. + +## Структура ADR + +### 1. Статус +- **Proposed** — предложено +- **Accepted** — принято +- **Deprecated** — устарело +- **Superseded** — заменено + +### 2. Контекст +- Описание ситуации +- Проблема, которую нужно решить +- Ограничения и требования + +### 3. Решение +- Описание принятого решения +- Обоснование выбора +- Альтернативы, которые рассматривались + +### 4. Последствия +- Положительные последствия +- Отрицательные последствия +- Риски и митигации + +## Пример ADR + +**Технический пример - Advanced ADR Management:** + +```markdown +# ADR-001: Использование микросервисной архитектуры + +## Статус +Принято + +## Дата +2024-01-15 + +## Контекст +Наша система электронной коммерции растет и становится сложнее. Монолитная архитектура создает следующие проблемы: + +- Сложность развертывания изменений +- Технологическая привязка к одному стеку +- Сложность масштабирования отдельных компонентов +- Риск каскадных отказов + +Команда разработки увеличилась до 15 человек, и мы планируем дальнейший рост. + +## Решение +Мы решили перейти к микросервисной архитектуре со следующими принципами: + +- Каждый сервис отвечает за один бизнес-домен +- Сервисы независимо развертываются +- Сервисы имеют собственные базы данных +- Коммуникация через REST API и события + +## Альтернативы +1. **Модульный монолит** — разделение на модули внутри одного приложения +2. **SOA** — сервисно-ориентированная архитектура +3. **Оставить монолит** — продолжить развитие текущей архитектуры + +## Последствия + +### Положительные +- Независимое развертывание сервисов +- Возможность использования разных технологий +- Лучшая масштабируемость +- Изоляция отказов + +### Отрицательные +- Увеличение сложности системы +- Проблемы с распределенными транзакциями +- Сложность мониторинга +- Overhead коммуникации + +### Риски +- **Сложность отладки** — митигация через distributed tracing +- **Проблемы с данными** — митигация через event sourcing +- **Сетевые задержки** — митигация через кэширование + +## Реализация +- Начать с выделения User Service +- Использовать API Gateway для маршрутизации +- Настроить мониторинг и логирование +- Постепенно выделять остальные сервисы + +## Метрики успеха +- Время развертывания: < 5 минут +- Время восстановления: < 30 минут +- Доступность: > 99.9% +- Производительность: P95 < 200ms +``` + +**Технический пример - ADR Management System:** + +```java +// ADR Management Service +@Service +public class ADRManagementService { + + @Autowired + private ADRRepository adrRepository; + + @Autowired + private NotificationService notificationService; + + @Autowired + private VersionControlService versionControlService; + + public ADR createADR(CreateADRRequest request) { + // Валидация + validateADRRequest(request); + + // Создание ADR + ADR adr = ADR.builder() + .id(generateADRId()) + .title(request.getTitle()) + .status(ADRStatus.PROPOSED) + .context(request.getContext()) + .decision(request.getDecision()) + .alternatives(request.getAlternatives()) + .consequences(request.getConsequences()) + .author(request.getAuthor()) + .createdDate(LocalDateTime.now()) + .build(); + + // Сохранение в БД + adr = adrRepository.save(adr); + + // Создание файла в Git + createADRFile(adr); + + // Уведомление команды + notifyTeamAboutNewADR(adr); + + return adr; + } + + public ADR updateADRStatus(Long adrId, ADRStatus newStatus, String comment) { + ADR adr = adrRepository.findById(adrId) + .orElseThrow(() -> new ADRNotFoundException("ADR not found")); + + // Проверяем права на изменение статуса + validateStatusChangePermission(adr, newStatus); + + // Обновляем статус + adr.setStatus(newStatus); + adr.setLastModified(LocalDateTime.now()); + adr.setStatusComment(comment); + + adr = adrRepository.save(adr); + + // Обновляем файл в Git + updateADRFile(adr); + + // Уведомляем команду об изменении + notifyTeamAboutStatusChange(adr); + + return adr; + } + + public List getADRsByStatus(ADRStatus status) { + return adrRepository.findByStatus(status); + } + + public List getADRsByDomain(String domain) { + return adrRepository.findByDomain(domain); + } + + public ADRImpactAnalysis analyzeImpact(Long adrId) { + ADR adr = adrRepository.findById(adrId) + .orElseThrow(() -> new ADRNotFoundException("ADR not found")); + + return ADRImpactAnalysis.builder() + .affectedServices(analyzeAffectedServices(adr)) + .requiredChanges(analyzeRequiredChanges(adr)) + .estimatedEffort(estimateEffort(adr)) + .risks(identifyRisks(adr)) + .build(); + } + + @Scheduled(cron = "0 0 9 * * MON") // Каждый понедельник в 9:00 + public void reviewADRs() { + List proposedADRs = getADRsByStatus(ADRStatus.PROPOSED); + + for (ADR adr : proposedADRs) { + if (isADRStale(adr)) { + notifyTeamAboutStaleADR(adr); + } + } + + // Генерируем отчет по ADR + generateADRReport(); + } + + private void createADRFile(ADR adr) { + String fileName = String.format("adr/%04d-%s.md", adr.getId(), + adr.getTitle().toLowerCase().replaceAll("\\s+", "-")); + + String content = generateADRMarkdown(adr); + + versionControlService.createFile(fileName, content, + String.format("Add ADR-%d: %s", adr.getId(), adr.getTitle())); + } + + private void updateADRFile(ADR adr) { + String fileName = String.format("adr/%04d-%s.md", adr.getId(), + adr.getTitle().toLowerCase().replaceAll("\\s+", "-")); + + String content = generateADRMarkdown(adr); + + versionControlService.updateFile(fileName, content, + String.format("Update ADR-%d: %s", adr.getId(), adr.getTitle())); + } + + private String generateADRMarkdown(ADR adr) { + StringBuilder markdown = new StringBuilder(); + + markdown.append(String.format("# ADR-%d: %s\n\n", adr.getId(), adr.getTitle())); + markdown.append("## Статус\n"); + markdown.append(adr.getStatus().getDisplayName()).append("\n\n"); + markdown.append("## Дата\n"); + markdown.append(adr.getCreatedDate().format(DateTimeFormatter.ISO_LOCAL_DATE)).append("\n\n"); + markdown.append("## Контекст\n"); + markdown.append(adr.getContext()).append("\n\n"); + markdown.append("## Решение\n"); + markdown.append(adr.getDecision()).append("\n\n"); + markdown.append("## Альтернативы\n"); + markdown.append(adr.getAlternatives()).append("\n\n"); + markdown.append("## Последствия\n"); + markdown.append(adr.getConsequences()).append("\n\n"); + + if (adr.getStatusComment() != null) { + markdown.append("## Комментарии\n"); + markdown.append(adr.getStatusComment()).append("\n\n"); + } + + return markdown.toString(); + } +} +``` + +## Лучшие практики ADR + +### 1. Ведите ADR для всех важных решений +- Технологические решения +- Архитектурные паттерны +- Интеграционные решения +- Операционные решения + +### 2. Обновляйте статус ADR +- Отслеживайте изменения +- Документируйте причины изменений +- Архивируйте устаревшие ADR + +### 3. Используйте шаблоны +- Стандартизируйте структуру +- Обеспечивайте консистентность +- Упрощайте создание новых ADR + +## Заключение + +ADR являются важным инструментом документирования архитектурных решений: + +1. **Фиксация решений** — записывают важные архитектурные выборы +2. **Контекст и обоснование** — объясняют почему было принято решение +3. **Отслеживание изменений** — показывают эволюцию архитектуры +4. **Обучение команды** — помогают новым участникам понять решения + +Правильное ведение ADR значительно улучшает понимание архитектуры проекта и облегчает принятие будущих решений. diff --git a/modules/module7/sections/05-technical-documentation.md b/modules/module7/sections/05-technical-documentation.md new file mode 100644 index 0000000..3d35b3f --- /dev/null +++ b/modules/module7/sections/05-technical-documentation.md @@ -0,0 +1,180 @@ +# Техническая документация + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 7](../README.md) + +--- + +## Документация API + +### 1. OpenAPI Specification +```yaml +openapi: 3.0.0 +info: + title: Order Service API + version: 1.0.0 + description: API for managing orders + +servers: + - url: https://api.example.com/v1 + description: Production server + - url: https://staging-api.example.com/v1 + description: Staging server + +paths: + /orders: + post: + summary: Create a new order + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateOrderRequest' + responses: + '201': + description: Order created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + +components: + schemas: + CreateOrderRequest: + type: object + required: + - customerId + - items + properties: + customerId: + type: string + format: uuid + items: + type: array + items: + $ref: '#/components/schemas/OrderItem' + + Order: + type: object + properties: + id: + type: string + format: uuid + customerId: + type: string + format: uuid + status: + type: string + enum: [CREATED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED] + totalAmount: + type: number + format: decimal + createdAt: + type: string + format: date-time +``` + +### 2. API Documentation Tools +- **Swagger UI** — интерактивная документация +- **ReDoc** — альтернативный интерфейс +- **Postman** — коллекции и тесты + +## Документация базы данных + +### 1. Entity Relationship Diagrams +```plantuml +@startuml +title Order Database Schema + +entity "users" { + * id : UUID + -- + * username : VARCHAR(50) + * email : VARCHAR(100) + * password_hash : VARCHAR(255) + * created_at : TIMESTAMP + * updated_at : TIMESTAMP +} + +entity "orders" { + * id : UUID + -- + * user_id : UUID + * status : VARCHAR(20) + * total_amount : DECIMAL(10,2) + * created_at : TIMESTAMP + * updated_at : TIMESTAMP +} + +entity "order_items" { + * id : UUID + -- + * order_id : UUID + * product_id : UUID + * quantity : INTEGER + * unit_price : DECIMAL(10,2) +} + +entity "products" { + * id : UUID + -- + * name : VARCHAR(100) + * description : TEXT + * price : DECIMAL(10,2) + * stock_quantity : INTEGER + * created_at : TIMESTAMP + * updated_at : TIMESTAMP +} + +users ||--o{ orders : "places" +orders ||--o{ order_items : "contains" +products ||--o{ order_items : "referenced in" + +@enduml +``` + +### 2. Data Dictionary +```markdown +# Data Dictionary + +## Table: users + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| id | UUID | PRIMARY KEY | Unique identifier for user | +| username | VARCHAR(50) | NOT NULL, UNIQUE | User's login name | +| email | VARCHAR(100) | NOT NULL, UNIQUE | User's email address | +| password_hash | VARCHAR(255) | NOT NULL | Hashed password | +| created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record creation timestamp | +| updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record update timestamp | + +## Table: orders + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| id | UUID | PRIMARY KEY | Unique identifier for order | +| user_id | UUID | NOT NULL, FOREIGN KEY | Reference to users.id | +| status | VARCHAR(20) | NOT NULL | Order status (CREATED, CONFIRMED, etc.) | +| total_amount | DECIMAL(10,2) | NOT NULL | Total order amount | +| created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record creation timestamp | +| updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record update timestamp | +``` + +## Заключение + +Техническая документация является основой для понимания и поддержки системы: + +1. **API документация** — OpenAPI спецификации и интерактивные инструменты +2. **Документация БД** — ER диаграммы и словари данных +3. **Структурированность** — четкая организация и стандартизация +4. **Актуальность** — регулярное обновление и синхронизация с кодом + +Правильная техническая документация значительно упрощает разработку, тестирование и поддержку системы. diff --git a/modules/module7/sections/06-architecture-communication.md b/modules/module7/sections/06-architecture-communication.md new file mode 100644 index 0000000..e734bea --- /dev/null +++ b/modules/module7/sections/06-architecture-communication.md @@ -0,0 +1,767 @@ +# Коммуникация архитектуры + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 7](../README.md) + +--- + +## Эффективное взаимодействие со стейкхолдерами + +### Типы стейкхолдеров и их потребности + +#### C-Level (CEO, CTO, CFO) +- **Потребности:** Бизнес-ценность, ROI, риски, временные рамки +- **Язык:** Бизнес-метрики, финансовые показатели, стратегические цели +- **Формат:** Executive Summary, высокоуровневые диаграммы + +```markdown +# Executive Summary - Миграция в облако + +## Бизнес-обоснование +- **Экономия затрат**: 40% снижение TCO за 3 года +- **Ускорение разработки**: 60% быстрее time-to-market +- **Повышение надежности**: 99.9% uptime vs 95% сейчас + +## Инвестиции +- **Начальные затраты**: $150,000 (миграция + обучение) +- **Ежемесячные затраты**: $25,000 (vs $45,000 сейчас) +- **ROI**: 200% за 18 месяцев + +## Риски и митигации +- **Риск**: Зависимость от облачного провайдера +- **Митигация**: Multi-cloud стратегия, exit plan +- **Вероятность**: Низкая (5%) +``` + +#### Product Managers +- **Потребности:** Функциональность, пользовательский опыт, временные рамки +- **Язык:** Пользовательские сценарии, фичи, метрики продукта +- **Формат:** User stories, wireframes, API спецификации + +```markdown +# Product Requirements - User Management System + +## Пользовательские сценарии +1. **Регистрация пользователя** + - Пользователь вводит email и пароль + - Система отправляет подтверждение + - Пользователь активирует аккаунт + +2. **Вход в систему** + - Пользователь вводит credentials + - Система аутентифицирует пользователя + - Пользователь получает доступ к системе + +## API Endpoints +- POST /api/users/register +- POST /api/users/login +- GET /api/users/profile +- PUT /api/users/profile + +## Метрики успеха +- Время регистрации < 30 секунд +- Успешность входа > 99% +- Время отклика API < 200ms +``` + +#### Development Teams +- **Потребности:** Технические детали, API, архитектурные паттерны +- **Язык:** Код, диаграммы, технические спецификации +- **Формат:** ADR, техническая документация, диаграммы + +```markdown +# Technical Specification - User Service + +## Архитектурные решения + +### ADR-001: Выбор базы данных +**Статус**: Принято +**Дата**: 2024-01-15 +**Участники**: Архитектор, Lead Developer, DBA + +**Контекст** +Нужно выбрать базу данных для хранения пользовательских данных. + +**Варианты** +1. PostgreSQL - реляционная БД +2. MongoDB - документная БД +3. DynamoDB - NoSQL БД + +**Решение** +Выбрана PostgreSQL по следующим причинам: +- ACID транзакции для критических данных +- Зрелая экосистема и инструменты +- Опыт команды с SQL +- Хорошая производительность для наших объемов + +**Последствия** +- Нужно спроектировать схему БД +- Настроить репликацию для отказоустойчивости +- Реализовать миграции данных +``` + +#### DevOps/Infrastructure Teams +- **Потребности:** Инфраструктура, мониторинг, безопасность, производительность +- **Язык:** Infrastructure as Code, метрики, алерты +- **Формат:** Terraform, Kubernetes manifests, мониторинг дашборды + +```yaml +# Infrastructure Configuration +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service + labels: + app: user-service + version: v1.0.0 +spec: + replicas: 3 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + version: v1.0.0 + spec: + containers: + - name: user-service + image: user-service:v1.0.0 + ports: + - containerPort: 8080 + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: user-service-secrets + key: database-url + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 +``` + +#### QA Teams +- **Потребности:** Тестируемость, качество, покрытие тестами +- **Язык:** Тест-кейсы, сценарии тестирования, метрики качества +- **Формат:** Test plans, API тесты, performance тесты + +```markdown +# Test Plan - User Service + +## Функциональное тестирование + +### Тест-кейсы +1. **TC001: Успешная регистрация пользователя** + - **Предусловия**: Система доступна + - **Шаги**: + 1. Отправить POST /api/users/register с валидными данными + 2. Проверить статус 201 + 3. Проверить создание пользователя в БД + - **Ожидаемый результат**: Пользователь создан, отправлен email + +2. **TC002: Регистрация с существующим email** + - **Предусловия**: Пользователь с email уже существует + - **Шаги**: + 1. Отправить POST /api/users/register с существующим email + - **Ожидаемый результат**: Статус 409, сообщение об ошибке + +## Нагрузочное тестирование +- **Цель**: Проверить производительность при 1000 RPS +- **Инструменты**: JMeter, K6 +- **Метрики**: Response time < 200ms, Error rate < 0.1% +``` + +### Стратегии коммуникации + +#### 1. Адаптация сообщения под аудиторию + +```python +# Пример адаптации сообщения +class StakeholderCommunication: + def __init__(self): + self.audiences = { + 'executive': { + 'focus': ['business_value', 'roi', 'risks'], + 'language': 'business', + 'detail_level': 'high_level' + }, + 'technical': { + 'focus': ['implementation', 'architecture', 'performance'], + 'language': 'technical', + 'detail_level': 'detailed' + }, + 'product': { + 'focus': ['features', 'user_experience', 'timeline'], + 'language': 'product', + 'detail_level': 'medium' + } + } + + def create_message(self, audience, topic, data): + """Создание сообщения для конкретной аудитории""" + audience_config = self.audiences[audience] + + if audience_config['language'] == 'business': + return self._create_business_message(topic, data) + elif audience_config['language'] == 'technical': + return self._create_technical_message(topic, data) + else: + return self._create_product_message(topic, data) + + def _create_business_message(self, topic, data): + """Создание бизнес-сообщения""" + return f""" + {topic} - Бизнес-обоснование + + Ключевые преимущества: + - Экономия затрат: {data.get('cost_savings', 'N/A')} + - Ускорение разработки: {data.get('time_savings', 'N/A')} + - Повышение качества: {data.get('quality_improvement', 'N/A')} + + Инвестиции: {data.get('investment', 'N/A')} + ROI: {data.get('roi', 'N/A')} + """ + + def _create_technical_message(self, topic, data): + """Создание технического сообщения""" + return f""" + {topic} - Техническая спецификация + + Архитектурные решения: + - Технологии: {data.get('technologies', 'N/A')} + - Паттерны: {data.get('patterns', 'N/A')} + - Производительность: {data.get('performance', 'N/A')} + + Реализация: + - Временные рамки: {data.get('timeline', 'N/A')} + - Ресурсы: {data.get('resources', 'N/A')} + - Риски: {data.get('risks', 'N/A')} + """ +``` + +#### 2. Визуализация для разных аудиторий + +```mermaid +graph TB + subgraph "Executive View" + A[Business Goals] --> B[Technical Solution] + B --> C[Business Value] + C --> D[ROI] + end + + subgraph "Technical View" + E[Microservices] --> F[API Gateway] + F --> G[Database] + G --> H[Monitoring] + end + + subgraph "Product View" + I[User Stories] --> J[Features] + J --> K[User Experience] + K --> L[Success Metrics] + end +``` + +#### 3. Управление ожиданиями + +```markdown +# Template: Управление ожиданиями + +## Проект: Миграция в микросервисы + +### Что мы ДЕЛАЕМ: +- ✅ Разбиваем монолит на микросервисы +- ✅ Внедряем API Gateway +- ✅ Настраиваем мониторинг +- ✅ Обеспечиваем отказоустойчивость + +### Что мы НЕ ДЕЛАЕМ: +- ❌ Полная переписывание с нуля +- ❌ Изменение бизнес-логики +- ❌ Замена всех технологий + +### Ограничения: +- Временные рамки: 6 месяцев +- Бюджет: $500,000 +- Команда: 8 разработчиков +- Технологии: Текущий стек + новые инструменты + +### Риски: +- Высокий: Сложность миграции данных +- Средний: Производительность в переходный период +- Низкий: Обучение команды новым технологиям +``` + +### Инструменты коммуникации + +#### 1. Архитектурные диаграммы + +```plantuml +@startuml +!theme plain +skinparam backgroundColor #FFFFFF +skinparam componentStyle rectangle + +package "E-commerce Platform" { + [Web Application] as Web + [Mobile App] as Mobile + [Admin Panel] as Admin + + package "API Gateway" { + [Load Balancer] as LB + [Rate Limiter] as RL + [Authentication] as Auth + } + + package "Microservices" { + [User Service] as US + [Product Service] as PS + [Order Service] as OS + [Payment Service] as PayS + } + + package "Data Layer" { + [User DB] as UDB + [Product DB] as PDB + [Order DB] as ODB + [Payment DB] as PayDB + } + + package "External Services" { + [Payment Gateway] as PG + [Email Service] as ES + [SMS Service] as SMS + } +} + +Web --> LB +Mobile --> LB +Admin --> LB + +LB --> RL +RL --> Auth +Auth --> US +Auth --> PS +Auth --> OS +Auth --> PayS + +US --> UDB +PS --> PDB +OS --> ODB +PayS --> PayDB + +PayS --> PG +US --> ES +US --> SMS + +@enduml +``` + +#### 2. Презентационные шаблоны + +```markdown +# Шаблон презентации для C-Level + +## Slide 1: Executive Summary +- **Проблема**: Текущая система не масштабируется +- **Решение**: Миграция в облако + микросервисы +- **Результат**: 40% экономия + 60% ускорение разработки +- **Инвестиции**: $150K, ROI за 18 месяцев + +## Slide 2: Текущее состояние +- Монолитная архитектура +- Высокие затраты на инфраструктуру +- Медленная разработка новых фич +- Проблемы с масштабированием + +## Slide 3: Предлагаемое решение +- Микросервисная архитектура +- Облачная инфраструктура +- Автоматизация DevOps +- Современные технологии + +## Slide 4: Бизнес-ценность +- **Экономия**: $200K/год +- **Скорость**: 3x быстрее разработка +- **Надежность**: 99.9% uptime +- **Гибкость**: Независимые команды + +## Slide 5: План реализации +- **Фаза 1** (2 мес): Подготовка инфраструктуры +- **Фаза 2** (3 мес): Миграция сервисов +- **Фаза 3** (1 мес): Тестирование и оптимизация +- **Фаза 4** (ongoing): Поддержка и развитие +``` + +#### 3. Интерактивные демонстрации + +```html + + + + + Architecture Demo + + + + +
+ + + + + +``` + +### Управление конфликтами и компромиссами + +#### 1. Framework для принятия решений + +```markdown +# Decision Framework + +## 1. Определение критериев +- **Функциональность**: Соответствие требованиям +- **Производительность**: Скорость и масштабируемость +- **Безопасность**: Защита данных и системы +- **Стоимость**: Бюджет и ROI +- **Время**: Временные рамки реализации +- **Риски**: Технические и бизнес-риски + +## 2. Оценка вариантов +| Критерий | Вариант A | Вариант B | Вариант C | Вес | +|----------|-----------|-----------|-----------|-----| +| Функциональность | 9/10 | 8/10 | 7/10 | 30% | +| Производительность | 7/10 | 9/10 | 8/10 | 25% | +| Безопасность | 8/10 | 7/10 | 9/10 | 20% | +| Стоимость | 6/10 | 8/10 | 9/10 | 15% | +| Время | 8/10 | 6/10 | 7/10 | 10% | +| **Итого** | **7.6** | **7.8** | **7.9** | **100%** | + +## 3. Анализ компромиссов +- **Вариант A**: Высокая функциональность, но дорогой +- **Вариант B**: Хорошая производительность, но долгая реализация +- **Вариант C**: Лучшая безопасность, но ограниченная функциональность + +## 4. Рекомендация +Выбран **Вариант C** как оптимальный баланс всех критериев. +``` + +#### 2. Техники разрешения конфликтов + +```python +# Conflict Resolution Framework +class ConflictResolver: + def __init__(self): + self.stakeholders = {} + self.conflicts = [] + + def identify_conflict(self, stakeholders, issue, positions): + """Идентификация конфликта""" + conflict = { + 'id': len(self.conflicts) + 1, + 'stakeholders': stakeholders, + 'issue': issue, + 'positions': positions, + 'status': 'open', + 'resolution': None + } + self.conflicts.append(conflict) + return conflict + + def analyze_conflict(self, conflict_id): + """Анализ конфликта""" + conflict = self.conflicts[conflict_id - 1] + + # Анализ интересов vs позиций + interests = self._extract_interests(conflict['positions']) + + # Поиск общих интересов + common_interests = self._find_common_interests(interests) + + # Генерация вариантов решения + solutions = self._generate_solutions(conflict, common_interests) + + return { + 'interests': interests, + 'common_interests': common_interests, + 'solutions': solutions + } + + def resolve_conflict(self, conflict_id, solution, rationale): + """Разрешение конфликта""" + conflict = self.conflicts[conflict_id - 1] + conflict['resolution'] = { + 'solution': solution, + 'rationale': rationale, + 'status': 'resolved', + 'date': datetime.now() + } + return conflict + + def _extract_interests(self, positions): + """Извлечение интересов из позиций""" + interests = {} + for stakeholder, position in positions.items(): + interests[stakeholder] = { + 'stated_position': position, + 'underlying_interests': self._analyze_underlying_interests(position) + } + return interests + + def _find_common_interests(self, interests): + """Поиск общих интересов""" + common = [] + stakeholder_interests = list(interests.values()) + + for i, stakeholder1 in enumerate(stakeholder_interests): + for j, stakeholder2 in enumerate(stakeholder_interests[i+1:], i+1): + common_interest = self._compare_interests( + stakeholder1['underlying_interests'], + stakeholder2['underlying_interests'] + ) + if common_interest: + common.append(common_interest) + + return common + + def _generate_solutions(self, conflict, common_interests): + """Генерация вариантов решения""" + solutions = [] + + # Win-Win решения + for common_interest in common_interests: + solutions.append({ + 'type': 'win_win', + 'description': f"Решение на основе общего интереса: {common_interest}", + 'benefits': "Удовлетворяет интересы всех сторон", + 'drawbacks': "Может потребовать дополнительных ресурсов" + }) + + # Компромиссные решения + solutions.append({ + 'type': 'compromise', + 'description': "Частичное удовлетворение интересов всех сторон", + 'benefits': "Быстрое решение", + 'drawbacks': "Никто не получает полного удовлетворения" + }) + + # Альтернативные решения + solutions.append({ + 'type': 'alternative', + 'description': "Поиск третьего варианта", + 'benefits': "Может привести к лучшему решению", + 'drawbacks': "Требует времени и креативности" + }) + + return solutions + +# Использование +resolver = ConflictResolver() + +# Идентификация конфликта +conflict = resolver.identify_conflict( + stakeholders=['CTO', 'Product Manager', 'Lead Developer'], + issue='Выбор технологии для нового сервиса', + positions={ + 'CTO': 'Java Spring Boot', + 'Product Manager': 'Node.js для быстрой разработки', + 'Lead Developer': 'Python Django для простоты' + } +) + +# Анализ и разрешение +analysis = resolver.analyze_conflict(conflict['id']) +resolution = resolver.resolve_conflict( + conflict['id'], + solution='Использовать Java Spring Boot с быстрыми итерациями', + rationale='Баланс между производительностью, скоростью разработки и опытом команды' +) +``` + +## Презентация архитектуры + +### Executive Summary +```markdown +# Архитектура системы электронной коммерции + +## Ключевые метрики +- **Доступность**: 99.9% uptime +- **Производительность**: < 200ms response time +- **Масштабируемость**: до 10,000 RPS +- **Безопасность**: PCI DSS compliant + +## Бизнес-ценность +- Быстрое время выхода на рынок +- Независимое развитие команд +- Высокая отказоустойчивость +- Гибкость в выборе технологий +``` + +### Technical Deep Dive +```markdown +# Техническая архитектура + +## Архитектурные принципы +1. **Микросервисы** — независимые сервисы +2. **API-First** — все взаимодействия через API +3. **Event-Driven** — асинхронная обработка +4. **Cloud-Native** — оптимизация для облака + +## Технологический стек +- **Backend**: Java, Spring Boot, PostgreSQL +- **Frontend**: React, TypeScript +- **Infrastructure**: Kubernetes, Docker +- **Monitoring**: Prometheus, Grafana +``` + +## Архитектурные ревью + +### Review Process +```markdown +# Процесс архитектурного ревью + +## Подготовка +1. Создание архитектурной диаграммы +2. Подготовка ADR +3. Анализ рисков и митигаций +4. Планирование миграции + +## Проведение ревью +1. Презентация архитектуры (30 мин) +2. Обсуждение и вопросы (30 мин) +3. Анализ рисков (15 мин) +4. Принятие решения (15 мин) + +## Результат +- Одобрение или отклонение +- Рекомендации по улучшению +- План реализации +- Следующие шаги +``` + +### Review Checklist +```markdown +# Чек-лист архитектурного ревью + +## Функциональные требования +- [ ] Все требования покрыты +- [ ] Пользовательские сценарии реализуемы +- [ ] Интеграции с внешними системами + +## Нефункциональные требования +- [ ] Производительность соответствует требованиям +- [ ] Масштабируемость обеспечена +- [ ] Безопасность учтена +- [ ] Надежность гарантирована + +## Техническая реализуемость +- [ ] Технологии подходят для задачи +- [ ] Команда имеет необходимые навыки +- [ ] Инфраструктура поддерживает решение +- [ ] Временные рамки реалистичны + +## Риски и митигации +- [ ] Основные риски идентифицированы +- [ ] План митигации разработан +- [ ] Fallback стратегии определены +- [ ] Мониторинг рисков настроен +``` diff --git a/modules/module7/sections/08-practical-tasks.md b/modules/module7/sections/08-practical-tasks.md new file mode 100644 index 0000000..1eb31bd --- /dev/null +++ b/modules/module7/sections/08-practical-tasks.md @@ -0,0 +1,24 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 7](../README.md) + +--- + +### Задание 1: C4 Model +1. Создайте Context диаграмму для системы интернет-магазина +2. Создайте Container диаграмму +3. Создайте Component диаграмму для одного сервиса + +### Задание 2: PlantUML +1. Создайте Sequence диаграмму для процесса заказа +2. Создайте Class диаграмму для доменной модели +3. Создайте Deployment диаграмму + +### Задание 3: ADR +1. Создайте ADR для выбора базы данных +2. Создайте ADR для выбора архитектурного стиля +3. Создайте ADR для стратегии кэширования + +--- \ No newline at end of file diff --git a/modules/module7/theory.md b/modules/module7/theory.md new file mode 100644 index 0000000..c3a5c41 --- /dev/null +++ b/modules/module7/theory.md @@ -0,0 +1,2612 @@ +# Модуль 7: Документирование архитектуры + +## Содержание модуля +1. [Основы архитектурной документации](#основы-архитектурной-документации) +2. [C4 Model](#c4-model) +3. [PlantUML](#plantuml) +4. [Architecture Decision Records (ADR)](#architecture-decision-records-adr) +5. [Техническая документация](#техническая-документация) +6. [Коммуникация архитектуры](#коммуникация-архитектуры) + +--- + +## Основы архитектурной документации + +### Зачем нужна архитектурная документация + +#### 1. Коммуникация +- Общий язык для команды +- Понимание системы новыми участниками +- Обсуждение архитектурных решений + +#### 2. Планирование +- Основа для принятия решений +- Анализ влияния изменений +- Планирование миграций + +#### 3. Поддержка +- Быстрое понимание системы +- Отладка проблем +- Обучение команды + +### Принципы архитектурной документации + +#### 1. Living Documentation +**Принцип:** Документация должна быть живой и актуальной. + +**Технический пример - Living Documentation с автоматизацией:** + +```markdown +# Архитектура системы 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:** + +```java +// 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 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 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 getAllServices() { + List services = new ArrayList<>(); + + List serviceNames = discoveryClient.getServices(); + + for (String serviceName : serviceNames) { + List 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 healthResponse = restTemplate.getForEntity(healthUrl, Map.class); + + String infoUrl = String.format("http://%s:%d/actuator/info", + instance.getHost(), instance.getPort()); + + ResponseEntity 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 info) { + Map build = (Map) info.get("build"); + return build != null ? build.get("version").toString() : "unknown"; + } + + private List getDependenciesFromInfo(Map info) { + // Логика извлечения зависимостей из actuator/info + return new ArrayList<>(); + } +} + +// Database Schema Analyzer +@Component +public class DatabaseSchemaAnalyzer { + + @Autowired + private JdbcTemplate jdbcTemplate; + + public DatabaseSchema analyzeSchema() { + List tables = new ArrayList<>(); + + // Получаем список таблиц + List tableNames = getTableNames(); + + for (String tableName : tableNames) { + TableInfo table = analyzeTable(tableName); + tables.add(table); + } + + return new DatabaseSchema(tables); + } + + private List 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 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 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 generateSpecifications() { + List specifications = new ArrayList<>(); + + List 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 response = restTemplate.getForEntity(openApiUrl, Map.class); + Map openApiSpec = response.getBody(); + + return ApiSpecification.builder() + .serviceName(service.getName()) + .version(service.getVersion()) + .openApiSpec(openApiSpec) + .endpoints(extractEndpoints(openApiSpec)) + .build(); + } + + private List extractEndpoints(Map openApiSpec) { + // Логика извлечения endpoints из OpenAPI спецификации + return new ArrayList<>(); + } +} +``` + +#### 2. Just Enough Documentation +**Принцип:** Документируйте достаточно, но не слишком много. + +#### 3. Audience-Driven +**Принцип:** Документация должна быть ориентирована на аудиторию. + +### Типы архитектурной документации + +#### 1. High-Level Architecture +- Общий обзор системы +- Основные компоненты +- Ключевые взаимодействия + +#### 2. Detailed Design +- Детальное описание компонентов +- API спецификации +- Схемы баз данных + +#### 3. Operational Documentation +- Процедуры развертывания +- Мониторинг и алертинг +- Troubleshooting guides + +--- + +## C4 Model + +### Что такое C4 Model + +**C4 Model** — методология для документирования архитектуры программного обеспечения через четыре уровня абстракции. + +### Уровни C4 Model + +#### 1. Context (Контекст) +**Цель:** Показать систему в контексте её окружения. + +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml + +title System Context diagram for Internet Banking System + +Person(customer, "Customer", "A customer of the bank, with personal bank accounts.") +Person(customerService, "Customer Service", "Customer service representative of the bank.") + +System(bankingSystem, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.") + +System_Ext(emailSystem, "E-mail System", "The internal Microsoft Exchange e-mail system.") +System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + +Rel(customer, bankingSystem, "Uses", "HTTPS") +Rel(customerService, bankingSystem, "Uses", "HTTPS") +Rel(bankingSystem, emailSystem, "Sends e-mails using", "SMTP") +Rel(bankingSystem, mainframeBankingSystem, "Gets account information from, and makes payments using", "XML/HTTPS") + +@enduml +``` + +#### 2. Container (Контейнеры) +**Цель:** Показать высокоуровневую форму системы. + +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml + +title Container diagram for Internet Banking System + +Person(customer, "Customer", "A customer of the bank, with personal bank accounts.") +Person(customerService, "Customer Service", "Customer service representative of the bank.") + +System_Boundary(c1, "Internet Banking System") { + Container(webApp, "Web Application", "Java, Spring MVC", "Delivers the static content and the Internet banking single page application.") + Container(api, "API Application", "Java, Spring Boot", "Provides Internet banking functionality via a JSON/HTTPS API.") + ContainerDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.") +} + +System_Ext(emailSystem, "E-mail System", "The internal Microsoft Exchange e-mail system.") +System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + +Rel(customer, webApp, "Uses", "HTTPS") +Rel(customerService, webApp, "Uses", "HTTPS") +Rel(webApp, api, "Makes API calls to", "JSON/HTTPS") +Rel(api, database, "Reads from and writes to", "JDBC") +Rel(api, emailSystem, "Sends e-mails using", "SMTP") +Rel(api, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS") + +@enduml +``` + +#### 3. Component (Компоненты) +**Цель:** Показать как контейнер состоит из компонентов. + +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml + +title Component diagram for Internet Banking System - API Application + +Container_Boundary(api, "API Application") { + Component(security, "Security Component", "Spring Security", "Provides authentication and authorisation functionality.") + Component(controllers, "Controllers", "Spring MVC", "Handles HTTP requests and responses.") + Component(services, "Services", "Spring", "Contains the business logic.") + Component(repositories, "Repositories", "Spring Data", "Handles data access.") +} + +ContainerDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.") +System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + +Rel(controllers, security, "Uses") +Rel(controllers, services, "Uses") +Rel(services, repositories, "Uses") +Rel(repositories, database, "Reads from and writes to", "JDBC") +Rel(services, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS") + +@enduml +``` + +#### 4. Code (Код) +**Цель:** Показать детали реализации компонентов. + +```plantuml +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml + +title Component diagram for Internet Banking System - Services + +Component_Boundary(services, "Services") { + Component(accountService, "AccountService", "Java Class", "Provides account related operations.") + Component(securityService, "SecurityService", "Java Class", "Provides security related operations.") + Component(emailService, "EmailService", "Java Class", "Provides email related operations.") +} + +ComponentDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.") +System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + +Rel(accountService, database, "Reads from and writes to", "JDBC") +Rel(accountService, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS") +Rel(securityService, database, "Reads from and writes to", "JDBC") +Rel(emailService, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS") + +@enduml +``` + +### Лучшие практики C4 Model + +#### 1. Используйте правильный уровень детализации +- Context: для стейкхолдеров +- Container: для разработчиков +- Component: для архитекторов +- Code: для разработчиков + +#### 2. Фокусируйтесь на взаимодействиях +- Показывайте потоки данных +- Указывайте протоколы +- Описывайте интерфейсы + +#### 3. Обновляйте документацию +- Синхронизируйте с кодом +- Версионируйте изменения +- Отслеживайте устаревшую документацию + +--- + +## PlantUML + +### Что такое PlantUML + +**PlantUML** — инструмент для создания диаграмм из текстового описания. + +### Типы диаграмм + +#### 1. Sequence Diagrams (Диаграммы последовательности) +```plantuml +@startuml +title Order Processing Sequence + +actor Customer +participant "Web App" as Web +participant "API Gateway" as Gateway +participant "Order Service" as Order +participant "Payment Service" as Payment +participant "Inventory Service" as Inventory +database "Database" as DB + +Customer -> Web: Create Order +Web -> Gateway: POST /api/orders +Gateway -> Order: Create Order Request +Order -> Inventory: Check Availability +Inventory --> Order: Availability Status +Order -> Payment: Process Payment +Payment --> Order: Payment Result +Order -> DB: Save Order +DB --> Order: Order Saved +Order --> Gateway: Order Created +Gateway --> Web: Order Response +Web --> Customer: Order Confirmation +@enduml +``` + +#### 2. Class Diagrams (Диаграммы классов) +```plantuml +@startuml +title Order Domain Model + +class Order { + - id: OrderId + - customerId: CustomerId + - items: List + - status: OrderStatus + - totalAmount: Money + - createdAt: LocalDateTime + + addItem(item: OrderItem): void + + removeItem(itemId: ItemId): void + + confirm(): void + + cancel(): void +} + +class OrderItem { + - id: ItemId + - productId: ProductId + - quantity: int + - unitPrice: Money + + getTotalPrice(): Money +} + +class Money { + - amount: BigDecimal + - currency: Currency + + add(other: Money): Money + + multiply(factor: BigDecimal): Money +} + +Order ||--o{ OrderItem : contains +OrderItem ||--|| Money : unitPrice +Order ||--|| Money : totalAmount + +@enduml +``` + +#### 3. Activity Diagrams (Диаграммы активности) +```plantuml +@startuml +title Order Processing Flow + +start +:Receive Order Request; +:Validate Order Data; +if (Data Valid?) then (yes) + :Check Inventory; + if (Items Available?) then (yes) + :Reserve Items; + :Process Payment; + if (Payment Successful?) then (yes) + :Create Order; + :Send Confirmation; + stop + else (no) + :Release Reserved Items; + :Return Payment Error; + stop + endif + else (no) + :Return Inventory Error; + stop + endif +else (no) + :Return Validation Error; + stop +endif +@enduml +``` + +#### 4. Deployment Diagrams (Диаграммы развертывания) +```plantuml +@startuml +title System Deployment + +node "Load Balancer" { + component "NGINX" as nginx +} + +node "Application Servers" { + component "API Gateway" as gateway + component "Order Service" as order + component "Payment Service" as payment + component "User Service" as user +} + +node "Database Servers" { + database "PostgreSQL" as db + database "Redis" as cache +} + +node "External Services" { + component "Payment Gateway" as ext_payment + component "Email Service" as ext_email +} + +nginx --> gateway +gateway --> order +gateway --> payment +gateway --> user +order --> db +payment --> db +user --> db +order --> cache +payment --> cache +user --> cache +payment --> ext_payment +user --> ext_email + +@enduml +``` + +### Лучшие практики PlantUML + +#### 1. Используйте семантические имена +```plantuml +# Хорошо +participant "Order Service" as OrderService +participant "Payment Service" as PaymentService + +# Плохо +participant "Service1" as S1 +participant "Service2" as S2 +``` + +#### 2. Группируйте связанные элементы +```plantuml +box "Order Processing" #LightBlue + participant "Order Service" as Order + participant "Inventory Service" as Inventory +end box + +box "Payment Processing" #LightGreen + participant "Payment Service" as Payment + participant "Payment Gateway" as Gateway +end box +``` + +#### 3. Используйте цвета для выделения +```plantuml +actor Customer #LightBlue +participant "Web App" as Web #LightGreen +participant "API Gateway" as Gateway #LightYellow +database "Database" as DB #LightCoral +``` + +--- + +## Architecture Decision Records (ADR) + +### Что такое ADR + +**Architecture Decision Record (ADR)** — документ, который фиксирует важное архитектурное решение, принятое в проекте. + +### Структура ADR + +#### 1. Статус +- **Proposed** — предложено +- **Accepted** — принято +- **Deprecated** — устарело +- **Superseded** — заменено + +#### 2. Контекст +- Описание ситуации +- Проблема, которую нужно решить +- Ограничения и требования + +#### 3. Решение +- Описание принятого решения +- Обоснование выбора +- Альтернативы, которые рассматривались + +#### 4. Последствия +- Положительные последствия +- Отрицательные последствия +- Риски и митигации + +### Пример ADR + +**Технический пример - Advanced ADR Management:** + +```markdown +# ADR-001: Использование микросервисной архитектуры + +## Статус +Принято + +## Дата +2024-01-15 + +## Контекст +Наша система электронной коммерции растет и становится сложнее. Монолитная архитектура создает следующие проблемы: + +- Сложность развертывания изменений +- Технологическая привязка к одному стеку +- Сложность масштабирования отдельных компонентов +- Риск каскадных отказов + +Команда разработки увеличилась до 15 человек, и мы планируем дальнейший рост. + +## Решение +Мы решили перейти к микросервисной архитектуре со следующими принципами: + +- Каждый сервис отвечает за один бизнес-домен +- Сервисы независимо развертываются +- Сервисы имеют собственные базы данных +- Коммуникация через REST API и события + +## Альтернативы +1. **Модульный монолит** — разделение на модули внутри одного приложения +2. **SOA** — сервисно-ориентированная архитектура +3. **Оставить монолит** — продолжить развитие текущей архитектуры + +## Последствия + +### Положительные +- Независимое развертывание сервисов +- Возможность использования разных технологий +- Лучшая масштабируемость +- Изоляция отказов + +### Отрицательные +- Увеличение сложности системы +- Проблемы с распределенными транзакциями +- Сложность мониторинга +- Overhead коммуникации + +### Риски +- **Сложность отладки** — митигация через distributed tracing +- **Проблемы с данными** — митигация через event sourcing +- **Сетевые задержки** — митигация через кэширование + +## Реализация +- Начать с выделения User Service +- Использовать API Gateway для маршрутизации +- Настроить мониторинг и логирование +- Постепенно выделять остальные сервисы + +## Метрики успеха +- Время развертывания: < 5 минут +- Время восстановления: < 30 минут +- Доступность: > 99.9% +- Производительность: P95 < 200ms +``` + +**Технический пример - ADR Management System:** + +```java +// ADR Management Service +@Service +public class ADRManagementService { + + @Autowired + private ADRRepository adrRepository; + + @Autowired + private NotificationService notificationService; + + @Autowired + private VersionControlService versionControlService; + + public ADR createADR(CreateADRRequest request) { + // Валидация + validateADRRequest(request); + + // Создание ADR + ADR adr = ADR.builder() + .id(generateADRId()) + .title(request.getTitle()) + .status(ADRStatus.PROPOSED) + .context(request.getContext()) + .decision(request.getDecision()) + .alternatives(request.getAlternatives()) + .consequences(request.getConsequences()) + .author(request.getAuthor()) + .createdDate(LocalDateTime.now()) + .build(); + + // Сохранение в БД + adr = adrRepository.save(adr); + + // Создание файла в Git + createADRFile(adr); + + // Уведомление команды + notifyTeamAboutNewADR(adr); + + return adr; + } + + public ADR updateADRStatus(Long adrId, ADRStatus newStatus, String comment) { + ADR adr = adrRepository.findById(adrId) + .orElseThrow(() -> new ADRNotFoundException("ADR not found")); + + // Проверяем права на изменение статуса + validateStatusChangePermission(adr, newStatus); + + // Обновляем статус + adr.setStatus(newStatus); + adr.setLastModified(LocalDateTime.now()); + adr.setStatusComment(comment); + + adr = adrRepository.save(adr); + + // Обновляем файл в Git + updateADRFile(adr); + + // Уведомляем команду об изменении + notifyTeamAboutStatusChange(adr); + + return adr; + } + + public List getADRsByStatus(ADRStatus status) { + return adrRepository.findByStatus(status); + } + + public List getADRsByDomain(String domain) { + return adrRepository.findByDomain(domain); + } + + public ADRImpactAnalysis analyzeImpact(Long adrId) { + ADR adr = adrRepository.findById(adrId) + .orElseThrow(() -> new ADRNotFoundException("ADR not found")); + + return ADRImpactAnalysis.builder() + .affectedServices(analyzeAffectedServices(adr)) + .requiredChanges(analyzeRequiredChanges(adr)) + .estimatedEffort(estimateEffort(adr)) + .risks(identifyRisks(adr)) + .build(); + } + + @Scheduled(cron = "0 0 9 * * MON") // Каждый понедельник в 9:00 + public void reviewADRs() { + List proposedADRs = getADRsByStatus(ADRStatus.PROPOSED); + + for (ADR adr : proposedADRs) { + if (isADRStale(adr)) { + notifyTeamAboutStaleADR(adr); + } + } + + // Генерируем отчет по ADR + generateADRReport(); + } + + private void createADRFile(ADR adr) { + String fileName = String.format("adr/%03d-%s.md", adr.getId(), + adr.getTitle().toLowerCase().replaceAll("\\s+", "-")); + + String content = generateADRMarkdown(adr); + + versionControlService.createFile(fileName, content, + String.format("Add ADR-%d: %s", adr.getId(), adr.getTitle())); + } + + private String generateADRMarkdown(ADR adr) { + StringBuilder markdown = new StringBuilder(); + + markdown.append(String.format("# ADR-%03d: %s\n\n", adr.getId(), adr.getTitle())); + markdown.append("## Статус\n"); + markdown.append(adr.getStatus().getDisplayName()).append("\n\n"); + markdown.append("## Дата\n"); + markdown.append(adr.getCreatedDate().toLocalDate()).append("\n\n"); + markdown.append("## Автор\n"); + markdown.append(adr.getAuthor()).append("\n\n"); + markdown.append("## Контекст\n"); + markdown.append(adr.getContext()).append("\n\n"); + markdown.append("## Решение\n"); + markdown.append(adr.getDecision()).append("\n\n"); + + if (adr.getAlternatives() != null && !adr.getAlternatives().isEmpty()) { + markdown.append("## Альтернативы\n"); + for (String alternative : adr.getAlternatives()) { + markdown.append("- ").append(alternative).append("\n"); + } + markdown.append("\n"); + } + + markdown.append("## Последствия\n"); + markdown.append(adr.getConsequences()).append("\n\n"); + + if (adr.getStatusComment() != null) { + markdown.append("## Комментарии\n"); + markdown.append(adr.getStatusComment()).append("\n\n"); + } + + return markdown.toString(); + } + + private boolean isADRStale(ADR adr) { + return adr.getCreatedDate().isBefore(LocalDateTime.now().minusWeeks(2)); + } + + private void generateADRReport() { + List allADRs = adrRepository.findAll(); + + ADRReport report = ADRReport.builder() + .totalADRs(allADRs.size()) + .proposedADRs(countByStatus(allADRs, ADRStatus.PROPOSED)) + .acceptedADRs(countByStatus(allADRs, ADRStatus.ACCEPTED)) + .deprecatedADRs(countByStatus(allADRs, ADRStatus.DEPRECATED)) + .supersededADRs(countByStatus(allADRs, ADRStatus.SUPERSEDED)) + .recentADRs(getRecentADRs(allADRs, 30)) + .staleADRs(getStaleADRs(allADRs)) + .build(); + + String reportContent = generateReportMarkdown(report); + writeToFile("docs/adr-report.md", reportContent); + + // Отправляем отчет команде + notificationService.sendReport("ADR Report", reportContent); + } +} + +// ADR Entity +@Entity +@Table(name = "architecture_decision_records") +public class ADR { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String title; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private ADRStatus status; + + @Column(columnDefinition = "TEXT") + private String context; + + @Column(columnDefinition = "TEXT") + private String decision; + + @ElementCollection + @CollectionTable(name = "adr_alternatives", joinColumns = @JoinColumn(name = "adr_id")) + @Column(name = "alternative", columnDefinition = "TEXT") + private List alternatives; + + @Column(columnDefinition = "TEXT") + private String consequences; + + @Column(nullable = false) + private String author; + + @Column(nullable = false) + private LocalDateTime createdDate; + + private LocalDateTime lastModified; + + private String statusComment; + + private String domain; + + // Getters and setters +} + +// ADR Status Enum +public enum ADRStatus { + PROPOSED("Предложено"), + ACCEPTED("Принято"), + DEPRECATED("Устарело"), + SUPERSEDED("Заменено"); + + private final String displayName; + + ADRStatus(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } +} + +// ADR Impact Analysis +public class ADRImpactAnalysis { + private List affectedServices; + private List requiredChanges; + private String estimatedEffort; + private List risks; + + // Builder pattern implementation + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private List affectedServices = new ArrayList<>(); + private List requiredChanges = new ArrayList<>(); + private String estimatedEffort; + private List risks = new ArrayList<>(); + + public Builder affectedServices(List affectedServices) { + this.affectedServices = affectedServices; + return this; + } + + public Builder requiredChanges(List requiredChanges) { + this.requiredChanges = requiredChanges; + return this; + } + + public Builder estimatedEffort(String estimatedEffort) { + this.estimatedEffort = estimatedEffort; + return this; + } + + public Builder risks(List risks) { + this.risks = risks; + return this; + } + + public ADRImpactAnalysis build() { + ADRImpactAnalysis analysis = new ADRImpactAnalysis(); + analysis.affectedServices = this.affectedServices; + analysis.requiredChanges = this.requiredChanges; + analysis.estimatedEffort = this.estimatedEffort; + analysis.risks = this.risks; + return analysis; + } + } +} + +// ADR Repository +@Repository +public interface ADRRepository extends JpaRepository { + + List findByStatus(ADRStatus status); + + List findByDomain(String domain); + + List findByAuthor(String author); + + List findByCreatedDateBetween(LocalDateTime start, LocalDateTime end); + + @Query("SELECT a FROM ADR a WHERE a.status = 'PROPOSED' AND a.createdDate < :cutoffDate") + List findStaleProposedADRs(@Param("cutoffDate") LocalDateTime cutoffDate); + + @Query("SELECT a FROM ADR a ORDER BY a.createdDate DESC") + List findRecentADRs(Pageable pageable); +} +``` + +**Node.js примеры - ADR Management System:** + +```typescript +import express, { Request, Response, NextFunction } from 'express'; +import { PrismaClient } from '@prisma/client'; +import fs from 'fs/promises'; +import path from 'path'; +import { z } from 'zod'; + +const app = express(); +const prisma = new PrismaClient(); + +// ADR Management Service +class ADRManagementService { + constructor( + private adrRepository: ADRRepository, + private notificationService: NotificationService, + private versionControlService: VersionControlService + ) {} + + async createADR(request: CreateADRRequest): Promise { + this.validateADRRequest(request); + + // Создание ADR + const adr = await this.adrRepository.save({ + id: await this.generateADRId(), + title: request.title, + domain: request.domain, + author: request.author, + status: ADRStatus.PROPOSED, + context: request.context, + decision: request.decision, + alternatives: request.alternatives, + consequences: request.consequences, + implementation: request.implementation, + createdDate: new Date(), + updatedDate: new Date() + }); + + // Создание файла ADR + await this.createADRFile(adr); + + // Интеграция с системой контроля версий + await this.versionControlService.commitADR(adr); + + // Уведомление команды + await this.notificationService.notifyTeamAboutNewADR(adr); + + return adr; + } + + async updateADRStatus(adrId: number, newStatus: ADRStatus, comment: string): Promise { + const adr = await this.adrRepository.findById(adrId); + if (!adr) { + throw new Error('ADR not found'); + } + + // Обновление статуса + adr.status = newStatus; + adr.updatedDate = new Date(); + if (comment) { + adr.comments = adr.comments || []; + adr.comments.push({ + author: 'System', + content: comment, + timestamp: new Date() + }); + } + + const updatedADR = await this.adrRepository.save(adr); + + // Обновление файла ADR + await this.updateADRFile(updatedADR); + + // Уведомление команды об изменении + await this.notificationService.notifyTeamAboutADRUpdate(updatedADR); + + return updatedADR; + } + + async getADRsByStatus(status: ADRStatus): Promise { + return await this.adrRepository.findByStatus(status); + } + + async getADRsByDomain(domain: string): Promise { + return await this.adrRepository.findByDomain(domain); + } + + async analyzeImpact(adrId: number): Promise { + const adr = await this.adrRepository.findById(adrId); + if (!adr) { + throw new Error('ADR not found'); + } + + return { + adrId: adr.id, + affectedServices: await this.getAffectedServices(adr), + estimatedEffort: await this.estimateEffort(adr), + risks: await this.identifyRisks(adr), + dependencies: await this.getDependencies(adr), + timeline: await this.estimateTimeline(adr) + }; + } + + @Scheduled('0 0 9 * * MON') // Каждый понедельник в 9:00 + async reviewADRs(): Promise { + const proposedADRs = await this.getADRsByStatus(ADRStatus.PROPOSED); + + for (const adr of proposedADRs) { + if (this.isADRStale(adr)) { + await this.notificationService.notifyTeamAboutStaleADR(adr); + } + } + + // Генерируем отчет по ADR + await this.generateADRReport(); + } + + private async createADRFile(adr: ADR): Promise { + const adrDir = path.join(process.cwd(), 'docs', 'adr'); + await fs.mkdir(adrDir, { recursive: true }); + + const content = this.generateADRMarkdown(adr); + const filePath = path.join(adrDir, `adr-${adr.id.toString().padStart(3, '0')}.md`); + + await fs.writeFile(filePath, content, 'utf8'); + } + + private generateADRMarkdown(adr: ADR): string { + const markdown: string[] = []; + + markdown.push(`# ADR-${adr.id.toString().padStart(3, '0')}: ${adr.title}\n`); + markdown.push(`## Статус\n${adr.status}\n`); + markdown.push(`## Дата\n${adr.createdDate.toISOString().split('T')[0]}\n`); + markdown.push(`## Автор\n${adr.author}\n`); + markdown.push(`## Домен\n${adr.domain}\n`); + markdown.push(`## Контекст\n${adr.context}\n`); + markdown.push(`## Решение\n${adr.decision}\n`); + + if (adr.alternatives) { + markdown.push(`## Альтернативы\n${adr.alternatives}\n`); + } + + if (adr.consequences) { + markdown.push(`## Последствия\n${adr.consequences}\n`); + } + + if (adr.implementation) { + markdown.push(`## Реализация\n${adr.implementation}\n`); + } + + return markdown.join(''); + } + + private async updateADRFile(adr: ADR): Promise { + const filePath = path.join(process.cwd(), 'docs', 'adr', `adr-${adr.id.toString().padStart(3, '0')}.md`); + const content = this.generateADRMarkdown(adr); + await fs.writeFile(filePath, content, 'utf8'); + } + + private isADRStale(adr: ADR): boolean { + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - 14); // 2 недели + return adr.createdDate < cutoffDate; + } + + private async generateADRReport(): Promise { + const allADRs = await this.adrRepository.findAll(); + + const report = { + totalADRs: allADRs.length, + proposedADRs: allADRs.filter(adr => adr.status === ADRStatus.PROPOSED).length, + acceptedADRs: allADRs.filter(adr => adr.status === ADRStatus.ACCEPTED).length, + deprecatedADRs: allADRs.filter(adr => adr.status === ADRStatus.DEPRECATED).length, + supersededADRs: allADRs.filter(adr => adr.status === ADRStatus.SUPERSEDED).length, + recentADRs: this.getRecentADRs(allADRs, 30), + staleADRs: this.getStaleADRs(allADRs) + }; + + const reportContent = this.formatADRReport(report); + await this.notificationService.sendReport('ADR Report', reportContent); + } + + private getRecentADRs(adrs: ADR[], days: number): ADR[] { + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - days); + return adrs.filter(adr => adr.createdDate >= cutoffDate); + } + + private getStaleADRs(adrs: ADR[]): ADR[] { + return adrs.filter(adr => this.isADRStale(adr)); + } + + private formatADRReport(report: any): string { + return ` +# ADR Report - ${new Date().toISOString().split('T')[0]} + +## Summary +- Total ADRs: ${report.totalADRs} +- Proposed: ${report.proposedADRs} +- Accepted: ${report.acceptedADRs} +- Deprecated: ${report.deprecatedADRs} +- Superseded: ${report.supersededADRs} + +## Recent ADRs (last 30 days) +${report.recentADRs.map(adr => `- ADR-${adr.id}: ${adr.title}`).join('\n')} + +## Stale ADRs (older than 14 days) +${report.staleADRs.map(adr => `- ADR-${adr.id}: ${adr.title}`).join('\n')} + `; + } + + private validateADRRequest(request: CreateADRRequest): void { + if (!request.title || !request.title.trim()) { + throw new Error('Title is required'); + } + if (!request.domain || !request.domain.trim()) { + throw new Error('Domain is required'); + } + if (!request.author || !request.author.trim()) { + throw new Error('Author is required'); + } + if (!request.context || !request.context.trim()) { + throw new Error('Context is required'); + } + if (!request.decision || !request.decision.trim()) { + throw new Error('Decision is required'); + } + } + + private async generateADRId(): Promise { + const lastADR = await this.adrRepository.findLast(); + return (lastADR?.id || 0) + 1; + } + + private async getAffectedServices(adr: ADR): Promise { + // Логика определения затронутых сервисов + return ['user-service', 'order-service', 'payment-service']; + } + + private async estimateEffort(adr: ADR): Promise { + // Логика оценки усилий + return 'Medium (2-4 weeks)'; + } + + private async identifyRisks(adr: ADR): Promise { + // Логика идентификации рисков + return ['Breaking changes', 'Performance impact', 'Data migration']; + } + + private async getDependencies(adr: ADR): Promise { + // Логика определения зависимостей + return ['Database schema changes', 'API updates', 'Infrastructure changes']; + } + + private async estimateTimeline(adr: ADR): Promise { + // Логика оценки временных рамок + return 'Q2 2024'; + } +} + +// ADR Entity +interface ADR { + id: number; + title: string; + domain: string; + author: string; + status: ADRStatus; + context: string; + decision: string; + alternatives?: string; + consequences?: string; + implementation?: string; + comments?: ADRComment[]; + createdDate: Date; + updatedDate: Date; +} + +interface ADRComment { + author: string; + content: string; + timestamp: Date; +} + +// ADR Status Enum +enum ADRStatus { + PROPOSED = 'PROPOSED', + ACCEPTED = 'ACCEPTED', + DEPRECATED = 'DEPRECATED', + SUPERSEDED = 'SUPERSEDED' +} + +// ADR Impact Analysis +interface ADRImpactAnalysis { + adrId: number; + affectedServices: string[]; + estimatedEffort: string; + risks: string[]; + dependencies: string[]; + timeline: string; +} + +// Repository +interface ADRRepository { + save(adr: Omit): Promise; + findById(id: number): Promise; + findByStatus(status: ADRStatus): Promise; + findByDomain(domain: string): Promise; + findAll(): Promise; + findLast(): Promise; +} + +class PrismaADRRepository implements ADRRepository { + async save(adr: Omit): Promise { + return await prisma.adr.create({ data: adr }); + } + + async findById(id: number): Promise { + return await prisma.adr.findUnique({ where: { id } }); + } + + async findByStatus(status: ADRStatus): Promise { + return await prisma.adr.findMany({ where: { status } }); + } + + async findByDomain(domain: string): Promise { + return await prisma.adr.findMany({ where: { domain } }); + } + + async findAll(): Promise { + return await prisma.adr.findMany(); + } + + async findLast(): Promise { + return await prisma.adr.findFirst({ + orderBy: { id: 'desc' } + }); + } +} + +// Services +class NotificationService { + async notifyTeamAboutNewADR(adr: ADR): Promise { + console.log(`New ADR created: ADR-${adr.id} - ${adr.title}`); + // Реализация уведомлений (email, Slack, etc.) + } + + async notifyTeamAboutADRUpdate(adr: ADR): Promise { + console.log(`ADR updated: ADR-${adr.id} - Status: ${adr.status}`); + } + + async notifyTeamAboutStaleADR(adr: ADR): Promise { + console.log(`Stale ADR detected: ADR-${adr.id} - ${adr.title}`); + } + + async sendReport(title: string, content: string): Promise { + console.log(`Report: ${title}\n${content}`); + } +} + +class VersionControlService { + async commitADR(adr: ADR): Promise { + console.log(`Committing ADR-${adr.id} to version control`); + // Интеграция с Git + } +} + +// Validation schemas +const createADRSchema = z.object({ + title: z.string().min(1, 'Title is required'), + domain: z.string().min(1, 'Domain is required'), + author: z.string().min(1, 'Author is required'), + context: z.string().min(1, 'Context is required'), + decision: z.string().min(1, 'Decision is required'), + alternatives: z.string().optional(), + consequences: z.string().optional(), + implementation: z.string().optional() +}); + +// Dependency injection +const adrRepository = new PrismaADRRepository(); +const notificationService = new NotificationService(); +const versionControlService = new VersionControlService(); +const adrManagementService = new ADRManagementService( + adrRepository, + notificationService, + versionControlService +); + +// API endpoints +app.post('/api/adr', async (req: Request, res: Response, next: NextFunction) => { + try { + const validatedData = createADRSchema.parse(req.body); + const adr = await adrManagementService.createADR(validatedData); + res.json(adr); + } catch (error) { + if (error instanceof z.ZodError) { + res.status(400).json({ error: error.errors }); + } else { + next(error); + } + } +}); + +app.put('/api/adr/:id/status', async (req: Request, res: Response, next: NextFunction) => { + try { + const id = parseInt(req.params.id); + const { status, comment } = req.body; + const adr = await adrManagementService.updateADRStatus(id, status, comment); + res.json(adr); + } catch (error) { + next(error); + } +}); + +app.get('/api/adr/status/:status', async (req: Request, res: Response, next: NextFunction) => { + try { + const status = req.params.status as ADRStatus; + const adrs = await adrManagementService.getADRsByStatus(status); + res.json(adrs); + } catch (error) { + next(error); + } +}); + +app.get('/api/adr/domain/:domain', async (req: Request, res: Response, next: NextFunction) => { + try { + const domain = req.params.domain; + const adrs = await adrManagementService.getADRsByDomain(domain); + res.json(adrs); + } catch (error) { + next(error); + } +}); + +app.get('/api/adr/:id/impact', async (req: Request, res: Response, next: NextFunction) => { + try { + const id = parseInt(req.params.id); + const analysis = await adrManagementService.analyzeImpact(id); + res.json(analysis); + } catch (error) { + next(error); + } +}); + +// Error handling middleware +app.use((error: Error, req: Request, res: Response, next: NextFunction) => { + console.error(error); + res.status(500).json({ error: 'Internal server error' }); +}); + +app.listen(3000, () => { + console.log('ADR Management Service running on port 3000'); +}); +``` + +### Шаблон ADR + +```markdown +# ADR-XXX: [Название решения] + +## Статус +[Proposed/Accepted/Deprecated/Superseded] + +## Дата +[YYYY-MM-DD] + +## Контекст +[Описание ситуации и проблемы] + +## Решение +[Описание принятого решения] + +## Альтернативы +[Список рассмотренных альтернатив] + +## Последствия + +### Положительные +[Список положительных последствий] + +### Отрицательные +[Список отрицательных последствий] + +### Риски +[Список рисков и митигаций] + +## Реализация +[План реализации решения] +``` + +--- + +## Техническая документация + +### Документация API + +#### 1. OpenAPI Specification +```yaml +openapi: 3.0.0 +info: + title: Order Service API + version: 1.0.0 + description: API for managing orders + +servers: + - url: https://api.example.com/v1 + description: Production server + - url: https://staging-api.example.com/v1 + description: Staging server + +paths: + /orders: + post: + summary: Create a new order + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateOrderRequest' + responses: + '201': + description: Order created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + +components: + schemas: + CreateOrderRequest: + type: object + required: + - customerId + - items + properties: + customerId: + type: string + format: uuid + items: + type: array + items: + $ref: '#/components/schemas/OrderItem' + + Order: + type: object + properties: + id: + type: string + format: uuid + customerId: + type: string + format: uuid + status: + type: string + enum: [CREATED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED] + totalAmount: + type: number + format: decimal + createdAt: + type: string + format: date-time +``` + +#### 2. API Documentation Tools +- **Swagger UI** — интерактивная документация +- **ReDoc** — альтернативный интерфейс +- **Postman** — коллекции и тесты + +### Документация базы данных + +#### 1. Entity Relationship Diagrams +```plantuml +@startuml +title Order Database Schema + +entity "users" { + * id : UUID + -- + * username : VARCHAR(50) + * email : VARCHAR(100) + * password_hash : VARCHAR(255) + * created_at : TIMESTAMP + * updated_at : TIMESTAMP +} + +entity "orders" { + * id : UUID + -- + * user_id : UUID + * status : VARCHAR(20) + * total_amount : DECIMAL(10,2) + * created_at : TIMESTAMP + * updated_at : TIMESTAMP +} + +entity "order_items" { + * id : UUID + -- + * order_id : UUID + * product_id : UUID + * quantity : INTEGER + * unit_price : DECIMAL(10,2) +} + +entity "products" { + * id : UUID + -- + * name : VARCHAR(100) + * description : TEXT + * price : DECIMAL(10,2) + * stock_quantity : INTEGER + * created_at : TIMESTAMP + * updated_at : TIMESTAMP +} + +users ||--o{ orders : "places" +orders ||--o{ order_items : "contains" +products ||--o{ order_items : "referenced in" + +@enduml +``` + +#### 2. Data Dictionary +```markdown +# Data Dictionary + +## Table: users + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| id | UUID | PRIMARY KEY | Unique identifier for user | +| username | VARCHAR(50) | NOT NULL, UNIQUE | User's login name | +| email | VARCHAR(100) | NOT NULL, UNIQUE | User's email address | +| password_hash | VARCHAR(255) | NOT NULL | Hashed password | +| created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record creation timestamp | +| updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record update timestamp | + +## Table: orders + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| id | UUID | PRIMARY KEY | Unique identifier for order | +| user_id | UUID | NOT NULL, FOREIGN KEY | Reference to users.id | +| status | VARCHAR(20) | NOT NULL | Order status (CREATED, CONFIRMED, etc.) | +| total_amount | DECIMAL(10,2) | NOT NULL | Total order amount | +| created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record creation timestamp | +| updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record update timestamp | +``` + +--- + +## Коммуникация архитектуры + +### Эффективное взаимодействие со стейкхолдерами + +#### 1. Типы стейкхолдеров и их потребности + +**C-Level (CEO, CTO, CFO)** +- **Потребности:** Бизнес-ценность, ROI, риски, временные рамки +- **Язык:** Бизнес-метрики, финансовые показатели, стратегические цели +- **Формат:** Executive Summary, высокоуровневые диаграммы + +```markdown +# Executive Summary - Миграция в облако + +## Бизнес-обоснование +- **Экономия затрат**: 40% снижение TCO за 3 года +- **Ускорение разработки**: 60% быстрее time-to-market +- **Повышение надежности**: 99.9% uptime vs 95% сейчас + +## Инвестиции +- **Начальные затраты**: $150,000 (миграция + обучение) +- **Ежемесячные затраты**: $25,000 (vs $45,000 сейчас) +- **ROI**: 200% за 18 месяцев + +## Риски и митигации +- **Риск**: Зависимость от облачного провайдера +- **Митигация**: Multi-cloud стратегия, exit plan +- **Вероятность**: Низкая (5%) +``` + +**Product Managers** +- **Потребности:** Функциональность, пользовательский опыт, временные рамки +- **Язык:** Пользовательские сценарии, фичи, метрики продукта +- **Формат:** User stories, wireframes, API спецификации + +```markdown +# Product Requirements - User Management System + +## Пользовательские сценарии +1. **Регистрация пользователя** + - Пользователь вводит email и пароль + - Система отправляет подтверждение + - Пользователь активирует аккаунт + +2. **Вход в систему** + - Пользователь вводит credentials + - Система аутентифицирует пользователя + - Пользователь получает доступ к системе + +## API Endpoints +- POST /api/users/register +- POST /api/users/login +- GET /api/users/profile +- PUT /api/users/profile + +## Метрики успеха +- Время регистрации < 30 секунд +- Успешность входа > 99% +- Время отклика API < 200ms +``` + +**Development Teams** +- **Потребности:** Технические детали, API, архитектурные паттерны +- **Язык:** Код, диаграммы, технические спецификации +- **Формат:** ADR, техническая документация, диаграммы + +```markdown +# Technical Specification - User Service + +## Архитектурные решения + +### ADR-001: Выбор базы данных +**Статус**: Принято +**Дата**: 2024-01-15 +**Участники**: Архитектор, Lead Developer, DBA + +**Контекст** +Нужно выбрать базу данных для хранения пользовательских данных. + +**Варианты** +1. PostgreSQL - реляционная БД +2. MongoDB - документная БД +3. DynamoDB - NoSQL БД + +**Решение** +Выбрана PostgreSQL по следующим причинам: +- ACID транзакции для критических данных +- Зрелая экосистема и инструменты +- Опыт команды с SQL +- Хорошая производительность для наших объемов + +**Последствия** +- Нужно спроектировать схему БД +- Настроить репликацию для отказоустойчивости +- Реализовать миграции данных +``` + +**DevOps/Infrastructure Teams** +- **Потребности:** Инфраструктура, мониторинг, безопасность, производительность +- **Язык:** Infrastructure as Code, метрики, алерты +- **Формат:** Terraform, Kubernetes manifests, мониторинг дашборды + +```yaml +# Infrastructure Configuration +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service + labels: + app: user-service + version: v1.0.0 +spec: + replicas: 3 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + version: v1.0.0 + spec: + containers: + - name: user-service + image: user-service:v1.0.0 + ports: + - containerPort: 8080 + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: user-service-secrets + key: database-url + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 +``` + +**QA Teams** +- **Потребности:** Тестируемость, качество, покрытие тестами +- **Язык:** Тест-кейсы, сценарии тестирования, метрики качества +- **Формат:** Test plans, API тесты, performance тесты + +```markdown +# Test Plan - User Service + +## Функциональное тестирование + +### Тест-кейсы +1. **TC001: Успешная регистрация пользователя** + - **Предусловия**: Система доступна + - **Шаги**: + 1. Отправить POST /api/users/register с валидными данными + 2. Проверить статус 201 + 3. Проверить создание пользователя в БД + - **Ожидаемый результат**: Пользователь создан, отправлен email + +2. **TC002: Регистрация с существующим email** + - **Предусловия**: Пользователь с email уже существует + - **Шаги**: + 1. Отправить POST /api/users/register с существующим email + - **Ожидаемый результат**: Статус 409, сообщение об ошибке + +## Нагрузочное тестирование +- **Цель**: Проверить производительность при 1000 RPS +- **Инструменты**: JMeter, K6 +- **Метрики**: Response time < 200ms, Error rate < 0.1% +``` + +#### 2. Стратегии коммуникации + +**1. Адаптация сообщения под аудиторию** + +```python +# Пример адаптации сообщения +class StakeholderCommunication: + def __init__(self): + self.audiences = { + 'executive': { + 'focus': ['business_value', 'roi', 'risks'], + 'language': 'business', + 'detail_level': 'high_level' + }, + 'technical': { + 'focus': ['implementation', 'architecture', 'performance'], + 'language': 'technical', + 'detail_level': 'detailed' + }, + 'product': { + 'focus': ['features', 'user_experience', 'timeline'], + 'language': 'product', + 'detail_level': 'medium' + } + } + + def create_message(self, audience, topic, data): + """Создание сообщения для конкретной аудитории""" + audience_config = self.audiences[audience] + + if audience_config['language'] == 'business': + return self._create_business_message(topic, data) + elif audience_config['language'] == 'technical': + return self._create_technical_message(topic, data) + else: + return self._create_product_message(topic, data) + + def _create_business_message(self, topic, data): + """Создание бизнес-сообщения""" + return f""" + {topic} - Бизнес-обоснование + + Ключевые преимущества: + - Экономия затрат: {data.get('cost_savings', 'N/A')} + - Ускорение разработки: {data.get('time_savings', 'N/A')} + - Повышение качества: {data.get('quality_improvement', 'N/A')} + + Инвестиции: {data.get('investment', 'N/A')} + ROI: {data.get('roi', 'N/A')} + """ + + def _create_technical_message(self, topic, data): + """Создание технического сообщения""" + return f""" + {topic} - Техническая спецификация + + Архитектурные решения: + - Технологии: {data.get('technologies', 'N/A')} + - Паттерны: {data.get('patterns', 'N/A')} + - Производительность: {data.get('performance', 'N/A')} + + Реализация: + - Временные рамки: {data.get('timeline', 'N/A')} + - Ресурсы: {data.get('resources', 'N/A')} + - Риски: {data.get('risks', 'N/A')} + """ +``` + +**2. Визуализация для разных аудиторий** + +```mermaid +graph TB + subgraph "Executive View" + A[Business Goals] --> B[Technical Solution] + B --> C[Business Value] + C --> D[ROI] + end + + subgraph "Technical View" + E[Microservices] --> F[API Gateway] + F --> G[Database] + G --> H[Monitoring] + end + + subgraph "Product View" + I[User Stories] --> J[Features] + J --> K[User Experience] + K --> L[Success Metrics] + end +``` + +**3. Управление ожиданиями** + +```markdown +# Template: Управление ожиданиями + +## Проект: Миграция в микросервисы + +### Что мы ДЕЛАЕМ: +- ✅ Разбиваем монолит на микросервисы +- ✅ Внедряем API Gateway +- ✅ Настраиваем мониторинг +- ✅ Обеспечиваем отказоустойчивость + +### Что мы НЕ ДЕЛАЕМ: +- ❌ Полная переписывание с нуля +- ❌ Изменение бизнес-логики +- ❌ Замена всех технологий + +### Ограничения: +- Временные рамки: 6 месяцев +- Бюджет: $500,000 +- Команда: 8 разработчиков +- Технологии: Текущий стек + новые инструменты + +### Риски: +- Высокий: Сложность миграции данных +- Средний: Производительность в переходный период +- Низкий: Обучение команды новым технологиям +``` + +#### 3. Инструменты коммуникации + +**1. Архитектурные диаграммы** + +```plantuml +@startuml +!theme plain +skinparam backgroundColor #FFFFFF +skinparam componentStyle rectangle + +package "E-commerce Platform" { + [Web Application] as Web + [Mobile App] as Mobile + [Admin Panel] as Admin + + package "API Gateway" { + [Load Balancer] as LB + [Rate Limiter] as RL + [Authentication] as Auth + } + + package "Microservices" { + [User Service] as US + [Product Service] as PS + [Order Service] as OS + [Payment Service] as PayS + } + + package "Data Layer" { + [User DB] as UDB + [Product DB] as PDB + [Order DB] as ODB + [Payment DB] as PayDB + } + + package "External Services" { + [Payment Gateway] as PG + [Email Service] as ES + [SMS Service] as SMS + } +} + +Web --> LB +Mobile --> LB +Admin --> LB + +LB --> RL +RL --> Auth +Auth --> US +Auth --> PS +Auth --> OS +Auth --> PayS + +US --> UDB +PS --> PDB +OS --> ODB +PayS --> PayDB + +PayS --> PG +US --> ES +US --> SMS + +@enduml +``` + +**2. Презентационные шаблоны** + +```markdown +# Шаблон презентации для C-Level + +## Slide 1: Executive Summary +- **Проблема**: Текущая система не масштабируется +- **Решение**: Миграция в облако + микросервисы +- **Результат**: 40% экономия + 60% ускорение разработки +- **Инвестиции**: $150K, ROI за 18 месяцев + +## Slide 2: Текущее состояние +- Монолитная архитектура +- Высокие затраты на инфраструктуру +- Медленная разработка новых фич +- Проблемы с масштабированием + +## Slide 3: Предлагаемое решение +- Микросервисная архитектура +- Облачная инфраструктура +- Автоматизация DevOps +- Современные технологии + +## Slide 4: Бизнес-ценность +- **Экономия**: $200K/год +- **Скорость**: 3x быстрее разработка +- **Надежность**: 99.9% uptime +- **Гибкость**: Независимые команды + +## Slide 5: План реализации +- **Фаза 1** (2 мес): Подготовка инфраструктуры +- **Фаза 2** (3 мес): Миграция сервисов +- **Фаза 3** (1 мес): Тестирование и оптимизация +- **Фаза 4** (ongoing): Поддержка и развитие +``` + +**3. Интерактивные демонстрации** + +```html + + + + + Architecture Demo + + + + +
+ + + + + +``` + +#### 4. Управление конфликтами и компромиссами + +**1. Framework для принятия решений** + +```markdown +# Decision Framework + +## 1. Определение критериев +- **Функциональность**: Соответствие требованиям +- **Производительность**: Скорость и масштабируемость +- **Безопасность**: Защита данных и системы +- **Стоимость**: Бюджет и ROI +- **Время**: Временные рамки реализации +- **Риски**: Технические и бизнес-риски + +## 2. Оценка вариантов +| Критерий | Вариант A | Вариант B | Вариант C | Вес | +|----------|-----------|-----------|-----------|-----| +| Функциональность | 9/10 | 8/10 | 7/10 | 30% | +| Производительность | 7/10 | 9/10 | 8/10 | 25% | +| Безопасность | 8/10 | 7/10 | 9/10 | 20% | +| Стоимость | 6/10 | 8/10 | 9/10 | 15% | +| Время | 8/10 | 6/10 | 7/10 | 10% | +| **Итого** | **7.6** | **7.8** | **7.9** | **100%** | + +## 3. Анализ компромиссов +- **Вариант A**: Высокая функциональность, но дорогой +- **Вариант B**: Хорошая производительность, но долгая реализация +- **Вариант C**: Лучшая безопасность, но ограниченная функциональность + +## 4. Рекомендация +Выбран **Вариант C** как оптимальный баланс всех критериев. +``` + +**2. Техники разрешения конфликтов** + +```python +# Conflict Resolution Framework +class ConflictResolver: + def __init__(self): + self.stakeholders = {} + self.conflicts = [] + + def identify_conflict(self, stakeholders, issue, positions): + """Идентификация конфликта""" + conflict = { + 'id': len(self.conflicts) + 1, + 'stakeholders': stakeholders, + 'issue': issue, + 'positions': positions, + 'status': 'open', + 'resolution': None + } + self.conflicts.append(conflict) + return conflict + + def analyze_conflict(self, conflict_id): + """Анализ конфликта""" + conflict = self.conflicts[conflict_id - 1] + + # Анализ интересов vs позиций + interests = self._extract_interests(conflict['positions']) + + # Поиск общих интересов + common_interests = self._find_common_interests(interests) + + # Генерация вариантов решения + solutions = self._generate_solutions(conflict, common_interests) + + return { + 'interests': interests, + 'common_interests': common_interests, + 'solutions': solutions + } + + def resolve_conflict(self, conflict_id, solution, rationale): + """Разрешение конфликта""" + conflict = self.conflicts[conflict_id - 1] + conflict['resolution'] = { + 'solution': solution, + 'rationale': rationale, + 'status': 'resolved', + 'date': datetime.now() + } + return conflict + + def _extract_interests(self, positions): + """Извлечение интересов из позиций""" + interests = {} + for stakeholder, position in positions.items(): + interests[stakeholder] = { + 'stated_position': position, + 'underlying_interests': self._analyze_underlying_interests(position) + } + return interests + + def _find_common_interests(self, interests): + """Поиск общих интересов""" + common = [] + stakeholder_interests = list(interests.values()) + + for i, stakeholder1 in enumerate(stakeholder_interests): + for j, stakeholder2 in enumerate(stakeholder_interests[i+1:], i+1): + common_interest = self._compare_interests( + stakeholder1['underlying_interests'], + stakeholder2['underlying_interests'] + ) + if common_interest: + common.append(common_interest) + + return common + + def _generate_solutions(self, conflict, common_interests): + """Генерация вариантов решения""" + solutions = [] + + # Win-Win решения + for common_interest in common_interests: + solutions.append({ + 'type': 'win_win', + 'description': f"Решение на основе общего интереса: {common_interest}", + 'benefits': "Удовлетворяет интересы всех сторон", + 'drawbacks': "Может потребовать дополнительных ресурсов" + }) + + # Компромиссные решения + solutions.append({ + 'type': 'compromise', + 'description': "Частичное удовлетворение интересов всех сторон", + 'benefits': "Быстрое решение", + 'drawbacks': "Никто не получает полного удовлетворения" + }) + + # Альтернативные решения + solutions.append({ + 'type': 'alternative', + 'description': "Поиск третьего варианта", + 'benefits': "Может привести к лучшему решению", + 'drawbacks': "Требует времени и креативности" + }) + + return solutions + +# Использование +resolver = ConflictResolver() + +# Идентификация конфликта +conflict = resolver.identify_conflict( + stakeholders=['CTO', 'Product Manager', 'Lead Developer'], + issue='Выбор технологии для нового сервиса', + positions={ + 'CTO': 'Java Spring Boot', + 'Product Manager': 'Node.js для быстрой разработки', + 'Lead Developer': 'Python Django для простоты' + } +) + +# Анализ и разрешение +analysis = resolver.analyze_conflict(conflict['id']) +resolution = resolver.resolve_conflict( + conflict['id'], + solution='Использовать Java Spring Boot с быстрыми итерациями', + rationale='Баланс между производительностью, скоростью разработки и опытом команды' +) +``` + +### Презентация архитектуры + +#### 1. Executive Summary +```markdown +# Архитектура системы электронной коммерции + +## Ключевые метрики +- **Доступность**: 99.9% uptime +- **Производительность**: < 200ms response time +- **Масштабируемость**: до 10,000 RPS +- **Безопасность**: PCI DSS compliant + +## Бизнес-ценность +- Быстрое время выхода на рынок +- Независимое развитие команд +- Высокая отказоустойчивость +- Гибкость в выборе технологий +``` + +#### 2. Technical Deep Dive +```markdown +# Техническая архитектура + +## Архитектурные принципы +1. **Микросервисы** — независимые сервисы +2. **API-First** — все взаимодействия через API +3. **Event-Driven** — асинхронная обработка +4. **Cloud-Native** — оптимизация для облака + +## Технологический стек +- **Backend**: Java, Spring Boot, PostgreSQL +- **Frontend**: React, TypeScript +- **Infrastructure**: Kubernetes, Docker +- **Monitoring**: Prometheus, Grafana +``` + +### Архитектурные ревью + +#### 1. Review Process +```markdown +# Процесс архитектурного ревью + +## Подготовка +1. Создание архитектурной диаграммы +2. Подготовка ADR +3. Анализ рисков и митигаций +4. Планирование миграции + +## Проведение ревью +1. Презентация архитектуры (30 мин) +2. Обсуждение и вопросы (30 мин) +3. Анализ рисков (15 мин) +4. Принятие решения (15 мин) + +## Результат +- Одобрение или отклонение +- Рекомендации по улучшению +- План реализации +- Следующие шаги +``` + +#### 2. Review Checklist +```markdown +# Чек-лист архитектурного ревью + +## Функциональные требования +- [ ] Все требования покрыты +- [ ] Пользовательские сценарии реализуемы +- [ ] Интеграции с внешними системами + +## Нефункциональные требования +- [ ] Производительность соответствует требованиям +- [ ] Масштабируемость обеспечена +- [ ] Безопасность учтена +- [ ] Надежность гарантирована + +## Техническая реализуемость +- [ ] Технологии подходят для задачи +- [ ] Команда имеет необходимые навыки +- [ ] Инфраструктура поддерживает решение +- [ ] Временные рамки реалистичны + +## Риски и митигации +- [ ] Основные риски идентифицированы +- [ ] План митигации разработан +- [ ] Fallback стратегии определены +- [ ] Мониторинг рисков настроен +``` + +--- + +## Практические задания + +### Задание 1: C4 Model +1. Создайте Context диаграмму для системы интернет-магазина +2. Создайте Container диаграмму +3. Создайте Component диаграмму для одного сервиса + +### Задание 2: PlantUML +1. Создайте Sequence диаграмму для процесса заказа +2. Создайте Class диаграмму для доменной модели +3. Создайте Deployment диаграмму + +### Задание 3: ADR +1. Создайте ADR для выбора базы данных +2. Создайте ADR для выбора архитектурного стиля +3. Создайте ADR для стратегии кэширования + +--- + +## Дополнительные ресурсы + +### Инструменты +- **C4 Model**: C4-PlantUML, Structurizr +- **PlantUML**: PlantUML Online, VS Code Extension +- **Documentation**: GitBook, Confluence, Notion +- **API Docs**: Swagger, Postman, Insomnia + +### Книги +- "Documenting Software Architectures" by Paul Clements +- "Architecture Documentation" by Rozanski & Woods +- "The C4 Model" by Simon Brown + +--- + +## Контрольные вопросы + +1. Зачем нужна архитектурная документация? +2. Что такое C4 Model и как его использовать? +3. Какие типы диаграмм можно создать в PlantUML? +4. Что такое ADR и как его структурировать? +5. Как документировать API? +6. Как проводить архитектурные ревью? +7. Какие принципы архитектурной документации вы знаете? +8. Как поддерживать актуальность документации? diff --git a/modules/module8/README.md b/modules/module8/README.md new file mode 100644 index 0000000..70dd30a --- /dev/null +++ b/modules/module8/README.md @@ -0,0 +1,120 @@ +# Модуль 8: Облачные технологии и миграция + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля + +Модуль посвящен изучению облачных технологий, стратегий миграции в облако и оптимизации затрат. Студенты изучат основные облачные платформы, научатся проектировать cloud-native архитектуры и планировать миграцию существующих систем. + +## Содержание модуля + +### 1. [Основы облачных технологий](sections/01-cloud-fundamentals.md) +- Модели обслуживания (IaaS, PaaS, SaaS) +- Модели развертывания (Public, Private, Hybrid, Multi-cloud) +- Ключевые характеристики облачных вычислений +- Преимущества и риски облачных технологий + +### 2. [AWS (Amazon Web Services)](sections/02-aws-services.md) +- Основные сервисы AWS +- Compute, Storage, Database, Networking сервисы +- Security и Monitoring сервисы +- Лучшие практики использования AWS + +### 3. [Azure (Microsoft Azure)](sections/03-azure-services.md) +- Основные сервисы Azure +- Compute, Storage, Database, Networking сервисы +- Security и Monitoring сервисы +- Лучшие практики использования Azure + +### 4. [GCP (Google Cloud Platform)](sections/04-gcp-services.md) +- Основные сервисы GCP +- Compute, Storage, Database, Networking сервисы +- Security и Monitoring сервисы +- Лучшие практики использования GCP + +### 5. [Cloud-Native архитектура](sections/05-cloud-native-architecture.md) +- Принципы Cloud-Native разработки +- Микросервисы и контейнеризация +- Serverless архитектура +- Cloud-Native паттерны + +### 6. [Мультиоблачные и гибридные стратегии](sections/06-multi-cloud-hybrid-strategies.md) +- Мультиоблачная архитектура +- Гибридная архитектура +- Инструменты для мультиоблака +- Стратегии миграции + +### 7. [Стратегии миграции](sections/07-migration-strategies.md) +- Типы миграции (Lift and Shift, Replatforming, Refactoring, Rebuilding) +- Процесс миграции +- Инструменты миграции +- Best practices + +### 8. [Экономика облака и калькулятор затрат](sections/08-cloud-economics-cost-calculator.md) +- Модели ценообразования +- Компоненты затрат +- Оптимизация затрат +- TCO анализ + +## Ключевые компетенции + +После изучения модуля вы сможете: + +- **Понимать облачные технологии** и их применение в различных сценариях +- **Работать с основными облачными платформами** (AWS, Azure, GCP) +- **Проектировать Cloud-Native архитектуры** с использованием современных паттернов +- **Планировать и выполнять миграцию** существующих систем в облако +- **Оптимизировать затраты** на облачные ресурсы +- **Выбирать подходящую стратегию** для мультиоблачных и гибридных решений +- **Использовать инструменты** для управления облачной инфраструктурой + +## Практические задания + +### Задание 1: Облачная инфраструктура +1. Создайте VPC с публичными и приватными подсетями +2. Настройте Security Groups и Load Balancer +3. Разверните веб-приложение с базой данных + +### Задание 2: Cloud-Native приложение +1. Создайте микросервисную архитектуру +2. Настройте Kubernetes кластер +3. Реализуйте мониторинг и логирование + +### Задание 3: Миграция приложения +1. Проанализируйте существующее приложение +2. Выберите стратегию миграции +3. Выполните миграцию с минимальными рисками + +### Задание 4: Оптимизация затрат +1. Проанализируйте текущие затраты +2. Выберите подходящие типы инстансов +3. Настройте автоматическое масштабирование + +## Дополнительные ресурсы + +### Инструменты +- **AWS**: AWS CLI, CloudFormation, Terraform +- **Azure**: Azure CLI, ARM Templates, Terraform +- **GCP**: gcloud CLI, Deployment Manager, Terraform +- **Kubernetes**: kubectl, Helm, Istio + +### Книги +- "Cloud Native Patterns" by Cornelia Davis +- "Building Microservices" by Sam Newman +- "Kubernetes: Up and Running" by Kelsey Hightower + +### Сертификации +- AWS Certified Solutions Architect +- Microsoft Azure Solutions Architect +- Google Cloud Professional Cloud Architect + +## Критерии оценки + +- **Понимание облачных концепций**: Знание основных принципов и моделей +- **Практические навыки**: Умение работать с облачными платформами +- **Архитектурное мышление**: Способность проектировать cloud-native решения +- **Миграционные навыки**: Планирование и выполнение миграции +- **Оптимизация затрат**: Умение оптимизировать облачные ресурсы diff --git a/modules/module8/sections/01-cloud-fundamentals.md b/modules/module8/sections/01-cloud-fundamentals.md new file mode 100644 index 0000000..f94c948 --- /dev/null +++ b/modules/module8/sections/01-cloud-fundamentals.md @@ -0,0 +1,701 @@ +# Основы облачных технологий + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +## Что такое облачные вычисления + +**Облачные вычисления** — модель предоставления вычислительных ресурсов через интернет по требованию. + +## Модели обслуживания + +### 1. IaaS (Infrastructure as a Service) +**Определение:** Предоставление виртуальной инфраструктуры. + +**Примеры:** +- AWS EC2 +- Azure Virtual Machines +- Google Compute Engine + +**Преимущества:** +- Полный контроль над инфраструктурой +- Гибкость в выборе ОС и ПО +- Масштабируемость + +**Технический пример - AWS EC2 Infrastructure as Code:** + +```yaml +# terraform/main.tf +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.aws_region +} + +# VPC Configuration +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "${var.project_name}-vpc" + Environment = var.environment + } +} + +# Internet Gateway +resource "aws_internet_gateway" "main" { + vpc_id = aws_vpc.main.id + + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Subnets +resource "aws_subnet" "public" { + count = length(var.availability_zones) + + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 1}.0/24" + availability_zone = var.availability_zones[count.index] + map_public_ip_on_launch = true + + tags = { + Name = "${var.project_name}-public-subnet-${count.index + 1}" + Type = "Public" + } +} + +# Private Subnets +resource "aws_subnet" "private" { + count = length(var.availability_zones) + + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 10}.0/24" + availability_zone = var.availability_zones[count.index] + + tags = { + Name = "${var.project_name}-private-subnet-${count.index + 1}" + Type = "Private" + } +} + +# Security Groups +resource "aws_security_group" "web" { + name_prefix = "${var.project_name}-web-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = [var.admin_cidr] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.project_name}-web-sg" + } +} + +resource "aws_security_group" "app" { + name_prefix = "${var.project_name}-app-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 8080 + to_port = 8080 + protocol = "tcp" + security_groups = [aws_security_group.web.id] + } + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = [var.admin_cidr] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.project_name}-app-sg" + } +} + +# Application Load Balancer +resource "aws_lb" "main" { + name = "${var.project_name}-alb" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.web.id] + subnets = aws_subnet.public[*].id + + enable_deletion_protection = false + + tags = { + Name = "${var.project_name}-alb" + } +} + +# Target Group +resource "aws_lb_target_group" "app" { + name = "${var.project_name}-tg" + port = 8080 + protocol = "HTTP" + vpc_id = aws_vpc.main.id + + health_check { + enabled = true + healthy_threshold = 2 + unhealthy_threshold = 2 + timeout = 5 + interval = 30 + path = "/health" + matcher = "200" + port = "traffic-port" + protocol = "HTTP" + } + + tags = { + Name = "${var.project_name}-tg" + } +} + +# Launch Template +resource "aws_launch_template" "app" { + name_prefix = "${var.project_name}-" + image_id = var.ami_id + instance_type = var.instance_type + + vpc_security_group_ids = [aws_security_group.app.id] + + user_data = base64encode(templatefile("${path.module}/user_data.sh", { + app_name = var.project_name + environment = var.environment + })) + + tag_specifications { + resource_type = "instance" + tags = { + Name = "${var.project_name}-instance" + Environment = var.environment + } + } + + lifecycle { + create_before_destroy = true + } +} + +# Auto Scaling Group +resource "aws_autoscaling_group" "app" { + name = "${var.project_name}-asg" + vpc_zone_identifier = aws_subnet.private[*].id + target_group_arns = [aws_lb_target_group.app.arn] + health_check_type = "ELB" + health_check_grace_period = 300 + + min_size = var.min_size + max_size = var.max_size + desired_capacity = var.desired_capacity + + launch_template { + id = aws_launch_template.app.id + version = "$Latest" + } + + tag { + key = "Name" + value = "${var.project_name}-asg" + propagate_at_launch = false + } + + tag { + key = "Environment" + value = var.environment + propagate_at_launch = true + } +} + +# Auto Scaling Policies +resource "aws_autoscaling_policy" "scale_up" { + name = "${var.project_name}-scale-up" + scaling_adjustment = 1 + adjustment_type = "ChangeInCapacity" + cooldown = 300 + autoscaling_group_name = aws_autoscaling_group.app.name +} + +resource "aws_autoscaling_policy" "scale_down" { + name = "${var.project_name}-scale-down" + scaling_adjustment = -1 + adjustment_type = "ChangeInCapacity" + cooldown = 300 + autoscaling_group_name = aws_autoscaling_group.app.name +} + +# CloudWatch Alarms +resource "aws_cloudwatch_metric_alarm" "cpu_high" { + alarm_name = "${var.project_name}-cpu-high" + comparison_operator = "GreaterThanThreshold" + evaluation_periods = "2" + metric_name = "CPUUtilization" + namespace = "AWS/EC2" + period = "300" + statistic = "Average" + threshold = "80" + alarm_description = "This metric monitors ec2 cpu utilization" + alarm_actions = [aws_autoscaling_policy.scale_up.arn] + + dimensions = { + AutoScalingGroupName = aws_autoscaling_group.app.name + } +} + +resource "aws_cloudwatch_metric_alarm" "cpu_low" { + alarm_name = "${var.project_name}-cpu-low" + comparison_operator = "LessThanThreshold" + evaluation_periods = "2" + metric_name = "CPUUtilization" + namespace = "AWS/EC2" + period = "300" + statistic = "Average" + threshold = "20" + alarm_description = "This metric monitors ec2 cpu utilization" + alarm_actions = [aws_autoscaling_policy.scale_down.arn] + + dimensions = { + AutoScalingGroupName = aws_autoscaling_group.app.name + } +} + +# RDS Database +resource "aws_db_subnet_group" "main" { + name = "${var.project_name}-db-subnet-group" + subnet_ids = aws_subnet.private[*].id + + tags = { + Name = "${var.project_name}-db-subnet-group" + } +} + +resource "aws_db_instance" "main" { + identifier = "${var.project_name}-db" + + engine = "postgres" + engine_version = "14.7" + instance_class = var.db_instance_class + + allocated_storage = 20 + max_allocated_storage = 100 + storage_type = "gp2" + storage_encrypted = true + + db_name = var.db_name + username = var.db_username + password = var.db_password + + vpc_security_group_ids = [aws_security_group.db.id] + db_subnet_group_name = aws_db_subnet_group.main.name + + backup_retention_period = 7 + backup_window = "03:00-04:00" + maintenance_window = "sun:04:00-sun:05:00" + + skip_final_snapshot = var.environment != "production" + + tags = { + Name = "${var.project_name}-db" + Environment = var.environment + } +} + +resource "aws_security_group" "db" { + name_prefix = "${var.project_name}-db-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 5432 + to_port = 5432 + protocol = "tcp" + security_groups = [aws_security_group.app.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.project_name}-db-sg" + } +} +``` + +```bash +# user_data.sh +#!/bin/bash +yum update -y + +# Install Docker +yum install -y docker +systemctl start docker +systemctl enable docker +usermod -a -G docker ec2-user + +# Install Docker Compose +curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +chmod +x /usr/local/bin/docker-compose + +# Create application directory +mkdir -p /opt/${app_name} +cd /opt/${app_name} + +# Create docker-compose.yml +cat > docker-compose.yml << EOF +version: '3.8' +services: + app: + image: ${app_name}:latest + ports: + - "8080:8080" + environment: + - SPRING_PROFILES_ACTIVE=${environment} + - DB_HOST=${db_host} + - DB_PORT=5432 + - DB_NAME=${db_name} + - DB_USERNAME=${db_username} + - DB_PASSWORD=${db_password} + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s +EOF + +# Start application +docker-compose up -d +``` + +#### 2. PaaS (Platform as a Service) +**Определение:** Предоставление платформы для разработки и развертывания приложений. + +**Примеры:** +- AWS Elastic Beanstalk +- Azure App Service +- Google App Engine + +**Преимущества:** +- Упрощенное развертывание +- Автоматическое масштабирование +- Управление инфраструктурой провайдером + +**Технический пример - AWS Elastic Beanstalk:** + +```yaml +# .ebextensions/01-packages.config +packages: + yum: + git: [] + htop: [] + +option_settings: + aws:elasticbeanstalk:container:nodejs: + NodeCommand: "npm start" + NodeVersion: 18.19.0 + aws:elasticbeanstalk:application:environment: + NODE_ENV: production + PORT: 8080 + aws:elasticbeanstalk:environment:process:default: + HealthCheckPath: /health + Port: 8080 + Protocol: HTTP + aws:autoscaling:launchconfiguration: + InstanceType: t3.medium + SecurityGroups: default + aws:elasticbeanstalk:environment: + LoadBalancerType: application + ServiceRole: aws-elasticbeanstalk-service-role + aws:elbv2:loadbalancer: + IdleTimeout: 60 + aws:autoscaling:asg: + MinSize: 1 + MaxSize: 10 + aws:autoscaling:trigger: + MeasureName: CPUUtilization + Unit: Percent + LowerThreshold: 20 + UpperThreshold: 80 + ScaleUpIncrement: 1 + ScaleDownIncrement: -1 +``` + +```json +// package.json +{ + "name": "my-app", + "version": "1.0.0", + "description": "My Node.js application", + "main": "app.js", + "scripts": { + "start": "node app.js", + "dev": "nodemon app.js", + "test": "jest" + }, + "dependencies": { + "express": "^4.18.2", + "cors": "^2.8.5", + "helmet": "^7.1.0", + "morgan": "^1.10.0", + "compression": "^1.7.4", + "dotenv": "^16.3.1" + }, + "devDependencies": { + "nodemon": "^3.0.2", + "jest": "^29.7.0" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + } +} +``` + +```javascript +// app.js +const express = require('express'); +const cors = require('cors'); +const helmet = require('helmet'); +const morgan = require('morgan'); +const compression = require('compression'); +require('dotenv').config(); + +const app = express(); +const PORT = process.env.PORT || 8080; + +// Middleware +app.use(helmet()); +app.use(cors()); +app.use(compression()); +app.use(morgan('combined')); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +// Health check endpoint +app.get('/health', (req, res) => { + res.status(200).json({ + status: 'healthy', + timestamp: new Date().toISOString(), + uptime: process.uptime(), + environment: process.env.NODE_ENV || 'development' + }); +}); + +// API routes +app.get('/api/status', (req, res) => { + res.json({ + message: 'API is running', + version: '1.0.0', + environment: process.env.NODE_ENV || 'development' + }); +}); + +// Error handling +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + error: 'Something went wrong!', + message: process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message + }); +}); + +// 404 handler +app.use('*', (req, res) => { + res.status(404).json({ + error: 'Not found', + message: 'The requested resource was not found' + }); +}); + +app.listen(PORT, '0.0.0.0', () => { + console.log(`Server is running on port ${PORT}`); + console.log(`Environment: ${process.env.NODE_ENV || 'development'}`); +}); +``` + +#### 3. SaaS (Software as a Service) +**Определение:** Предоставление готового программного обеспечения через интернет. + +**Примеры:** +- Salesforce +- Microsoft 365 +- Google Workspace + +**Преимущества:** +- Готовое решение +- Обновления от провайдера +- Нет необходимости в установке + +### Модели развертывания + +#### 1. Public Cloud +- Ресурсы принадлежат провайдеру +- Общие с другими клиентами +- Примеры: AWS, Azure, GCP + +#### 2. Private Cloud +- Ресурсы принадлежат организации +- Изолированная инфраструктура +- Примеры: VMware vCloud, OpenStack + +#### 3. Hybrid Cloud +- Комбинация public и private cloud +- Гибкость в выборе размещения данных +- Примеры: AWS Outposts, Azure Arc + +#### 4. Multi-Cloud +- Использование нескольких провайдеров +- Избежание vendor lock-in +- Примеры: AWS + Azure + GCP + +### Ключевые характеристики облачных вычислений + +#### 1. On-Demand Self-Service +- Автоматическое предоставление ресурсов +- Самообслуживание через портал или API + +#### 2. Broad Network Access +- Доступ через стандартные сетевые протоколы +- Поддержка различных устройств + +#### 3. Resource Pooling +- Объединение ресурсов для множества клиентов +- Динамическое перераспределение + +#### 4. Rapid Elasticity +- Быстрое масштабирование ресурсов +- Автоматическое управление нагрузкой + +#### 5. Measured Service +- Мониторинг и измерение использования +- Прозрачность затрат + +### Преимущества облачных вычислений + +#### 1. Экономические преимущества +- **CAPEX vs OPEX**: Переход от капитальных к операционным расходам +- **Экономия масштаба**: Снижение стоимости за счет больших объемов +- **Pay-as-you-go**: Оплата только за используемые ресурсы + +#### 2. Технические преимущества +- **Масштабируемость**: Горизонтальное и вертикальное масштабирование +- **Надежность**: Высокая доступность и отказоустойчивость +- **Безопасность**: Профессиональная защита данных + +#### 3. Операционные преимущества +- **Быстрое развертывание**: Ускорение time-to-market +- **Автоматизация**: Уменьшение ручной работы +- **Глобальное присутствие**: Развертывание в разных регионах + +### Риски и вызовы + +#### 1. Безопасность +- **Data Breaches**: Утечки данных +- **Compliance**: Соответствие регулятивным требованиям +- **Access Control**: Управление доступом + +#### 2. Vendor Lock-in +- **Dependency**: Зависимость от провайдера +- **Migration**: Сложность миграции +- **Standards**: Отсутствие стандартов + +#### 3. Управление затратами +- **Cost Visibility**: Видимость затрат +- **Resource Optimization**: Оптимизация ресурсов +- **Budget Control**: Контроль бюджета + +### Стратегии миграции в облако + +#### 1. Lift and Shift +- Перенос приложений без изменений +- Быстрая миграция +- Минимальные риски + +#### 2. Replatforming +- Небольшие изменения для облака +- Улучшение производительности +- Баланс скорости и оптимизации + +#### 3. Refactoring +- Переписывание для облака +- Максимальная оптимизация +- Долгосрочные преимущества + +#### 4. Rebuilding +- Полная перестройка +- Cloud-native архитектура +- Максимальная эффективность + +### Инструменты и технологии + +#### 1. Infrastructure as Code (IaC) +- **Terraform**: Multi-cloud IaC +- **CloudFormation**: AWS-specific +- **ARM Templates**: Azure-specific + +#### 2. Containerization +- **Docker**: Контейнеризация приложений +- **Kubernetes**: Оркестрация контейнеров +- **Helm**: Управление пакетами + +#### 3. Monitoring and Observability +- **CloudWatch**: AWS мониторинг +- **Azure Monitor**: Azure мониторинг +- **Stackdriver**: GCP мониторинг + +#### 4. Security +- **IAM**: Управление доступом +- **VPC**: Виртуальные сети +- **Encryption**: Шифрование данных diff --git a/modules/module8/sections/02-aws-services.md b/modules/module8/sections/02-aws-services.md new file mode 100644 index 0000000..6a60b27 --- /dev/null +++ b/modules/module8/sections/02-aws-services.md @@ -0,0 +1,561 @@ +# AWS (Amazon Web Services) + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +## Основные сервисы AWS + +### 1. Compute Services + +#### EC2 (Elastic Compute Cloud) +- Виртуальные серверы в облаке +- Различные типы инстансов (t3, m5, c5, r5, etc.) +- Автомасштабирование с Auto Scaling Groups +- Spot Instances для экономии + +```yaml +# EC2 Instance Configuration +Resources: + WebServerInstance: + Type: AWS::EC2::Instance + Properties: + ImageId: ami-0c02fb55956c7d316 # Amazon Linux 2 + InstanceType: t3.micro + KeyName: my-key-pair + SecurityGroupIds: + - !Ref WebServerSecurityGroup + UserData: + Fn::Base64: !Sub | + #!/bin/bash + yum update -y + yum install -y docker + systemctl start docker + systemctl enable docker + usermod -a -G docker ec2-user + docker run -d -p 80:80 nginx + Tags: + - Key: Name + Value: WebServer + - Key: Environment + Value: Production +``` + +#### Lambda (Serverless Computing) +- Выполнение кода без управления серверами +- Оплата только за время выполнения +- Автоматическое масштабирование +- Интеграция с другими AWS сервисами + +```python +# Lambda Function Example +import json +import boto3 + +def lambda_handler(event, context): + """ + AWS Lambda function для обработки заказов + """ + # Получение данных из события + order_data = event.get('order', {}) + + # Валидация данных + if not validate_order(order_data): + return { + 'statusCode': 400, + 'body': json.dumps({'error': 'Invalid order data'}) + } + + # Обработка заказа + order_id = process_order(order_data) + + # Отправка уведомления через SNS + sns = boto3.client('sns') + sns.publish( + TopicArn='arn:aws:sns:us-east-1:123456789012:order-notifications', + Message=f'Order {order_id} has been processed', + Subject='Order Processed' + ) + + return { + 'statusCode': 200, + 'body': json.dumps({ + 'orderId': order_id, + 'status': 'processed' + }) + } + +def validate_order(order_data): + """Валидация данных заказа""" + required_fields = ['customerId', 'items', 'total'] + return all(field in order_data for field in required_fields) + +def process_order(order_data): + """Обработка заказа""" + # Логика обработки заказа + return f"ORD-{order_data.get('customerId')}-{int(time.time())}" +``` + +#### Elastic Beanstalk +- Платформа для развертывания приложений +- Автоматическое управление инфраструктурой +- Поддержка различных языков и фреймворков + +```yaml +# Elastic Beanstalk Configuration +Resources: + WebAppEnvironment: + Type: AWS::ElasticBeanstalk::Environment + Properties: + ApplicationName: !Ref WebApplication + EnvironmentName: !Sub "${AWS::StackName}-env" + SolutionStackName: "64bit Amazon Linux 2 v3.4.0 running Docker" + VersionLabel: !Ref ApplicationVersion + OptionSettings: + - Namespace: aws:autoscaling:launchconfiguration + OptionName: InstanceType + Value: t3.micro + - Namespace: aws:autoscaling:asg + OptionName: MinSize + Value: "1" + - Namespace: aws:autoscaling:asg + OptionName: MaxSize + Value: "3" +``` + +### 2. Storage Services + +#### S3 (Simple Storage Service) +- Объектное хранилище для файлов +- Высокая доступность и долговечность +- Различные классы хранения +- Интеграция с другими AWS сервисами + +```yaml +# S3 Bucket Configuration +Resources: + MyBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: my-app-bucket + VersioningConfiguration: + Status: Enabled + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + LifecycleConfiguration: + Rules: + - Id: TransitionToIA + Status: Enabled + Transitions: + - StorageClass: STANDARD_IA + TransitionInDays: 30 + - Id: DeleteOldVersions + Status: Enabled + NoncurrentVersionTransitions: + - StorageClass: GLACIER + TransitionInDays: 30 + NoncurrentVersionExpirationInDays: 90 +``` + +#### EBS (Elastic Block Store) +- Блочное хранилище для EC2 инстансов +- Высокая производительность +- Различные типы томов (gp3, io2, st1, sc1) + +```yaml +# EBS Volume Configuration +Resources: + MyVolume: + Type: AWS::EC2::Volume + Properties: + Size: 100 + VolumeType: gp3 + Iops: 3000 + Throughput: 125 + Encrypted: true + AvailabilityZone: !GetAZs[0] + Tags: + - Key: Name + Value: MyDataVolume +``` + +#### Glacier +- Архивное хранилище для долгосрочного хранения +- Низкая стоимость +- Различные классы доступа + +### 3. Database Services + +#### RDS (Relational Database Service) +- Управляемые реляционные базы данных +- Поддержка MySQL, PostgreSQL, Oracle, SQL Server +- Автоматические бэкапы и патчи +- Multi-AZ для высокой доступности + +```yaml +# RDS Configuration +Resources: + MyDatabase: + Type: AWS::RDS::DBInstance + Properties: + DBInstanceIdentifier: my-database + DBInstanceClass: db.t3.micro + Engine: postgres + EngineVersion: "13.7" + MasterUsername: admin + MasterUserPassword: !Ref DatabasePassword + AllocatedStorage: 20 + StorageType: gp2 + VPCSecurityGroups: + - !Ref DatabaseSecurityGroup + DBSubnetGroupName: !Ref DBSubnetGroup + BackupRetentionPeriod: 7 + MultiAZ: true + StorageEncrypted: true + DeletionProtection: true +``` + +#### DynamoDB +- NoSQL база данных +- Управляемый сервис +- Автоматическое масштабирование +- Глобальные таблицы + +```yaml +# DynamoDB Table Configuration +Resources: + MyTable: + Type: AWS::DynamoDB::Table + Properties: + TableName: my-table + AttributeDefinitions: + - AttributeName: id + AttributeType: S + - AttributeName: timestamp + AttributeType: N + KeySchema: + - AttributeName: id + KeyType: HASH + - AttributeName: timestamp + KeyType: RANGE + BillingMode: PAY_PER_REQUEST + StreamSpecification: + StreamViewType: NEW_AND_OLD_IMAGES + GlobalSecondaryIndexes: + - IndexName: GSI1 + KeySchema: + - AttributeName: status + KeyType: HASH + Projection: + ProjectionType: ALL +``` + +#### Redshift +- Data warehouse для аналитики +- Columnar storage +- Масштабирование кластера + +### 4. Networking Services + +#### VPC (Virtual Private Cloud) +- Изолированная виртуальная сеть +- Полный контроль над сетевой конфигурацией +- Интеграция с on-premises инфраструктурой + +```yaml +# VPC Configuration +Resources: + MyVPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + EnableDnsHostnames: true + EnableDnsSupport: true + Tags: + - Key: Name + Value: MyVPC + + PublicSubnet: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref MyVPC + CidrBlock: 10.0.1.0/24 + AvailabilityZone: !Select [0, !GetAZs ''] + MapPublicIpOnLaunch: true + Tags: + - Key: Name + Value: PublicSubnet + + PrivateSubnet: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref MyVPC + CidrBlock: 10.0.2.0/24 + AvailabilityZone: !Select [1, !GetAZs ''] + Tags: + - Key: Name + Value: PrivateSubnet + + InternetGateway: + Type: AWS::EC2::InternetGateway + Properties: + Tags: + - Key: Name + Value: MyIGW + + InternetGatewayAttachment: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + InternetGatewayId: !Ref InternetGateway + VpcId: !Ref MyVPC +``` + +#### CloudFront +- CDN для глобального распространения контента +- Интеграция с S3 и другими источниками +- SSL/TLS сертификаты + +```yaml +# CloudFront Distribution +Resources: + MyDistribution: + Type: AWS::CloudFront::Distribution + Properties: + DistributionConfig: + Origins: + - Id: S3Origin + DomainName: !GetAtt MyBucket.RegionalDomainName + S3OriginConfig: + OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/${MyOriginAccessIdentity}' + DefaultCacheBehavior: + TargetOriginId: S3Origin + ViewerProtocolPolicy: redirect-to-https + AllowedMethods: [GET, HEAD] + CachedMethods: [GET, HEAD] + ForwardedValues: + QueryString: false + Cookies: + Forward: none + Enabled: true + DefaultRootObject: index.html + PriceClass: PriceClass_100 +``` + +### 5. Security Services + +#### IAM (Identity and Access Management) +- Управление пользователями и доступом +- Роли и политики +- MFA и временные credentials + +```yaml +# IAM Role Configuration +Resources: + EC2Role: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: ec2.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: S3Access + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - s3:GetObject + - s3:PutObject + Resource: !Sub '${MyBucket}/*' + ManagedPolicyArns: + - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy + + EC2InstanceProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Roles: + - !Ref EC2Role +``` + +#### KMS (Key Management Service) +- Управление ключами шифрования +- Шифрование данных +- Интеграция с другими сервисами + +### 6. Monitoring and Logging + +#### CloudWatch +- Мониторинг ресурсов и приложений +- Логи и метрики +- Алерты и автоматические действия + +```yaml +# CloudWatch Alarms +Resources: + HighCPUAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + AlarmName: HighCPUUtilization + AlarmDescription: Alarm when CPU exceeds 80% + MetricName: CPUUtilization + Namespace: AWS/EC2 + Statistic: Average + Period: 300 + EvaluationPeriods: 2 + Threshold: 80 + ComparisonOperator: GreaterThanThreshold + Dimensions: + - Name: InstanceId + Value: !Ref MyInstance + AlarmActions: + - !Ref ScaleUpPolicy + + LowCPUAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + AlarmName: LowCPUUtilization + AlarmDescription: Alarm when CPU is below 20% + MetricName: CPUUtilization + Namespace: AWS/EC2 + Statistic: Average + Period: 300 + EvaluationPeriods: 2 + Threshold: 20 + ComparisonOperator: LessThanThreshold + Dimensions: + - Name: InstanceId + Value: !Ref MyInstance + AlarmActions: + - !Ref ScaleDownPolicy +``` + +#### CloudTrail +- Аудит API вызовов +- Логирование действий пользователей +- Соответствие требованиям + +### 7. Application Services + +#### API Gateway +- Управление API +- Аутентификация и авторизация +- Rate limiting и throttling + +```yaml +# API Gateway Configuration +Resources: + MyAPI: + Type: AWS::ApiGateway::RestApi + Properties: + Name: MyAPI + Description: My API Gateway + EndpointConfiguration: + Types: + - REGIONAL + + MyResource: + Type: AWS::ApiGateway::Resource + Properties: + RestApiId: !Ref MyAPI + ParentId: !GetAtt MyAPI.RootResourceId + PathPart: myresource + + MyMethod: + Type: AWS::ApiGateway::Method + Properties: + RestApiId: !Ref MyAPI + ResourceId: !Ref MyResource + HttpMethod: GET + AuthorizationType: NONE + Integration: + Type: AWS_PROXY + IntegrationHttpMethod: POST + Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaFunction.Arn}/invocations' +``` + +#### SNS (Simple Notification Service) +- Уведомления и сообщения +- Push, SMS, Email +- Интеграция с приложениями + +```yaml +# SNS Topic Configuration +Resources: + MyTopic: + Type: AWS::SNS::Topic + Properties: + TopicName: my-topic + DisplayName: My Topic + Subscription: + - Protocol: email + Endpoint: admin@example.com + - Protocol: sqs + Endpoint: !GetAtt MyQueue.Arn +``` + +#### SQS (Simple Queue Service) +- Очереди сообщений +- Асинхронная обработка +- Dead letter queues + +```yaml +# SQS Queue Configuration +Resources: + MyQueue: + Type: AWS::SQS::Queue + Properties: + QueueName: my-queue + VisibilityTimeoutSeconds: 30 + MessageRetentionPeriod: 1209600 + ReceiveMessageWaitTimeSeconds: 20 + RedrivePolicy: + deadLetterTargetArn: !GetAtt MyDeadLetterQueue.Arn + maxReceiveCount: 3 + + MyDeadLetterQueue: + Type: AWS::SQS::Queue + Properties: + QueueName: my-dlq + MessageRetentionPeriod: 1209600 +``` + +## Лучшие практики AWS + +### 1. Security +- Используйте IAM роли вместо ключей доступа +- Включайте шифрование для всех данных +- Регулярно ротируйте ключи +- Используйте VPC для изоляции ресурсов + +### 2. Cost Optimization +- Используйте Spot Instances для не критичных workload +- Настройте Lifecycle policies для S3 +- Мониторьте использование ресурсов +- Используйте Reserved Instances для стабильных workload + +### 3. Monitoring +- Настройте CloudWatch алерты +- Используйте CloudTrail для аудита +- Мониторьте затраты с помощью Cost Explorer +- Настройте автоматическое масштабирование + +### 4. Backup and Recovery +- Включите автоматические бэкапы для RDS +- Используйте Cross-Region replication для критичных данных +- Тестируйте процедуры восстановления +- Документируйте disaster recovery планы + +### 5. Networking +- Используйте приватные подсети для баз данных +- Настройте NAT Gateway для исходящего трафика +- Используйте VPC Endpoints для AWS сервисов +- Настройте Security Groups с минимальными правами diff --git a/modules/module8/sections/03-azure-services.md b/modules/module8/sections/03-azure-services.md new file mode 100644 index 0000000..bb16a63 --- /dev/null +++ b/modules/module8/sections/03-azure-services.md @@ -0,0 +1,412 @@ +# Azure (Microsoft Azure) + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +## Основные сервисы Azure + +### 1. Compute Services + +#### Virtual Machines +- Виртуальные серверы в облаке +- Различные серии (B, D, E, F, G, H, L, M, N) +- Автомасштабирование с Virtual Machine Scale Sets +- Spot VMs для экономии + +```yaml +# Azure VM Configuration +apiVersion: 2021-03-01 +type: Microsoft.Compute/virtualMachines +properties: + hardwareProfile: + vmSize: Standard_B1s + storageProfile: + imageReference: + publisher: Canonical + offer: UbuntuServer + sku: 18.04-LTS + version: latest + osDisk: + createOption: FromImage + managedDisk: + storageAccountType: Standard_LRS + osProfile: + computerName: my-vm + adminUsername: azureuser + adminPassword: !Ref AdminPassword + networkProfile: + networkInterfaces: + - id: !Ref NetworkInterface +``` + +#### Azure Functions +- Serverless вычисления +- Поддержка различных языков +- Интеграция с другими Azure сервисами + +```javascript +// Azure Function Example +module.exports = async function (context, req) { + context.log('Processing order request'); + + const order = req.body; + + // Валидация заказа + if (!order || !order.customerId || !order.items) { + context.res = { + status: 400, + body: { error: 'Invalid order data' } + }; + return; + } + + // Обработка заказа + const orderId = await processOrder(order); + + // Отправка уведомления + context.bindings.outputQueue = { + orderId: orderId, + status: 'processed', + timestamp: new Date().toISOString() + }; + + context.res = { + status: 200, + body: { + orderId: orderId, + status: 'processed' + } + }; +}; + +async function processOrder(order) { + // Логика обработки заказа + return `ORD-${order.customerId}-${Date.now()}`; +} +``` + +#### App Service +- Платформа для веб-приложений +- Поддержка различных языков и фреймворков +- Автоматическое масштабирование + +```yaml +# App Service Configuration +apiVersion: 2021-02-01 +type: Microsoft.Web/sites +properties: + name: my-app + location: East US + kind: app + properties: + serverFarmId: !Ref AppServicePlan + siteConfig: + appSettings: + - name: WEBSITE_NODE_DEFAULT_VERSION + value: "18.0.0" + - name: NODE_ENV + value: "production" + alwaysOn: true + httpLoggingEnabled: true + detailedErrorLoggingEnabled: true +``` + +### 2. Storage Services + +#### Blob Storage +- Объектное хранилище для файлов +- Различные уровни доступа (Hot, Cool, Archive) +- Интеграция с CDN + +```yaml +# Azure Storage Account +apiVersion: 2021-02-01 +type: Microsoft.Storage/storageAccounts +properties: + sku: + name: Standard_LRS + kind: StorageV2 + accessTier: Hot + supportsHttpsTrafficOnly: true + minimumTlsVersion: TLS1_2 + allowBlobPublicAccess: false + networkAcls: + defaultAction: Deny + bypass: AzureServices +``` + +#### Managed Disks +- Управляемые диски для VM +- Различные типы (Premium SSD, Standard SSD, Standard HDD) +- Автоматические бэкапы + +### 3. Database Services + +#### Azure SQL Database +- Управляемая реляционная БД +- Автоматические бэкапы и патчи +- Elastic pools для оптимизации затрат + +```yaml +# Azure SQL Database +apiVersion: 2021-02-01 +type: Microsoft.Sql/servers +properties: + administratorLogin: admin + administratorLoginPassword: !Ref AdminPassword + version: 12.0 + publicNetworkAccess: Disabled + minimalTlsVersion: 1.2 + databases: + - name: my-database + properties: + collation: SQL_Latin1_General_CP1_CI_AS + maxSizeBytes: 10737418240 + requestedServiceObjectiveName: S0 +``` + +#### Cosmos DB +- NoSQL база данных +- Глобальное распределение +- Множественные API (SQL, MongoDB, Cassandra, etc.) + +```yaml +# Cosmos DB Configuration +apiVersion: 2021-03-15 +type: Microsoft.DocumentDB/databaseAccounts +properties: + databaseAccountOfferType: Standard + locations: + - locationName: East US + failoverPriority: 0 + isZoneRedundant: false + capabilities: + - name: EnableServerless + consistencyPolicy: + defaultConsistencyLevel: Session + enableAutomaticFailover: false +``` + +### 4. Networking Services + +#### Virtual Network +- Изолированная виртуальная сеть +- Подсети и маршрутизация +- Интеграция с on-premises + +```yaml +# Azure Virtual Network +apiVersion: 2021-02-01 +type: Microsoft.Network/virtualNetworks +properties: + addressSpace: + addressPrefixes: + - 10.0.0.0/16 + subnets: + - name: default + properties: + addressPrefix: 10.0.1.0/24 + networkSecurityGroup: + id: !Ref NetworkSecurityGroup + - name: database + properties: + addressPrefix: 10.0.2.0/24 + networkSecurityGroup: + id: !Ref DatabaseSecurityGroup +``` + +#### Load Balancer +- Распределение нагрузки +- Health checks +- SSL termination + +```yaml +# Azure Load Balancer +apiVersion: 2021-02-01 +type: Microsoft.Network/loadBalancers +properties: + frontendIPConfigurations: + - name: LoadBalancerFrontEnd + properties: + publicIPAddress: + id: !Ref PublicIP + backendAddressPools: + - name: BackendPool + loadBalancingRules: + - name: LBRule + properties: + frontendIPConfiguration: + id: !Ref LoadBalancerFrontEnd + backendAddressPool: + id: !Ref BackendPool + protocol: Tcp + frontendPort: 80 + backendPort: 80 + enableFloatingIP: false + idleTimeoutInMinutes: 4 +``` + +### 5. Security Services + +#### Azure Active Directory +- Управление идентификацией +- Single Sign-On (SSO) +- Multi-Factor Authentication (MFA) + +#### Key Vault +- Управление секретами и ключами +- Шифрование данных +- Аудит доступа + +```yaml +# Azure Key Vault +apiVersion: 2021-06-01-preview +type: Microsoft.KeyVault/vaults +properties: + sku: + family: A + name: standard + tenantId: !Ref TenantId + accessPolicies: + - tenantId: !Ref TenantId + objectId: !Ref UserObjectId + permissions: + keys: [get, list, create, delete, update, import, backup, restore, recover] + secrets: [get, list, set, delete, backup, restore, recover] + certificates: [get, list, create, delete, update, import, backup, restore, managecontacts, manageissuers, getissuers, listissuers, setissuers, deleteissuers] + enabledForDeployment: false + enabledForDiskEncryption: false + enabledForTemplateDeployment: false + enableSoftDelete: true + softDeleteRetentionInDays: 90 + enableRbacAuthorization: false +``` + +### 6. Monitoring and Logging + +#### Azure Monitor +- Мониторинг ресурсов и приложений +- Логи и метрики +- Алерты и автоматические действия + +```yaml +# Azure Monitor Alert +apiVersion: 2021-02-01 +type: Microsoft.Insights/metricAlerts +properties: + description: Alert when CPU exceeds 80% + severity: 2 + enabled: true + scopes: + - !Ref VirtualMachine + criteria: + metricName: Percentage CPU + operator: GreaterThan + threshold: 80 + timeAggregation: Average + actions: + - actionGroupId: !Ref ActionGroup +``` + +#### Application Insights +- APM (Application Performance Monitoring) +- Трассировка запросов +- Анализ производительности + +### 7. Application Services + +#### API Management +- Управление API +- Аутентификация и авторизация +- Rate limiting и throttling + +```yaml +# API Management +apiVersion: 2021-08-01 +type: Microsoft.ApiManagement/service +properties: + sku: + name: Developer + capacity: 1 + publisherName: My Company + publisherEmail: admin@mycompany.com + notificationSenderEmail: notifications@mycompany.com + hostnameConfigurations: + - type: Proxy + hostname: myapi.azure-api.net + defaultSslBinding: true +``` + +#### Service Bus +- Очереди сообщений +- Topics и Subscriptions +- Dead letter queues + +```yaml +# Service Bus Namespace +apiVersion: 2021-06-01-preview +type: Microsoft.ServiceBus/namespaces +properties: + sku: + name: Standard + tier: Standard + location: East US + tags: + Environment: Production +``` + +## Лучшие практики Azure + +### 1. Resource Groups +- Организуйте ресурсы по проектам или средам +- Используйте теги для категоризации +- Применяйте принцип least privilege + +```yaml +# Resource Group Organization +Resources: + ProductionRG: + Type: Microsoft.Resources/resourceGroups + Properties: + location: East US + tags: + Environment: Production + CostCenter: IT + Owner: Development Team + + DevelopmentRG: + Type: Microsoft.Resources/resourceGroups + Properties: + location: East US + tags: + Environment: Development + CostCenter: IT + Owner: Development Team +``` + +### 2. Security +- Используйте Azure AD для аутентификации +- Включайте шифрование для всех данных +- Настройте Network Security Groups +- Используйте Private Endpoints для чувствительных сервисов + +### 3. Monitoring +- Настройте Azure Monitor алерты +- Используйте Application Insights для приложений +- Мониторьте затраты с помощью Cost Management +- Настройте автоматическое масштабирование + +### 4. Backup and Recovery +- Включите автоматические бэкапы +- Используйте Azure Site Recovery для disaster recovery +- Тестируйте процедуры восстановления +- Документируйте recovery procedures + +### 5. Networking +- Используйте приватные подсети для баз данных +- Настройте NAT Gateway для исходящего трафика +- Используйте Private Endpoints для Azure сервисов +- Настройте NSG с минимальными правами diff --git a/modules/module8/sections/04-gcp-services.md b/modules/module8/sections/04-gcp-services.md new file mode 100644 index 0000000..6aa791f --- /dev/null +++ b/modules/module8/sections/04-gcp-services.md @@ -0,0 +1,507 @@ +# GCP (Google Cloud Platform) + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +## Основные сервисы GCP + +### 1. Compute Services + +#### Compute Engine +- Виртуальные машины в облаке +- Различные типы машин (e2, n2, c2, m2, etc.) +- Автомасштабирование с Managed Instance Groups +- Preemptible VMs для экономии + +```yaml +# GCP VM Instance +resources: +- name: my-vm + type: compute.v1.instance + properties: + zone: us-central1-a + machineType: zones/us-central1-a/machineTypes/e2-micro + disks: + - deviceName: boot + type: PERSISTENT + boot: true + autoDelete: true + initializeParams: + sourceImage: projects/ubuntu-os-cloud/global/images/family/ubuntu-2004-lts + diskSizeGb: 20 + networkInterfaces: + - network: global/networks/default + accessConfigs: + - name: External NAT + type: ONE_TO_ONE_NAT + metadata: + items: + - key: startup-script + value: | + #!/bin/bash + apt-get update + apt-get install -y docker.io + systemctl start docker + systemctl enable docker + usermod -a -G docker $USER + docker run -d -p 80:80 nginx + tags: + items: + - web-server +``` + +#### Cloud Functions +- Serverless вычисления +- Поддержка различных языков +- Интеграция с другими GCP сервисами + +```javascript +// Cloud Function Example +const { Storage } = require('@google-cloud/storage'); +const storage = new Storage(); + +exports.processOrder = async (req, res) => { + try { + const order = req.body; + + // Валидация заказа + if (!order || !order.customerId || !order.items) { + res.status(400).json({ error: 'Invalid order data' }); + return; + } + + // Обработка заказа + const orderId = await processOrder(order); + + // Сохранение в Cloud Storage + const bucket = storage.bucket('my-orders-bucket'); + const file = bucket.file(`orders/${orderId}.json`); + await file.save(JSON.stringify(order)); + + // Отправка уведомления через Pub/Sub + const { PubSub } = require('@google-cloud/pubsub'); + const pubsub = new PubSub(); + const topic = pubsub.topic('order-notifications'); + await topic.publishMessage({ + data: Buffer.from(JSON.stringify({ + orderId: orderId, + status: 'processed', + timestamp: new Date().toISOString() + })) + }); + + res.status(200).json({ + orderId: orderId, + status: 'processed' + }); + } catch (error) { + console.error('Error processing order:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}; + +async function processOrder(order) { + // Логика обработки заказа + return `ORD-${order.customerId}-${Date.now()}`; +} +``` + +#### App Engine +- Платформа для веб-приложений +- Автоматическое масштабирование +- Поддержка различных языков + +```yaml +# app.yaml +runtime: nodejs18 +service: my-service +instance_class: F2 +automatic_scaling: + min_instances: 1 + max_instances: 10 + target_cpu_utilization: 0.6 +env_variables: + NODE_ENV: production + DATABASE_URL: "postgresql://user:password@/dbname?host=/cloudsql/project:region:instance" +handlers: +- url: /.* + script: auto + secure: always +``` + +### 2. Storage Services + +#### Cloud Storage +- Объектное хранилище для файлов +- Различные классы хранения (Standard, Nearline, Coldline, Archive) +- Интеграция с CDN + +```yaml +# GCP Cloud Storage Bucket +resources: +- name: my-bucket + type: storage.v1.bucket + properties: + location: US + storageClass: STANDARD + versioning: + enabled: true + lifecycle: + rule: + - action: + type: SetStorageClass + storageClass: NEARLINE + condition: + age: 30 + - action: + type: SetStorageClass + storageClass: COLDLINE + condition: + age: 90 + - action: + type: Delete + condition: + age: 365 + cors: + - origin: ["https://example.com"] + method: ["GET", "POST"] + responseHeader: ["Content-Type"] + maxAgeSeconds: 3600 +``` + +#### Persistent Disk +- Блочное хранилище для VM +- Различные типы (SSD, HDD) +- Автоматические снимки + +```yaml +# Persistent Disk +resources: +- name: my-disk + type: compute.v1.disk + properties: + zone: us-central1-a + sizeGb: 100 + type: zones/us-central1-a/diskTypes/pd-ssd + sourceImage: projects/ubuntu-os-cloud/global/images/family/ubuntu-2004-lts +``` + +### 3. Database Services + +#### Cloud SQL +- Управляемые реляционные БД +- Поддержка MySQL, PostgreSQL, SQL Server +- Автоматические бэкапы и патчи + +```yaml +# GCP Cloud SQL +resources: +- name: my-database + type: sqladmin.v1beta4.instance + properties: + name: my-database + region: us-central1 + settings: + tier: db-f1-micro + dataDiskType: PD_SSD + dataDiskSizeGb: 20 + backupConfiguration: + enabled: true + startTime: "03:00" + binaryLogEnabled: true + ipConfiguration: + ipv4Enabled: true + authorizedNetworks: + - value: "0.0.0.0/0" + name: "all" + locationPreference: + zone: us-central1-a + maintenanceWindow: + day: 7 + hour: 3 + databaseVersion: POSTGRES_13 + rootPassword: !Ref DatabasePassword +``` + +#### Firestore +- NoSQL база данных +- Автоматическое масштабирование +- Offline поддержка + +```yaml +# Firestore Database +resources: +- name: my-firestore + type: firestore.googleapis.com/Database + properties: + locationId: us-central1 + type: FIRESTORE_NATIVE + concurrencyMode: OPTIMISTIC +``` + +#### BigQuery +- Data warehouse для аналитики +- Columnar storage +- SQL queries + +```yaml +# BigQuery Dataset +resources: +- name: my-dataset + type: bigquery.v2.dataset + properties: + datasetReference: + datasetId: my_dataset + location: US + description: My BigQuery dataset +``` + +### 4. Networking Services + +#### Virtual Private Cloud (VPC) +- Изолированная виртуальная сеть +- Подсети и маршрутизация +- Интеграция с on-premises + +```yaml +# VPC Network +resources: +- name: my-vpc + type: compute.v1.network + properties: + autoCreateSubnetworks: false + routingConfig: + routingMode: REGIONAL + +- name: my-subnet + type: compute.v1.subnetwork + properties: + network: !Ref my-vpc + ipCidrRange: 10.0.0.0/24 + region: us-central1 + privateIpGoogleAccess: true +``` + +#### Cloud Load Balancing +- Распределение нагрузки +- Health checks +- SSL termination + +```yaml +# HTTP Load Balancer +resources: +- name: my-load-balancer + type: compute.v1.urlMap + properties: + name: my-load-balancer + defaultService: !Ref backend-service + hostRules: + - hosts: ["example.com"] + pathMatcher: path-matcher + pathMatchers: + - name: path-matcher + defaultService: !Ref backend-service + pathRules: + - paths: ["/api/*"] + service: !Ref api-backend-service + +- name: backend-service + type: compute.v1.backendService + properties: + name: backend-service + backends: + - group: !Ref instance-group + healthChecks: + - !Ref health-check + loadBalancingScheme: EXTERNAL + protocol: HTTP + portName: http +``` + +### 5. Security Services + +#### Cloud Identity and Access Management (IAM) +- Управление пользователями и доступом +- Роли и политики +- Service accounts + +```yaml +# IAM Service Account +resources: +- name: my-service-account + type: iam.v1.serviceAccount + properties: + accountId: my-service-account + displayName: My Service Account + description: Service account for my application + +- name: my-service-account-key + type: iam.v1.serviceAccountKey + properties: + name: !Ref my-service-account + keyAlgorithm: KEY_ALG_RSA_1024 + privateKeyType: TYPE_GOOGLE_CREDENTIALS_FILE +``` + +#### Secret Manager +- Управление секретами +- Шифрование данных +- Аудит доступа + +```yaml +# Secret Manager Secret +resources: +- name: my-secret + type: secretmanager.v1.secret + properties: + secretId: my-secret + replication: + automatic: {} +``` + +### 6. Monitoring and Logging + +#### Cloud Monitoring +- Мониторинг ресурсов и приложений +- Логи и метрики +- Алерты и автоматические действия + +```yaml +# Monitoring Alert Policy +resources: +- name: my-alert-policy + type: monitoring.v3.alertPolicy + properties: + displayName: High CPU Usage + conditions: + - displayName: CPU usage is high + conditionThreshold: + filter: 'resource.type="gce_instance" AND metric.type="compute.googleapis.com/instance/cpu/utilization"' + comparison: COMPARISON_GREATER_THAN + thresholdValue: 0.8 + duration: 300s + alertStrategy: + autoClose: 604800s + enabled: true + notificationChannels: + - !Ref notification-channel +``` + +#### Cloud Logging +- Централизованное логирование +- Структурированные логи +- Интеграция с мониторингом + +### 7. Application Services + +#### Cloud Endpoints +- Управление API +- Аутентификация и авторизация +- Rate limiting + +```yaml +# Cloud Endpoints Service +resources: +- name: my-endpoints-service + type: endpoints.googleapis.com/Service + properties: + serviceName: my-api.endpoints.my-project.cloud.goog + openapi: | + swagger: "2.0" + info: + title: My API + version: "1.0.0" + paths: + /api/v1/orders: + post: + summary: Create order + operationId: createOrder + parameters: + - name: order + in: body + required: true + schema: + $ref: "#/definitions/Order" + responses: + 200: + description: Order created + schema: + $ref: "#/definitions/OrderResponse" + definitions: + Order: + type: object + properties: + customerId: + type: string + items: + type: array + items: + type: string + OrderResponse: + type: object + properties: + orderId: + type: string + status: + type: string +``` + +#### Pub/Sub +- Очереди сообщений +- Topics и Subscriptions +- Dead letter topics + +```yaml +# Pub/Sub Topic +resources: +- name: my-topic + type: pubsub.v1.topic + properties: + name: my-topic + messageRetentionDuration: 604800s + +- name: my-subscription + type: pubsub.v1.subscription + properties: + name: my-subscription + topic: !Ref my-topic + ackDeadlineSeconds: 60 + messageRetentionDuration: 604800s + deadLetterPolicy: + deadLetterTopic: !Ref dead-letter-topic + maxDeliveryAttempts: 5 +``` + +## Лучшие практики GCP + +### 1. Project Organization +- Используйте проекты для изоляции ресурсов +- Применяйте labels для категоризации +- Используйте folders для иерархии + +### 2. Security +- Используйте IAM для управления доступом +- Включайте шифрование для всех данных +- Настройте VPC firewall rules +- Используйте Private Google Access + +### 3. Monitoring +- Настройте Cloud Monitoring алерты +- Используйте Cloud Logging для централизованного логирования +- Мониторьте затраты с помощью Billing API +- Настройте автоматическое масштабирование + +### 4. Backup and Recovery +- Включите автоматические снимки дисков +- Используйте Cloud Storage для долгосрочного хранения +- Тестируйте процедуры восстановления +- Документируйте disaster recovery планы + +### 5. Networking +- Используйте приватные подсети для баз данных +- Настройте Cloud NAT для исходящего трафика +- Используйте Private Google Access для сервисов +- Настройте firewall rules с минимальными правами diff --git a/modules/module8/sections/05-cloud-native-architecture.md b/modules/module8/sections/05-cloud-native-architecture.md new file mode 100644 index 0000000..86cd329 --- /dev/null +++ b/modules/module8/sections/05-cloud-native-architecture.md @@ -0,0 +1,721 @@ +# Cloud-Native архитектура + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +## Принципы Cloud-Native + +### 1. Microservices +- Разбиение монолитных приложений на независимые сервисы +- Каждый сервис отвечает за одну бизнес-функцию +- Независимое развертывание и масштабирование + +```yaml +# Kubernetes Deployment для микросервиса +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service + labels: + app: user-service +spec: + replicas: 3 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + spec: + containers: + - name: user-service + image: user-service:v1.0.0 + ports: + - containerPort: 8080 + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: user-service-secrets + key: database-url + - name: REDIS_URL + valueFrom: + configMapKeyRef: + name: user-service-config + key: redis-url + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 +``` + +### 2. Containerization +- Упаковка приложений в контейнеры +- Изоляция зависимостей +- Портабельность между средами + +```dockerfile +# Dockerfile для Node.js приложения +FROM node:18-alpine + +# Установка рабочей директории +WORKDIR /app + +# Копирование package.json и package-lock.json +COPY package*.json ./ + +# Установка зависимостей +RUN npm ci --only=production + +# Копирование исходного кода +COPY . . + +# Создание пользователя для безопасности +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nextjs -u 1001 + +# Изменение владельца файлов +RUN chown -R nextjs:nodejs /app +USER nextjs + +# Открытие порта +EXPOSE 3000 + +# Команда запуска +CMD ["npm", "start"] +``` + +### 3. Orchestration +- Управление контейнерами в кластере +- Автоматическое масштабирование +- Отказоустойчивость + +```yaml +# Kubernetes Service +apiVersion: v1 +kind: Service +metadata: + name: user-service + labels: + app: user-service +spec: + selector: + app: user-service + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + type: ClusterIP + +--- +# Kubernetes Ingress +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: user-service-ingress + annotations: + kubernetes.io/ingress.class: nginx + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/ssl-redirect: "true" +spec: + tls: + - hosts: + - api.example.com + secretName: user-service-tls + rules: + - host: api.example.com + http: + paths: + - path: /users + pathType: Prefix + backend: + service: + name: user-service + port: + number: 80 +``` + +### 4. API-First Design +- Приоритет API в архитектуре +- Версионирование API +- Документирование через OpenAPI + +```yaml +# OpenAPI Specification +openapi: 3.0.0 +info: + title: User Service API + version: 1.0.0 + description: API for managing users +servers: + - url: https://api.example.com/v1 + description: Production server +paths: + /users: + get: + summary: Get all users + operationId: getUsers + parameters: + - name: limit + in: query + schema: + type: integer + default: 10 + - name: offset + in: query + schema: + type: integer + default: 0 + responses: + '200': + description: List of users + content: + application/json: + schema: + type: object + properties: + users: + type: array + items: + $ref: '#/components/schemas/User' + total: + type: integer + post: + summary: Create a new user + operationId: createUser + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserRequest' + responses: + '201': + description: User created + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + schemas: + User: + type: object + properties: + id: + type: string + format: uuid + email: + type: string + format: email + name: + type: string + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + CreateUserRequest: + type: object + required: + - email + - name + properties: + email: + type: string + format: email + name: + type: string + password: + type: string + minLength: 8 + Error: + type: object + properties: + code: + type: string + message: + type: string + details: + type: string +``` + +### 5. Event-Driven Architecture +- Асинхронная обработка событий +- Loose coupling между сервисами +- Event sourcing и CQRS + +```javascript +// Event-driven микросервис на Node.js +const express = require('express'); +const { PubSub } = require('@google-cloud/pubsub'); +const { v4: uuidv4 } = require('uuid'); + +const app = express(); +const pubsub = new PubSub(); +const topic = pubsub.topic('order-events'); + +app.use(express.json()); + +// Обработка создания заказа +app.post('/orders', async (req, res) => { + try { + const order = { + id: uuidv4(), + customerId: req.body.customerId, + items: req.body.items, + total: req.body.total, + status: 'created', + createdAt: new Date().toISOString() + }; + + // Сохранение заказа в базе данных + await saveOrder(order); + + // Публикация события + await topic.publishMessage({ + data: Buffer.from(JSON.stringify({ + type: 'OrderCreated', + orderId: order.id, + customerId: order.customerId, + total: order.total, + timestamp: order.createdAt + })) + }); + + res.status(201).json(order); + } catch (error) { + console.error('Error creating order:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Обработка событий +app.post('/events/order-created', async (req, res) => { + try { + const event = req.body; + + if (event.type === 'OrderCreated') { + // Отправка email уведомления + await sendOrderConfirmationEmail(event.customerId, event.orderId); + + // Обновление инвентаря + await updateInventory(event.items); + + // Обновление статуса заказа + await updateOrderStatus(event.orderId, 'confirmed'); + } + + res.status(200).json({ status: 'processed' }); + } catch (error) { + console.error('Error processing event:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +async function saveOrder(order) { + // Логика сохранения заказа +} + +async function sendOrderConfirmationEmail(customerId, orderId) { + // Логика отправки email +} + +async function updateInventory(items) { + // Логика обновления инвентаря +} + +async function updateOrderStatus(orderId, status) { + // Логика обновления статуса заказа +} + +app.listen(3000, () => { + console.log('Order service listening on port 3000'); +}); +``` + +## Cloud-Native Patterns + +### 1. Circuit Breaker +- Защита от каскадных сбоев +- Автоматическое восстановление +- Fallback механизмы + +```javascript +// Circuit Breaker implementation +class CircuitBreaker { + constructor(service, options = {}) { + this.service = service; + this.failureThreshold = options.failureThreshold || 5; + this.timeout = options.timeout || 60000; + this.resetTimeout = options.resetTimeout || 30000; + this.state = 'CLOSED'; + this.failureCount = 0; + this.lastFailureTime = null; + } + + async call(...args) { + if (this.state === 'OPEN') { + if (Date.now() - this.lastFailureTime > this.resetTimeout) { + this.state = 'HALF_OPEN'; + } else { + throw new Error('Circuit breaker is OPEN'); + } + } + + try { + const result = await this.service(...args); + this.onSuccess(); + return result; + } catch (error) { + this.onFailure(); + throw error; + } + } + + onSuccess() { + this.failureCount = 0; + this.state = 'CLOSED'; + } + + onFailure() { + this.failureCount++; + this.lastFailureTime = Date.now(); + + if (this.failureCount >= this.failureThreshold) { + this.state = 'OPEN'; + } + } +} + +// Использование Circuit Breaker +const userService = async (userId) => { + // Вызов внешнего сервиса + const response = await fetch(`https://user-service/api/users/${userId}`); + if (!response.ok) { + throw new Error('User service error'); + } + return response.json(); +}; + +const circuitBreaker = new CircuitBreaker(userService, { + failureThreshold: 3, + resetTimeout: 30000 +}); + +// Использование в API endpoint +app.get('/users/:id', async (req, res) => { + try { + const user = await circuitBreaker.call(req.params.id); + res.json(user); + } catch (error) { + res.status(500).json({ error: 'Service unavailable' }); + } +}); +``` + +### 2. Retry Pattern +- Повторные попытки при временных сбоях +- Exponential backoff +- Jitter для избежания thundering herd + +```javascript +// Retry pattern implementation +class RetryPolicy { + constructor(options = {}) { + this.maxAttempts = options.maxAttempts || 3; + this.baseDelay = options.baseDelay || 1000; + this.maxDelay = options.maxDelay || 10000; + this.backoffMultiplier = options.backoffMultiplier || 2; + this.jitter = options.jitter || true; + } + + async execute(operation) { + let lastError; + + for (let attempt = 1; attempt <= this.maxAttempts; attempt++) { + try { + return await operation(); + } catch (error) { + lastError = error; + + if (attempt === this.maxAttempts) { + throw error; + } + + const delay = this.calculateDelay(attempt); + await this.sleep(delay); + } + } + + throw lastError; + } + + calculateDelay(attempt) { + let delay = this.baseDelay * Math.pow(this.backoffMultiplier, attempt - 1); + delay = Math.min(delay, this.maxDelay); + + if (this.jitter) { + delay = delay * (0.5 + Math.random() * 0.5); + } + + return delay; + } + + sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} + +// Использование Retry Policy +const retryPolicy = new RetryPolicy({ + maxAttempts: 3, + baseDelay: 1000, + maxDelay: 5000, + backoffMultiplier: 2, + jitter: true +}); + +app.get('/external-data', async (req, res) => { + try { + const data = await retryPolicy.execute(async () => { + const response = await fetch('https://external-api.com/data'); + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + return response.json(); + }); + + res.json(data); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch external data' }); + } +}); +``` + +### 3. Bulkhead Pattern +- Изоляция ресурсов +- Предотвращение каскадных сбоев +- Независимое масштабирование + +```javascript +// Bulkhead pattern implementation +class BulkheadPool { + constructor(options = {}) { + this.maxConcurrency = options.maxConcurrency || 10; + this.queue = []; + this.active = 0; + } + + async execute(operation) { + return new Promise((resolve, reject) => { + this.queue.push({ operation, resolve, reject }); + this.process(); + }); + } + + async process() { + if (this.active >= this.maxConcurrency || this.queue.length === 0) { + return; + } + + this.active++; + const { operation, resolve, reject } = this.queue.shift(); + + try { + const result = await operation(); + resolve(result); + } catch (error) { + reject(error); + } finally { + this.active--; + this.process(); + } + } +} + +// Создание отдельных пулов для разных операций +const databasePool = new BulkheadPool({ maxConcurrency: 5 }); +const externalApiPool = new BulkheadPool({ maxConcurrency: 3 }); +const fileProcessingPool = new BulkheadPool({ maxConcurrency: 2 }); + +// Использование в API endpoints +app.post('/orders', async (req, res) => { + try { + // Использование database pool + const order = await databasePool.execute(async () => { + return await saveOrder(req.body); + }); + + // Использование external API pool + const paymentResult = await externalApiPool.execute(async () => { + return await processPayment(order.id, order.total); + }); + + res.json({ order, payment: paymentResult }); + } catch (error) { + res.status(500).json({ error: 'Order processing failed' }); + } +}); +``` + +## Serverless Architecture + +### 1. Function as a Service (FaaS) +- Выполнение кода без управления серверами +- Автоматическое масштабирование +- Pay-per-execution модель + +```javascript +// AWS Lambda function +exports.handler = async (event, context) => { + const { Records } = event; + + for (const record of Records) { + if (record.eventSource === 'aws:s3') { + await processS3Event(record); + } else if (record.eventSource === 'aws:sqs') { + await processSQSMessage(record); + } + } + + return { statusCode: 200, body: 'Processed successfully' }; +}; + +async function processS3Event(record) { + const bucket = record.s3.bucket.name; + const key = record.s3.object.key; + + // Обработка файла из S3 + console.log(`Processing file: ${bucket}/${key}`); + + // Логика обработки файла + // ... +} + +async function processSQSMessage(record) { + const body = JSON.parse(record.body); + + // Обработка сообщения из SQS + console.log('Processing message:', body); + + // Логика обработки сообщения + // ... +} +``` + +### 2. Event-Driven Serverless +- Обработка событий через serverless функции +- Интеграция с различными источниками событий +- Асинхронная обработка + +```yaml +# Serverless Framework configuration +service: my-serverless-app + +provider: + name: aws + runtime: nodejs18.x + region: us-east-1 + environment: + DATABASE_URL: ${env:DATABASE_URL} + REDIS_URL: ${env:REDIS_URL} + +functions: + processOrder: + handler: handlers/order.handler + events: + - http: + path: /orders + method: post + cors: true + - sqs: + arn: !GetAtt OrderQueue.Arn + batchSize: 10 + environment: + QUEUE_URL: !Ref OrderQueue + + processPayment: + handler: handlers/payment.handler + events: + - sns: + topic: order-created + filterPolicy: + status: [created] + environment: + PAYMENT_API_URL: ${env:PAYMENT_API_URL} + + sendNotification: + handler: handlers/notification.handler + events: + - sns: + topic: order-processed + environment: + EMAIL_SERVICE_URL: ${env:EMAIL_SERVICE_URL} + +resources: + Resources: + OrderQueue: + Type: AWS::SQS::Queue + Properties: + QueueName: order-queue + VisibilityTimeoutSeconds: 300 + MessageRetentionPeriod: 1209600 + + OrderTopic: + Type: AWS::SNS::Topic + Properties: + TopicName: order-created +``` + +## Best Practices + +### 1. Design for Failure +- Предполагайте, что компоненты могут выйти из строя +- Реализуйте graceful degradation +- Используйте health checks и monitoring + +### 2. Stateless Services +- Не храните состояние в сервисах +- Используйте внешние хранилища для состояния +- Обеспечивайте горизонтальное масштабирование + +### 3. Configuration Management +- Выносите конфигурацию в переменные окружения +- Используйте секреты для чувствительных данных +- Применяйте принцип least privilege + +### 4. Monitoring and Observability +- Настройте comprehensive monitoring +- Используйте structured logging +- Реализуйте distributed tracing + +### 5. Security +- Применяйте defense in depth +- Используйте service mesh для безопасности +- Регулярно обновляйте зависимости diff --git a/modules/module8/sections/06-multi-cloud-hybrid-strategies.md b/modules/module8/sections/06-multi-cloud-hybrid-strategies.md new file mode 100644 index 0000000..3f602f9 --- /dev/null +++ b/modules/module8/sections/06-multi-cloud-hybrid-strategies.md @@ -0,0 +1,669 @@ +# Мультиоблачные и гибридные стратегии + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +## Мультиоблачная архитектура + +### Преимущества мультиоблачной стратегии + +#### 1. Избежание Vendor Lock-in +- Независимость от одного провайдера +- Гибкость в выборе лучших сервисов +- Снижение рисков + +#### 2. Оптимизация затрат +- Сравнение цен между провайдерами +- Использование spot instances +- Выбор наиболее экономичных решений + +#### 3. Повышение надежности +- Распределение рисков +- Географическое распределение +- Disaster recovery + +### Архитектурные паттерны + +#### 1. Cloud-Agnostic Design +- Абстракция от конкретных облачных провайдеров +- Использование стандартных технологий +- Портабельность приложений + +```yaml +# Kubernetes deployment для мультиоблака +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service + labels: + app: user-service +spec: + replicas: 3 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + spec: + containers: + - name: user-service + image: user-service:v1.0.0 + ports: + - containerPort: 8080 + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: user-service-secrets + key: database-url + - name: CACHE_URL + valueFrom: + configMapKeyRef: + name: user-service-config + key: cache-url + - name: MESSAGE_QUEUE_URL + valueFrom: + configMapKeyRef: + name: user-service-config + key: message-queue-url + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" +``` + +#### 2. Service Mesh для мультиоблака +- Управление трафиком между облаками +- Безопасность и мониторинг +- Load balancing и failover + +```yaml +# Istio Service Mesh configuration +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: user-service +spec: + host: user-service + trafficPolicy: + loadBalancer: + simple: ROUND_ROBIN + connectionPool: + tcp: + maxConnections: 10 + http: + http1MaxPendingRequests: 10 + maxRequestsPerConnection: 2 + circuitBreaker: + consecutiveErrors: 3 + interval: 30s + baseEjectionTime: 30s + maxEjectionPercent: 50 + outlierDetection: + consecutiveErrors: 3 + interval: 30s + baseEjectionTime: 30s + maxEjectionPercent: 50 +--- +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: user-service +spec: + hosts: + - user-service + http: + - match: + - headers: + cloud: + exact: aws + route: + - destination: + host: user-service-aws + subset: v1 + - match: + - headers: + cloud: + exact: azure + route: + - destination: + host: user-service-azure + subset: v1 + - route: + - destination: + host: user-service + subset: v1 + weight: 100 +``` + +### Инструменты для мультиоблака + +#### 1. Terraform для мультиоблака +- Infrastructure as Code для разных провайдеров +- Управление ресурсами из одного места +- Версионирование инфраструктуры + +```hcl +# terraform/main.tf +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + azure = { + source = "hashicorp/azurerm" + version = "~> 3.0" + } + gcp = { + source = "hashicorp/google" + version = "~> 4.0" + } + } +} + +# AWS Resources +provider "aws" { + region = var.aws_region +} + +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "${var.project_name}-vpc" + Environment = var.environment + } +} + +resource "aws_subnet" "public" { + count = length(var.aws_availability_zones) + + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 1}.0/24" + availability_zone = var.aws_availability_zones[count.index] + map_public_ip_on_launch = true + + tags = { + Name = "${var.project_name}-public-subnet-${count.index + 1}" + Type = "Public" + } +} + +# Azure Resources +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "main" { + name = "${var.project_name}-rg" + location = var.azure_location + + tags = { + Environment = var.environment + Project = var.project_name + } +} + +resource "azurerm_virtual_network" "main" { + name = "${var.project_name}-vnet" + address_space = ["10.1.0.0/16"] + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + + tags = { + Environment = var.environment + Project = var.project_name + } +} + +resource "azurerm_subnet" "public" { + count = length(var.azure_availability_zones) + + name = "public-subnet-${count.index + 1}" + resource_group_name = azurerm_resource_group.main.name + virtual_network_name = azurerm_virtual_network.main.name + address_prefixes = ["10.1.${count.index + 1}.0/24"] +} + +# GCP Resources +provider "google" { + project = var.gcp_project_id + region = var.gcp_region +} + +resource "google_compute_network" "main" { + name = "${var.project_name}-vpc" + auto_create_subnetworks = false + mtu = 1460 +} + +resource "google_compute_subnetwork" "public" { + count = length(var.gcp_zones) + + name = "public-subnet-${count.index + 1}" + ip_cidr_range = "10.2.${count.index + 1}.0/24" + region = var.gcp_region + network = google_compute_network.main.id +} +``` + +#### 2. Kubernetes для мультиоблака +- Единая платформа для всех облаков +- Портабельность приложений +- Управление ресурсами + +```yaml +# Kubernetes Cluster API для мультиоблака +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: multi-cloud-cluster + namespace: default +spec: + clusterNetwork: + pods: + cidrBlocks: ["192.168.0.0/16"] + services: + cidrBlocks: ["10.96.0.0/12"] + controlPlaneRef: + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + kind: KubeadmControlPlane + name: multi-cloud-cluster-control-plane + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: AWSCluster + name: multi-cloud-cluster +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineDeployment +metadata: + name: multi-cloud-cluster-md-0 + namespace: default +spec: + clusterName: multi-cloud-cluster + replicas: 3 + selector: + matchLabels: + cluster.x-k8s.io/cluster-name: multi-cloud-cluster + cluster.x-k8s.io/deployment-name: multi-cloud-cluster-md-0 + template: + spec: + clusterName: multi-cloud-cluster + bootstrap: + configRef: + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: KubeadmConfigTemplate + name: multi-cloud-cluster-md-0 + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: AWSMachineTemplate + name: multi-cloud-cluster-md-0 +``` + +## Гибридная архитектура + +### Преимущества гибридной стратегии + +#### 1. Постепенная миграция +- Минимальные риски +- Тестирование в облаке +- Поэтапный переход + +#### 2. Соответствие требованиям +- Соблюдение регулятивных требований +- Контроль над чувствительными данными +- Гибридные рабочие нагрузки + +#### 3. Оптимизация затрат +- Использование существующих инвестиций +- Выбор оптимального размещения для каждой нагрузки +- Гибкость в управлении ресурсами + +### Архитектурные паттерны + +#### 1. Hybrid Cloud Connectivity +- VPN соединения между on-premises и облаком +- Dedicated connections (AWS Direct Connect, Azure ExpressRoute) +- Secure tunnels и encryption + +```yaml +# AWS Direct Connect configuration +Resources: + DirectConnectGateway: + Type: AWS::DirectConnect::Gateway + Properties: + Name: my-dx-gateway + AmazonSideAsn: 64512 + + VirtualPrivateGateway: + Type: AWS::EC2::VPNGateway + Properties: + Type: ipsec.1 + Tags: + - Key: Name + Value: my-vpg + + VPNConnection: + Type: AWS::EC2::VPNConnection + Properties: + Type: ipsec.1 + CustomerGatewayId: !Ref CustomerGateway + VpnGatewayId: !Ref VirtualPrivateGateway + StaticRoutesOnly: false + Tags: + - Key: Name + Value: my-vpn-connection + + CustomerGateway: + Type: AWS::EC2::CustomerGateway + Properties: + BgpAsn: 65000 + IpAddress: 203.0.113.12 + Type: ipsec.1 + Tags: + - Key: Name + Value: my-customer-gateway +``` + +#### 2. Data Synchronization +- Синхронизация данных между on-premises и облаком +- Real-time и batch processing +- Conflict resolution + +```javascript +// Data synchronization service +const express = require('express'); +const { Kafka } = require('kafkajs'); +const { Pool } = require('pg'); + +const app = express(); +app.use(express.json()); + +// Database connections +const onPremisesDB = new Pool({ + host: process.env.ON_PREMISES_DB_HOST, + port: process.env.ON_PREMISES_DB_PORT, + database: process.env.ON_PREMISES_DB_NAME, + user: process.env.ON_PREMISES_DB_USER, + password: process.env.ON_PREMISES_DB_PASSWORD +}); + +const cloudDB = new Pool({ + host: process.env.CLOUD_DB_HOST, + port: process.env.CLOUD_DB_PORT, + database: process.env.CLOUD_DB_NAME, + user: process.env.CLOUD_DB_USER, + password: process.env.CLOUD_DB_PASSWORD +}); + +// Kafka configuration +const kafka = new Kafka({ + clientId: 'data-sync-service', + brokers: [process.env.KAFKA_BROKER] +}); + +const producer = kafka.producer(); +const consumer = kafka.consumer({ groupId: 'data-sync-group' }); + +// Data synchronization functions +async function syncDataToCloud(tableName, data) { + try { + // Insert or update data in cloud database + const query = ` + INSERT INTO ${tableName} (id, data, updated_at) + VALUES ($1, $2, $3) + ON CONFLICT (id) DO UPDATE SET + data = EXCLUDED.data, + updated_at = EXCLUDED.updated_at + `; + + await cloudDB.query(query, [data.id, JSON.stringify(data), new Date()]); + + console.log(`Data synced to cloud: ${tableName}/${data.id}`); + } catch (error) { + console.error('Error syncing data to cloud:', error); + throw error; + } +} + +async function syncDataToOnPremises(tableName, data) { + try { + // Insert or update data in on-premises database + const query = ` + INSERT INTO ${tableName} (id, data, updated_at) + VALUES ($1, $2, $3) + ON CONFLICT (id) DO UPDATE SET + data = EXCLUDED.data, + updated_at = EXCLUDED.updated_at + `; + + await onPremisesDB.query(query, [data.id, JSON.stringify(data), new Date()]); + + console.log(`Data synced to on-premises: ${tableName}/${data.id}`); + } catch (error) { + console.error('Error syncing data to on-premises:', error); + throw error; + } +} + +// Kafka consumer for data changes +async function startDataSync() { + await consumer.connect(); + await consumer.subscribe({ topic: 'data-changes' }); + + await consumer.run({ + eachMessage: async ({ topic, partition, message }) => { + try { + const data = JSON.parse(message.value.toString()); + + if (data.source === 'on-premises') { + await syncDataToCloud(data.table, data.payload); + } else if (data.source === 'cloud') { + await syncDataToOnPremises(data.table, data.payload); + } + } catch (error) { + console.error('Error processing message:', error); + } + } + }); +} + +// API endpoints for manual sync +app.post('/sync/on-premises-to-cloud', async (req, res) => { + try { + const { table, data } = req.body; + await syncDataToCloud(table, data); + res.json({ status: 'success' }); + } catch (error) { + res.status(500).json({ error: 'Sync failed' }); + } +}); + +app.post('/sync/cloud-to-on-premises', async (req, res) => { + try { + const { table, data } = req.body; + await syncDataToOnPremises(table, data); + res.json({ status: 'success' }); + } catch (error) { + res.status(500).json({ error: 'Sync failed' }); + } +}); + +// Start the service +async function start() { + await producer.connect(); + await startDataSync(); + + app.listen(3000, () => { + console.log('Data sync service listening on port 3000'); + }); +} + +start().catch(console.error); +``` + +#### 3. Workload Placement +- Интеллектуальное размещение рабочих нагрузок +- Автоматическое масштабирование +- Cost optimization + +```yaml +# Kubernetes scheduler для гибридного облака +apiVersion: v1 +kind: ConfigMap +metadata: + name: scheduler-config +data: + policy.cfg: | + { + "kind": "Policy", + "apiVersion": "v1", + "predicates": [ + { + "name": "NodeAffinity" + }, + { + "name": "PodToleratesNodeTaints" + }, + { + "name": "CheckNodeMemoryPressure" + }, + { + "name": "CheckNodeDiskPressure" + }, + { + "name": "CheckNodePIDPressure" + }, + { + "name": "MatchNodeSelector" + }, + { + "name": "CheckVolumeBinding" + } + ], + "priorities": [ + { + "name": "NodeAffinityPriority", + "weight": 10 + }, + { + "name": "BalancedResourceAllocation", + "weight": 1 + }, + { + "name": "LeastRequestedPriority", + "weight": 1 + }, + { + "name": "MostRequestedPriority", + "weight": 1 + }, + { + "name": "NodePreferAvoidPodsPriority", + "weight": 10000 + }, + { + "name": "NodeLabelPriority", + "weight": 10, + "argument": { + "presentLabels": ["cloud-provider"], + "absentLabels": ["on-premises"] + } + } + ] + } +--- +apiVersion: v1 +kind: Pod +metadata: + name: workload-placement-example +spec: + containers: + - name: app + image: my-app:latest + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + nodeSelector: + cloud-provider: "aws" + instance-type: "compute-optimized" + tolerations: + - key: "cloud-provider" + operator: "Equal" + value: "aws" + effect: "NoSchedule" + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud-provider + operator: In + values: ["aws", "azure"] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + preference: + matchExpressions: + - key: instance-type + operator: In + values: ["compute-optimized"] +``` + +## Стратегии миграции + +### 1. Lift and Shift +- Перенос приложений без изменений +- Быстрая миграция +- Минимальные риски + +### 2. Replatforming +- Небольшие изменения для облака +- Улучшение производительности +- Баланс скорости и оптимизации + +### 3. Refactoring +- Переписывание для облака +- Максимальная оптимизация +- Долгосрочные преимущества + +### 4. Rebuilding +- Полная перестройка +- Cloud-native архитектура +- Максимальная эффективность + +## Best Practices + +### 1. Security +- Единая политика безопасности +- Централизованное управление доступом +- Мониторинг и аудит + +### 2. Monitoring +- Единая система мониторинга +- Централизованное логирование +- Alerting и notification + +### 3. Cost Management +- Мониторинг затрат +- Оптимизация ресурсов +- Автоматическое масштабирование + +### 4. Disaster Recovery +- План восстановления +- Тестирование процедур +- Документирование процессов diff --git a/modules/module8/sections/07-migration-strategies.md b/modules/module8/sections/07-migration-strategies.md new file mode 100644 index 0000000..c22f5d8 --- /dev/null +++ b/modules/module8/sections/07-migration-strategies.md @@ -0,0 +1,672 @@ +# Стратегии миграции + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +## Типы миграции + +### 1. Lift and Shift (Rehosting) +- Перенос приложений без изменений +- Быстрая миграция +- Минимальные риски + +#### Преимущества: +- Быстрое время миграции +- Низкий риск +- Сохранение существующей функциональности + +#### Недостатки: +- Не использует преимущества облака +- Высокие операционные затраты +- Ограниченная масштабируемость + +```yaml +# Пример Lift and Shift миграции +# AWS EC2 Instance для переноса существующего приложения +Resources: + MigratedAppInstance: + Type: AWS::EC2::Instance + Properties: + ImageId: ami-0c02fb55956c7d316 # Amazon Linux 2 + InstanceType: t3.large + KeyName: my-key-pair + SecurityGroupIds: + - !Ref WebServerSecurityGroup + UserData: + Fn::Base64: !Sub | + #!/bin/bash + yum update -y + + # Установка существующего приложения + yum install -y java-1.8.0-openjdk + yum install -y tomcat8 + + # Копирование приложения + aws s3 cp s3://my-migration-bucket/app.war /var/lib/tomcat8/webapps/ + + # Настройка базы данных + aws s3 cp s3://my-migration-bucket/db-config.properties /opt/app/config/ + + # Запуск приложения + systemctl start tomcat8 + systemctl enable tomcat8 + Tags: + - Key: Name + Value: MigratedApp + - Key: MigrationType + Value: LiftAndShift +``` + +### 2. Replatforming (Rehosting with Optimization) +- Небольшие изменения для облака +- Улучшение производительности +- Баланс скорости и оптимизации + +#### Преимущества: +- Улучшенная производительность +- Снижение операционных затрат +- Лучшая интеграция с облачными сервисами + +#### Недостатки: +- Требует изменений в приложении +- Средний уровень риска +- Ограниченные возможности масштабирования + +```yaml +# Пример Replatforming миграции +# AWS Elastic Beanstalk для оптимизированного развертывания +Resources: + ReplatformedApp: + Type: AWS::ElasticBeanstalk::Application + Properties: + ApplicationName: my-replatformed-app + Description: Replatformed application with cloud optimizations + + ReplatformedEnvironment: + Type: AWS::ElasticBeanstalk::Environment + Properties: + ApplicationName: !Ref ReplatformedApp + EnvironmentName: !Sub "${AWS::StackName}-env" + SolutionStackName: "64bit Amazon Linux 2 v3.4.0 running Docker" + OptionSettings: + - Namespace: aws:autoscaling:launchconfiguration + OptionName: InstanceType + Value: t3.medium + - Namespace: aws:autoscaling:asg + OptionName: MinSize + Value: "2" + - Namespace: aws:autoscaling:asg + OptionName: MaxSize + Value: "10" + - Namespace: aws:elasticbeanstalk:environment + OptionName: LoadBalancerType + Value: application + - Namespace: aws:elasticbeanstalk:environment:process:default + OptionName: HealthCheckPath + Value: /health + - Namespace: aws:elasticbeanstalk:environment:process:default + OptionName: Port + Value: "8080" + - Namespace: aws:elasticbeanstalk:environment:process:default + OptionName: Protocol + Value: HTTP +``` + +### 3. Refactoring (Re-architecting) +- Переписывание для облака +- Максимальная оптимизация +- Долгосрочные преимущества + +#### Преимущества: +- Максимальная производительность +- Полное использование облачных возможностей +- Высокая масштабируемость + +#### Недостатки: +- Высокие затраты на разработку +- Длительное время миграции +- Высокий уровень риска + +```yaml +# Пример Refactoring миграции +# Микросервисная архитектура на Kubernetes +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service + labels: + app: user-service + version: v1 +spec: + replicas: 3 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + version: v1 + spec: + containers: + - name: user-service + image: user-service:v1.0.0 + ports: + - containerPort: 8080 + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: user-service-secrets + key: database-url + - name: REDIS_URL + valueFrom: + configMapKeyRef: + name: user-service-config + key: redis-url + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: user-service-config + key: kafka-brokers + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: user-service + labels: + app: user-service +spec: + selector: + app: user-service + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + type: ClusterIP +``` + +### 4. Rebuilding (Replace) +- Полная перестройка +- Cloud-native архитектура +- Максимальная эффективность + +#### Преимущества: +- Современная архитектура +- Максимальная производительность +- Полная интеграция с облаком + +#### Недостатки: +- Очень высокие затраты +- Длительное время разработки +- Высокий уровень риска + +## Процесс миграции + +### 1. Планирование миграции + +#### Анализ приложений +- Инвентаризация существующих приложений +- Оценка сложности миграции +- Приоритизация приложений + +```markdown +# Application Migration Assessment Template + +## Application: User Management System +**Current State:** +- Technology: Java Spring Boot +- Database: MySQL 5.7 +- Deployment: On-premises VM +- Dependencies: 15 external services + +**Migration Assessment:** +- Complexity: Medium +- Risk Level: Low +- Migration Type: Replatforming +- Estimated Effort: 4 weeks +- Dependencies: 3 other applications + +**Cloud Readiness:** +- Stateless: Yes +- Database: Compatible +- External Dependencies: 2 need migration +- Security: Compliant + +**Migration Plan:** +1. Week 1: Environment setup +2. Week 2: Database migration +3. Week 3: Application deployment +4. Week 4: Testing and cutover + +**Success Criteria:** +- 99.9% uptime +- < 200ms response time +- Zero data loss +- All functionality preserved +``` + +#### Оценка инфраструктуры +- Анализ текущей инфраструктуры +- Определение требований к облаку +- Планирование сетевой архитектуры + +```yaml +# Infrastructure Assessment Template +InfrastructureAssessment: + CurrentState: + Servers: 25 + Databases: 8 + NetworkDevices: 12 + Storage: 50TB + Bandwidth: 1Gbps + + CloudRequirements: + ComputeInstances: 30 + DatabaseInstances: 10 + Storage: 100TB + Bandwidth: 10Gbps + SecurityGroups: 15 + LoadBalancers: 5 + + MigrationStrategy: + Phase1: Core applications (8 weeks) + Phase2: Supporting services (6 weeks) + Phase3: Legacy systems (4 weeks) + Phase4: Optimization (2 weeks) +``` + +### 2. Подготовка к миграции + +#### Создание облачной инфраструктуры +- Настройка VPC и подсетей +- Конфигурация безопасности +- Подготовка баз данных + +```yaml +# Cloud Infrastructure Setup +Resources: + # VPC Configuration + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + EnableDnsHostnames: true + EnableDnsSupport: true + Tags: + - Key: Name + Value: MigrationVPC + - Key: Environment + Value: Production + + # Public Subnets + PublicSubnet1: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: 10.0.1.0/24 + AvailabilityZone: !Select [0, !GetAZs ''] + MapPublicIpOnLaunch: true + Tags: + - Key: Name + Value: PublicSubnet1 + - Key: Type + Value: Public + + PublicSubnet2: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: 10.0.2.0/24 + AvailabilityZone: !Select [1, !GetAZs ''] + MapPublicIpOnLaunch: true + Tags: + - Key: Name + Value: PublicSubnet2 + - Key: Type + Value: Public + + # Private Subnets + PrivateSubnet1: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: 10.0.10.0/24 + AvailabilityZone: !Select [0, !GetAZs ''] + Tags: + - Key: Name + Value: PrivateSubnet1 + - Key: Type + Value: Private + + PrivateSubnet2: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: 10.0.20.0/24 + AvailabilityZone: !Select [1, !GetAZs ''] + Tags: + - Key: Name + Value: PrivateSubnet2 + - Key: Type + Value: Private + + # Internet Gateway + InternetGateway: + Type: AWS::EC2::InternetGateway + Properties: + Tags: + - Key: Name + Value: MigrationIGW + + InternetGatewayAttachment: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + InternetGatewayId: !Ref InternetGateway + VpcId: !Ref VPC + + # NAT Gateway + NatGateway1: + Type: AWS::EC2::NatGateway + Properties: + AllocationId: !GetAtt NatGateway1EIP.AllocationId + SubnetId: !Ref PublicSubnet1 + Tags: + - Key: Name + Value: NatGateway1 + + NatGateway1EIP: + Type: AWS::EC2::EIP + Properties: + Domain: vpc + Tags: + - Key: Name + Value: NatGateway1EIP + + # Route Tables + PublicRouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: PublicRouteTable + + PublicRoute: + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PublicRouteTable + DestinationCidrBlock: 0.0.0.0/0 + GatewayId: !Ref InternetGateway + + PrivateRouteTable1: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: PrivateRouteTable1 + + PrivateRoute1: + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PrivateRouteTable1 + DestinationCidrBlock: 0.0.0.0/0 + NatGatewayId: !Ref NatGateway1 + + # Security Groups + WebSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupName: WebSecurityGroup + GroupDescription: Security group for web servers + VpcId: !Ref VPC + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 443 + ToPort: 443 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: 10.0.0.0/16 + Tags: + - Key: Name + Value: WebSecurityGroup + + AppSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupName: AppSecurityGroup + GroupDescription: Security group for application servers + VpcId: !Ref VPC + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 8080 + ToPort: 8080 + SourceSecurityGroupId: !Ref WebSecurityGroup + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: 10.0.0.0/16 + Tags: + - Key: Name + Value: AppSecurityGroup + + DatabaseSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupName: DatabaseSecurityGroup + GroupDescription: Security group for database servers + VpcId: !Ref VPC + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 3306 + ToPort: 3306 + SourceSecurityGroupId: !Ref AppSecurityGroup + - IpProtocol: tcp + FromPort: 5432 + ToPort: 5432 + SourceSecurityGroupId: !Ref AppSecurityGroup + Tags: + - Key: Name + Value: DatabaseSecurityGroup +``` + +#### Настройка мониторинга +- CloudWatch/CloudTrail для AWS +- Azure Monitor для Azure +- Stackdriver для GCP + +```yaml +# Monitoring Setup +Resources: + # CloudWatch Dashboard + MigrationDashboard: + Type: AWS::CloudWatch::Dashboard + Properties: + DashboardName: MigrationDashboard + DashboardBody: !Sub | + { + "widgets": [ + { + "type": "metric", + "x": 0, + "y": 0, + "width": 12, + "height": 6, + "properties": { + "metrics": [ + [ "AWS/EC2", "CPUUtilization", "InstanceId", "i-1234567890abcdef0" ], + [ ".", "NetworkIn", ".", "." ], + [ ".", "NetworkOut", ".", "." ] + ], + "view": "timeSeries", + "stacked": false, + "region": "${AWS::Region}", + "title": "EC2 Metrics", + "period": 300 + } + }, + { + "type": "metric", + "x": 12, + "y": 0, + "width": 12, + "height": 6, + "properties": { + "metrics": [ + [ "AWS/RDS", "CPUUtilization", "DBInstanceIdentifier", "mydb" ], + [ ".", "DatabaseConnections", ".", "." ], + [ ".", "FreeableMemory", ".", "." ] + ], + "view": "timeSeries", + "stacked": false, + "region": "${AWS::Region}", + "title": "RDS Metrics", + "period": 300 + } + } + ] + } + + # CloudWatch Alarms + HighCPUAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + AlarmName: HighCPUUtilization + AlarmDescription: Alarm when CPU exceeds 80% + MetricName: CPUUtilization + Namespace: AWS/EC2 + Statistic: Average + Period: 300 + EvaluationPeriods: 2 + Threshold: 80 + ComparisonOperator: GreaterThanThreshold + AlarmActions: + - !Ref ScaleUpPolicy + + LowCPUAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + AlarmName: LowCPUUtilization + AlarmDescription: Alarm when CPU is below 20% + MetricName: CPUUtilization + Namespace: AWS/EC2 + Statistic: Average + Period: 300 + EvaluationPeriods: 2 + Threshold: 20 + ComparisonOperator: LessThanThreshold + AlarmActions: + - !Ref ScaleDownPolicy +``` + +### 3. Выполнение миграции + +#### Фаза 1: Foundation (Month 1-2) +- Настройка базовой инфраструктуры +- Миграция простых приложений +- Тестирование процессов + +#### Фаза 2: Pilot Migration (Month 3-4) +- Миграция критических приложений +- Тестирование в production +- Оптимизация процессов + +#### Фаза 3: Production Migration (Month 5-8) +- Массовая миграция приложений +- Переключение трафика +- Мониторинг и оптимизация + +#### Фаза 4: Optimization (Month 9-12) +- Оптимизация производительности +- Снижение затрат +- Автоматизация процессов + +### 4. Тестирование и валидация + +#### Функциональное тестирование +- Проверка всех функций приложения +- Сравнение с оригинальной версией +- Валидация бизнес-логики + +#### Нагрузочное тестирование +- Тестирование под нагрузкой +- Проверка производительности +- Валидация масштабируемости + +#### Безопасность +- Проверка безопасности +- Валидация доступа +- Тестирование шифрования + +## Инструменты миграции + +### 1. AWS Migration Tools +- AWS Application Migration Service +- AWS Database Migration Service +- AWS Server Migration Service + +### 2. Azure Migration Tools +- Azure Migrate +- Azure Database Migration Service +- Azure Site Recovery + +### 3. GCP Migration Tools +- Migrate for Compute Engine +- Database Migration Service +- Transfer Service + +### 4. Third-party Tools +- CloudEndure +- Carbonite +- Zerto + +## Best Practices + +### 1. Планирование +- Детальное планирование миграции +- Оценка рисков и митигаций +- Подготовка rollback планов + +### 2. Тестирование +- Comprehensive testing +- Staging environment +- User acceptance testing + +### 3. Мониторинг +- Continuous monitoring +- Performance metrics +- Cost optimization + +### 4. Документация +- Документирование процессов +- Knowledge transfer +- Training team members diff --git a/modules/module8/sections/08-cloud-economics-cost-calculator.md b/modules/module8/sections/08-cloud-economics-cost-calculator.md new file mode 100644 index 0000000..35f56ea --- /dev/null +++ b/modules/module8/sections/08-cloud-economics-cost-calculator.md @@ -0,0 +1,621 @@ +# Экономика облака и калькулятор затрат + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +## Основы экономики облака + +### 1. Модели ценообразования + +#### Pay-as-you-go (PAYG) +- Оплата только за используемые ресурсы +- Гибкость в масштабировании +- Низкие начальные затраты + +```javascript +// Калькулятор затрат для Pay-as-you-go +class PayAsYouGoCalculator { + constructor() { + this.resources = { + compute: { + instances: [], + storage: [], + networking: [] + }, + database: { + instances: [], + storage: [] + }, + services: { + monitoring: [], + security: [], + backup: [] + } + }; + } + + addComputeInstance(instance) { + this.resources.compute.instances.push({ + type: instance.type, + region: instance.region, + hours: instance.hours, + pricePerHour: instance.pricePerHour, + monthlyCost: instance.hours * instance.pricePerHour * 30 + }); + } + + addStorage(storage) { + this.resources.compute.storage.push({ + type: storage.type, + size: storage.size, // GB + pricePerGB: storage.pricePerGB, + monthlyCost: storage.size * storage.pricePerGB + }); + } + + calculateMonthlyCost() { + let totalCost = 0; + + // Compute costs + totalCost += this.resources.compute.instances.reduce((sum, instance) => sum + instance.monthlyCost, 0); + totalCost += this.resources.compute.storage.reduce((sum, storage) => sum + storage.monthlyCost, 0); + + // Database costs + totalCost += this.resources.database.instances.reduce((sum, instance) => sum + instance.monthlyCost, 0); + totalCost += this.resources.database.storage.reduce((sum, storage) => sum + storage.monthlyCost, 0); + + // Services costs + totalCost += this.resources.services.monitoring.reduce((sum, service) => sum + service.monthlyCost, 0); + totalCost += this.resources.services.security.reduce((sum, service) => sum + service.monthlyCost, 0); + totalCost += this.resources.services.backup.reduce((sum, service) => sum + service.monthlyCost, 0); + + return totalCost; + } + + generateReport() { + const monthlyCost = this.calculateMonthlyCost(); + const yearlyCost = monthlyCost * 12; + + return { + monthlyCost: monthlyCost, + yearlyCost: yearlyCost, + breakdown: { + compute: this.resources.compute.instances.reduce((sum, instance) => sum + instance.monthlyCost, 0) + + this.resources.compute.storage.reduce((sum, storage) => sum + storage.monthlyCost, 0), + database: this.resources.database.instances.reduce((sum, instance) => sum + instance.monthlyCost, 0) + + this.resources.database.storage.reduce((sum, storage) => sum + storage.monthlyCost, 0), + services: this.resources.services.monitoring.reduce((sum, service) => sum + service.monthlyCost, 0) + + this.resources.services.security.reduce((sum, service) => sum + service.monthlyCost, 0) + + this.resources.services.backup.reduce((sum, service) => sum + service.monthlyCost, 0) + } + }; + } +} + +// Использование калькулятора +const calculator = new PayAsYouGoCalculator(); + +// Добавление compute instances +calculator.addComputeInstance({ + type: 't3.medium', + region: 'us-east-1', + hours: 24, + pricePerHour: 0.0416 +}); + +calculator.addComputeInstance({ + type: 't3.large', + region: 'us-east-1', + hours: 24, + pricePerHour: 0.0832 +}); + +// Добавление storage +calculator.addStorage({ + type: 'gp3', + size: 1000, // 1TB + pricePerGB: 0.08 +}); + +// Генерация отчета +const report = calculator.generateReport(); +console.log('Monthly Cost:', report.monthlyCost); +console.log('Yearly Cost:', report.yearlyCost); +console.log('Breakdown:', report.breakdown); +``` + +#### Reserved Instances +- Предоплата за 1-3 года +- Скидка до 75% +- Предсказуемые затраты + +```javascript +// Калькулятор для Reserved Instances +class ReservedInstanceCalculator { + constructor() { + this.instances = []; + } + + addReservedInstance(instance) { + this.instances.push({ + type: instance.type, + region: instance.region, + term: instance.term, // 1 or 3 years + paymentOption: instance.paymentOption, // 'No Upfront', 'Partial Upfront', 'All Upfront' + upfrontCost: instance.upfrontCost, + monthlyCost: instance.monthlyCost, + totalCost: instance.upfrontCost + (instance.monthlyCost * instance.term * 12) + }); + } + + calculateSavings(ondemandCost) { + const reservedCost = this.instances.reduce((sum, instance) => sum + instance.totalCost, 0); + const savings = ondemandCost - reservedCost; + const savingsPercentage = (savings / ondemandCost) * 100; + + return { + ondemandCost: ondemandCost, + reservedCost: reservedCost, + savings: savings, + savingsPercentage: savingsPercentage + }; + } +} + +// Пример использования +const reservedCalculator = new ReservedInstanceCalculator(); + +reservedCalculator.addReservedInstance({ + type: 't3.medium', + region: 'us-east-1', + term: 1, + paymentOption: 'No Upfront', + upfrontCost: 0, + monthlyCost: 30 +}); + +const ondemandCost = 50; // Monthly on-demand cost +const savings = reservedCalculator.calculateSavings(ondemandCost * 12); +console.log('Savings:', savings); +``` + +#### Spot Instances +- Использование неиспользуемых ресурсов +- Скидка до 90% +- Риск прерывания + +```javascript +// Калькулятор для Spot Instances +class SpotInstanceCalculator { + constructor() { + this.instances = []; + } + + addSpotInstance(instance) { + this.instances.push({ + type: instance.type, + region: instance.region, + spotPrice: instance.spotPrice, + ondemandPrice: instance.ondemandPrice, + interruptionRate: instance.interruptionRate, // Percentage + monthlyCost: instance.spotPrice * 24 * 30, + ondemandCost: instance.ondemandPrice * 24 * 30 + }); + } + + calculateSavings() { + const totalSpotCost = this.instances.reduce((sum, instance) => sum + instance.monthlyCost, 0); + const totalOndemandCost = this.instances.reduce((sum, instance) => sum + instance.ondemandCost, 0); + const savings = totalOndemandCost - totalSpotCost; + const savingsPercentage = (savings / totalOndemandCost) * 100; + + return { + spotCost: totalSpotCost, + ondemandCost: totalOndemandCost, + savings: savings, + savingsPercentage: savingsPercentage + }; + } + + calculateRisk() { + const totalInterruptionRate = this.instances.reduce((sum, instance) => sum + instance.interruptionRate, 0) / this.instances.length; + + return { + averageInterruptionRate: totalInterruptionRate, + riskLevel: totalInterruptionRate > 20 ? 'High' : totalInterruptionRate > 10 ? 'Medium' : 'Low', + recommendation: totalInterruptionRate > 20 ? 'Use for non-critical workloads only' : 'Suitable for most workloads' + }; + } +} +``` + +### 2. Компоненты затрат + +#### Compute Costs +- EC2 instances, Azure VMs, GCP Compute Engine +- CPU, Memory, Storage +- Operating System licensing + +#### Storage Costs +- Block storage (EBS, Azure Disks, GCP Persistent Disk) +- Object storage (S3, Azure Blob, GCP Cloud Storage) +- Database storage + +#### Networking Costs +- Data transfer +- Load balancers +- CDN + +#### Services Costs +- Monitoring and logging +- Security services +- Backup and disaster recovery + +### 3. Оптимизация затрат + +#### Right-sizing +- Выбор правильного размера инстансов +- Мониторинг использования ресурсов +- Автоматическое масштабирование + +```javascript +// Right-sizing рекомендации +class RightSizingAnalyzer { + constructor() { + this.metrics = []; + } + + addMetrics(instanceId, metrics) { + this.metrics.push({ + instanceId: instanceId, + cpuUtilization: metrics.cpuUtilization, + memoryUtilization: metrics.memoryUtilization, + networkUtilization: metrics.networkUtilization, + storageUtilization: metrics.storageUtilization, + timestamp: new Date() + }); + } + + analyzeUtilization() { + const recommendations = []; + + this.metrics.forEach(metric => { + const recommendation = { + instanceId: metric.instanceId, + currentUtilization: { + cpu: metric.cpuUtilization, + memory: metric.memoryUtilization, + network: metric.networkUtilization, + storage: metric.storageUtilization + }, + recommendations: [] + }; + + // CPU recommendations + if (metric.cpuUtilization < 20) { + recommendation.recommendations.push({ + type: 'CPU', + action: 'Downsize', + reason: 'CPU utilization is below 20%', + potentialSavings: '30-50%' + }); + } else if (metric.cpuUtilization > 80) { + recommendation.recommendations.push({ + type: 'CPU', + action: 'Upsize', + reason: 'CPU utilization is above 80%', + potentialCost: '20-30% increase' + }); + } + + // Memory recommendations + if (metric.memoryUtilization < 30) { + recommendation.recommendations.push({ + type: 'Memory', + action: 'Downsize', + reason: 'Memory utilization is below 30%', + potentialSavings: '25-40%' + }); + } else if (metric.memoryUtilization > 90) { + recommendation.recommendations.push({ + type: 'Memory', + action: 'Upsize', + reason: 'Memory utilization is above 90%', + potentialCost: '15-25% increase' + }); + } + + recommendations.push(recommendation); + }); + + return recommendations; + } +} +``` + +#### Reserved Instances Planning +- Анализ использования ресурсов +- Планирование покупки RI +- Оптимизация портфеля RI + +```javascript +// Reserved Instances планировщик +class ReservedInstancePlanner { + constructor() { + this.usageHistory = []; + } + + addUsageHistory(instanceType, region, hours, months) { + this.usageHistory.push({ + instanceType: instanceType, + region: region, + hours: hours, + months: months, + utilizationRate: hours / (months * 30 * 24) + }); + } + + calculateRIRecommendations() { + const recommendations = []; + + // Группировка по типу инстанса и региону + const groupedUsage = this.usageHistory.reduce((acc, usage) => { + const key = `${usage.instanceType}-${usage.region}`; + if (!acc[key]) { + acc[key] = { + instanceType: usage.instanceType, + region: usage.region, + totalHours: 0, + totalMonths: 0, + utilizationRate: 0 + }; + } + acc[key].totalHours += usage.hours; + acc[key].totalMonths += usage.months; + acc[key].utilizationRate = acc[key].totalHours / (acc[key].totalMonths * 30 * 24); + return acc; + }, {}); + + Object.values(groupedUsage).forEach(usage => { + if (usage.utilizationRate > 0.7) { // 70% utilization + recommendations.push({ + instanceType: usage.instanceType, + region: usage.region, + utilizationRate: usage.utilizationRate, + recommendation: 'Strong candidate for Reserved Instance', + potentialSavings: '30-60%', + term: usage.utilizationRate > 0.9 ? '3 years' : '1 year' + }); + } + }); + + return recommendations; + } +} +``` + +#### Spot Instances Strategy +- Анализ толерантности к прерываниям +- Выбор подходящих workload +- Гибридная стратегия + +```javascript +// Spot Instances стратегия +class SpotInstanceStrategy { + constructor() { + this.workloads = []; + } + + addWorkload(workload) { + this.workloads.push({ + name: workload.name, + type: workload.type, + criticality: workload.criticality, // 'low', 'medium', 'high' + interruptionTolerance: workload.interruptionTolerance, // 'low', 'medium', 'high' + duration: workload.duration, // hours + spotEligibility: this.calculateSpotEligibility(workload) + }); + } + + calculateSpotEligibility(workload) { + let score = 0; + + // Criticality scoring + if (workload.criticality === 'low') score += 3; + else if (workload.criticality === 'medium') score += 2; + else score += 1; + + // Interruption tolerance scoring + if (workload.interruptionTolerance === 'high') score += 3; + else if (workload.interruptionTolerance === 'medium') score += 2; + else score += 1; + + // Duration scoring (shorter is better for spot) + if (workload.duration < 1) score += 3; + else if (workload.duration < 4) score += 2; + else score += 1; + + return { + score: score, + recommendation: score >= 7 ? 'Highly recommended' : score >= 5 ? 'Recommended' : 'Not recommended', + potentialSavings: score >= 7 ? '60-90%' : score >= 5 ? '40-60%' : '20-40%' + }; + } + + generateStrategy() { + const strategy = { + spotWorkloads: [], + ondemandWorkloads: [], + hybridWorkloads: [] + }; + + this.workloads.forEach(workload => { + if (workload.spotEligibility.score >= 7) { + strategy.spotWorkloads.push(workload); + } else if (workload.spotEligibility.score >= 5) { + strategy.hybridWorkloads.push(workload); + } else { + strategy.ondemandWorkloads.push(workload); + } + }); + + return strategy; + } +} +``` + +## Калькуляторы затрат + +### 1. AWS Pricing Calculator +- Детальный расчет затрат +- Различные типы инстансов +- Региональные различия + +### 2. Azure Pricing Calculator +- Комплексный калькулятор +- Различные сервисы +- Скидки и предложения + +### 3. GCP Pricing Calculator +- Интерактивный калькулятор +- Детализация по сервисам +- Экспорт в различные форматы + +### 4. Third-party Tools +- CloudHealth +- Cloudyn +- Cloudability + +## TCO (Total Cost of Ownership) + +### 1. On-premises vs Cloud +- Сравнение затрат +- Учет всех компонентов +- Долгосрочная перспектива + +```javascript +// TCO калькулятор +class TCOCalculator { + constructor() { + this.onPremisesCosts = { + hardware: 0, + software: 0, + maintenance: 0, + power: 0, + cooling: 0, + space: 0, + personnel: 0, + security: 0, + backup: 0, + disasterRecovery: 0 + }; + + this.cloudCosts = { + compute: 0, + storage: 0, + networking: 0, + services: 0, + support: 0, + migration: 0, + training: 0 + }; + } + + setOnPremisesCosts(costs) { + this.onPremisesCosts = { ...this.onPremisesCosts, ...costs }; + } + + setCloudCosts(costs) { + this.cloudCosts = { ...this.cloudCosts, ...costs }; + } + + calculateTCO(years = 3) { + const onPremisesTotal = Object.values(this.onPremisesCosts).reduce((sum, cost) => sum + cost, 0) * years; + const cloudTotal = Object.values(this.cloudCosts).reduce((sum, cost) => sum + cost, 0) * years; + + return { + onPremises: { + total: onPremisesTotal, + yearly: onPremisesTotal / years, + breakdown: this.onPremisesCosts + }, + cloud: { + total: cloudTotal, + yearly: cloudTotal / years, + breakdown: this.cloudCosts + }, + comparison: { + difference: cloudTotal - onPremisesTotal, + percentage: ((cloudTotal - onPremisesTotal) / onPremisesTotal) * 100, + recommendation: cloudTotal < onPremisesTotal ? 'Cloud is more cost-effective' : 'On-premises is more cost-effective' + } + }; + } +} + +// Пример использования +const tcoCalculator = new TCOCalculator(); + +// On-premises costs (per year) +tcoCalculator.setOnPremisesCosts({ + hardware: 50000, + software: 20000, + maintenance: 15000, + power: 5000, + cooling: 3000, + space: 10000, + personnel: 80000, + security: 10000, + backup: 5000, + disasterRecovery: 8000 +}); + +// Cloud costs (per year) +tcoCalculator.setCloudCosts({ + compute: 30000, + storage: 5000, + networking: 2000, + services: 8000, + support: 5000, + migration: 15000, + training: 3000 +}); + +const tco = tcoCalculator.calculateTCO(3); +console.log('TCO Comparison:', tco); +``` + +### 2. ROI Analysis +- Анализ возврата инвестиций +- Временные рамки +- Риски и возможности + +### 3. Cost Optimization +- Постоянная оптимизация +- Автоматизация процессов +- Мониторинг затрат + +## Best Practices + +### 1. Cost Monitoring +- Регулярный мониторинг затрат +- Алерты при превышении бюджета +- Анализ трендов + +### 2. Resource Optimization +- Right-sizing ресурсов +- Автоматическое масштабирование +- Удаление неиспользуемых ресурсов + +### 3. Budget Management +- Установка бюджетных лимитов +- Контроль расходов +- Планирование затрат + +### 4. Cost Allocation +- Тегирование ресурсов +- Центры затрат +- Отчетность по подразделениям diff --git a/modules/module8/sections/08-practical-tasks.md b/modules/module8/sections/08-practical-tasks.md new file mode 100644 index 0000000..8a6ed31 --- /dev/null +++ b/modules/module8/sections/08-practical-tasks.md @@ -0,0 +1,24 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 8](../README.md) + +--- + +### Задание 1: Cloud Infrastructure +1. Создайте инфраструктуру в AWS/Azure/GCP +2. Разверните приложение в облаке +3. Настройте мониторинг и алерты + +### Задание 2: Migration Planning +1. Создайте план миграции для существующего приложения +2. Оцените затраты и риски +3. Разработайте стратегию тестирования + +### Задание 3: Cost Optimization +1. Проанализируйте текущие затраты +2. Предложите стратегии оптимизации +3. Реализуйте автоматическое масштабирование + +--- \ No newline at end of file diff --git a/modules/module8/theory.md b/modules/module8/theory.md new file mode 100644 index 0000000..f7060f4 --- /dev/null +++ b/modules/module8/theory.md @@ -0,0 +1,3612 @@ +# Модуль 8: Облачные технологии и миграция + +## Содержание модуля +1. [Основы облачных технологий](#основы-облачных-технологий) +2. [AWS (Amazon Web Services)](#aws-amazon-web-services) +3. [Azure (Microsoft Azure)](#azure-microsoft-azure) +4. [GCP (Google Cloud Platform)](#gcp-google-cloud-platform) +5. [Cloud-Native архитектура](#cloud-native-архитектура) +6. [Стратегии миграции](#стратегии-миграции) +7. [Cost Optimization](#cost-optimization) + +--- + +## Основы облачных технологий + +### Что такое облачные вычисления + +**Облачные вычисления** — модель предоставления вычислительных ресурсов через интернет по требованию. + +### Модели обслуживания + +#### 1. IaaS (Infrastructure as a Service) +**Определение:** Предоставление виртуальной инфраструктуры. + +**Примеры:** +- AWS EC2 +- Azure Virtual Machines +- Google Compute Engine + +**Преимущества:** +- Полный контроль над инфраструктурой +- Гибкость в выборе ОС и ПО +- Масштабируемость + +**Технический пример - AWS EC2 Infrastructure as Code:** + +```yaml +# terraform/main.tf +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.aws_region +} + +# VPC Configuration +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "${var.project_name}-vpc" + Environment = var.environment + } +} + +# Internet Gateway +resource "aws_internet_gateway" "main" { + vpc_id = aws_vpc.main.id + + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Subnets +resource "aws_subnet" "public" { + count = length(var.availability_zones) + + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 1}.0/24" + availability_zone = var.availability_zones[count.index] + map_public_ip_on_launch = true + + tags = { + Name = "${var.project_name}-public-subnet-${count.index + 1}" + Type = "Public" + } +} + +# Private Subnets +resource "aws_subnet" "private" { + count = length(var.availability_zones) + + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 10}.0/24" + availability_zone = var.availability_zones[count.index] + + tags = { + Name = "${var.project_name}-private-subnet-${count.index + 1}" + Type = "Private" + } +} + +# Security Groups +resource "aws_security_group" "web" { + name_prefix = "${var.project_name}-web-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = [var.admin_cidr] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.project_name}-web-sg" + } +} + +resource "aws_security_group" "app" { + name_prefix = "${var.project_name}-app-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 8080 + to_port = 8080 + protocol = "tcp" + security_groups = [aws_security_group.web.id] + } + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = [var.admin_cidr] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.project_name}-app-sg" + } +} + +# Application Load Balancer +resource "aws_lb" "main" { + name = "${var.project_name}-alb" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.web.id] + subnets = aws_subnet.public[*].id + + enable_deletion_protection = false + + tags = { + Name = "${var.project_name}-alb" + } +} + +# Target Group +resource "aws_lb_target_group" "app" { + name = "${var.project_name}-tg" + port = 8080 + protocol = "HTTP" + vpc_id = aws_vpc.main.id + + health_check { + enabled = true + healthy_threshold = 2 + unhealthy_threshold = 2 + timeout = 5 + interval = 30 + path = "/health" + matcher = "200" + port = "traffic-port" + protocol = "HTTP" + } + + tags = { + Name = "${var.project_name}-tg" + } +} + +# Launch Template +resource "aws_launch_template" "app" { + name_prefix = "${var.project_name}-" + image_id = var.ami_id + instance_type = var.instance_type + + vpc_security_group_ids = [aws_security_group.app.id] + + user_data = base64encode(templatefile("${path.module}/user_data.sh", { + app_name = var.project_name + environment = var.environment + })) + + tag_specifications { + resource_type = "instance" + tags = { + Name = "${var.project_name}-instance" + Environment = var.environment + } + } + + lifecycle { + create_before_destroy = true + } +} + +# Auto Scaling Group +resource "aws_autoscaling_group" "app" { + name = "${var.project_name}-asg" + vpc_zone_identifier = aws_subnet.private[*].id + target_group_arns = [aws_lb_target_group.app.arn] + health_check_type = "ELB" + health_check_grace_period = 300 + + min_size = var.min_size + max_size = var.max_size + desired_capacity = var.desired_capacity + + launch_template { + id = aws_launch_template.app.id + version = "$Latest" + } + + tag { + key = "Name" + value = "${var.project_name}-asg" + propagate_at_launch = false + } + + tag { + key = "Environment" + value = var.environment + propagate_at_launch = true + } +} + +# Auto Scaling Policies +resource "aws_autoscaling_policy" "scale_up" { + name = "${var.project_name}-scale-up" + scaling_adjustment = 1 + adjustment_type = "ChangeInCapacity" + cooldown = 300 + autoscaling_group_name = aws_autoscaling_group.app.name +} + +resource "aws_autoscaling_policy" "scale_down" { + name = "${var.project_name}-scale-down" + scaling_adjustment = -1 + adjustment_type = "ChangeInCapacity" + cooldown = 300 + autoscaling_group_name = aws_autoscaling_group.app.name +} + +# CloudWatch Alarms +resource "aws_cloudwatch_metric_alarm" "cpu_high" { + alarm_name = "${var.project_name}-cpu-high" + comparison_operator = "GreaterThanThreshold" + evaluation_periods = "2" + metric_name = "CPUUtilization" + namespace = "AWS/EC2" + period = "300" + statistic = "Average" + threshold = "80" + alarm_description = "This metric monitors ec2 cpu utilization" + alarm_actions = [aws_autoscaling_policy.scale_up.arn] + + dimensions = { + AutoScalingGroupName = aws_autoscaling_group.app.name + } +} + +resource "aws_cloudwatch_metric_alarm" "cpu_low" { + alarm_name = "${var.project_name}-cpu-low" + comparison_operator = "LessThanThreshold" + evaluation_periods = "2" + metric_name = "CPUUtilization" + namespace = "AWS/EC2" + period = "300" + statistic = "Average" + threshold = "20" + alarm_description = "This metric monitors ec2 cpu utilization" + alarm_actions = [aws_autoscaling_policy.scale_down.arn] + + dimensions = { + AutoScalingGroupName = aws_autoscaling_group.app.name + } +} + +# RDS Database +resource "aws_db_subnet_group" "main" { + name = "${var.project_name}-db-subnet-group" + subnet_ids = aws_subnet.private[*].id + + tags = { + Name = "${var.project_name}-db-subnet-group" + } +} + +resource "aws_db_instance" "main" { + identifier = "${var.project_name}-db" + + engine = "postgres" + engine_version = "14.7" + instance_class = var.db_instance_class + + allocated_storage = 20 + max_allocated_storage = 100 + storage_type = "gp2" + storage_encrypted = true + + db_name = var.db_name + username = var.db_username + password = var.db_password + + vpc_security_group_ids = [aws_security_group.db.id] + db_subnet_group_name = aws_db_subnet_group.main.name + + backup_retention_period = 7 + backup_window = "03:00-04:00" + maintenance_window = "sun:04:00-sun:05:00" + + skip_final_snapshot = var.environment != "production" + + tags = { + Name = "${var.project_name}-db" + Environment = var.environment + } +} + +resource "aws_security_group" "db" { + name_prefix = "${var.project_name}-db-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 5432 + to_port = 5432 + protocol = "tcp" + security_groups = [aws_security_group.app.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.project_name}-db-sg" + } +} +``` + +```bash +# user_data.sh +#!/bin/bash +yum update -y + +# Install Docker +yum install -y docker +systemctl start docker +systemctl enable docker +usermod -a -G docker ec2-user + +# Install Docker Compose +curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +chmod +x /usr/local/bin/docker-compose + +# Create application directory +mkdir -p /opt/${app_name} +cd /opt/${app_name} + +# Create docker-compose.yml +cat > docker-compose.yml << EOF +version: '3.8' +services: + app: + image: ${app_name}:latest + ports: + - "8080:8080" + environment: + - SPRING_PROFILES_ACTIVE=${environment} + - DB_HOST=${db_host} + - DB_PORT=5432 + - DB_NAME=${db_name} + - DB_USERNAME=${db_username} + - DB_PASSWORD=${db_password} + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + depends_on: + - app + restart: unless-stopped +EOF + +# Start application +docker-compose up -d + +# Install CloudWatch agent +yum install -y amazon-cloudwatch-agent +cat > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json << EOF +{ + "metrics": { + "namespace": "CWAgent", + "metrics_collected": { + "cpu": { + "measurement": ["cpu_usage_idle", "cpu_usage_iowait", "cpu_usage_user", "cpu_usage_system"], + "metrics_collection_interval": 60 + }, + "disk": { + "measurement": ["used_percent"], + "metrics_collection_interval": 60, + "resources": ["*"] + }, + "mem": { + "measurement": ["mem_used_percent"], + "metrics_collection_interval": 60 + } + } + } +} +EOF + +/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ + -a fetch-config \ + -m ec2 \ + -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json \ + -s +``` + +```hcl +# variables.tf +variable "aws_region" { + description = "AWS region" + type = string + default = "us-west-2" +} + +variable "project_name" { + description = "Name of the project" + type = string +} + +variable "environment" { + description = "Environment name" + type = string + default = "dev" +} + +variable "availability_zones" { + description = "List of availability zones" + type = list(string) + default = ["us-west-2a", "us-west-2b", "us-west-2c"] +} + +variable "admin_cidr" { + description = "CIDR block for admin access" + type = string + default = "0.0.0.0/0" +} + +variable "ami_id" { + description = "AMI ID for EC2 instances" + type = string + default = "ami-0c02fb55956c7d316" # Amazon Linux 2 +} + +variable "instance_type" { + description = "EC2 instance type" + type = string + default = "t3.medium" +} + +variable "min_size" { + description = "Minimum number of instances" + type = number + default = 2 +} + +variable "max_size" { + description = "Maximum number of instances" + type = number + default = 10 +} + +variable "desired_capacity" { + description = "Desired number of instances" + type = number + default = 3 +} + +variable "db_instance_class" { + description = "RDS instance class" + type = string + default = "db.t3.micro" +} + +variable "db_name" { + description = "Database name" + type = string +} + +variable "db_username" { + description = "Database username" + type = string +} + +variable "db_password" { + description = "Database password" + type = string + sensitive = true +} +``` + +#### 2. PaaS (Platform as a Service) +**Определение:** Предоставление платформы для разработки приложений. + +**Примеры:** +- AWS Elastic Beanstalk +- Azure App Service +- Google App Engine + +**Преимущества:** +- Фокус на разработке приложений +- Автоматическое управление инфраструктурой +- Встроенные сервисы + +**Технический пример - Azure App Service Deployment:** + +```yaml +# azure-pipelines.yml +trigger: +- main + +pool: + vmImage: 'ubuntu-latest' + +variables: + azureSubscription: 'your-subscription-id' + appName: 'your-app-name' + resourceGroupName: 'your-resource-group' + location: 'West Europe' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: Build + displayName: Build + steps: + - task: NodeTool@0 + inputs: + versionSpec: '18.x' + displayName: 'Install Node.js' + + - script: | + npm install + npm run build + displayName: 'Install dependencies and build' + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: '$(System.DefaultWorkingDirectory)' + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + displayName: Deploy + environment: 'production' + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Azure Web App Deploy' + inputs: + azureSubscription: '$(azureSubscription)' + appType: 'webApp' + appName: '$(appName)' + package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' + deploymentMethod: 'zipDeploy' +``` + +```json +// azure-resources.json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "appName": { + "type": "string", + "defaultValue": "my-app", + "metadata": { + "description": "Name of the App Service" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources" + } + }, + "sku": { + "type": "string", + "defaultValue": "S1", + "allowedValues": [ + "F1", + "D1", + "B1", + "B2", + "B3", + "S1", + "S2", + "S3", + "P1", + "P2", + "P3", + "P4" + ], + "metadata": { + "description": "The pricing tier for the hosting plan" + } + }, + "nodeVersion": { + "type": "string", + "defaultValue": "18-lts", + "metadata": { + "description": "The version of Node.js to use" + } + } + }, + "variables": { + "hostingPlanName": "[concat(parameters('appName'), '-plan')]", + "applicationInsightsName": "[concat(parameters('appName'), '-ai')]" + }, + "resources": [ + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2021-02-01", + "name": "[variables('hostingPlanName')]", + "location": "[parameters('location')]", + "sku": { + "name": "[parameters('sku')]" + }, + "kind": "linux", + "properties": { + "reserved": true + } + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2021-02-01", + "name": "[parameters('appName')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]" + ], + "kind": "app,linux", + "properties": { + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]", + "siteConfig": { + "linuxFxVersion": "[concat('NODE|', parameters('nodeVersion'))]", + "appSettings": [ + { + "name": "WEBSITE_NODE_DEFAULT_VERSION", + "value": "[parameters('nodeVersion')]" + }, + { + "name": "WEBSITE_RUN_FROM_PACKAGE", + "value": "1" + } + ] + } + } + }, + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[variables('applicationInsightsName')]", + "location": "[parameters('location')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "Request_Source": "rest" + } + } + ], + "outputs": { + "appUrl": { + "type": "string", + "value": "[concat('https://', reference(resourceId('Microsoft.Web/sites', parameters('appName'))).defaultHostName)]" + }, + "appServicePlanId": { + "type": "string", + "value": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]" + } + } +} +``` + +```javascript +// app.js - Express.js application for Azure App Service +const express = require('express'); +const path = require('path'); +const cors = require('cors'); +const helmet = require('helmet'); +const compression = require('compression'); +const rateLimit = require('express-rate-limit'); + +const app = express(); +const PORT = process.env.PORT || 3000; + +// Security middleware +app.use(helmet()); +app.use(compression()); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs + message: 'Too many requests from this IP, please try again later.' +}); +app.use(limiter); + +// CORS configuration +app.use(cors({ + origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'], + credentials: true +})); + +// Body parsing middleware +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); + +// Health check endpoint +app.get('/health', (req, res) => { + res.status(200).json({ + status: 'healthy', + timestamp: new Date().toISOString(), + uptime: process.uptime(), + environment: process.env.NODE_ENV || 'development', + version: process.env.npm_package_version || '1.0.0' + }); +}); + +// API routes +app.use('/api', require('./routes/api')); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + error: 'Something went wrong!', + message: process.env.NODE_ENV === 'development' ? err.message : 'Internal server error' + }); +}); + +// 404 handler +app.use('*', (req, res) => { + res.status(404).json({ + error: 'Not Found', + message: 'The requested resource was not found' + }); +}); + +// Graceful shutdown +process.on('SIGTERM', () => { + console.log('SIGTERM received, shutting down gracefully'); + server.close(() => { + console.log('Process terminated'); + }); +}); + +const server = app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); + console.log(`Environment: ${process.env.NODE_ENV || 'development'}`); +}); + +module.exports = app; +``` + +**Дополнительные Node.js примеры для облачных технологий:** + +**Node.js приложение для AWS Lambda:** +```typescript +// lambda/handler.ts +import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient, PutCommand, GetCommand } from '@aws-sdk/lib-dynamodb'; + +const dynamoClient = new DynamoDBClient({ region: process.env.AWS_REGION }); +const docClient = DynamoDBDocumentClient.from(dynamoClient); + +interface User { + id: string; + name: string; + email: string; + createdAt: string; +} + +export const handler = async ( + event: APIGatewayProxyEvent, + context: Context +): Promise => { + console.log('Event:', JSON.stringify(event, null, 2)); + console.log('Context:', JSON.stringify(context, null, 2)); + + try { + const method = event.httpMethod; + const path = event.path; + + switch (method) { + case 'POST': + if (path === '/users') { + return await createUser(event); + } + break; + case 'GET': + if (path.startsWith('/users/')) { + const userId = path.split('/')[2]; + return await getUser(userId); + } + break; + default: + return { + statusCode: 405, + body: JSON.stringify({ error: 'Method not allowed' }) + }; + } + + return { + statusCode: 404, + body: JSON.stringify({ error: 'Not found' }) + }; + } catch (error) { + console.error('Error:', error); + return { + statusCode: 500, + body: JSON.stringify({ error: 'Internal server error' }) + }; + } +}; + +async function createUser(event: APIGatewayProxyEvent): Promise { + if (!event.body) { + return { + statusCode: 400, + body: JSON.stringify({ error: 'Request body is required' }) + }; + } + + const userData = JSON.parse(event.body); + const user: User = { + id: generateId(), + name: userData.name, + email: userData.email, + createdAt: new Date().toISOString() + }; + + await docClient.send(new PutCommand({ + TableName: process.env.USERS_TABLE, + Item: user + })); + + return { + statusCode: 201, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + body: JSON.stringify(user) + }; +} + +async function getUser(userId: string): Promise { + const result = await docClient.send(new GetCommand({ + TableName: process.env.USERS_TABLE, + Key: { id: userId } + })); + + if (!result.Item) { + return { + statusCode: 404, + body: JSON.stringify({ error: 'User not found' }) + }; + } + + return { + statusCode: 200, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + body: JSON.stringify(result.Item) + }; +} + +function generateId(): string { + return Math.random().toString(36).substr(2, 9); +} +``` + +**Node.js приложение для Google Cloud Functions:** +```typescript +// functions/index.ts +import { Request, Response } from 'express'; +import { Storage } from '@google-cloud/storage'; +import { Firestore } from '@google-cloud/firestore'; + +const storage = new Storage(); +const firestore = new Firestore(); + +interface FileUploadRequest extends Request { + file?: Express.Multer.File; +} + +export const uploadFile = async (req: FileUploadRequest, res: Response): Promise => { + try { + if (!req.file) { + res.status(400).json({ error: 'No file uploaded' }); + return; + } + + const bucketName = process.env.BUCKET_NAME; + const fileName = `${Date.now()}-${req.file.originalname}`; + + // Upload to Google Cloud Storage + const bucket = storage.bucket(bucketName!); + const file = bucket.file(fileName); + + await file.save(req.file.buffer, { + metadata: { + contentType: req.file.mimetype, + metadata: { + originalName: req.file.originalname, + uploadedBy: req.body.userId || 'anonymous' + } + } + }); + + // Save metadata to Firestore + const fileMetadata = { + fileName, + originalName: req.file.originalname, + size: req.file.size, + contentType: req.file.mimetype, + uploadedAt: new Date(), + uploadedBy: req.body.userId || 'anonymous', + downloadUrl: `https://storage.googleapis.com/${bucketName}/${fileName}` + }; + + await firestore.collection('files').add(fileMetadata); + + res.status(200).json({ + message: 'File uploaded successfully', + file: fileMetadata + }); + } catch (error) { + console.error('Upload error:', error); + res.status(500).json({ error: 'Upload failed' }); + } +}; + +export const getFiles = async (req: Request, res: Response): Promise => { + try { + const userId = req.query.userId as string; + let query = firestore.collection('files').orderBy('uploadedAt', 'desc'); + + if (userId) { + query = query.where('uploadedBy', '==', userId); + } + + const snapshot = await query.limit(50).get(); + const files = snapshot.docs.map(doc => ({ + id: doc.id, + ...doc.data() + })); + + res.status(200).json({ files }); + } catch (error) { + console.error('Get files error:', error); + res.status(500).json({ error: 'Failed to retrieve files' }); + } +}; +``` + +**Node.js приложение для Azure Functions:** +```typescript +// azure-functions/index.ts +import { AzureFunction, Context, HttpRequest } from '@azure/functions'; +import { CosmosClient } from '@azure/cosmos'; + +const cosmosClient = new CosmosClient({ + endpoint: process.env.COSMOS_DB_ENDPOINT!, + key: process.env.COSMOS_DB_KEY! +}); + +const database = cosmosClient.database(process.env.COSMOS_DB_NAME!); +const container = database.container('orders'); + +interface Order { + id: string; + userId: string; + items: OrderItem[]; + total: number; + status: string; + createdAt: string; +} + +interface OrderItem { + productId: string; + quantity: number; + price: number; +} + +export const httpTrigger: AzureFunction = async ( + context: Context, + req: HttpRequest +): Promise => { + context.log('HTTP trigger function processed a request.'); + + try { + const method = req.method; + const orderId = req.params.id; + + switch (method) { + case 'POST': + await createOrder(context, req); + break; + case 'GET': + if (orderId) { + await getOrder(context, orderId); + } else { + await getOrders(context, req); + } + break; + case 'PUT': + await updateOrder(context, orderId, req); + break; + default: + context.res = { + status: 405, + body: { error: 'Method not allowed' } + }; + } + } catch (error) { + context.log.error('Error:', error); + context.res = { + status: 500, + body: { error: 'Internal server error' } + }; + } +}; + +async function createOrder(context: Context, req: HttpRequest): Promise { + const orderData = req.body; + + const order: Order = { + id: generateId(), + userId: orderData.userId, + items: orderData.items, + total: orderData.items.reduce((sum: number, item: OrderItem) => sum + (item.price * item.quantity), 0), + status: 'pending', + createdAt: new Date().toISOString() + }; + + await container.items.create(order); + + context.res = { + status: 201, + headers: { + 'Content-Type': 'application/json' + }, + body: order + }; +} + +async function getOrder(context: Context, orderId: string): Promise { + const { resource: order } = await container.item(orderId, orderId).read(); + + if (!order) { + context.res = { + status: 404, + body: { error: 'Order not found' } + }; + return; + } + + context.res = { + status: 200, + headers: { + 'Content-Type': 'application/json' + }, + body: order + }; +} + +async function getOrders(context: Context, req: HttpRequest): Promise { + const userId = req.query.userId; + let query = 'SELECT * FROM c'; + + if (userId) { + query += ` WHERE c.userId = "${userId}"`; + } + + query += ' ORDER BY c.createdAt DESC'; + + const { resources: orders } = await container.items.query(query).fetchAll(); + + context.res = { + status: 200, + headers: { + 'Content-Type': 'application/json' + }, + body: { orders } + }; +} + +async function updateOrder(context: Context, orderId: string, req: HttpRequest): Promise { + const { resource: existingOrder } = await container.item(orderId, orderId).read(); + + if (!existingOrder) { + context.res = { + status: 404, + body: { error: 'Order not found' } + }; + return; + } + + const updatedOrder = { + ...existingOrder, + ...req.body, + updatedAt: new Date().toISOString() + }; + + await container.item(orderId, orderId).replace(updatedOrder); + + context.res = { + status: 200, + headers: { + 'Content-Type': 'application/json' + }, + body: updatedOrder + }; +} + +function generateId(): string { + return Math.random().toString(36).substr(2, 9); +} +``` + +**Terraform для Node.js приложения на AWS:** +```hcl +# terraform/main.tf +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.aws_region +} + +# VPC +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "${var.project_name}-vpc" + } +} + +# Internet Gateway +resource "aws_internet_gateway" "main" { + vpc_id = aws_vpc.main.id + + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Subnets +resource "aws_subnet" "public" { + count = 2 + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 1}.0/24" + availability_zone = data.aws_availability_zones.available.names[count.index] + + map_public_ip_on_launch = true + + tags = { + Name = "${var.project_name}-public-subnet-${count.index + 1}" + } +} + +# Private Subnets +resource "aws_subnet" "private" { + count = 2 + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 10}.0/24" + availability_zone = data.aws_availability_zones.available.names[count.index] + + tags = { + Name = "${var.project_name}-private-subnet-${count.index + 1}" + } +} + +# Application Load Balancer +resource "aws_lb" "main" { + name = "${var.project_name}-alb" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.alb.id] + subnets = aws_subnet.public[*].id + + enable_deletion_protection = false + + tags = { + Name = "${var.project_name}-alb" + } +} + +# ALB Target Group +resource "aws_lb_target_group" "app" { + name = "${var.project_name}-tg" + port = 3000 + protocol = "HTTP" + vpc_id = aws_vpc.main.id + + health_check { + enabled = true + healthy_threshold = 2 + interval = 30 + matcher = "200" + path = "/health" + port = "traffic-port" + protocol = "HTTP" + timeout = 5 + unhealthy_threshold = 2 + } + + tags = { + Name = "${var.project_name}-tg" + } +} + +# ECS Cluster +resource "aws_ecs_cluster" "main" { + name = "${var.project_name}-cluster" + + setting { + name = "containerInsights" + value = "enabled" + } + + tags = { + Name = "${var.project_name}-cluster" + } +} + +# ECS Task Definition +resource "aws_ecs_task_definition" "app" { + family = "${var.project_name}-app" + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + cpu = 256 + memory = 512 + execution_role_arn = aws_iam_role.ecs_execution_role.arn + task_role_arn = aws_iam_role.ecs_task_role.arn + + container_definitions = jsonencode([ + { + name = "${var.project_name}-app" + image = "${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/${var.project_name}:latest" + + portMappings = [ + { + containerPort = 3000 + protocol = "tcp" + } + ] + + environment = [ + { + name = "NODE_ENV" + value = "production" + }, + { + name = "PORT" + value = "3000" + } + ] + + secrets = [ + { + name = "DATABASE_URL" + valueFrom = aws_secretsmanager_secret.database_url.arn + } + ] + + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = aws_cloudwatch_log_group.app.name + awslogs-region = var.aws_region + awslogs-stream-prefix = "ecs" + } + } + } + ]) + + tags = { + Name = "${var.project_name}-task-definition" + } +} + +# ECS Service +resource "aws_ecs_service" "app" { + name = "${var.project_name}-service" + cluster = aws_ecs_cluster.main.id + task_definition = aws_ecs_task_definition.app.arn + desired_count = 2 + launch_type = "FARGATE" + + network_configuration { + security_groups = [aws_security_group.ecs_tasks.id] + subnets = aws_subnet.private[*].id + assign_public_ip = false + } + + load_balancer { + target_group_arn = aws_lb_target_group.app.arn + container_name = "${var.project_name}-app" + container_port = 3000 + } + + depends_on = [aws_lb_listener.app] + + tags = { + Name = "${var.project_name}-service" + } +} + +# RDS PostgreSQL +resource "aws_db_instance" "main" { + identifier = "${var.project_name}-db" + + engine = "postgres" + engine_version = "15.4" + instance_class = "db.t3.micro" + + allocated_storage = 20 + max_allocated_storage = 100 + storage_type = "gp2" + storage_encrypted = true + + db_name = var.database_name + username = var.database_username + password = var.database_password + + vpc_security_group_ids = [aws_security_group.rds.id] + db_subnet_group_name = aws_db_subnet_group.main.name + + backup_retention_period = 7 + backup_window = "03:00-04:00" + maintenance_window = "sun:04:00-sun:05:00" + + skip_final_snapshot = true + + tags = { + Name = "${var.project_name}-db" + } +} + +# CloudWatch Log Group +resource "aws_cloudwatch_log_group" "app" { + name = "/ecs/${var.project_name}" + retention_in_days = 30 + + tags = { + Name = "${var.project_name}-log-group" + } +} + +# Secrets Manager +resource "aws_secretsmanager_secret" "database_url" { + name = "${var.project_name}-database-url" + + tags = { + Name = "${var.project_name}-database-url" + } +} + +resource "aws_secretsmanager_secret_version" "database_url" { + secret_id = aws_secretsmanager_secret.database_url.id + secret_string = "postgresql://${var.database_username}:${var.database_password}@${aws_db_instance.main.endpoint}/${var.database_name}" +} +``` + +```yaml +# docker-compose.yml for local development +version: '3.8' + +services: + app: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=development + - PORT=3000 + - ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001 + volumes: + - .:/app + - /app/node_modules + depends_on: + - redis + - postgres + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + + postgres: + image: postgres:15-alpine + environment: + - POSTGRES_DB=myapp + - POSTGRES_USER=myapp + - POSTGRES_PASSWORD=myapp + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + redis_data: + postgres_data: +``` + +```dockerfile +# Dockerfile +FROM node:18-alpine AS builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci --only=production && npm cache clean --force + +# Copy source code +COPY . . + +# Build application +RUN npm run build + +# Production stage +FROM node:18-alpine AS production + +WORKDIR /app + +# Create non-root user +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nextjs -u 1001 + +# Copy built application +COPY --from=builder --chown=nextjs:nodejs /app . + +# Switch to non-root user +USER nextjs + +# Expose port +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD node healthcheck.js + +# Start application +CMD ["npm", "start"] +``` + +```javascript +// healthcheck.js +const http = require('http'); + +const options = { + hostname: 'localhost', + port: process.env.PORT || 3000, + path: '/health', + method: 'GET', + timeout: 2000 +}; + +const req = http.request(options, (res) => { + if (res.statusCode === 200) { + process.exit(0); + } else { + process.exit(1); + } +}); + +req.on('error', () => { + process.exit(1); +}); + +req.on('timeout', () => { + req.destroy(); + process.exit(1); +}); + +req.end(); +``` + +#### 3. SaaS (Software as a Service) +**Определение:** Предоставление готового программного обеспечения. + +**Примеры:** +- Salesforce +- Microsoft Office 365 +- Google Workspace + +**Преимущества:** +- Готовое решение +- Нет необходимости в разработке +- Автоматические обновления + +### Модели развертывания + +#### 1. Public Cloud +**Определение:** Облачные ресурсы, доступные публично. + +**Характеристики:** +- Общие ресурсы +- Оплата по использованию +- Высокая масштабируемость + +#### 2. Private Cloud +**Определение:** Облачные ресурсы для одной организации. + +**Характеристики:** +- Изолированные ресурсы +- Высокий уровень безопасности +- Полный контроль + +#### 3. Hybrid Cloud +**Определение:** Комбинация публичного и приватного облака. + +**Характеристики:** +- Гибкость в выборе +- Оптимизация затрат +- Сложность управления + +#### 4. Multi-Cloud +**Определение:** Использование нескольких облачных провайдеров. + +**Характеристики:** +- Избежание vendor lock-in +- Оптимизация по сервисам +- Высокая сложность + +--- + +## AWS (Amazon Web Services) + +### Основные сервисы AWS + +#### 1. Compute Services + +**EC2 (Elastic Compute Cloud)** +- Виртуальные серверы в облаке +- Различные типы инстансов (t3, m5, c5, r5, etc.) +- Автомасштабирование с Auto Scaling Groups +- Spot Instances для экономии + +```yaml +# EC2 Instance Configuration +Resources: + WebServerInstance: + Type: AWS::EC2::Instance + Properties: + ImageId: ami-0c02fb55956c7d316 # Amazon Linux 2 + InstanceType: t3.micro + KeyName: my-key-pair + SecurityGroupIds: + - !Ref WebServerSecurityGroup + UserData: + Fn::Base64: !Sub | + #!/bin/bash + yum update -y + yum install -y docker + systemctl start docker + systemctl enable docker + usermod -a -G docker ec2-user + docker run -d -p 80:80 nginx + Tags: + - Key: Name + Value: WebServer + - Key: Environment + Value: Production +``` + +**Lambda (Serverless Computing)** +- Выполнение кода без управления серверами +- Оплата только за время выполнения +- Автоматическое масштабирование +- Интеграция с другими AWS сервисами + +```python +# Lambda Function Example +import json +import boto3 + +def lambda_handler(event, context): + """ + AWS Lambda function для обработки заказов + """ + # Получение данных из события + order_data = event.get('order', {}) + + # Валидация данных + if not validate_order(order_data): + return { + 'statusCode': 400, + 'body': json.dumps({'error': 'Invalid order data'}) + } + + # Обработка заказа + order_id = process_order(order_data) + + # Отправка уведомления через SNS + sns = boto3.client('sns') + sns.publish( + TopicArn='arn:aws:sns:us-east-1:123456789012:order-notifications', + Message=f'Order {order_id} has been processed', + Subject='Order Processed' + ) + + return { + 'statusCode': 200, + 'body': json.dumps({ + 'orderId': order_id, + 'status': 'processed' + }) + } + +def validate_order(order_data): + """Валидация данных заказа""" + required_fields = ['customerId', 'items', 'total'] + return all(field in order_data for field in required_fields) + +def process_order(order_data): + """Обработка заказа""" + # Логика обработки заказа + return f"ORD-{order_data.get('customerId')}-{int(time.time())}" +``` + +**Elastic Beanstalk** +- Платформа для развертывания приложений +- Автоматическое управление инфраструктурой +- Поддержка различных языков и фреймворков + +```yaml +# Elastic Beanstalk Configuration +Resources: + WebAppEnvironment: + Type: AWS::ElasticBeanstalk::Environment + Properties: + ApplicationName: !Ref WebApplication + EnvironmentName: !Sub "${AWS::StackName}-env" + SolutionStackName: "64bit Amazon Linux 2 v3.4.0 running Docker" + VersionLabel: !Ref ApplicationVersion + OptionSettings: + - Namespace: aws:autoscaling:launchconfiguration + OptionName: InstanceType + Value: t3.micro + - Namespace: aws:autoscaling:asg + OptionName: MinSize + Value: "1" + - Namespace: aws:autoscaling:asg + OptionName: MaxSize + Value: "3" +``` + +#### 2. Storage Services +```yaml +# S3 Bucket Configuration +Resources: + MyBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: my-app-bucket + VersioningConfiguration: + Status: Enabled + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true +``` + +#### 3. Database Services +```yaml +# RDS Configuration +Resources: + MyDatabase: + Type: AWS::RDS::DBInstance + Properties: + DBInstanceIdentifier: my-database + DBInstanceClass: db.t3.micro + Engine: postgres + EngineVersion: "13.7" + MasterUsername: admin + MasterUserPassword: !Ref DatabasePassword + AllocatedStorage: 20 + StorageType: gp2 + VPCSecurityGroups: + - !Ref DatabaseSecurityGroup +``` + +#### 4. Networking Services +```yaml +# VPC Configuration +Resources: + MyVPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + EnableDnsHostnames: true + EnableDnsSupport: true + Tags: + - Key: Name + Value: MyVPC + + PublicSubnet: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref MyVPC + CidrBlock: 10.0.1.0/24 + AvailabilityZone: !Select [0, !GetAZs ''] + MapPublicIpOnLaunch: true +``` + +### Лучшие практики AWS + +#### 1. Security +```yaml +# IAM Role Configuration +Resources: + EC2Role: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: ec2.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: S3Access + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - s3:GetObject + - s3:PutObject + Resource: !Sub '${MyBucket}/*' +``` + +#### 2. Monitoring +```yaml +# CloudWatch Alarms +Resources: + HighCPUAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + AlarmName: HighCPUUtilization + AlarmDescription: Alarm when CPU exceeds 80% + MetricName: CPUUtilization + Namespace: AWS/EC2 + Statistic: Average + Period: 300 + EvaluationPeriods: 2 + Threshold: 80 + ComparisonOperator: GreaterThanThreshold + Dimensions: + - Name: InstanceId + Value: !Ref MyInstance +``` + +--- + +## Azure (Microsoft Azure) + +### Основные сервисы Azure + +#### 1. Compute Services +```yaml +# Azure VM Configuration +apiVersion: 2021-03-01 +type: Microsoft.Compute/virtualMachines +properties: + hardwareProfile: + vmSize: Standard_B1s + storageProfile: + imageReference: + publisher: Canonical + offer: UbuntuServer + sku: 18.04-LTS + version: latest + osDisk: + createOption: FromImage + managedDisk: + storageAccountType: Standard_LRS + osProfile: + computerName: my-vm + adminUsername: azureuser + adminPassword: !Ref AdminPassword + networkProfile: + networkInterfaces: + - id: !Ref NetworkInterface +``` + +#### 2. Storage Services +```yaml +# Azure Storage Account +apiVersion: 2021-02-01 +type: Microsoft.Storage/storageAccounts +properties: + sku: + name: Standard_LRS + kind: StorageV2 + accessTier: Hot + supportsHttpsTrafficOnly: true + minimumTlsVersion: TLS1_2 + allowBlobPublicAccess: false +``` + +#### 3. Database Services +```yaml +# Azure SQL Database +apiVersion: 2021-02-01 +type: Microsoft.Sql/servers +properties: + administratorLogin: admin + administratorLoginPassword: !Ref AdminPassword + version: 12.0 + publicNetworkAccess: Disabled + minimalTlsVersion: 1.2 +``` + +#### 4. Networking Services +```yaml +# Azure Virtual Network +apiVersion: 2021-02-01 +type: Microsoft.Network/virtualNetworks +properties: + addressSpace: + addressPrefixes: + - 10.0.0.0/16 + subnets: + - name: default + properties: + addressPrefix: 10.0.1.0/24 +``` + +### Лучшие практики Azure + +#### 1. Resource Groups +```yaml +# Resource Group Organization +Resources: + ProductionRG: + Type: Microsoft.Resources/resourceGroups + Properties: + location: East US + tags: + Environment: Production + CostCenter: IT + Owner: Development Team + + DevelopmentRG: + Type: Microsoft.Resources/resourceGroups + Properties: + location: East US + tags: + Environment: Development + CostCenter: IT + Owner: Development Team +``` + +#### 2. Monitoring +```yaml +# Azure Monitor Alerts +Resources: + HighCPUAlert: + Type: Microsoft.Insights/metricAlerts + Properties: + description: Alert when CPU exceeds 80% + severity: 2 + enabled: true + scopes: + - !Ref VirtualMachine + criteria: + metricName: Percentage CPU + operator: GreaterThan + threshold: 80 + timeAggregation: Average +``` + +--- + +## GCP (Google Cloud Platform) + +### Основные сервисы GCP + +#### 1. Compute Services +```yaml +# GCP VM Instance +resources: +- name: my-vm + type: compute.v1.instance + properties: + zone: us-central1-a + machineType: zones/us-central1-a/machineTypes/e2-micro + disks: + - deviceName: boot + type: PERSISTENT + boot: true + autoDelete: true + initializeParams: + sourceImage: projects/ubuntu-os-cloud/global/images/family/ubuntu-2004-lts + networkInterfaces: + - network: global/networks/default + accessConfigs: + - name: External NAT + type: ONE_TO_ONE_NAT +``` + +#### 2. Storage Services +```yaml +# GCP Cloud Storage Bucket +resources: +- name: my-bucket + type: storage.v1.bucket + properties: + location: US + storageClass: STANDARD + versioning: + enabled: true + lifecycle: + rule: + - action: + type: Delete + condition: + age: 365 +``` + +#### 3. Database Services +```yaml +# GCP Cloud SQL +resources: +- name: my-database + type: sqladmin.v1beta4.instance + properties: + region: us-central1 + settings: + tier: db-f1-micro + dataDiskType: PD_SSD + dataDiskSizeGb: 10 + backupConfiguration: + enabled: true + startTime: 03:00 + ipConfiguration: + ipv4Enabled: true + authorizedNetworks: + - value: 0.0.0.0/0 +``` + +#### 4. Networking Services +```yaml +# GCP VPC Network +resources: +- name: my-vpc + type: compute.v1.network + properties: + autoCreateSubnetworks: false + routingConfig: + routingMode: REGIONAL + +- name: my-subnet + type: compute.v1.subnetwork + properties: + network: $(ref.my-vpc.selfLink) + ipCidrRange: 10.0.0.0/24 + region: us-central1 +``` + +### Лучшие практики GCP + +#### 1. IAM +```yaml +# GCP IAM Policy +resources: +- name: my-iam-policy + type: gcp-types/cloudresourcemanager-v1:projects.iamPolicyBinding + properties: + resource: projects/my-project + role: roles/storage.objectViewer + members: + - user:user@example.com + - serviceAccount:service@project.iam.gserviceaccount.com +``` + +#### 2. Monitoring +```yaml +# GCP Monitoring Alert +resources: +- name: high-cpu-alert + type: monitoring.v3.alertPolicy + properties: + displayName: High CPU Usage + conditions: + - displayName: CPU usage is high + conditionThreshold: + filter: 'resource.type="gce_instance"' + comparison: COMPARISON_GREATER_THAN + thresholdValue: 0.8 + duration: 300s + alertStrategy: + autoClose: 1800s +``` + +--- + +## Cloud-Native архитектура + +### Принципы Cloud-Native + +#### 1. 12-Factor App +```markdown +# 12-Factor App Principles + +1. **Codebase** — один код, множество развертываний +2. **Dependencies** — явно объявлять и изолировать зависимости +3. **Config** — хранить конфигурацию в переменных окружения +4. **Backing Services** — рассматривать внешние сервисы как подключенные ресурсы +5. **Build, Release, Run** — строго разделять этапы сборки и выполнения +6. **Processes** — выполнять приложение как один или несколько stateless процессов +7. **Port Binding** — экспортировать сервисы через привязку к порту +8. **Concurrency** — масштабировать через модель процессов +9. **Disposability** — максимизировать надежность через быстрый запуск и graceful shutdown +10. **Dev/Prod Parity** — держать разработку, staging и production максимально похожими +11. **Logs** — рассматривать логи как потоки событий +12. **Admin Processes** — выполнять административные задачи как одноразовые процессы +``` + +#### 2. Microservices Architecture +```yaml +# Kubernetes Microservices Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service +spec: + replicas: 3 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + spec: + containers: + - name: user-service + image: user-service:latest + ports: + - containerPort: 8080 + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: database-secret + key: url + - name: REDIS_URL + valueFrom: + configMapKeyRef: + name: redis-config + key: url + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 +``` + +#### 3. Container Orchestration +```yaml +# Kubernetes Service +apiVersion: v1 +kind: Service +metadata: + name: user-service +spec: + selector: + app: user-service + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + type: ClusterIP + +--- +# Kubernetes Ingress +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: user-service-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - host: api.example.com + http: + paths: + - path: /users + pathType: Prefix + backend: + service: + name: user-service + port: + number: 80 +``` + +### Serverless архитектура + +#### 1. AWS Lambda +```python +import json +import boto3 + +def lambda_handler(event, context): + # Обработка события + user_id = event['pathParameters']['id'] + + # Получение данных пользователя + dynamodb = boto3.resource('dynamodb') + table = dynamodb.Table('Users') + + try: + response = table.get_item(Key={'id': user_id}) + user = response['Item'] + + return { + 'statusCode': 200, + 'body': json.dumps(user) + } + except Exception as e: + return { + 'statusCode': 500, + 'body': json.dumps({'error': str(e)}) + } +``` + +#### 2. Azure Functions +```csharp +[FunctionName("GetUser")] +public static async Task Run( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "users/{id}")] HttpRequest req, + string id, + ILogger log) +{ + log.LogInformation($"Getting user with ID: {id}"); + + try + { + var user = await userService.GetUserAsync(id); + + if (user == null) + { + return new NotFoundResult(); + } + + return new OkObjectResult(user); + } + catch (Exception ex) + { + log.LogError(ex, "Error getting user"); + return new StatusCodeResult(500); + } +} +``` + +#### 3. Google Cloud Functions +```javascript +const { Firestore } = require('@google-cloud/firestore'); + +exports.getUser = async (req, res) => { + const userId = req.params.id; + + try { + const firestore = new Firestore(); + const doc = await firestore.collection('users').doc(userId).get(); + + if (!doc.exists) { + return res.status(404).json({ error: 'User not found' }); + } + + res.json(doc.data()); + } catch (error) { + console.error('Error getting user:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}; +``` + +--- + +## Мультиоблачные и гибридные стратегии + +### Мультиоблачная архитектура + +**Мультиоблачная архитектура** — использование нескольких облачных провайдеров для различных сервисов или в качестве резервных копий. + +#### Преимущества мультиоблачной стратегии + +1. **Избежание vendor lock-in** +2. **Лучшие цены** за счет конкуренции +3. **Географическое распределение** +4. **Отказоустойчивость** +5. **Специализированные сервисы** + +#### Архитектурные паттерны + +```yaml +# Multi-Cloud Architecture +apiVersion: v1 +kind: ConfigMap +metadata: + name: multi-cloud-config +data: + strategy: | + primary_cloud: "AWS" + secondary_cloud: "Azure" + disaster_recovery: "GCP" + + services: + - name: "user-service" + cloud: "AWS" + region: "us-east-1" + backup_cloud: "Azure" + + - name: "payment-service" + cloud: "Azure" + region: "eastus" + backup_cloud: "AWS" + + - name: "analytics-service" + cloud: "GCP" + region: "us-central1" + backup_cloud: "AWS" +``` + +#### Реализация мультиоблачной стратегии + +```python +# Multi-Cloud Service Manager +class MultiCloudManager: + def __init__(self): + self.aws_client = boto3.client('ec2') + self.azure_client = get_azure_client() + self.gcp_client = get_gcp_client() + + self.cloud_configs = { + 'aws': { + 'client': self.aws_client, + 'regions': ['us-east-1', 'us-west-2', 'eu-west-1'], + 'services': ['ec2', 's3', 'rds', 'lambda'] + }, + 'azure': { + 'client': self.azure_client, + 'regions': ['eastus', 'westus2', 'westeurope'], + 'services': ['vm', 'storage', 'sql', 'functions'] + }, + 'gcp': { + 'client': self.gcp_client, + 'regions': ['us-central1', 'us-east1', 'europe-west1'], + 'services': ['compute', 'storage', 'sql', 'cloud-functions'] + } + } + + def deploy_service(self, service_name, service_config): + """Развертывание сервиса в мультиоблачной среде""" + primary_cloud = service_config.get('primary_cloud', 'aws') + backup_cloud = service_config.get('backup_cloud') + + # Развертывание в основном облаке + primary_result = self._deploy_to_cloud( + primary_cloud, + service_name, + service_config + ) + + # Развертывание в резервном облаке + if backup_cloud: + backup_result = self._deploy_to_cloud( + backup_cloud, + service_name, + service_config + ) + + return { + 'primary': primary_result, + 'backup': backup_result, + 'status': 'deployed' + } + + return primary_result + + def _deploy_to_cloud(self, cloud_provider, service_name, config): + """Развертывание сервиса в конкретном облаке""" + cloud_config = self.cloud_configs[cloud_provider] + + if cloud_provider == 'aws': + return self._deploy_aws_service(service_name, config) + elif cloud_provider == 'azure': + return self._deploy_azure_service(service_name, config) + elif cloud_provider == 'gcp': + return self._deploy_gcp_service(service_name, config) + + def _deploy_aws_service(self, service_name, config): + """Развертывание в AWS""" + # Создание EC2 инстанса + response = self.aws_client.run_instances( + ImageId=config.get('ami_id'), + MinCount=1, + MaxCount=1, + InstanceType=config.get('instance_type', 't3.micro'), + SecurityGroupIds=config.get('security_groups', []), + TagSpecifications=[{ + 'ResourceType': 'instance', + 'Tags': [ + {'Key': 'Name', 'Value': service_name}, + {'Key': 'Environment', 'Value': config.get('environment', 'production')} + ] + }] + ) + + return { + 'cloud': 'aws', + 'instance_id': response['Instances'][0]['InstanceId'], + 'status': 'running' + } +``` + +### Гибридная облачная архитектура + +**Гибридная облачная архитектура** — комбинация частного облака (on-premises) и публичного облака. + +#### Архитектурные компоненты + +```mermaid +graph TB + subgraph "On-Premises Data Center" + A[Private Cloud] --> B[Local Services] + B --> C[Database] + B --> D[File Storage] + end + + subgraph "Public Cloud" + E[Cloud Services] --> F[Web Applications] + E --> G[Analytics] + E --> H[Backup] + end + + subgraph "Hybrid Connectivity" + I[VPN/ExpressRoute] --> J[Load Balancer] + J --> K[API Gateway] + end + + A <--> I + E <--> I + K --> A + K --> E +``` + +#### Гибридная интеграция + +```java +// Hybrid Cloud Integration Service +@Service +public class HybridCloudService { + + @Autowired + private OnPremisesService onPremisesService; + + @Autowired + private CloudService cloudService; + + @Value("${hybrid.routing.strategy}") + private String routingStrategy; + + public ResponseEntity processRequest(String serviceType, Object request) { + // Определение стратегии маршрутизации + CloudProvider provider = determineProvider(serviceType, request); + + switch (provider) { + case ON_PREMISES: + return onPremisesService.process(request); + case CLOUD: + return cloudService.process(request); + case HYBRID: + return processHybrid(request); + default: + throw new IllegalArgumentException("Unknown provider: " + provider); + } + } + + private CloudProvider determineProvider(String serviceType, Object request) { + // Логика определения провайдера + if (routingStrategy.equals("data-locality")) { + return determineByDataLocality(request); + } else if (routingStrategy.equals("performance")) { + return determineByPerformance(serviceType); + } else if (routingStrategy.equals("cost")) { + return determineByCost(serviceType); + } + + return CloudProvider.CLOUD; // По умолчанию + } + + private ResponseEntity processHybrid(Object request) { + // Обработка в гибридном режиме + // Часть данных обрабатывается локально, часть в облаке + + // 1. Обработка чувствительных данных локально + String sensitiveData = extractSensitiveData(request); + String processedSensitiveData = onPremisesService.processSensitive(sensitiveData); + + // 2. Обработка остальных данных в облаке + Object cloudRequest = prepareCloudRequest(request, processedSensitiveData); + ResponseEntity cloudResponse = cloudService.process(cloudRequest); + + // 3. Объединение результатов + return combineResults(processedSensitiveData, cloudResponse); + } +} +``` + +### Стратегии миграции + +#### 1. Lift and Shift (Rehosting) + +**Lift and Shift** — перенос приложения в облако без изменений архитектуры. + +```yaml +# Lift and Shift Migration Plan +migration_plan: + phase_1_assessment: + - "Inventory current applications" + - "Identify dependencies" + - "Assess cloud readiness" + - "Calculate migration costs" + + phase_2_preparation: + - "Set up cloud infrastructure" + - "Configure networking" + - "Prepare security policies" + - "Train team on cloud tools" + + phase_3_migration: + - "Migrate non-critical applications first" + - "Test in cloud environment" + - "Migrate critical applications" + - "Validate functionality" + + phase_4_optimization: + - "Optimize for cloud-native features" + - "Implement monitoring" + - "Fine-tune performance" + - "Cost optimization" +``` + +#### 2. Refactoring (Replatforming) + +**Refactoring** — модификация приложения для использования облачных сервисов. + +```java +// Before: On-premises application +@RestController +public class LegacyOrderController { + + @Autowired + private FileSystemOrderService fileSystemService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody Order order) { + // Сохранение в файловую систему + fileSystemService.saveOrder(order); + return ResponseEntity.ok(order); + } +} + +// After: Cloud-native application +@RestController +public class CloudOrderController { + + @Autowired + private DynamoDBOrderService dynamoDBService; + + @Autowired + private S3DocumentService s3Service; + + @Autowired + private SNSNotificationService notificationService; + + @PostMapping("/orders") + public ResponseEntity createOrder(@RequestBody Order order) { + // Сохранение в DynamoDB + Order savedOrder = dynamoDBService.saveOrder(order); + + // Сохранение документов в S3 + s3Service.saveOrderDocuments(order); + + // Отправка уведомления через SNS + notificationService.sendOrderCreatedNotification(savedOrder); + + return ResponseEntity.ok(savedOrder); + } +} +``` + +#### 3. Re-architecting + +**Re-architecting** — полная переработка приложения с использованием облачных паттернов. + +```yaml +# Re-architecting Strategy +architecture_transformation: + from_monolith: + - "Single application" + - "Tightly coupled components" + - "Single database" + - "Vertical scaling" + + to_microservices: + - "Multiple independent services" + - "Loosely coupled via APIs" + - "Database per service" + - "Horizontal scaling" + + cloud_native_patterns: + - "Event-driven architecture" + - "Serverless functions" + - "Container orchestration" + - "API Gateway" + - "Service Mesh" +``` + +### Управление мультиоблачной средой + +#### 1. Единая точка управления + +```python +# Multi-Cloud Management Dashboard +class CloudManagementDashboard: + def __init__(self): + self.cloud_managers = { + 'aws': AWSManager(), + 'azure': AzureManager(), + 'gcp': GCPManager() + } + + def get_unified_metrics(self): + """Получение унифицированных метрик со всех облаков""" + metrics = {} + + for cloud_name, manager in self.cloud_managers.items(): + metrics[cloud_name] = { + 'cost': manager.get_monthly_cost(), + 'usage': manager.get_resource_usage(), + 'performance': manager.get_performance_metrics(), + 'security': manager.get_security_status() + } + + return self.aggregate_metrics(metrics) + + def optimize_costs(self): + """Оптимизация затрат в мультиоблачной среде""" + recommendations = [] + + for cloud_name, manager in self.cloud_managers.items(): + cloud_recommendations = manager.get_cost_optimization_recommendations() + recommendations.extend(cloud_recommendations) + + return self.prioritize_recommendations(recommendations) + + def disaster_recovery_plan(self): + """План аварийного восстановления""" + return { + 'rto': '4 hours', # Recovery Time Objective + 'rpo': '1 hour', # Recovery Point Objective + 'backup_strategy': 'Cross-cloud replication', + 'failover_procedure': self.get_failover_procedure() + } +``` + +#### 2. Безопасность в мультиоблачной среде + +```yaml +# Multi-Cloud Security Configuration +security: + identity_management: + primary: "Azure AD" + federation: + - "AWS IAM" + - "GCP IAM" + + network_security: + vpn_connections: + - "AWS VPN Gateway" + - "Azure VPN Gateway" + - "GCP Cloud VPN" + + firewall_rules: + - "AWS Security Groups" + - "Azure NSG" + - "GCP Firewall Rules" + + data_encryption: + at_rest: + - "AWS KMS" + - "Azure Key Vault" + - "GCP Cloud KMS" + + in_transit: + - "TLS 1.3" + - "IPSec" + - "mTLS" + + compliance: + standards: + - "SOC 2" + - "ISO 27001" + - "GDPR" + - "HIPAA" +``` + +### Сравнение стратегий + +| Стратегия | Сложность | Время | Стоимость | Риск | Гибкость | +|-----------|-----------|-------|-----------|------|----------| +| **Lift and Shift** | Низкая | Быстро | Низкая | Низкий | Низкая | +| **Refactoring** | Средняя | Средне | Средняя | Средний | Средняя | +| **Re-architecting** | Высокая | Долго | Высокая | Высокий | Высокая | +| **Hybrid** | Очень высокая | Очень долго | Очень высокая | Очень высокий | Очень высокая | + +--- + +## Стратегии миграции + +### Типы миграции + +#### 1. Lift and Shift (Rehosting) +**Определение:** Перенос приложения в облако без изменений. + +**Преимущества:** +- Быстрая миграция +- Минимальные изменения +- Низкий риск + +**Недостатки:** +- Не использует преимущества облака +- Высокие затраты +- Ограниченная масштабируемость + +```yaml +# Lift and Shift Example +# Original on-premises application +# Migrated to AWS EC2 +Resources: + MyApplication: + Type: AWS::EC2::Instance + Properties: + ImageId: ami-0c02fb55956c7d316 + InstanceType: t3.large + SecurityGroups: + - !Ref ApplicationSecurityGroup + UserData: + Fn::Base64: !Sub | + #!/bin/bash + # Install application dependencies + yum update -y + yum install -y java-1.8.0-openjdk + # Deploy application + aws s3 cp s3://my-bucket/app.jar /opt/app.jar + java -jar /opt/app.jar +``` + +#### 2. Replatforming +**Определение:** Миграция с минимальными изменениями для использования облачных сервисов. + +**Преимущества:** +- Использование облачных сервисов +- Улучшенная производительность +- Снижение затрат + +**Недостатки:** +- Требует изменений в приложении +- Средний риск +- Время на адаптацию + +```yaml +# Replatforming Example +# Migrate from on-premises database to RDS +Resources: + MyDatabase: + Type: AWS::RDS::DBInstance + Properties: + DBInstanceIdentifier: my-app-db + DBInstanceClass: db.t3.micro + Engine: mysql + EngineVersion: "8.0" + MasterUsername: admin + MasterUserPassword: !Ref DatabasePassword + AllocatedStorage: 20 + VPCSecurityGroups: + - !Ref DatabaseSecurityGroup + + MyApplication: + Type: AWS::ECS::Service + Properties: + ServiceName: my-app-service + Cluster: !Ref ECSCluster + TaskDefinition: !Ref TaskDefinition + DesiredCount: 2 +``` + +#### 3. Refactoring (Re-architecting) +**Определение:** Полная переработка приложения для облачной архитектуры. + +**Преимущества:** +- Максимальное использование облака +- Высокая масштабируемость +- Оптимальная производительность + +**Недостатки:** +- Высокие затраты +- Длительное время +- Высокий риск + +```yaml +# Refactoring Example +# Migrate to microservices architecture +Resources: + UserService: + Type: AWS::ECS::Service + Properties: + ServiceName: user-service + Cluster: !Ref ECSCluster + TaskDefinition: !Ref UserServiceTaskDefinition + DesiredCount: 3 + + OrderService: + Type: AWS::ECS::Service + Properties: + ServiceName: order-service + Cluster: !Ref ECSCluster + TaskDefinition: !Ref OrderServiceTaskDefinition + DesiredCount: 3 + + APIGateway: + Type: AWS::ApiGateway::RestApi + Properties: + Name: microservices-api + Description: API Gateway for microservices +``` + +### Миграционная стратегия + +#### 1. Assessment Phase +```markdown +# Assessment Checklist + +## Application Analysis +- [ ] Inventory all applications +- [ ] Analyze dependencies +- [ ] Assess performance requirements +- [ ] Identify security requirements +- [ ] Calculate current costs + +## Infrastructure Analysis +- [ ] Map network topology +- [ ] Identify data flows +- [ ] Assess storage requirements +- [ ] Analyze backup strategies +- [ ] Document disaster recovery + +## Business Analysis +- [ ] Define migration goals +- [ ] Identify critical applications +- [ ] Assess business impact +- [ ] Plan migration timeline +- [ ] Allocate resources +``` + +#### 2. Planning Phase +```markdown +# Migration Plan + +## Phase 1: Foundation (Month 1-2) +- Set up cloud infrastructure +- Configure networking +- Implement security controls +- Set up monitoring + +## Phase 2: Pilot Migration (Month 3-4) +- Migrate non-critical applications +- Test cloud services +- Validate performance +- Train team + +## Phase 3: Production Migration (Month 5-8) +- Migrate critical applications +- Implement disaster recovery +- Optimize performance +- Monitor and adjust + +## Phase 4: Optimization (Month 9-12) +- Optimize costs +- Implement automation +- Enhance security +- Continuous improvement +``` + +#### 3. Execution Phase +```yaml +# Migration Automation +Resources: + MigrationPipeline: + Type: AWS::CodePipeline::Pipeline + Properties: + Name: application-migration-pipeline + RoleArn: !GetAtt CodePipelineRole.Arn + Stages: + - Name: Source + Actions: + - Name: SourceAction + ActionTypeId: + Category: Source + Owner: AWS + Provider: S3 + Version: 1 + Configuration: + S3Bucket: !Ref SourceBucket + S3ObjectKey: source.zip + - Name: Build + Actions: + - Name: BuildAction + ActionTypeId: + Category: Build + Owner: AWS + Provider: CodeBuild + Version: 1 + Configuration: + ProjectName: !Ref BuildProject + - Name: Deploy + Actions: + - Name: DeployAction + ActionTypeId: + Category: Deploy + Owner: AWS + Provider: CloudFormation + Version: 1 + Configuration: + StackName: !Ref StackName + TemplatePath: BuildArtifact::template.yaml +``` + +--- + +## Экономика облака и калькулятор затрат + +### Понимание модели оплаты облака + +#### 1. Pay-as-you-go модель + +**Принцип:** Оплата только за используемые ресурсы. + +**Преимущества:** +- Нет предварительных инвестиций +- Гибкость в масштабировании +- Оплата только за фактическое использование + +**Примеры затрат:** +```yaml +# Пример расчета затрат на EC2 +ec2_costs: + instance_type: "t3.micro" + hourly_rate: "$0.0104" + monthly_cost: "$7.49" # 24/7 использование + annual_cost: "$89.88" + +# Пример расчета затрат на S3 +s3_costs: + storage_class: "Standard" + storage_gb: 100 + monthly_storage: "$2.30" + requests_per_month: 1000000 + monthly_requests: "$0.40" + total_monthly: "$2.70" +``` + +#### 2. Модели ценообразования AWS + +**On-Demand Instances** +- Оплата по часам/секундам +- Без долгосрочных обязательств +- Максимальная гибкость + +```python +# Калькулятор затрат On-Demand +class OnDemandCostCalculator: + def __init__(self): + self.pricing = { + 't3.micro': 0.0104, # USD per hour + 't3.small': 0.0208, + 't3.medium': 0.0416, + 'm5.large': 0.096, + 'c5.large': 0.085 + } + + def calculate_monthly_cost(self, instance_type, hours_per_month=744): + """Расчет месячной стоимости""" + hourly_rate = self.pricing.get(instance_type, 0) + return hourly_rate * hours_per_month + + def calculate_annual_cost(self, instance_type, hours_per_month=744): + """Расчет годовой стоимости""" + monthly_cost = self.calculate_monthly_cost(instance_type, hours_per_month) + return monthly_cost * 12 + +# Использование +calculator = OnDemandCostCalculator() +monthly_cost = calculator.calculate_monthly_cost('t3.medium') +print(f"Monthly cost for t3.medium: ${monthly_cost:.2f}") +``` + +**Reserved Instances** +- Скидка до 75% при предоплате +- Обязательство на 1-3 года +- Предсказуемые затраты + +```python +# Калькулятор Reserved Instances +class ReservedInstanceCalculator: + def __init__(self): + self.reserved_pricing = { + 't3.medium': { + '1_year_no_upfront': 0.025, + '1_year_partial_upfront': 0.020, + '1_year_all_upfront': 0.015, + '3_year_no_upfront': 0.018, + '3_year_partial_upfront': 0.014, + '3_year_all_upfront': 0.010 + } + } + + def calculate_savings(self, instance_type, on_demand_rate, reserved_rate): + """Расчет экономии от Reserved Instances""" + monthly_on_demand = on_demand_rate * 744 + monthly_reserved = reserved_rate * 744 + monthly_savings = monthly_on_demand - monthly_reserved + annual_savings = monthly_savings * 12 + savings_percentage = (monthly_savings / monthly_on_demand) * 100 + + return { + 'monthly_savings': monthly_savings, + 'annual_savings': annual_savings, + 'savings_percentage': savings_percentage + } + +# Использование +ri_calculator = ReservedInstanceCalculator() +savings = ri_calculator.calculate_savings( + 't3.medium', + 0.0416, # On-demand rate + 0.015 # 1-year all upfront rate +) +print(f"Annual savings: ${savings['annual_savings']:.2f} ({savings['savings_percentage']:.1f}%)") +``` + +**Spot Instances** +- До 90% скидка от On-Demand +- Могут быть прерваны в любой момент +- Подходят для fault-tolerant workloads + +```python +# Калькулятор Spot Instances +class SpotInstanceCalculator: + def __init__(self): + self.spot_pricing = { + 't3.medium': 0.0125, # Примерная цена + 'm5.large': 0.024, + 'c5.large': 0.021 + } + + def calculate_spot_savings(self, instance_type, on_demand_rate): + """Расчет экономии от Spot Instances""" + spot_rate = self.spot_pricing.get(instance_type, 0) + monthly_on_demand = on_demand_rate * 744 + monthly_spot = spot_rate * 744 + monthly_savings = monthly_on_demand - monthly_spot + savings_percentage = (monthly_savings / monthly_on_demand) * 100 + + return { + 'monthly_savings': monthly_savings, + 'savings_percentage': savings_percentage, + 'spot_rate': spot_rate + } + +# Использование +spot_calculator = SpotInstanceCalculator() +spot_savings = spot_calculator.calculate_spot_savings('t3.medium', 0.0416) +print(f"Spot savings: {spot_savings['savings_percentage']:.1f}%") +``` + +#### 3. AWS Cost Calculator + +**Онлайн калькулятор AWS:** +- https://calculator.aws/ +- Детальный расчет затрат +- Сравнение различных конфигураций +- Экспорт в Excel/PDF + +**Программный доступ к ценам:** + +```python +# AWS Pricing API +import boto3 +import json + +class AWSPricingCalculator: + def __init__(self): + self.pricing_client = boto3.client('pricing', region_name='us-east-1') + + def get_ec2_pricing(self, instance_type, region='us-east-1'): + """Получение цен на EC2 инстансы""" + try: + response = self.pricing_client.get_products( + ServiceCode='AmazonEC2', + Filters=[ + { + 'Type': 'TERM_MATCH', + 'Field': 'instanceType', + 'Value': instance_type + }, + { + 'Type': 'TERM_MATCH', + 'Field': 'location', + 'Value': self.get_region_name(region) + } + ] + ) + return response + except Exception as e: + print(f"Error getting pricing: {e}") + return None + + def get_region_name(self, region_code): + """Преобразование кода региона в название""" + region_mapping = { + 'us-east-1': 'US East (N. Virginia)', + 'us-west-2': 'US West (Oregon)', + 'eu-west-1': 'Europe (Ireland)', + 'ap-southeast-1': 'Asia Pacific (Singapore)' + } + return region_mapping.get(region_code, region_code) + +# Использование +pricing_calc = AWSPricingCalculator() +pricing = pricing_calc.get_ec2_pricing('t3.micro') +``` + +#### 4. Cost Optimization Strategies + +**Right-Sizing** +- Выбор подходящего размера инстансов +- Мониторинг использования ресурсов +- Автоматическое масштабирование + +```yaml +# CloudWatch Metrics для Right-Sizing +Resources: + RightSizingAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + AlarmName: LowCPUUtilization + ComparisonOperator: LessThanThreshold + EvaluationPeriods: 2 + MetricName: CPUUtilization + Namespace: AWS/EC2 + Period: 300 + Statistic: Average + Threshold: 20 + AlarmActions: + - !Ref SNSNotificationTopic + Dimensions: + - Name: InstanceId + Value: !Ref EC2Instance +``` + +**Reserved Instances Planning** + +```python +# Планировщик Reserved Instances +class ReservedInstancePlanner: + def __init__(self): + self.usage_history = {} + + def analyze_usage_pattern(self, instance_type, hours_per_month): + """Анализ паттернов использования""" + if hours_per_month >= 744: # 24/7 использование + return "Full-time usage - consider Reserved Instances" + elif hours_per_month >= 372: # 12/7 использование + return "Part-time usage - consider Scheduled Reserved Instances" + else: + return "Variable usage - stick with On-Demand" + + def calculate_break_even(self, on_demand_rate, reserved_rate, upfront_cost=0): + """Расчет точки безубыточности""" + monthly_savings = (on_demand_rate - reserved_rate) * 744 + if upfront_cost > 0: + months_to_break_even = upfront_cost / monthly_savings + return months_to_break_even + return float('inf') + +# Использование +planner = ReservedInstancePlanner() +break_even = planner.calculate_break_even(0.0416, 0.015, 100) +print(f"Break-even point: {break_even:.1f} months") +``` + +**Storage Optimization** + +```yaml +# S3 Lifecycle Configuration +Resources: + S3Bucket: + Type: AWS::S3::Bucket + Properties: + LifecycleConfiguration: + Rules: + - Id: TransitionToIA + Status: Enabled + Transitions: + - StorageClass: STANDARD_IA + TransitionInDays: 30 + - Id: TransitionToGlacier + Status: Enabled + Transitions: + - StorageClass: GLACIER + TransitionInDays: 90 + - Id: DeleteOldVersions + Status: Enabled + NoncurrentVersionTransitions: + - StorageClass: GLACIER + TransitionInDays: 30 + NoncurrentVersionExpirationInDays: 90 +``` + +#### 5. Cost Monitoring and Alerting + +**AWS Cost Explorer** +- Визуализация затрат +- Фильтрация по сервисам, регионам, тегам +- Прогнозирование затрат + +**Billing Alerts** + +```yaml +# CloudWatch Billing Alarm +Resources: + BillingAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + AlarmName: MonthlyBillingAlarm + AlarmDescription: Alert when monthly costs exceed threshold + MetricName: EstimatedCharges + Namespace: AWS/Billing + Statistic: Maximum + Period: 86400 # 24 hours + EvaluationPeriods: 1 + Threshold: 100 # $100 + ComparisonOperator: GreaterThanThreshold + Dimensions: + - Name: Currency + Value: USD + AlarmActions: + - !Ref SNSNotificationTopic +``` + +**Cost Allocation Tags** + +```yaml +# Tagging Strategy +Resources: + EC2Instance: + Type: AWS::EC2::Instance + Properties: + Tags: + - Key: Environment + Value: Production + - Key: Project + Value: E-commerce + - Key: Owner + Value: DevelopmentTeam + - Key: CostCenter + Value: IT-001 + - Key: AutoShutdown + Value: "false" +``` + +#### 6. Multi-Cloud Cost Comparison + +**Сравнение облачных провайдеров:** + +```python +# Сравнительный калькулятор облачных затрат +class MultiCloudCostComparator: + def __init__(self): + self.pricing = { + 'aws': { + 't3.micro': 0.0104, + 't3.small': 0.0208, + 't3.medium': 0.0416 + }, + 'azure': { + 'B1s': 0.0052, + 'B1ms': 0.0104, + 'B2s': 0.0416 + }, + 'gcp': { + 'e2-micro': 0.006, + 'e2-small': 0.012, + 'e2-medium': 0.024 + } + } + + def compare_costs(self, instance_type, hours=744): + """Сравнение затрат между облачными провайдерами""" + results = {} + for provider, instances in self.pricing.items(): + if instance_type in instances: + monthly_cost = instances[instance_type] * hours + results[provider] = { + 'monthly_cost': monthly_cost, + 'annual_cost': monthly_cost * 12 + } + return results + + def find_cheapest(self, instance_type, hours=744): + """Поиск самого дешевого варианта""" + costs = self.compare_costs(instance_type, hours) + if costs: + cheapest = min(costs.items(), key=lambda x: x[1]['monthly_cost']) + return cheapest[0], cheapest[1] + +# Использование +comparator = MultiCloudCostComparator() +cheapest_provider, cost = comparator.find_cheapest('t3.micro') +print(f"Cheapest provider: {cheapest_provider}, Monthly cost: ${cost['monthly_cost']:.2f}") +``` + +### Экономические принципы облачных вычислений + +#### 1. CAPEX vs OPEX + +**CAPEX (Capital Expenditure) - Капитальные затраты:** +- Покупка оборудования +- Предварительные инвестиции +- Амортизация активов + +**OPEX (Operational Expenditure) - Операционные затраты:** +- Оплата по факту использования +- Переменные затраты +- Налоговые преимущества + +#### 2. Total Cost of Ownership (TCO) + +**Компоненты TCO:** +- Стоимость инфраструктуры +- Стоимость персонала +- Стоимость лицензий +- Стоимость поддержки +- Стоимость безопасности + +```python +# TCO Calculator +class TCOCalculator: + def __init__(self): + self.components = { + 'infrastructure': 0, + 'personnel': 0, + 'licenses': 0, + 'support': 0, + 'security': 0 + } + + def calculate_tco(self, years=3): + """Расчет TCO за указанный период""" + total_tco = sum(self.components.values()) * years + return { + 'total_tco': total_tco, + 'annual_tco': total_tco / years, + 'monthly_tco': total_tco / (years * 12), + 'breakdown': self.components + } + + def compare_on_premise_vs_cloud(self, on_premise_tco, cloud_tco): + """Сравнение On-Premise vs Cloud""" + savings = on_premise_tco - cloud_tco + savings_percentage = (savings / on_premise_tco) * 100 + + return { + 'savings': savings, + 'savings_percentage': savings_percentage, + 'cloud_advantage': savings > 0 + } + +# Использование +tco_calc = TCOCalculator() +tco_calc.components = { + 'infrastructure': 50000, + 'personnel': 120000, + 'licenses': 15000, + 'support': 25000, + 'security': 10000 +} + +tco_result = tco_calc.calculate_tco(3) +print(f"3-year TCO: ${tco_result['total_tco']:,.2f}") +``` + +--- + +## Cost Optimization + +### Стратегии оптимизации затрат + +#### 1. Right-Sizing +```yaml +# Auto Scaling Configuration +Resources: + AutoScalingGroup: + Type: AWS::AutoScaling::AutoScalingGroup + Properties: + MinSize: 2 + MaxSize: 10 + DesiredCapacity: 3 + LaunchTemplate: + LaunchTemplateId: !Ref LaunchTemplate + Version: !GetAtt LaunchTemplate.LatestVersionNumber + TargetGroupARNs: + - !Ref TargetGroup + MetricsCollection: + - Granularity: 1Minute + Metrics: + - GroupDesiredCapacity + - GroupInServiceInstances + - GroupTotalInstances + + ScalingPolicy: + Type: AWS::AutoScaling::ScalingPolicy + Properties: + AutoScalingGroupName: !Ref AutoScalingGroup + PolicyType: TargetTrackingScaling + TargetTrackingConfiguration: + TargetValue: 70.0 + PredefinedMetricSpecification: + PredefinedMetricType: ASGAverageCPUUtilization +``` + +#### 2. Reserved Instances +```yaml +# Reserved Instance Purchase +Resources: + ReservedInstance: + Type: AWS::EC2::ReservedInstances + Properties: + InstanceCount: 3 + InstanceType: t3.medium + OfferingType: All Upfront + ProductDescription: Linux/UNIX + ReservedInstancesOfferingId: !Ref ReservedInstancesOffering +``` + +#### 3. Spot Instances +```yaml +# Spot Instance Configuration +Resources: + SpotFleet: + Type: AWS::EC2::SpotFleetRequest + Properties: + SpotFleetRequestConfig: + IamFleetRole: !GetAtt SpotFleetRole.Arn + TargetCapacity: 5 + LaunchSpecifications: + - ImageId: ami-0c02fb55956c7d316 + InstanceType: t3.micro + KeyName: !Ref KeyPair + SecurityGroups: + - !Ref SecurityGroup + UserData: + Fn::Base64: !Sub | + #!/bin/bash + yum update -y + yum install -y aws-cli +``` + +### Мониторинг затрат + +#### 1. Cost Alerts +```yaml +# Cost Budget Alert +Resources: + CostBudget: + Type: AWS::Budgets::Budget + Properties: + Budget: + BudgetName: MonthlyCostBudget + BudgetLimit: + Amount: 1000 + Unit: USD + TimeUnit: MONTHLY + BudgetType: COST + CostFilters: + Service: + - Amazon Elastic Compute Cloud - Compute + - Amazon Relational Database Service + NotificationsWithSubscribers: + - Notification: + NotificationType: ACTUAL + ComparisonOperator: GREATER_THAN + Threshold: 80 + ThresholdType: PERCENTAGE + Subscribers: + - SubscriptionType: EMAIL + Address: admin@example.com +``` + +#### 2. Cost Analysis +```python +import boto3 +from datetime import datetime, timedelta + +def analyze_costs(): + ce_client = boto3.client('ce') + + # Get costs for last 30 days + end_date = datetime.now().strftime('%Y-%m-%d') + start_date = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d') + + response = ce_client.get_cost_and_usage( + TimePeriod={ + 'Start': start_date, + 'End': end_date + }, + Granularity='DAILY', + Metrics=['BlendedCost'], + GroupBy=[ + { + 'Type': 'DIMENSION', + 'Key': 'SERVICE' + } + ] + ) + + # Analyze costs by service + for result in response['ResultsByTime']: + for group in result['Groups']: + service = group['Keys'][0] + cost = group['Metrics']['BlendedCost']['Amount'] + print(f"{service}: ${cost}") + +if __name__ == "__main__": + analyze_costs() +``` + +--- + +## Практические задания + +### Задание 1: Cloud Infrastructure +1. Создайте инфраструктуру в AWS/Azure/GCP +2. Разверните приложение в облаке +3. Настройте мониторинг и алерты + +### Задание 2: Migration Planning +1. Создайте план миграции для существующего приложения +2. Оцените затраты и риски +3. Разработайте стратегию тестирования + +### Задание 3: Cost Optimization +1. Проанализируйте текущие затраты +2. Предложите стратегии оптимизации +3. Реализуйте автоматическое масштабирование + +--- + +## Дополнительные ресурсы + +### Облачные провайдеры +- **AWS**: Amazon Web Services +- **Azure**: Microsoft Azure +- **GCP**: Google Cloud Platform +- **Alibaba Cloud**: Alibaba Cloud +- **IBM Cloud**: IBM Cloud + +### Инструменты +- **Infrastructure as Code**: Terraform, CloudFormation, ARM Templates +- **Container Orchestration**: Kubernetes, Docker Swarm +- **Monitoring**: CloudWatch, Azure Monitor, Stackdriver +- **Cost Management**: AWS Cost Explorer, Azure Cost Management + +--- + +## Контрольные вопросы + +1. Какие модели обслуживания облачных вычислений вы знаете? +2. В чем разница между IaaS, PaaS и SaaS? +3. Какие основные сервисы предоставляют AWS, Azure и GCP? +4. Что такое Cloud-Native архитектура? +5. Какие стратегии миграции в облако существуют? +6. Как оптимизировать затраты в облаке? +7. Что такое 12-Factor App? +8. Как выбрать подходящую стратегию миграции? diff --git a/modules/module9/README.md b/modules/module9/README.md new file mode 100644 index 0000000..6652144 --- /dev/null +++ b/modules/module9/README.md @@ -0,0 +1,123 @@ +# Модуль 9: Управление командой и процессы + +## Навигация +- [🏠 Главная страница курса](../../README.md) + +--- + +## Описание модуля + +Модуль посвящен изучению навыков управления командой разработки, архитектурными процессами и техническими процессами. Студенты изучат принципы технического лидерства, Agile методологии, DevOps культуру и инструменты для обеспечения качества кода. + +## Содержание модуля + +### 1. [Техническое лидерство](sections/01-technical-leadership.md) +- Роль технического лидера +- Обязанности и навыки +- Принятие технических решений +- Наставничество и развитие команды + +### 2. [Управление архитектурными процессами](sections/02-architecture-process-management.md) +- Architecture Review Process +- Architecture Governance +- Непрерывная архитектура +- Инструменты управления + +### 3. [Agile и архитектура](sections/03-agile-architecture.md) +- Принципы Agile архитектуры +- Emergent Architecture +- Architecture in Sprints +- Continuous Architecture + +### 4. [DevOps культура](sections/04-devops-culture.md) +- Принципы DevOps +- CI/CD практики +- Infrastructure as Code +- Monitoring и Logging + +### 5. [Code Review и архитектурные ревью](sections/05-code-review-architecture-review.md) +- Процесс Code Review +- Checklist для ревью +- Архитектурные ревью +- Инструменты и best practices + +### 6. [Стандарты кодирования и инструменты](sections/06-coding-standards-tools.md) +- Общие принципы кодирования +- Стандарты для разных языков +- Инструменты форматирования и линтинга +- Pre-commit hooks + +### 7. [Управление техническим долгом](sections/07-technical-debt-management.md) +- Типы технического долга +- Оценка и приоритизация +- Стратегии управления +- Инструменты анализа + +### 8. [Коммуникация и презентации](sections/08-communication-presentations.md) +- Техническая коммуникация +- Адаптация под аудиторию +- Шаблоны презентаций +- Инструменты коммуникации + +## Ключевые компетенции + +После изучения модуля вы сможете: + +- **Управлять командой разработки** и обеспечивать техническое лидерство +- **Планировать архитектурные процессы** и обеспечивать их соблюдение +- **Интегрировать Agile методологии** в архитектурную работу +- **Внедрять DevOps культуру** и автоматизировать процессы +- **Проводить эффективные ревью** кода и архитектуры +- **Устанавливать стандарты кодирования** и обеспечивать их соблюдение +- **Управлять техническим долгом** и планировать его устранение +- **Эффективно коммуницировать** с различными стейкхолдерами + +## Практические задания + +### Задание 1: Техническое лидерство +1. Создайте план развития команды +2. Проведите архитектурное ревью +3. Примите техническое решение с обоснованием + +### Задание 2: Agile архитектура +1. Спланируйте архитектуру в спринтах +2. Создайте ADR для ключевых решений +3. Проведите retrospective с архитектурным фокусом + +### Задание 3: DevOps процессы +1. Настройте CI/CD pipeline +2. Создайте Infrastructure as Code +3. Настройте мониторинг и логирование + +### Задание 4: Управление качеством +1. Настройте инструменты для code review +2. Создайте стандарты кодирования +3. Проведите анализ технического долга + +## Дополнительные ресурсы + +### Инструменты +- **CI/CD**: GitHub Actions, GitLab CI/CD, Jenkins +- **Code Quality**: SonarQube, CodeClimate, ESLint +- **Documentation**: Confluence, Notion, GitBook +- **Communication**: Slack, Teams, Zoom + +### Книги +- "The Phoenix Project" by Gene Kim +- "The DevOps Handbook" by Gene Kim +- "Clean Code" by Robert Martin +- "The Pragmatic Programmer" by David Thomas + +### Сертификации +- AWS Certified DevOps Engineer +- Microsoft Azure DevOps Engineer +- Google Cloud Professional DevOps Engineer + +## Критерии оценки + +- **Техническое лидерство**: Способность управлять командой и принимать решения +- **Процессы**: Знание и применение архитектурных процессов +- **Agile**: Интеграция Agile методологий в архитектурную работу +- **DevOps**: Понимание и применение DevOps практик +- **Качество**: Обеспечение качества кода и архитектуры +- **Коммуникация**: Эффективная коммуникация с различными аудиториями diff --git a/modules/module9/sections/01-technical-leadership.md b/modules/module9/sections/01-technical-leadership.md new file mode 100644 index 0000000..cb37224 --- /dev/null +++ b/modules/module9/sections/01-technical-leadership.md @@ -0,0 +1,255 @@ +# Техническое лидерство + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +## Роль технического лидера + +**Технический лидер** — это специалист, который сочетает глубокие технические знания с лидерскими качествами для направления технического развития команды и проекта. + +## Обязанности технического лидера + +### 1. Техническое планирование + +```markdown +# Техническое планирование + +## Архитектурное планирование +- Определение технической стратегии +- Выбор технологий и инструментов +- Планирование архитектурных изменений +- Оценка технических рисков + +## Планирование разработки +- Разбиение задач на подзадачи +- Оценка сложности и времени +- Планирование зависимостей +- Координация между командами +``` + +### 2. Наставничество и развитие команды + +**Технический пример - Advanced Team Management System:** + +```java +// Team Management Service +@Service +@Transactional +public class TeamManagementService { + + @Autowired + private DeveloperRepository developerRepository; + + @Autowired + private ProjectRepository projectRepository; + + @Autowired + private SkillAssessmentService skillAssessmentService; + + // Создание команды для проекта + public Team createTeamForProject(Long projectId, TeamCreationRequest request) { + Project project = projectRepository.findById(projectId) + .orElseThrow(() -> new ProjectNotFoundException("Project not found")); + + // Анализ требований проекта + List requirements = analyzeProjectRequirements(project); + + // Подбор команды + List teamMembers = selectTeamMembers(requirements, request.getTeamSize()); + + // Создание команды + Team team = Team.builder() + .name(request.getTeamName()) + .project(project) + .members(teamMembers) + .lead(selectTeamLead(teamMembers)) + .createdDate(LocalDateTime.now()) + .status(TeamStatus.FORMING) + .build(); + + return teamRepository.save(team); + } + + // Оценка навыков команды + public TeamSkillAssessment assessTeamSkills(Long teamId) { + Team team = teamRepository.findById(teamId) + .orElseThrow(() -> new TeamNotFoundException("Team not found")); + + Map teamSkills = new HashMap<>(); + + for (Developer developer : team.getMembers()) { + Map developerSkills = skillAssessmentService.assessDeveloperSkills(developer.getId()); + + for (Map.Entry entry : developerSkills.entrySet()) { + String skill = entry.getKey(); + SkillLevel level = entry.getValue(); + + // Агрегируем навыки команды + teamSkills.merge(skill, level, (existing, newLevel) -> + existing.getLevel() > newLevel.getLevel() ? existing : newLevel); + } + } + + return TeamSkillAssessment.builder() + .teamId(teamId) + .teamSkills(teamSkills) + .assessmentDate(LocalDateTime.now()) + .build(); + } + + private List analyzeProjectRequirements(Project project) { + List technologies = project.getTechnologies(); + List requirements = new ArrayList<>(); + + for (String technology : technologies) { + requirements.add(SkillRequirement.builder() + .skill(technology) + .requiredLevel(SkillLevel.INTERMEDIATE) + .requiredDevelopers(1) + .priority(Priority.HIGH) + .build()); + } + + return requirements; + } + + private List selectTeamMembers(List requirements, int teamSize) { + List availableDevelopers = developerRepository.findAvailableDevelopers(); + + return availableDevelopers.stream() + .limit(teamSize) + .collect(Collectors.toList()); + } + + private Developer selectTeamLead(List teamMembers) { + return teamMembers.stream() + .max(Comparator.comparing(Developer::getExperience)) + .orElse(teamMembers.get(0)); + } +} + +// Code Review Management System +@Service +public class CodeReviewManagementService { + + @Autowired + private CodeReviewRepository codeReviewRepository; + + @Autowired + private DeveloperRepository developerRepository; + + // Создание Code Review + public CodeReview createCodeReview(CreateCodeReviewRequest request) { + Developer author = developerRepository.findById(request.getAuthorId()) + .orElseThrow(() -> new DeveloperNotFoundException("Developer not found")); + + // Подбор ревьюеров + List reviewers = selectReviewers(author, request.getTechnologies()); + + // Создание Code Review + CodeReview review = CodeReview.builder() + .title(request.getTitle()) + .description(request.getDescription()) + .author(author) + .reviewers(reviewers) + .pullRequestId(request.getPullRequestId()) + .status(CodeReviewStatus.PENDING) + .createdDate(LocalDateTime.now()) + .build(); + + return codeReviewRepository.save(review); + } + + // Подбор ревьюеров на основе экспертизы + private List selectReviewers(Developer author, List technologies) { + List availableReviewers = developerRepository.findAvailableReviewers(); + + return availableReviewers.stream() + .filter(reviewer -> !reviewer.equals(author)) + .filter(reviewer -> hasRequiredSkills(reviewer, technologies)) + .limit(3) + .collect(Collectors.toList()); + } + + private boolean hasRequiredSkills(Developer developer, List technologies) { + Map skills = skillAssessmentService.assessDeveloperSkills(developer.getId()); + + return technologies.stream() + .allMatch(tech -> skills.containsKey(tech) && + skills.get(tech).getLevel() >= SkillLevel.INTERMEDIATE.getLevel()); + } +} +``` + +### 3. Принятие технических решений + +```markdown +# Процесс принятия технических решений + +## 1. Анализ проблемы +- Понимание бизнес-требований +- Анализ технических ограничений +- Оценка влияния на существующую систему + +## 2. Исследование решений +- Поиск альтернативных подходов +- Анализ преимуществ и недостатков +- Изучение лучших практик + +## 3. Принятие решения +- Консультация с командой +- Документирование решения (ADR) +- Коммуникация решения стейкхолдерам + +## 4. Реализация и мониторинг +- Планирование реализации +- Отслеживание прогресса +- Оценка результатов +``` + +## Ключевые навыки технического лидера + +### 1. Техническая экспертиза +- Глубокое понимание технологий +- Знание архитектурных паттернов +- Опыт в решении сложных задач + +### 2. Лидерские качества +- Способность вдохновлять команду +- Умение принимать решения +- Эффективная коммуникация + +### 3. Стратегическое мышление +- Видение технического развития +- Планирование на долгосрочную перспективу +- Анализ рисков и возможностей + +### 4. Управление изменениями +- Внедрение новых технологий +- Управление техническим долгом +- Адаптация к изменениям + +## Лучшие практики + +### 1. Создание технической культуры +- Поощрение инноваций +- Обмен знаниями +- Непрерывное обучение + +### 2. Управление конфликтами +- Разрешение технических споров +- Поиск компромиссов +- Фокус на результатах + +### 3. Развитие команды +- Выявление потенциала +- Планирование карьерного роста +- Создание возможностей для роста + +### 4. Коммуникация +- Ясное изложение технических концепций +- Адаптация сообщения под аудиторию +- Активное слушание diff --git a/modules/module9/sections/02-architecture-process-management.md b/modules/module9/sections/02-architecture-process-management.md new file mode 100644 index 0000000..8ffc8db --- /dev/null +++ b/modules/module9/sections/02-architecture-process-management.md @@ -0,0 +1,296 @@ +# Управление архитектурными процессами + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +## Архитектурные процессы + +### 1. Architecture Review Process + +```markdown +# Процесс архитектурного ревью + +## Подготовка к ревью +1. **Создание архитектурной диаграммы** + - C4 Model диаграммы + - Sequence диаграммы + - Deployment диаграммы + +2. **Подготовка ADR** + - Описание проблемы + - Анализ решений + - Обоснование выбора + +3. **Анализ рисков** + - Технические риски + - Бизнес-риски + - Планы митигации + +## Проведение ревью +1. **Презентация (30 мин)** + - Обзор архитектуры + - Ключевые решения + - Влияние на систему + +2. **Обсуждение (30 мин)** + - Вопросы и уточнения + - Альтернативные решения + - Детали реализации + +3. **Принятие решения (15 мин)** + - Голосование + - Условия одобрения + - Следующие шаги +``` + +### 2. Architecture Governance + +```yaml +# Architecture Governance Framework +architecture_governance: + principles: + - name: "Technology Standardization" + description: "Use standardized technologies across the organization" + enforcement: "Mandatory" + + - name: "Security by Design" + description: "Security must be considered from the beginning" + enforcement: "Mandatory" + + - name: "Scalability" + description: "Systems must be designed for scalability" + enforcement: "Recommended" + + review_process: + triggers: + - "New system development" + - "Major system changes" + - "Technology adoption" + + participants: + - "Solution Architect" + - "Technical Lead" + - "Security Architect" + - "Business Analyst" + + criteria: + - "Alignment with principles" + - "Technical feasibility" + - "Business value" + - "Risk assessment" + + compliance: + monitoring: + - "Regular architecture reviews" + - "Code review checklists" + - "Automated compliance checks" + + reporting: + - "Monthly compliance reports" + - "Quarterly architecture health checks" + - "Annual architecture assessment" +``` + +## Непрерывная архитектура + +### 1. Architecture as Code + +```yaml +# Architecture Definition +architecture: + version: "1.0" + name: "E-commerce Platform" + + components: + - name: "User Service" + type: "microservice" + technology: "Java Spring Boot" + responsibilities: + - "User management" + - "Authentication" + - "Authorization" + interfaces: + - "REST API" + - "Event publishing" + + - name: "Order Service" + type: "microservice" + technology: "Java Spring Boot" + responsibilities: + - "Order processing" + - "Order state management" + - "Order history" + interfaces: + - "REST API" + - "Event consumption" + - "Event publishing" + + relationships: + - from: "User Service" + to: "Order Service" + type: "synchronous" + protocol: "HTTP/REST" + purpose: "User validation for orders" + + - from: "Order Service" + to: "Payment Service" + type: "asynchronous" + protocol: "Events" + purpose: "Payment processing" + + quality_attributes: + - name: "Performance" + target: "95% of requests < 200ms" + measurement: "Response time monitoring" + + - name: "Availability" + target: "99.9% uptime" + measurement: "Uptime monitoring" + + - name: "Scalability" + target: "Handle 10,000 RPS" + measurement: "Load testing" +``` + +### 2. Architecture Evolution + +```markdown +# Процесс эволюции архитектуры + +## 1. Мониторинг архитектуры +- Отслеживание метрик качества +- Анализ технического долга +- Обратная связь от команды + +## 2. Идентификация проблем +- Производительность +- Масштабируемость +- Поддерживаемость +- Безопасность + +## 3. Планирование изменений +- Приоритизация проблем +- Разработка решений +- Планирование миграции + +## 4. Реализация изменений +- Поэтапная миграция +- Тестирование изменений +- Мониторинг результатов +``` + +## Инструменты управления архитектурой + +### 1. Architecture Decision Records (ADR) + +```markdown +# ADR-001: Выбор базы данных для пользовательского сервиса + +## Статус +Принято + +## Контекст +Нужно выбрать базу данных для хранения пользовательских данных в новом микросервисе. + +## Варианты +1. PostgreSQL - реляционная БД +2. MongoDB - документная БД +3. DynamoDB - NoSQL БД + +## Решение +Выбрана PostgreSQL по следующим причинам: +- ACID транзакции для критических данных +- Зрелая экосистема и инструменты +- Опыт команды с SQL +- Хорошая производительность для наших объемов + +## Последствия +- Нужно спроектировать схему БД +- Настроить репликацию для отказоустойчивости +- Реализовать миграции данных +``` + +### 2. Architecture Compliance + +```yaml +# Architecture Compliance Check +compliance_check: + rules: + - name: "Microservice Communication" + description: "Microservices must communicate only through APIs" + check: "No direct database access between services" + severity: "Error" + + - name: "Security Headers" + description: "All HTTP responses must include security headers" + check: "X-Content-Type-Options, X-Frame-Options, X-XSS-Protection" + severity: "Warning" + + - name: "Logging Standards" + description: "All services must use structured logging" + check: "JSON format with correlation ID" + severity: "Info" + + enforcement: + - "Pre-commit hooks" + - "CI/CD pipeline checks" + - "Architecture review process" +``` + +### 3. Architecture Metrics + +```yaml +# Architecture Quality Metrics +architecture_metrics: + performance: + - name: "Response Time" + target: "< 200ms" + measurement: "95th percentile" + + - name: "Throughput" + target: "> 1000 RPS" + measurement: "Requests per second" + + reliability: + - name: "Uptime" + target: "99.9%" + measurement: "Monthly availability" + + - name: "Error Rate" + target: "< 0.1%" + measurement: "Failed requests percentage" + + maintainability: + - name: "Code Coverage" + target: "> 80%" + measurement: "Unit test coverage" + + - name: "Technical Debt" + target: "< 5%" + measurement: "SonarQube technical debt ratio" +``` + +## Best Practices + +### 1. Governance +- Установка четких принципов +- Регулярные ревью +- Автоматизация проверок + +### 2. Документация +- Ведение ADR +- Обновление диаграмм +- Синхронизация с кодом + +### 3. Мониторинг +- Отслеживание метрик +- Анализ трендов +- Проактивные действия + +### 4. Эволюция +- Планирование изменений +- Поэтапная миграция +- Обратная связь от команды diff --git a/modules/module9/sections/03-agile-architecture.md b/modules/module9/sections/03-agile-architecture.md new file mode 100644 index 0000000..e99413e --- /dev/null +++ b/modules/module9/sections/03-agile-architecture.md @@ -0,0 +1,269 @@ +# Agile и архитектура + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +## Принципы Agile архитектуры + +### 1. Emergent Architecture + +**Принцип:** Архитектура возникает в процессе разработки, а не планируется заранее. + +```markdown +# Emergent Architecture Process + +## Итерация 1: MVP +- Простая монолитная архитектура +- Основной функционал +- Быстрый вывод на рынок + +## Итерация 2: Масштабирование +- Добавление кэширования +- Оптимизация производительности +- Улучшение пользовательского опыта + +## Итерация 3: Микросервисы +- Выделение критических сервисов +- Независимое развертывание +- Улучшение надежности + +## Итерация 4: Оптимизация +- Рефакторинг архитектуры +- Улучшение производительности +- Снижение затрат +``` + +### 2. Architecture in Sprints + +```markdown +# Архитектура в спринтах + +## Sprint 0: Architecture Foundation +- Определение архитектурных принципов +- Выбор технологического стека +- Создание базовой инфраструктуры + +## Sprint 1-N: Feature Development +- Реализация пользовательских историй +- Эволюция архитектуры +- Непрерывная интеграция + +## Sprint N+1: Architecture Review +- Анализ архитектурных решений +- Планирование улучшений +- Рефакторинг при необходимости +``` + +### 3. Continuous Architecture + +```markdown +# Непрерывная архитектура + +## Еженедельные ADR ревью +- Обсуждение архитектурных решений +- Документирование изменений +- Обучение команды + +## Sprint Planning с архитектурным фокусом +- Учет архитектурных требований +- Планирование технических задач +- Баланс между функциональностью и архитектурой + +## Retrospective с архитектурным фокусом +- Анализ архитектурных решений +- Выявление проблем +- Планирование улучшений +``` + +## Agile архитектурные практики + +### 1. User Story Mapping + +```markdown +# User Story Mapping для архитектуры + +## Epic: User Management +- **As a** user +- **I want to** manage my account +- **So that** I can use the system + +### User Stories: +1. **As a** user, **I want to** register an account +2. **As a** user, **I want to** login to my account +3. **As a** user, **I want to** update my profile +4. **As a** user, **I want to** change my password + +### Technical Stories: +1. **As a** developer, **I want to** implement user authentication +2. **As a** developer, **I want to** implement user authorization +3. **As a** developer, **I want to** implement user data storage +4. **As a** developer, **I want to** implement user data validation +``` + +### 2. Architecture Spikes + +```markdown +# Architecture Spike: Microservices Communication + +## Цель +Исследовать способы коммуникации между микросервисами + +## Вопросы +- Какой протокол использовать (HTTP, gRPC, Message Queue)? +- Как обеспечить надежность коммуникации? +- Как обрабатывать ошибки? + +## План +1. Создать простые микросервисы +2. Реализовать разные способы коммуникации +3. Протестировать производительность +4. Оценить сложность реализации + +## Результат +- Рекомендация по выбору протокола +- Примеры реализации +- Оценка времени на внедрение +``` + +### 3. Architecture Katas + +```markdown +# Architecture Kata: E-commerce Platform + +## Задача +Спроектировать архитектуру платформы электронной коммерции + +## Требования +- 1M пользователей +- 10K заказов в день +- 99.9% доступность +- < 200ms время отклика + +## Ограничения +- Бюджет: $100K/месяц +- Команда: 10 разработчиков +- Время: 6 месяцев + +## Подход +1. Анализ требований +2. Выбор архитектурного стиля +3. Проектирование компонентов +4. Планирование реализации +5. Оценка рисков +``` + +## Инструменты Agile архитектуры + +### 1. Architecture Decision Records (ADR) + +```markdown +# ADR-002: Выбор архитектурного стиля + +## Статус +Принято + +## Контекст +Нужно выбрать архитектурный стиль для нового проекта + +## Варианты +1. Монолитная архитектура +2. Микросервисная архитектура +3. Модульная монолитная архитектура + +## Решение +Выбрана модульная монолитная архитектура по следующим причинам: +- Простота разработки и развертывания +- Возможность эволюции в микросервисы +- Подходит для команды из 5 разработчиков +- Быстрый time-to-market + +## Последствия +- Нужно спроектировать модульную структуру +- Подготовить план миграции в микросервисы +- Настроить мониторинг модулей +``` + +### 2. Architecture Backlog + +```markdown +# Architecture Backlog + +## High Priority +- [ ] Реализовать аутентификацию и авторизацию +- [ ] Настроить мониторинг и логирование +- [ ] Создать базовую инфраструктуру + +## Medium Priority +- [ ] Реализовать кэширование +- [ ] Настроить автоматическое тестирование +- [ ] Создать CI/CD pipeline + +## Low Priority +- [ ] Оптимизировать производительность +- [ ] Улучшить безопасность +- [ ] Добавить метрики бизнес-логики +``` + +### 3. Architecture Metrics + +```yaml +# Architecture Quality Metrics +architecture_metrics: + performance: + - name: "Response Time" + target: "< 200ms" + current: "150ms" + trend: "improving" + + - name: "Throughput" + target: "> 1000 RPS" + current: "800 RPS" + trend: "stable" + + reliability: + - name: "Uptime" + target: "99.9%" + current: "99.5%" + trend: "improving" + + - name: "Error Rate" + target: "< 0.1%" + current: "0.05%" + trend: "stable" + + maintainability: + - name: "Code Coverage" + target: "> 80%" + current: "75%" + trend: "improving" + + - name: "Technical Debt" + target: "< 5%" + current: "8%" + trend: "worsening" +``` + +## Best Practices + +### 1. Планирование +- Включать архитектурные задачи в спринты +- Планировать архитектурные спайки +- Учитывать технический долг + +### 2. Коммуникация +- Регулярные архитектурные ревью +- Документирование решений +- Обучение команды + +### 3. Мониторинг +- Отслеживание архитектурных метрик +- Анализ технического долга +- Проактивные действия + +### 4. Эволюция +- Планирование архитектурных изменений +- Поэтапная миграция +- Обратная связь от команды diff --git a/modules/module9/sections/04-devops-culture.md b/modules/module9/sections/04-devops-culture.md new file mode 100644 index 0000000..8c406e7 --- /dev/null +++ b/modules/module9/sections/04-devops-culture.md @@ -0,0 +1,429 @@ +# DevOps культура + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +## Принципы DevOps + +### 1. Collaboration +- Тесное взаимодействие между разработкой и операциями +- Общие цели и метрики +- Культура взаимопомощи + +### 2. Automation +- Автоматизация рутинных процессов +- Infrastructure as Code +- Continuous Integration/Continuous Deployment + +### 3. Monitoring +- Непрерывный мониторинг системы +- Проактивное выявление проблем +- Быстрое восстановление + +### 4. Feedback +- Быстрая обратная связь +- Непрерывное улучшение +- Обучение на ошибках + +## DevOps Practices + +### 1. Continuous Integration (CI) + +```yaml +# GitHub Actions CI Pipeline +name: CI/CD Pipeline + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Maven dependencies + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + - name: Run tests + run: mvn clean test + + - name: Generate test report + uses: dorny/test-reporter@v1 + if: success() || failure() + with: + name: Maven Tests + path: target/surefire-reports/*.xml + reporter: java-junit + + build: + needs: test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Build with Maven + run: mvn clean package -DskipTests + + - name: Build Docker image + run: docker build -t myapp:${{ github.sha }} . + + - name: Push to registry + run: | + echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin + docker push myapp:${{ github.sha }} + + deploy: + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + + steps: + - name: Deploy to production + run: | + echo "Deploying to production..." + # Deployment logic here +``` + +### 2. Continuous Deployment (CD) + +```yaml +# Kubernetes Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp + labels: + app: myapp +spec: + replicas: 3 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:latest + ports: + - containerPort: 8080 + env: + - name: SPRING_PROFILES_ACTIVE + value: "production" + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: myapp-secrets + key: database-url + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + +--- +apiVersion: v1 +kind: Service +metadata: + name: myapp-service +spec: + selector: + app: myapp + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + type: LoadBalancer +``` + +### 3. Infrastructure as Code (IaC) + +```hcl +# Terraform configuration +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.aws_region +} + +# VPC +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "${var.project_name}-vpc" + } +} + +# Subnets +resource "aws_subnet" "public" { + count = length(var.availability_zones) + + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 1}.0/24" + availability_zone = var.availability_zones[count.index] + map_public_ip_on_launch = true + + tags = { + Name = "${var.project_name}-public-subnet-${count.index + 1}" + } +} + +resource "aws_subnet" "private" { + count = length(var.availability_zones) + + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 10}.0/24" + availability_zone = var.availability_zones[count.index] + + tags = { + Name = "${var.project_name}-private-subnet-${count.index + 1}" + } +} + +# Security Groups +resource "aws_security_group" "web" { + name_prefix = "${var.project_name}-web-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.project_name}-web-sg" + } +} + +# RDS Database +resource "aws_db_subnet_group" "main" { + name = "${var.project_name}-db-subnet-group" + subnet_ids = aws_subnet.private[*].id + + tags = { + Name = "${var.project_name}-db-subnet-group" + } +} + +resource "aws_db_instance" "main" { + identifier = "${var.project_name}-db" + + engine = "postgres" + engine_version = "14.7" + instance_class = var.db_instance_class + + allocated_storage = 20 + max_allocated_storage = 100 + storage_type = "gp2" + storage_encrypted = true + + db_name = var.db_name + username = var.db_username + password = var.db_password + + vpc_security_group_ids = [aws_security_group.db.id] + db_subnet_group_name = aws_db_subnet_group.main.name + + backup_retention_period = 7 + backup_window = "03:00-04:00" + maintenance_window = "sun:04:00-sun:05:00" + + skip_final_snapshot = var.environment != "production" + + tags = { + Name = "${var.project_name}-db" + } +} +``` + +### 4. Monitoring and Logging + +```yaml +# Prometheus configuration +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + - "alert_rules.yml" + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + - job_name: 'myapp' + static_configs: + - targets: ['myapp:8080'] + metrics_path: '/actuator/prometheus' + scrape_interval: 5s + + - job_name: 'node-exporter' + static_configs: + - targets: ['node-exporter:9100'] + +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 + +--- +# Grafana dashboard +apiVersion: v1 +kind: ConfigMap +metadata: + name: grafana-dashboard +data: + dashboard.json: | + { + "dashboard": { + "title": "MyApp Dashboard", + "panels": [ + { + "title": "Request Rate", + "type": "graph", + "targets": [ + { + "expr": "rate(http_requests_total[5m])", + "legendFormat": "{{method}} {{endpoint}}" + } + ] + }, + { + "title": "Response Time", + "type": "graph", + "targets": [ + { + "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))", + "legendFormat": "95th percentile" + } + ] + }, + { + "title": "Error Rate", + "type": "graph", + "targets": [ + { + "expr": "rate(http_requests_total{status=~\"5..\"}[5m])", + "legendFormat": "5xx errors" + } + ] + } + ] + } + } +``` + +## DevOps Tools + +### 1. Version Control +- Git для управления кодом +- GitFlow для управления ветками +- Pull Request для code review + +### 2. CI/CD +- GitHub Actions +- GitLab CI/CD +- Jenkins +- Azure DevOps + +### 3. Containerization +- Docker для контейнеризации +- Kubernetes для оркестрации +- Helm для управления пакетами + +### 4. Monitoring +- Prometheus для метрик +- Grafana для визуализации +- ELK Stack для логирования +- Jaeger для трассировки + +## Best Practices + +### 1. Culture +- Создание культуры доверия +- Общие цели и метрики +- Непрерывное обучение + +### 2. Automation +- Автоматизация всех процессов +- Infrastructure as Code +- Self-healing systems + +### 3. Monitoring +- Comprehensive monitoring +- Proactive alerting +- Fast incident response + +### 4. Security +- Security by design +- Automated security testing +- Regular security audits diff --git a/modules/module9/sections/05-code-review-architecture-review.md b/modules/module9/sections/05-code-review-architecture-review.md new file mode 100644 index 0000000..a0f1aab --- /dev/null +++ b/modules/module9/sections/05-code-review-architecture-review.md @@ -0,0 +1,333 @@ +# Code Review и архитектурные ревью + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +## Code Review + +### Цели Code Review + +#### 1. Качество кода +- Соответствие стандартам кодирования +- Читаемость и поддерживаемость +- Производительность и безопасность + +#### 2. Обучение команды +- Обмен знаниями +- Передача лучших практик +- Развитие навыков + +#### 3. Выявление проблем +- Баги и уязвимости +- Архитектурные проблемы +- Технический долг + +### Процесс Code Review + +```markdown +# Code Review Process + +## 1. Подготовка +- Создание Pull Request +- Написание описания изменений +- Добавление тестов + +## 2. Ревью +- Проверка кода +- Комментирование +- Обсуждение решений + +## 3. Исправления +- Устранение замечаний +- Обновление кода +- Повторное ревью + +## 4. Мерж +- Одобрение изменений +- Мерж в основную ветку +- Удаление ветки +``` + +### Checklist для Code Review + +```markdown +# Code Review Checklist + +## Функциональность +- [ ] Код выполняет требуемую функциональность +- [ ] Обрабатываются все edge cases +- [ ] Нет неиспользуемого кода +- [ ] Нет закомментированного кода + +## Качество кода +- [ ] Код читаемый и понятный +- [ ] Соблюдены стандарты кодирования +- [ ] Нет дублирования кода +- [ ] Правильная структура классов и методов + +## Производительность +- [ ] Нет неэффективных алгоритмов +- [ ] Правильное использование ресурсов +- [ ] Нет memory leaks +- [ ] Оптимизированы database queries + +## Безопасность +- [ ] Нет уязвимостей безопасности +- [ ] Правильная валидация входных данных +- [ ] Безопасное хранение паролей +- [ ] Нет SQL injection + +## Тестирование +- [ ] Покрытие тестами +- [ ] Тесты проходят +- [ ] Тесты покрывают edge cases +- [ ] Нет flaky tests + +## Документация +- [ ] Обновлена документация +- [ ] Добавлены комментарии +- [ ] Обновлены README +- [ ] Обновлены API docs +``` + +### Инструменты Code Review + +#### 1. GitHub Pull Requests +```yaml +# GitHub Actions для автоматического ревью +name: Code Review + +on: + pull_request: + types: [opened, synchronize] + +jobs: + code-review: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Run SonarQube analysis + uses: sonarqube-quality-gate-action@master + with: + scanMetadataReportFile: target/sonar/report-task.txt + + - name: Run security scan + uses: securecodewarrior/github-action-add-sarif@v1 + with: + sarif-file: 'security-scan-results.sarif' + + - name: Run dependency check + uses: dependency-check/Dependency-Check_Action@main + with: + project: 'MyProject' + path: '.' + format: 'SARIF' + out: 'dependency-check-report.sarif' +``` + +#### 2. SonarQube +```yaml +# SonarQube configuration +sonar: + projectKey: myproject + projectName: My Project + projectVersion: 1.0 + + sources: + - src/main/java + + exclusions: + - "**/test/**" + - "**/generated/**" + + coverage: + jacoco: + reportPath: target/jacoco.exec + + quality: + rules: + - "java:S1068" # Unused private fields + - "java:S1186" # Empty methods + - "java:S1481" # Unused local variables +``` + +## Архитектурные ревью + +### Цели архитектурного ревью + +#### 1. Соответствие принципам +- Архитектурные принципы +- Паттерны и best practices +- Стандарты организации + +#### 2. Качественные атрибуты +- Производительность +- Масштабируемость +- Надежность +- Безопасность + +#### 3. Техническая реализуемость +- Выбор технологий +- Сложность реализации +- Временные рамки + +### Процесс архитектурного ревью + +```markdown +# Architecture Review Process + +## 1. Подготовка +- Создание архитектурной диаграммы +- Подготовка ADR +- Анализ рисков + +## 2. Презентация +- Обзор архитектуры +- Ключевые решения +- Влияние на систему + +## 3. Обсуждение +- Вопросы и уточнения +- Альтернативные решения +- Детали реализации + +## 4. Принятие решения +- Голосование +- Условия одобрения +- Следующие шаги +``` + +### Checklist для архитектурного ревью + +```markdown +# Architecture Review Checklist + +## Архитектурные принципы +- [ ] Соответствие принципам SOLID +- [ ] Соблюдение принципов DRY и KISS +- [ ] Правильное разделение ответственности +- [ ] Слабая связанность компонентов + +## Качественные атрибуты +- [ ] Производительность соответствует требованиям +- [ ] Масштабируемость обеспечена +- [ ] Надежность гарантирована +- [ ] Безопасность учтена + +## Техническая реализуемость +- [ ] Технологии подходят для задачи +- [ ] Команда имеет необходимые навыки +- [ ] Инфраструктура поддерживает решение +- [ ] Временные рамки реалистичны + +## Риски и митигации +- [ ] Основные риски идентифицированы +- [ ] План митигации разработан +- [ ] Fallback стратегии определены +- [ ] Мониторинг рисков настроен +``` + +### Инструменты архитектурного ревью + +#### 1. Architecture Decision Records (ADR) +```markdown +# ADR-001: Выбор базы данных + +## Статус +Принято + +## Контекст +Нужно выбрать базу данных для хранения пользовательских данных + +## Варианты +1. PostgreSQL - реляционная БД +2. MongoDB - документная БД +3. DynamoDB - NoSQL БД + +## Решение +Выбрана PostgreSQL по следующим причинам: +- ACID транзакции для критических данных +- Зрелая экосистема и инструменты +- Опыт команды с SQL +- Хорошая производительность для наших объемов + +## Последствия +- Нужно спроектировать схему БД +- Настроить репликацию для отказоустойчивости +- Реализовать миграции данных +``` + +#### 2. Architecture Diagrams +```plantuml +@startuml +!theme plain +skinparam componentStyle rectangle + +package "E-commerce Platform" { + [Web Application] as Web + [Mobile App] as Mobile + + package "API Gateway" { + [Load Balancer] as LB + [Rate Limiter] as RL + [Authentication] as Auth + } + + package "Microservices" { + [User Service] as US + [Product Service] as PS + [Order Service] as OS + [Payment Service] as PayS + } + + package "Data Layer" { + [User DB] as UDB + [Product DB] as PDB + [Order DB] as ODB + [Payment DB] as PayDB + } +} + +Web --> LB +Mobile --> LB +LB --> RL +RL --> Auth +Auth --> US +Auth --> PS +Auth --> OS +Auth --> PayS + +US --> UDB +PS --> PDB +OS --> ODB +PayS --> PayDB + +@enduml +``` + +## Best Practices + +### 1. Code Review +- Регулярные ревью +- Конструктивная критика +- Обучение команды + +### 2. Architecture Review +- Планирование ревью +- Подготовка материалов +- Документирование решений + +### 3. Инструменты +- Автоматизация проверок +- Интеграция с CI/CD +- Метрики качества + +### 4. Культура +- Открытость к критике +- Непрерывное обучение +- Совместная ответственность diff --git a/modules/module9/sections/06-coding-standards-tools.md b/modules/module9/sections/06-coding-standards-tools.md new file mode 100644 index 0000000..ef49b7a --- /dev/null +++ b/modules/module9/sections/06-coding-standards-tools.md @@ -0,0 +1,516 @@ +# Стандарты кодирования и инструменты + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +## Общие принципы + +### 1. Читаемость +- Код должен быть понятен без комментариев +- Использование осмысленных имен +- Правильная структура и форматирование + +### 2. Поддерживаемость +- Модульная структура +- Слабая связанность +- Высокая когезия + +### 3. Производительность +- Эффективные алгоритмы +- Оптимальное использование ресурсов +- Профилирование и оптимизация + +## Стандарты кодирования + +### 1. Именование + +#### Java +```java +// Классы - PascalCase +public class UserService { + + // Константы - UPPER_SNAKE_CASE + private static final String DEFAULT_ROLE = "USER"; + + // Поля - camelCase + private String userName; + private int userAge; + + // Методы - camelCase + public void createUser(String userName, int userAge) { + // Локальные переменные - camelCase + String userRole = DEFAULT_ROLE; + + // Временные переменные - короткие имена + for (int i = 0; i < users.size(); i++) { + User user = users.get(i); + processUser(user); + } + } + + // Булевы методы - is/has/can + public boolean isUserActive() { + return userStatus == UserStatus.ACTIVE; + } + + public boolean hasPermission(String permission) { + return userPermissions.contains(permission); + } +} +``` + +#### Python +```python +# Модули и пакеты - snake_case +import user_service +from models.user import User + +# Классы - PascalCase +class UserService: + + # Константы - UPPER_SNAKE_CASE + DEFAULT_ROLE = "USER" + + # Методы и переменные - snake_case + def create_user(self, user_name: str, user_age: int) -> User: + # Локальные переменные - snake_case + user_role = self.DEFAULT_ROLE + + # Временные переменные - короткие имена + for i, user in enumerate(self.users): + self.process_user(user) + + return User(user_name, user_age, user_role) + + # Булевы методы - is/has/can + def is_user_active(self) -> bool: + return self.user_status == UserStatus.ACTIVE + + def has_permission(self, permission: str) -> bool: + return permission in self.user_permissions +``` + +#### JavaScript/TypeScript +```typescript +// Файлы - kebab-case +// user-service.ts + +// Классы - PascalCase +class UserService { + + // Константы - UPPER_SNAKE_CASE + private static readonly DEFAULT_ROLE = 'USER'; + + // Методы и переменные - camelCase + public createUser(userName: string, userAge: number): User { + // Локальные переменные - camelCase + const userRole = UserService.DEFAULT_ROLE; + + // Временные переменные - короткие имена + for (let i = 0; i < this.users.length; i++) { + const user = this.users[i]; + this.processUser(user); + } + + return new User(userName, userAge, userRole); + } + + // Булевы методы - is/has/can + public isUserActive(): boolean { + return this.userStatus === UserStatus.ACTIVE; + } + + public hasPermission(permission: string): boolean { + return this.userPermissions.includes(permission); + } +} +``` + +### 2. Структура файлов + +#### Java +``` +src/ +├── main/ +│ ├── java/ +│ │ └── com/ +│ │ └── company/ +│ │ └── project/ +│ │ ├── controller/ +│ │ │ └── UserController.java +│ │ ├── service/ +│ │ │ └── UserService.java +│ │ ├── repository/ +│ │ │ └── UserRepository.java +│ │ ├── model/ +│ │ │ └── User.java +│ │ └── config/ +│ │ └── DatabaseConfig.java +│ └── resources/ +│ ├── application.yml +│ └── logback-spring.xml +└── test/ + └── java/ + └── com/ + └── company/ + └── project/ + ├── controller/ + │ └── UserControllerTest.java + └── service/ + └── UserServiceTest.java +``` + +#### Python +``` +project/ +├── src/ +│ └── project/ +│ ├── __init__.py +│ ├── controllers/ +│ │ ├── __init__.py +│ │ └── user_controller.py +│ ├── services/ +│ │ ├── __init__.py +│ │ └── user_service.py +│ ├── repositories/ +│ │ ├── __init__.py +│ │ └── user_repository.py +│ ├── models/ +│ │ ├── __init__.py +│ │ └── user.py +│ └── config/ +│ ├── __init__.py +│ └── database.py +├── tests/ +│ ├── __init__.py +│ ├── test_controllers/ +│ │ └── test_user_controller.py +│ └── test_services/ +│ └── test_user_service.py +├── requirements.txt +└── setup.py +``` + +### 3. Комментарии + +#### Java +```java +/** + * Сервис для управления пользователями + * + * @author John Doe + * @version 1.0 + * @since 2024-01-01 + */ +@Service +public class UserService { + + /** + * Создает нового пользователя + * + * @param userName имя пользователя + * @param userAge возраст пользователя + * @return созданный пользователь + * @throws IllegalArgumentException если имя пользователя пустое + */ + public User createUser(String userName, int userAge) { + if (userName == null || userName.trim().isEmpty()) { + throw new IllegalArgumentException("User name cannot be empty"); + } + + // Создаем пользователя с дефолтной ролью + User user = new User(userName, userAge, DEFAULT_ROLE); + + // Сохраняем в базе данных + return userRepository.save(user); + } + + /** + * Проверяет, активен ли пользователь + * + * @return true если пользователь активен, false иначе + */ + public boolean isUserActive() { + return userStatus == UserStatus.ACTIVE; + } +} +``` + +#### Python +```python +""" +Сервис для управления пользователями + +Автор: John Doe +Версия: 1.0 +Дата: 2024-01-01 +""" + +class UserService: + """Сервис для управления пользователями""" + + def create_user(self, user_name: str, user_age: int) -> User: + """ + Создает нового пользователя + + Args: + user_name: имя пользователя + user_age: возраст пользователя + + Returns: + созданный пользователь + + Raises: + ValueError: если имя пользователя пустое + """ + if not user_name or not user_name.strip(): + raise ValueError("User name cannot be empty") + + # Создаем пользователя с дефолтной ролью + user = User(user_name, user_age, self.DEFAULT_ROLE) + + # Сохраняем в базе данных + return self.user_repository.save(user) + + def is_user_active(self) -> bool: + """ + Проверяет, активен ли пользователь + + Returns: + True если пользователь активен, False иначе + """ + return self.user_status == UserStatus.ACTIVE +``` + +## Инструменты + +### 1. Форматирование кода + +#### Java - Spotless +```gradle +// build.gradle +plugins { + id "com.diffplug.spotless" version "6.19.0" +} + +spotless { + java { + googleJavaFormat() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + } +} +``` + +#### Python - Black +```toml +# pyproject.toml +[tool.black] +line-length = 88 +target-version = ['py38'] +include = '\.pyi?$' +extend-exclude = ''' +/( + # directories + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | build + | dist +)/ +''' +``` + +#### JavaScript/TypeScript - Prettier +```json +// .prettierrc +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false +} +``` + +### 2. Линтеры + +#### Java - Checkstyle +```xml + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +#### Python - flake8 +```ini +# .flake8 +[flake8] +max-line-length = 88 +extend-ignore = E203, W503 +exclude = + .git, + __pycache__, + .venv, + .eggs, + *.egg, + build, + dist +``` + +#### JavaScript/TypeScript - ESLint +```json +// .eslintrc.json +{ + "extends": [ + "eslint:recommended", + "@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "rules": { + "no-console": "warn", + "no-unused-vars": "error", + "@typescript-eslint/no-explicit-any": "warn", + "prefer-const": "error" + } +} +``` + +### 3. Type Checkers + +#### Python - mypy +```ini +# mypy.ini +[mypy] +python_version = 3.8 +warn_return_any = True +warn_unused_configs = True +disallow_untyped_defs = True +disallow_incomplete_defs = True +check_untyped_defs = True +disallow_untyped_decorators = True +no_implicit_optional = True +warn_redundant_casts = True +warn_unused_ignores = True +warn_no_return = True +warn_unreachable = True +strict_equality = True +``` + +#### TypeScript - tsc +```json +// tsconfig.json +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} +``` + +### 4. Pre-commit Hooks + +```yaml +# .pre-commit-config.yaml +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + - repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black + language_version: python3 + + - repo: https://github.com/pycqa/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.3.0 + hooks: + - id: mypy + additional_dependencies: [types-requests] + + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.42.0 + hooks: + - id: eslint + files: \.(js|ts)$ + types: [file] +``` + +## Best Practices + +### 1. Консистентность +- Единые стандарты в команде +- Автоматизация форматирования +- Регулярные ревью + +### 2. Читаемость +- Осмысленные имена +- Правильная структура +- Минимум комментариев + +### 3. Поддерживаемость +- Модульная архитектура +- Слабая связанность +- Высокая когезия + +### 4. Производительность +- Эффективные алгоритмы +- Профилирование +- Оптимизация diff --git a/modules/module9/sections/07-technical-debt-management.md b/modules/module9/sections/07-technical-debt-management.md new file mode 100644 index 0000000..f9d97bd --- /dev/null +++ b/modules/module9/sections/07-technical-debt-management.md @@ -0,0 +1,392 @@ +# Управление техническим долгом + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +## Что такое технический долг + +**Технический долг** — это накопленные в процессе разработки технические проблемы, которые требуют дополнительных усилий для их решения в будущем. + +## Типы технического долга + +### 1. Проблемы кода +- Дублирование кода +- Сложные методы +- Неиспользуемый код +- Плохие имена переменных + +### 2. Проблемы архитектуры +- Нарушение принципов SOLID +- Слабая связанность +- Отсутствие абстракций +- Неправильное разделение ответственности + +### 3. Проблемы инфраструктуры +- Устаревшие зависимости +- Неоптимальная конфигурация +- Отсутствие мониторинга +- Проблемы безопасности + +## Оценка технического долга + +### 1. Метрики кода + +```yaml +# SonarQube Quality Gates +quality_gates: + code_smells: + - name: "Code Smells" + threshold: 100 + severity: "Warning" + + - name: "Duplicated Lines" + threshold: 3% + severity: "Warning" + + - name: "Complexity" + threshold: 10 + severity: "Warning" + + bugs: + - name: "Bugs" + threshold: 0 + severity: "Error" + + - name: "Vulnerabilities" + threshold: 0 + severity: "Error" + + coverage: + - name: "Coverage" + threshold: 80% + severity: "Warning" + + - name: "Duplicated Lines" + threshold: 3% + severity: "Warning" +``` + +### 2. Метрики архитектуры + +```yaml +# Architecture Quality Metrics +architecture_metrics: + coupling: + - name: "Afferent Coupling" + threshold: 10 + severity: "Warning" + + - name: "Efferent Coupling" + threshold: 10 + severity: "Warning" + + cohesion: + - name: "Lack of Cohesion" + threshold: 0.5 + severity: "Warning" + + - name: "Tight Coupling" + threshold: 0.8 + severity: "Error" + + complexity: + - name: "Cyclomatic Complexity" + threshold: 10 + severity: "Warning" + + - name: "Cognitive Complexity" + threshold: 15 + severity: "Warning" +``` + +## Управление техническим долгом + +### 1. Идентификация проблем + +```markdown +# Technical Debt Inventory + +## High Priority +- [ ] Устаревшие зависимости (security vulnerabilities) +- [ ] Критические баги в production +- [ ] Проблемы производительности +- [ ] Уязвимости безопасности + +## Medium Priority +- [ ] Дублирование кода +- [ ] Сложные методы +- [ ] Отсутствие тестов +- [ ] Проблемы архитектуры + +## Low Priority +- [ ] Неиспользуемый код +- [ ] Плохие имена переменных +- [ ] Отсутствие документации +- [ ] Стилистические проблемы +``` + +### 2. Приоритизация + +```yaml +# Technical Debt Prioritization Matrix +prioritization: + criteria: + - name: "Impact" + weight: 0.4 + values: [1, 2, 3, 4, 5] + + - name: "Urgency" + weight: 0.3 + values: [1, 2, 3, 4, 5] + + - name: "Effort" + weight: 0.2 + values: [1, 2, 3, 4, 5] + + - name: "Risk" + weight: 0.1 + values: [1, 2, 3, 4, 5] + + scoring: + - name: "High Priority" + threshold: 4.0 + color: "red" + + - name: "Medium Priority" + threshold: 3.0 + color: "yellow" + + - name: "Low Priority" + threshold: 2.0 + color: "green" +``` + +### 3. Планирование исправлений + +```markdown +# Technical Debt Remediation Plan + +## Sprint 1: Critical Issues +- [ ] Обновить устаревшие зависимости +- [ ] Исправить критические баги +- [ ] Устранить уязвимости безопасности + +## Sprint 2: Architecture Issues +- [ ] Рефакторинг сложных методов +- [ ] Устранение дублирования кода +- [ ] Улучшение архитектуры + +## Sprint 3: Quality Issues +- [ ] Добавление тестов +- [ ] Улучшение документации +- [ ] Оптимизация производительности +``` + +## Инструменты управления техническим долгом + +### 1. SonarQube + +```yaml +# sonar-project.properties +sonar.projectKey=myproject +sonar.projectName=My Project +sonar.projectVersion=1.0 + +sonar.sources=src/main/java +sonar.tests=src/test/java +sonar.java.binaries=target/classes +sonar.java.libraries=target/lib/*.jar + +sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml +sonar.junit.reportPaths=target/surefire-reports + +sonar.qualitygate.wait=true +sonar.qualitygate.timeout=300 +``` + +### 2. CodeClimate + +```yaml +# .codeclimate.yml +version: "2" + +checks: + argument-count: + config: + threshold: 4 + complex-logic: + config: + threshold: 4 + file-lines: + config: + threshold: 250 + method-complexity: + config: + threshold: 5 + method-lines: + config: + threshold: 50 + nested-control-flow: + config: + threshold: 4 + return-statements: + config: + threshold: 4 + similar-code: + config: + threshold: 50 + identical-code: + config: + threshold: 50 + +exclude_patterns: + - "spec/**/*" + - "test/**/*" + - "vendor/**/*" + - "node_modules/**/*" +``` + +### 3. Custom Tools + +```python +# technical_debt_analyzer.py +import ast +import os +from typing import List, Dict, Any + +class TechnicalDebtAnalyzer: + def __init__(self): + self.issues = [] + + def analyze_file(self, file_path: str) -> List[Dict[str, Any]]: + """Анализирует файл на предмет технического долга""" + with open(file_path, 'r', encoding='utf-8') as file: + content = file.read() + + tree = ast.parse(content) + issues = [] + + # Анализ сложности методов + for node in ast.walk(tree): + if isinstance(node, ast.FunctionDef): + complexity = self.calculate_complexity(node) + if complexity > 10: + issues.append({ + 'type': 'high_complexity', + 'severity': 'warning', + 'message': f'Method {node.name} has complexity {complexity}', + 'line': node.lineno, + 'file': file_path + }) + + # Анализ длины методов + if len(node.body) > 20: + issues.append({ + 'type': 'long_method', + 'severity': 'info', + 'message': f'Method {node.name} is too long ({len(node.body)} lines)', + 'line': node.lineno, + 'file': file_path + }) + + return issues + + def calculate_complexity(self, node: ast.FunctionDef) -> int: + """Вычисляет цикломатическую сложность метода""" + complexity = 1 # Базовая сложность + + for child in ast.walk(node): + if isinstance(child, (ast.If, ast.While, ast.For, ast.AsyncFor)): + complexity += 1 + elif isinstance(child, ast.ExceptHandler): + complexity += 1 + elif isinstance(child, ast.BoolOp): + complexity += len(child.values) - 1 + + return complexity + + def analyze_project(self, project_path: str) -> List[Dict[str, Any]]: + """Анализирует весь проект""" + all_issues = [] + + for root, dirs, files in os.walk(project_path): + for file in files: + if file.endswith('.py'): + file_path = os.path.join(root, file) + issues = self.analyze_file(file_path) + all_issues.extend(issues) + + return all_issues + + def generate_report(self, issues: List[Dict[str, Any]]) -> str: + """Генерирует отчет о техническом долге""" + report = "# Technical Debt Report\n\n" + + # Группировка по типам + by_type = {} + for issue in issues: + issue_type = issue['type'] + if issue_type not in by_type: + by_type[issue_type] = [] + by_type[issue_type].append(issue) + + # Генерация отчета + for issue_type, type_issues in by_type.items(): + report += f"## {issue_type.replace('_', ' ').title()}\n" + report += f"Count: {len(type_issues)}\n\n" + + for issue in type_issues: + report += f"- **{issue['file']}:{issue['line']}** - {issue['message']}\n" + + report += "\n" + + return report + +# Использование +analyzer = TechnicalDebtAnalyzer() +issues = analyzer.analyze_project('./src') +report = analyzer.generate_report(issues) +print(report) +``` + +## Стратегии управления + +### 1. Проактивное управление +- Регулярные рефакторинги +- Непрерывная интеграция +- Автоматизированные проверки + +### 2. Реактивное управление +- Исправление по мере обнаружения +- Планирование исправлений +- Приоритизация проблем + +### 3. Гибридный подход +- Критические проблемы исправляются немедленно +- Остальные планируются в спринтах +- Регулярные рефакторинги + +## Best Practices + +### 1. Измерение +- Регулярный анализ метрик +- Отслеживание трендов +- Установка пороговых значений + +### 2. Планирование +- Включение в спринты +- Приоритизация по важности +- Оценка усилий + +### 3. Коммуникация +- Информирование команды +- Документирование проблем +- Отчеты для руководства + +### 4. Автоматизация +- Интеграция в CI/CD +- Автоматические проверки +- Уведомления о проблемах diff --git a/modules/module9/sections/08-communication-presentations.md b/modules/module9/sections/08-communication-presentations.md new file mode 100644 index 0000000..f891568 --- /dev/null +++ b/modules/module9/sections/08-communication-presentations.md @@ -0,0 +1,125 @@ +# Коммуникация и презентации + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +## Техническая коммуникация + +### 1. Ежедневные стендапы +- Краткий обзор прогресса +- Обсуждение блокеров +- Планирование на день + +### 2. Еженедельные технические ревью +- Обзор архитектурных решений +- Обсуждение технических проблем +- Планирование улучшений + +### 3. Ежемесячные архитектурные сессии +- Презентация архитектурных изменений +- Обсуждение стратегических решений +- Планирование технической дорожной карты + +## Адаптация сообщения под аудиторию + +### 1. Бизнес-стейкхолдеры +- Фокус на бизнес-ценности +- Простой язык +- Визуальные диаграммы + +### 2. Технические стейкхолдеры +- Детальные технические решения +- Архитектурные диаграммы +- Примеры кода + +## Шаблоны презентаций + +### 1. Слайд 1: Проблема +- Описание текущей ситуации +- Влияние на бизнес +- Необходимость изменений + +### 2. Слайд 2: Решение +- Предлагаемое решение +- Ключевые преимущества +- Архитектурный обзор + +### 3. Слайд 3: Реализация +- План реализации +- Временные рамки +- Ресурсы + +### 4. Слайд 4: Риски +- Основные риски +- Планы митигации +- Fallback стратегии + +## Техническая документация + +### 1. Архитектурные принципы +- SOLID принципы +- Паттерны проектирования +- Best practices + +### 2. Технологический стек +- Выбранные технологии +- Обоснование выбора +- Альтернативы + +### 3. Качественные атрибуты +- Производительность +- Масштабируемость +- Надежность +- Безопасность + +### 4. Реализация +- Детали реализации +- Примеры кода +- Конфигурация + +## Инструменты коммуникации + +### 1. Диаграммы +- C4 Model +- Sequence диаграммы +- Deployment диаграммы + +### 2. Документация +- Confluence +- Notion +- GitBook + +### 3. Презентации +- PowerPoint +- Google Slides +- Prezi + +### 4. Видео +- Loom +- Zoom +- Teams + +## Best Practices + +### 1. Подготовка +- Изучение аудитории +- Подготовка материалов +- Репетиция + +### 2. Презентация +- Четкая структура +- Визуальные элементы +- Интерактивность + +### 3. Обратная связь +- Вопросы и ответы +- Обсуждение +- Действия + +### 4. Последующие действия +- Документирование +- Планирование +- Отслеживание прогресса diff --git a/modules/module9/sections/08-practical-tasks.md b/modules/module9/sections/08-practical-tasks.md new file mode 100644 index 0000000..deee667 --- /dev/null +++ b/modules/module9/sections/08-practical-tasks.md @@ -0,0 +1,24 @@ +# Практические задания + +## Навигация +- [🏠 Главная страница курса](../../../README.md) +- [📚 Модуль 9](../README.md) + +--- + +### Задание 1: Техническое лидерство +1. Проведите архитектурное ревью +2. Создайте план развития команды +3. Проведите техническую презентацию + +### Задание 2: DevOps процессы +1. Настройте CI/CD пайплайн +2. Реализуйте Infrastructure as Code +3. Настройте мониторинг и алерты + +### Задание 3: Управление техническим долгом +1. Проведите аудит технического долга +2. Создайте план рефакторинга +3. Реализуйте меры по предотвращению долга + +--- \ No newline at end of file diff --git a/modules/module9/theory.md b/modules/module9/theory.md new file mode 100644 index 0000000..7c053c8 --- /dev/null +++ b/modules/module9/theory.md @@ -0,0 +1,1918 @@ +# Модуль 9: Управление командой и процессы + +## Содержание модуля +1. [Техническое лидерство](#техническое-лидерство) +2. [Управление архитектурными процессами](#управление-архитектурными-процессами) +3. [Agile и архитектура](#agile-и-архитектура) +4. [DevOps культура](#devops-культура) +5. [Code Review и архитектурные ревью](#code-review-и-архитектурные-ревью) +6. [Стандарты кодирования и инструменты](#стандарты-кодирования-и-инструменты) +7. [Управление техническим долгом](#управление-техническим-долгом) +8. [Коммуникация и презентации](#коммуникация-и-презентации) + +--- + +## Техническое лидерство + +### Роль технического лидера + +**Технический лидер** — это специалист, который сочетает глубокие технические знания с лидерскими качествами для направления технического развития команды и проекта. + +### Обязанности технического лидера + +#### 1. Техническое планирование +```markdown +# Техническое планирование + +## Архитектурное планирование +- Определение технической стратегии +- Выбор технологий и инструментов +- Планирование архитектурных изменений +- Оценка технических рисков + +## Планирование разработки +- Разбиение задач на подзадачи +- Оценка сложности и времени +- Планирование зависимостей +- Координация между командами +``` + +#### 2. Наставничество и развитие команды +**Технический пример - Advanced Team Management System:** + +```java +// Team Management Service +@Service +@Transactional +public class TeamManagementService { + + @Autowired + private DeveloperRepository developerRepository; + + @Autowired + private ProjectRepository projectRepository; + + @Autowired + private SkillAssessmentService skillAssessmentService; + + // Создание команды для проекта + public Team createTeamForProject(Long projectId, TeamCreationRequest request) { + Project project = projectRepository.findById(projectId) + .orElseThrow(() -> new ProjectNotFoundException("Project not found")); + + // Анализ требований проекта + List requirements = analyzeProjectRequirements(project); + + // Подбор команды + List teamMembers = selectTeamMembers(requirements, request.getTeamSize()); + + // Создание команды + Team team = Team.builder() + .name(request.getTeamName()) + .project(project) + .members(teamMembers) + .lead(selectTeamLead(teamMembers)) + .createdDate(LocalDateTime.now()) + .status(TeamStatus.FORMING) + .build(); + + return teamRepository.save(team); + } + + // Оценка навыков команды + public TeamSkillAssessment assessTeamSkills(Long teamId) { + Team team = teamRepository.findById(teamId) + .orElseThrow(() -> new TeamNotFoundException("Team not found")); + + Map teamSkills = new HashMap<>(); + + for (Developer developer : team.getMembers()) { + Map developerSkills = skillAssessmentService.assessDeveloperSkills(developer.getId()); + + for (Map.Entry entry : developerSkills.entrySet()) { + String skill = entry.getKey(); + SkillLevel level = entry.getValue(); + + // Агрегируем навыки команды + teamSkills.merge(skill, level, (existing, newLevel) -> + existing.getLevel() > newLevel.getLevel() ? existing : newLevel); + } + } + + return TeamSkillAssessment.builder() + .teamId(teamId) + .teamSkills(teamSkills) + .assessmentDate(LocalDateTime.now()) + .build(); + } + + private List analyzeProjectRequirements(Project project) { + List technologies = project.getTechnologies(); + List requirements = new ArrayList<>(); + + for (String technology : technologies) { + requirements.add(SkillRequirement.builder() + .skill(technology) + .requiredLevel(SkillLevel.INTERMEDIATE) + .requiredDevelopers(1) + .priority(Priority.HIGH) + .build()); + } + + return requirements; + } + + private List selectTeamMembers(List requirements, int teamSize) { + List availableDevelopers = developerRepository.findAvailableDevelopers(); + + return availableDevelopers.stream() + .limit(teamSize) + .collect(Collectors.toList()); + } + + private Developer selectTeamLead(List teamMembers) { + return teamMembers.stream() + .max(Comparator.comparing(Developer::getExperience)) + .orElse(teamMembers.get(0)); + } +} + +// Code Review Management System +@Service +public class CodeReviewManagementService { + + @Autowired + private CodeReviewRepository codeReviewRepository; + + @Autowired + private DeveloperRepository developerRepository; + + // Создание Code Review + public CodeReview createCodeReview(CreateCodeReviewRequest request) { + Developer author = developerRepository.findById(request.getAuthorId()) + .orElseThrow(() -> new DeveloperNotFoundException("Developer not found")); + + // Подбор ревьюеров + List reviewers = selectReviewers(author, request.getTechnologies()); + + // Создание Code Review + CodeReview review = CodeReview.builder() + .title(request.getTitle()) + .description(request.getDescription()) + .author(author) + .reviewers(reviewers) + .pullRequestId(request.getPullRequestId()) + .status(CodeReviewStatus.PENDING) + .createdDate(LocalDateTime.now()) + .build(); + + return codeReviewRepository.save(review); + } + + // Подбор ревьюеров на основе экспертизы + private List selectReviewers(Developer author, List technologies) { + List availableReviewers = developerRepository.findAvailableReviewers(); + + return availableReviewers.stream() + .filter(reviewer -> !reviewer.equals(author)) + .filter(reviewer -> hasRequiredSkills(reviewer, technologies)) + .limit(3) + .collect(Collectors.toList()); + } + + private boolean hasRequiredSkills(Developer developer, List technologies) { + Map skills = skillAssessmentService.assessDeveloperSkills(developer.getId()); + + return technologies.stream() + .allMatch(tech -> skills.containsKey(tech) && + skills.get(tech).getLevel() >= SkillLevel.INTERMEDIATE.getLevel()); + } +} +``` + +#### 3. Принятие технических решений +```markdown +# Процесс принятия технических решений + +## 1. Анализ проблемы +- Понимание бизнес-требований +- Анализ технических ограничений +- Оценка влияния на существующую систему + +## 2. Исследование решений +- Поиск альтернативных подходов +- Анализ преимуществ и недостатков +- Изучение лучших практик + +## 3. Принятие решения +- Консультация с командой +- Документирование решения (ADR) +- Коммуникация решения стейкхолдерам + +## 4. Реализация и мониторинг +- Планирование реализации +- Отслеживание прогресса +- Оценка результатов +``` + +### Стили технического лидерства + +#### 1. Авторитарный лидер +**Характеристики:** +- Принимает решения единолично +- Четкие инструкции команде +- Быстрые решения + +**Когда использовать:** +- Критические ситуации +- Опытная команда +- Четкие технические требования + +#### 2. Демократический лидер +**Характеристики:** +- Совместное принятие решений +- Учет мнения команды +- Коллективная ответственность + +**Когда использовать:** +- Сложные технические задачи +- Инновационные проекты +- Развитие команды + +#### 3. Коучинговый лидер +**Характеристики:** +- Фокус на развитии команды +- Обучение и наставничество +- Долгосрочное развитие + +**Когда использовать:** +- Новые члены команды +- Обучение новым технологиям +- Развитие экспертизы + +--- + +## Управление архитектурными процессами + +### Архитектурные процессы + +#### 1. Architecture Review Process +```markdown +# Процесс архитектурного ревью + +## Подготовка к ревью +1. **Создание архитектурной диаграммы** + - C4 Model диаграммы + - Sequence диаграммы + - Deployment диаграммы + +2. **Подготовка ADR** + - Описание проблемы + - Анализ решений + - Обоснование выбора + +3. **Анализ рисков** + - Технические риски + - Бизнес-риски + - Планы митигации + +## Проведение ревью +1. **Презентация (30 мин)** + - Обзор архитектуры + - Ключевые решения + - Влияние на систему + +2. **Обсуждение (30 мин)** + - Вопросы и уточнения + - Альтернативные решения + - Детали реализации + +3. **Принятие решения (15 мин)** + - Голосование + - Условия одобрения + - Следующие шаги +``` + +#### 2. Architecture Governance +```yaml +# Architecture Governance Framework +architecture_governance: + principles: + - name: "Technology Standardization" + description: "Use standardized technologies across the organization" + enforcement: "Mandatory" + + - name: "Security by Design" + description: "Security must be considered from the beginning" + enforcement: "Mandatory" + + - name: "Scalability" + description: "Systems must be designed for scalability" + enforcement: "Recommended" + + review_process: + triggers: + - "New system development" + - "Major system changes" + - "Technology adoption" + + participants: + - "Solution Architect" + - "Technical Lead" + - "Security Architect" + - "Business Analyst" + + criteria: + - "Alignment with principles" + - "Technical feasibility" + - "Business value" + - "Risk assessment" + + compliance: + monitoring: + - "Regular architecture reviews" + - "Code review checklists" + - "Automated compliance checks" + + reporting: + - "Monthly compliance reports" + - "Quarterly architecture health checks" + - "Annual architecture assessment" +``` + +### Непрерывная архитектура + +#### 1. Architecture as Code +```yaml +# Architecture Definition +architecture: + version: "1.0" + name: "E-commerce Platform" + + components: + - name: "User Service" + type: "microservice" + technology: "Java Spring Boot" + responsibilities: + - "User management" + - "Authentication" + - "Authorization" + interfaces: + - "REST API" + - "Event publishing" + + - name: "Order Service" + type: "microservice" + technology: "Java Spring Boot" + responsibilities: + - "Order processing" + - "Order state management" + - "Order history" + interfaces: + - "REST API" + - "Event consumption" + - "Event publishing" + + relationships: + - from: "User Service" + to: "Order Service" + type: "synchronous" + protocol: "HTTP/REST" + purpose: "User validation for orders" + + - from: "Order Service" + to: "Payment Service" + type: "asynchronous" + protocol: "Events" + purpose: "Payment processing" + + quality_attributes: + - name: "Performance" + target: "95% of requests < 200ms" + measurement: "Response time monitoring" + + - name: "Availability" + target: "99.9% uptime" + measurement: "Uptime monitoring" + + - name: "Scalability" + target: "Handle 10,000 RPS" + measurement: "Load testing" +``` + +#### 2. Architecture Evolution +```markdown +# Процесс эволюции архитектуры + +## 1. Мониторинг архитектуры +- Отслеживание метрик качества +- Анализ технического долга +- Обратная связь от команды + +## 2. Идентификация проблем +- Производительность +- Масштабируемость +- Поддерживаемость +- Безопасность + +## 3. Планирование изменений +- Приоритизация проблем +- Разработка решений +- Планирование миграции + +## 4. Реализация изменений +- Поэтапная миграция +- Тестирование изменений +- Мониторинг результатов +``` + +--- + +## Agile и архитектура + +### Принципы Agile архитектуры + +#### 1. Emergent Architecture +**Принцип:** Архитектура возникает в процессе разработки, а не планируется заранее. + +```markdown +# Emergent Architecture Process + +## Итерация 1: MVP +- Простая монолитная архитектура +- Основной функционал +- Быстрый вывод на рынок + +## Итерация 2: Масштабирование +- Добавление кэширования +- Оптимизация производительности +- Улучшение пользовательского опыта + +## Итерация 3: Микросервисы +- Выделение критических сервисов +- Независимое развертывание +- Улучшение надежности + +## Итерация 4: Оптимизация +- Полная микросервисная архитектура +- Автоматизация процессов +- Продвинутый мониторинг +``` + +#### 2. Architecture Sprints +```markdown +# Architecture Sprint Planning + +## Sprint 0: Architecture Foundation +- Определение архитектурных принципов +- Выбор технологического стека +- Создание базовой инфраструктуры + +## Sprint 1-N: Feature Development +- Разработка функциональности +- Архитектурные рефакторинги +- Улучшение качества кода + +## Sprint N+1: Architecture Review +- Анализ архитектуры +- Планирование улучшений +- Техническое планирование +``` + +### Практики Agile архитектуры + +#### 1. Architecture Decision Records in Agile +```markdown +# ADR в Agile процессе + +## Еженедельные ADR ревью +- Обсуждение новых решений +- Обновление существующих ADR +- Планирование архитектурных изменений + +## Sprint Planning с ADR +- Учет архитектурных решений в планировании +- Оценка влияния на архитектуру +- Планирование архитектурных задач + +## Retrospective с архитектурным фокусом +- Анализ архитектурных проблем +- Планирование улучшений +- Обучение команды +``` + +#### 2. Architecture Testing in Agile +```java +// Архитектурные тесты в Agile +@ArchTest +static final ArchRule layeredArchitecture = layeredArchitecture() + .layer("Controller").definedBy("..controller..") + .layer("Service").definedBy("..service..") + .layer("Repository").definedBy("..repository..") + .whereLayer("Controller").mayNotBeAccessedByAnyLayer() + .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") + .whereLayer("Repository").mayOnlyBeAccessedByLayers("Service"); + +@ArchTest +static final ArchRule noClassesShouldUseJodaTime = + noClasses().should().dependOnClassesThat().belongToAnyOf(DateTime.class); + +@ArchTest +static final ArchRule servicesShouldNotDependOnWebLayer = + noClasses().that().resideInAPackage("..service..") + .should().dependOnClassesThat().resideInAPackage("..web.."); +``` + +--- + +## DevOps культура + +### Принципы DevOps + +#### 1. Collaboration +**Принцип:** Тесное сотрудничество между разработкой и операциями. + +```yaml +# DevOps Collaboration Framework +collaboration: + daily_standups: + participants: ["Developers", "Operations", "QA", "Product"] + focus: ["Deployment status", "Incident response", "Feature progress"] + + shared_responsibilities: + - "Code quality" + - "System reliability" + - "Performance monitoring" + - "Security" + + communication_channels: + - "Slack channels" + - "Shared documentation" + - "Regular meetings" + - "Incident response procedures" +``` + +#### 2. Automation +**Принцип:** Автоматизация всех повторяющихся процессов. + +```yaml +# CI/CD Pipeline +pipeline: + stages: + - name: "Build" + steps: + - "Code checkout" + - "Dependency installation" + - "Compilation" + - "Unit tests" + - "Code quality checks" + + - name: "Test" + steps: + - "Integration tests" + - "Performance tests" + - "Security scans" + - "Architecture tests" + + - name: "Deploy" + steps: + - "Build Docker image" + - "Push to registry" + - "Deploy to staging" + - "Smoke tests" + - "Deploy to production" + - "Health checks" +``` + +#### 3. Monitoring and Feedback +**Принцип:** Непрерывный мониторинг и обратная связь. + +```java +// Monitoring Integration +@Component +public class ApplicationMetrics { + private final MeterRegistry meterRegistry; + + public ApplicationMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @EventListener + public void handleOrderCreated(OrderCreatedEvent event) { + Counter.builder("orders.created") + .tag("customer_type", event.getCustomerType()) + .register(meterRegistry) + .increment(); + } + + @EventListener + public void handlePaymentProcessed(PaymentProcessedEvent event) { + Timer.builder("payment.processing.time") + .tag("payment_method", event.getPaymentMethod()) + .register(meterRegistry) + .record(event.getProcessingTime()); + } +} +``` + +### Практики DevOps + +#### 1. Continuous Integration (CI) + +**Continuous Integration** — практика частого слияния кода в общую ветку с автоматической проверкой. + +```yaml +# GitHub Actions CI Pipeline +name: Continuous Integration + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + java-version: [11, 17] + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK ${{ matrix.java-version }} + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java-version }} + distribution: 'temurin' + + - name: Cache Maven dependencies + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + - name: Run tests + run: mvn clean test + + - name: Generate test report + uses: dorny/test-reporter@v1 + if: success() || failure() + with: + name: Maven Tests + path: target/surefire-reports/*.xml + reporter: java-junit + + - name: Code coverage + run: mvn jacoco:report + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./target/site/jacoco/jacoco.xml +``` + +#### 2. Continuous Deployment (CD) + +**Continuous Deployment** — автоматическое развертывание кода в продакшн после успешного прохождения тестов. + +```yaml +# GitLab CI/CD Pipeline +stages: + - build + - test + - security + - deploy-staging + - deploy-production + +variables: + DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + DOCKER_LATEST: $CI_REGISTRY_IMAGE:latest + +build: + stage: build + script: + - docker build -t $DOCKER_IMAGE . + - docker tag $DOCKER_IMAGE $DOCKER_LATEST + - docker push $DOCKER_IMAGE + - docker push $DOCKER_LATEST + only: + - main + - develop + +test: + stage: test + script: + - docker run --rm $DOCKER_IMAGE npm test + - docker run --rm $DOCKER_IMAGE npm run test:e2e + coverage: '/Lines\s*:\s*(\d+\.\d+)%/' + +security: + stage: security + script: + - docker run --rm -v $(pwd):/app $DOCKER_IMAGE npm audit + - docker run --rm -v $(pwd):/app $DOCKER_IMAGE npm run security:scan + allow_failure: true + +deploy-staging: + stage: deploy-staging + script: + - kubectl set image deployment/app app=$DOCKER_IMAGE + - kubectl rollout status deployment/app + environment: + name: staging + url: https://staging.example.com + only: + - develop + +deploy-production: + stage: deploy-production + script: + - kubectl set image deployment/app app=$DOCKER_IMAGE + - kubectl rollout status deployment/app + environment: + name: production + url: https://example.com + when: manual + only: + - main +``` + +#### 3. Blue-Green Deployment + +**Blue-Green Deployment** — стратегия развертывания с двумя идентичными продакшн средами. + +```yaml +# Blue-Green Deployment with Kubernetes +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: app-rollout +spec: + replicas: 5 + strategy: + blueGreen: + activeService: app-active + previewService: app-preview + autoPromotionEnabled: false + scaleDownDelaySeconds: 30 + prePromotionAnalysis: + templates: + - templateName: success-rate + args: + - name: service-name + value: app-preview + postPromotionAnalysis: + templates: + - templateName: success-rate + args: + - name: service-name + value: app-active + selector: + matchLabels: + app: app + template: + metadata: + labels: + app: app + spec: + containers: + - name: app + image: app:latest + ports: + - containerPort: 8080 + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 +``` + +#### 4. Canary Deployment + +**Canary Deployment** — постепенное развертывание новой версии для части пользователей. + +```yaml +# Canary Deployment with Istio +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: app-vs +spec: + hosts: + - app + http: + - match: + - headers: + canary: + exact: "true" + route: + - destination: + host: app + subset: v2 + weight: 100 + - route: + - destination: + host: app + subset: v1 + weight: 90 + - destination: + host: app + subset: v2 + weight: 10 + +--- +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: app +spec: + host: app + subsets: + - name: v1 + labels: + version: v1 + - name: v2 + labels: + version: v2 +``` + +#### 5. Infrastructure as Code +```hcl +# Terraform Configuration +resource "aws_instance" "web_server" { + ami = var.ami_id + instance_type = var.instance_type + + tags = { + Name = "Web Server" + Environment = var.environment + Project = var.project_name + } +} + +resource "aws_security_group" "web_sg" { + name_prefix = "web-sg-" + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } +} +``` + +#### 2. Configuration Management +```yaml +# Ansible Playbook +- name: Deploy Application + hosts: web_servers + become: yes + vars: + app_version: "{{ lookup('env', 'APP_VERSION') }}" + db_host: "{{ lookup('env', 'DB_HOST') }}" + + tasks: + - name: Update application + git: + repo: "https://github.com/company/app.git" + version: "{{ app_version }}" + dest: /opt/app + + - name: Install dependencies + pip: + requirements: /opt/app/requirements.txt + + - name: Update configuration + template: + src: config.j2 + dest: /opt/app/config.yaml + backup: yes + + - name: Restart application + systemd: + name: app + state: restarted + enabled: yes +``` + +--- + +## Code Review и архитектурные ревью + +### Процесс ревью кода + +#### 1. Code Review Checklist +```markdown +# Code Review Checklist + +## Функциональность +- [ ] Код выполняет требуемую функциональность +- [ ] Обрабатываются граничные случаи +- [ ] Нет неиспользуемого кода +- [ ] Логика понятна и корректна + +## Качество кода +- [ ] Соблюдены принципы SOLID +- [ ] Нет дублирования кода +- [ ] Имена переменных и функций понятны +- [ ] Код хорошо структурирован + +--- + +## Стандарты кодирования и инструменты + +### Принципы стандартов кодирования + +#### 1. Важность единого стиля кода + +**Преимущества единого стиля:** +- Повышение читаемости кода +- Ускорение разработки +- Упрощение code review +- Снижение количества ошибок +- Улучшение поддержки кода + +**Основные принципы:** +- **Консистентность** — одинаковый стиль во всем проекте +- **Читаемость** — код должен быть понятен любому разработчику +- **Простота** — избегание сложных конструкций +- **Документированность** — четкие комментарии и документация + +#### 2. Стандарты кодирования по языкам + +**Java - Google Java Style Guide:** + +```java +// Правильное именование +public class UserService { + private static final String DEFAULT_ROLE = "USER"; + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public User createUser(CreateUserRequest request) { + validateUserRequest(request); + User user = buildUser(request); + return userRepository.save(user); + } + + private void validateUserRequest(CreateUserRequest request) { + if (request.getEmail() == null || request.getEmail().isEmpty()) { + throw new IllegalArgumentException("Email is required"); + } + } + + private User buildUser(CreateUserRequest request) { + return User.builder() + .email(request.getEmail()) + .name(request.getName()) + .role(DEFAULT_ROLE) + .createdAt(LocalDateTime.now()) + .build(); + } +} + +// Неправильное именование +public class userservice { + private static final String default_role = "USER"; + private final userrepository userrepository; + + public userservice(userrepository userrepository) { + this.userrepository = userrepository; + } + + public user createuser(createuserrequest request) { + validateuserrequest(request); + user user = builduser(request); + return userrepository.save(user); + } +} +``` + +**Rust - Rust Style Guide:** + +```rust +// Правильное именование +use std::collections::HashMap; + +pub struct UserService { + user_repository: Box, +} + +impl UserService { + pub fn new(user_repository: Box) -> Self { + Self { user_repository } + } + + pub fn create_user(&self, request: CreateUserRequest) -> Result { + self.validate_user_request(&request)?; + let user = self.build_user(request); + self.user_repository.save(user) + } + + fn validate_user_request(&self, request: &CreateUserRequest) -> Result<(), ServiceError> { + if request.email.is_empty() { + return Err(ServiceError::ValidationError("Email is required".to_string())); + } + Ok(()) + } + + fn build_user(&self, request: CreateUserRequest) -> User { + User { + email: request.email, + name: request.name, + role: "USER".to_string(), + created_at: chrono::Utc::now(), + } + } +} + +// Неправильное именование +pub struct userservice { + userrepository: Box, +} + +impl userservice { + pub fn new(userrepository: Box) -> Self { + Self { userrepository } + } + + pub fn createuser(&self, request: createuserrequest) -> Result { + // ... + } +} +``` + +**Python - PEP 8:** + +```python +# Правильное именование +import logging +from typing import Optional, List +from dataclasses import dataclass +from datetime import datetime + +logger = logging.getLogger(__name__) + +@dataclass +class CreateUserRequest: + email: str + name: str + password: str + +class UserService: + DEFAULT_ROLE = "USER" + + def __init__(self, user_repository: UserRepository): + self.user_repository = user_repository + self.logger = logging.getLogger(self.__class__.__name__) + + def create_user(self, request: CreateUserRequest) -> User: + self._validate_user_request(request) + user = self._build_user(request) + return self.user_repository.save(user) + + def _validate_user_request(self, request: CreateUserRequest) -> None: + if not request.email or not request.email.strip(): + raise ValueError("Email is required") + + if len(request.password) < 8: + raise ValueError("Password must be at least 8 characters") + + def _build_user(self, request: CreateUserRequest) -> User: + return User( + email=request.email, + name=request.name, + role=self.DEFAULT_ROLE, + created_at=datetime.utcnow() + ) + +# Неправильное именование +class userservice: + default_role = "USER" + + def __init__(self, userrepository): + self.userrepository = userrepository + + def createuser(self, request): + self.validateuserrequest(request) + user = self.builduser(request) + return self.userrepository.save(user) +``` + +**Node.js/TypeScript - Airbnb Style Guide:** + +```typescript +// Правильное именование +import { UserRepository } from './user-repository'; +import { CreateUserRequest, User } from './types'; +import { ServiceError } from './errors'; + +export class UserService { + private static readonly DEFAULT_ROLE = 'USER'; + + constructor( + private readonly userRepository: UserRepository, + private readonly logger: Logger + ) {} + + public async createUser(request: CreateUserRequest): Promise { + this.validateUserRequest(request); + const user = this.buildUser(request); + return this.userRepository.save(user); + } + + private validateUserRequest(request: CreateUserRequest): void { + if (!request.email || request.email.trim() === '') { + throw new ServiceError('Email is required'); + } + + if (request.password.length < 8) { + throw new ServiceError('Password must be at least 8 characters'); + } + } + + private buildUser(request: CreateUserRequest): User { + return { + email: request.email, + name: request.name, + role: UserService.DEFAULT_ROLE, + createdAt: new Date(), + }; + } +} + +// Неправильное именование +export class userservice { + private static readonly default_role = 'USER'; + + constructor( + private readonly userrepository: userrepository, + private readonly logger: logger + ) {} + + public async createuser(request: createuserrequest): Promise { + this.validateuserrequest(request); + const user = this.builduser(request); + return this.userrepository.save(user); + } +} +``` + +#### 3. Инструменты автоматизации стиля кода + +**Java - Spotless:** + +```gradle +// build.gradle +plugins { + id 'com.diffplug.spotless' version '6.0.0' +} + +spotless { + java { + googleJavaFormat() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + } + + format 'misc', { + target '*.gradle', '*.md', '.gitignore' + trimTrailingWhitespace() + indentWithSpaces(4) + endWithNewline() + } +} +``` + +**Rust - rustfmt:** + +```toml +# rustfmt.toml +max_width = 100 +tab_spaces = 4 +newline_style = "Unix" +use_small_heuristics = "Default" +imports_granularity = "Crate" +group_imports = "StdExternalCrate" +``` + +**Python - Black + isort:** + +```toml +# pyproject.toml +[tool.black] +line-length = 88 +target-version = ['py38'] +include = '\.pyi?$' +extend-exclude = ''' +/( + # directories + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | build + | dist +)/ +''' + +[tool.isort] +profile = "black" +multi_line_output = 3 +line_length = 88 +known_first_party = ["myproject"] +``` + +**Node.js/TypeScript - Prettier + ESLint:** + +```json +// .prettierrc +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false +} +``` + +```json +// .eslintrc.json +{ + "extends": [ + "airbnb-base", + "airbnb-typescript/base", + "@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json" + }, + "rules": { + "import/prefer-default-export": "off", + "class-methods-use-this": "off", + "no-console": "warn", + "@typescript-eslint/no-unused-vars": "error" + } +} +``` + +#### 4. Pre-commit хуки + +```yaml +# .pre-commit-config.yaml +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: check-merge-conflict + + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + language_version: python3 + + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + + - repo: https://github.com/pycqa/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + args: [--max-line-length=88, --extend-ignore=E203] + + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.28.0 + hooks: + - id: eslint + files: \.(js|ts)$ + types: [file] +``` + +#### 5. CI/CD интеграция + +```yaml +# .github/workflows/code-style.yml +name: Code Style Check + +on: + pull_request: + branches: [ main, develop ] + push: + branches: [ main, develop ] + +jobs: + code-style: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Run Spotless Check + run: ./gradlew spotlessCheck + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install Python dependencies + run: | + pip install black isort flake8 mypy + + - name: Run Black + run: black --check . + + - name: Run isort + run: isort --check-only . + + - name: Run Flake8 + run: flake8 . + + - name: Run MyPy + run: mypy . + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install Node.js dependencies + run: npm ci + + - name: Run ESLint + run: npm run lint + + - name: Run Prettier + run: npm run format:check +``` + +#### 6. Автоматическое форматирование в IDE + +**VS Code настройки:** + +```json +// .vscode/settings.json +{ + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true, + "source.fixAll.eslint": true + }, + "python.formatting.provider": "black", + "python.linting.enabled": true, + "python.linting.flake8Enabled": true, + "typescript.preferences.organizeImportsOnSave": true, + "java.format.settings.url": ".vscode/java-google-style.xml" +} +``` + +**IntelliJ IDEA настройки:** + +```xml + + + + + + + + +``` + +#### 7. Метрики качества кода + +```java +// SonarQube правила для Java +public class CodeQualityMetrics { + + // Плохо: слишком длинный метод + public void processUserData(String name, String email, String phone, + String address, String city, String country, + String zipCode, String dateOfBirth, String gender) { + // 50+ строк кода + } + + // Хорошо: разбиение на более мелкие методы + public void processUserData(UserData userData) { + validateUserData(userData); + enrichUserData(userData); + saveUserData(userData); + } + + private void validateUserData(UserData userData) { + // валидация + } + + private void enrichUserData(UserData userData) { + // обогащение данных + } + + private void saveUserData(UserData userData) { + // сохранение + } +} +``` + +#### 8. Документирование стандартов + +```markdown +# Coding Standards + +## Общие принципы + +1. **Читаемость превыше всего** — код должен быть понятен любому разработчику +2. **Консистентность** — одинаковый стиль во всем проекте +3. **Простота** — избегание сложных конструкций +4. **Документированность** — четкие комментарии + +## Именование + +### Java +- Классы: `PascalCase` (UserService) +- Методы: `camelCase` (createUser) +- Константы: `UPPER_SNAKE_CASE` (DEFAULT_ROLE) +- Переменные: `camelCase` (userName) + +### Python +- Классы: `PascalCase` (UserService) +- Функции: `snake_case` (create_user) +- Константы: `UPPER_SNAKE_CASE` (DEFAULT_ROLE) +- Переменные: `snake_case` (user_name) + +### TypeScript +- Классы: `PascalCase` (UserService) +- Функции: `camelCase` (createUser) +- Константы: `UPPER_SNAKE_CASE` (DEFAULT_ROLE) +- Переменные: `camelCase` (userName) + +## Структура файлов + +``` +src/ +├── controllers/ # Контроллеры +├── services/ # Бизнес-логика +├── repositories/ # Доступ к данным +├── models/ # Модели данных +├── utils/ # Утилиты +└── config/ # Конфигурация +``` + +## Комментарии + +```java +/** + * Создает нового пользователя в системе + * + * @param request данные для создания пользователя + * @return созданный пользователь + * @throws ValidationException если данные некорректны + */ +public User createUser(CreateUserRequest request) { + // Валидация входных данных + validateUserRequest(request); + + // Создание пользователя + User user = buildUser(request); + + // Сохранение в базу данных + return userRepository.save(user); +} +``` +``` + +--- + +## Производительность +- [ ] Нет очевидных проблем с производительностью +- [ ] Используются подходящие алгоритмы +- [ ] Нет утечек памяти +- [ ] Оптимизированы запросы к БД + +## Безопасность +- [ ] Нет уязвимостей безопасности +- [ ] Валидация входных данных +- [ ] Безопасная работа с паролями +- [ ] Нет SQL injection + +## Тестирование +- [ ] Покрытие тестами +- [ ] Тесты понятны и корректны +- [ ] Интеграционные тесты +- [ ] Тесты производительности +``` + +#### 2. Code Review Tools +```yaml +# SonarQube Configuration +sonar: + project_key: "my-project" + project_name: "My Project" + + quality_gates: + - "Code Coverage > 80%" + - "Duplicated Lines < 3%" + - "Maintainability Rating A" + - "Reliability Rating A" + - "Security Rating A" + + rules: + - "java:S1144" # Unused private methods + - "java:S1186" # Empty methods + - "java:S1481" # Unused local variables + - "java:S1854" # Dead stores +``` + +### Процесс архитектурного ревью + +#### 1. Architecture Review Checklist +```markdown +# Architecture Review Checklist + +## Архитектурные принципы +- [ ] Соответствие архитектурным принципам +- [ ] Соблюдение паттернов проектирования +- [ ] Правильное разделение ответственности +- [ ] Слабая связанность компонентов + +## Качественные атрибуты +- [ ] Производительность соответствует требованиям +- [ ] Масштабируемость обеспечена +- [ ] Безопасность учтена +- [ ] Надежность гарантирована + +## Техническая реализуемость +- [ ] Технологии подходят для задачи +- [ ] Команда имеет необходимые навыки +- [ ] Инфраструктура поддерживает решение +- [ ] Временные рамки реалистичны + +## Риски и митигации +- [ ] Основные риски идентифицированы +- [ ] План митигации разработан +- [ ] Fallback стратегии определены +- [ ] Мониторинг рисков настроен +``` + +#### 2. Architecture Review Tools +```java +// Architecture Tests +@ArchTest +static final ArchRule noClassesShouldUseSystemOut = + noClasses().should().callMethod(System.class, "out", "println", String.class); + +@ArchTest +static final ArchRule repositoryClassesShouldHaveRepositoryAnnotation = + classes().that().haveNameMatching(".*Repository") + .should().beAnnotatedWith(Repository.class); + +@ArchTest +static final ArchRule serviceClassesShouldHaveServiceAnnotation = + classes().that().haveNameMatching(".*Service") + .should().beAnnotatedWith(Service.class); +``` + +--- + +## Управление техническим долгом + +### Что такое технический долг + +**Технический долг** — это компромиссы в качестве кода, которые делаются для ускорения разработки в краткосрочной перспективе, но создают дополнительные затраты в долгосрочной. + +### Типы технического долга + +#### 1. Code Debt +```java +// Пример технического долга в коде +public class UserService { + // Долг: отсутствие валидации + public User createUser(String name, String email) { + User user = new User(); + user.setName(name); + user.setEmail(email); + return userRepository.save(user); + } + + // Долг: дублирование кода + public User updateUser(Long id, String name, String email) { + User user = userRepository.findById(id); + user.setName(name); + user.setEmail(email); + return userRepository.save(user); + } + + // Долг: отсутствие обработки ошибок + public User getUser(Long id) { + return userRepository.findById(id); + } +} +``` + +#### 2. Architecture Debt +```markdown +# Архитектурный долг + +## Проблемы архитектуры +- Монолитная архитектура в микросервисном мире +- Тесная связанность компонентов +- Отсутствие четких границ доменов +- Неоптимальные паттерны взаимодействия + +## Последствия +- Сложность внесения изменений +- Высокие затраты на разработку +- Проблемы с масштабированием +- Сложность тестирования +``` + +#### 3. Infrastructure Debt +```yaml +# Инфраструктурный долг +infrastructure_debt: + problems: + - "Manual deployment processes" + - "Outdated dependencies" + - "Inconsistent environments" + - "Lack of monitoring" + - "Manual configuration management" + + consequences: + - "High deployment risk" + - "Security vulnerabilities" + - "Environment inconsistencies" + - "Difficult troubleshooting" + - "High operational costs" +``` + +### Управление техническим долгом + +#### 1. Debt Tracking +```markdown +# Technical Debt Backlog + +## High Priority +- [ ] Refactor UserService validation logic +- [ ] Implement proper error handling +- [ ] Remove code duplication +- [ ] Add missing unit tests + +## Medium Priority +- [ ] Update outdated dependencies +- [ ] Improve database queries +- [ ] Add integration tests +- [ ] Implement caching + +## Low Priority +- [ ] Code style improvements +- [ ] Documentation updates +- [ ] Performance optimizations +- [ ] Logging improvements +``` + +#### 2. Debt Prevention +```java +// Предотвращение технического долга +@Service +public class UserService { + @Autowired + private UserValidator userValidator; + + @Autowired + private UserRepository userRepository; + + // Предотвращение долга: валидация + public User createUser(CreateUserRequest request) { + userValidator.validate(request); + + User user = new User(request); + return userRepository.save(user); + } + + // Предотвращение долга: обработка ошибок + public User getUser(Long id) { + return userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found: " + id)); + } +} +``` + +#### 3. Debt Refactoring +```java +// Рефакторинг технического долга +@Service +public class UserService { + @Autowired + private UserRepository userRepository; + + @Autowired + private UserMapper userMapper; + + // Рефакторинг: использование маппера + public User createUser(CreateUserRequest request) { + User user = userMapper.toEntity(request); + return userRepository.save(user); + } + + // Рефакторинг: использование маппера + public UserResponse getUser(Long id) { + User user = userRepository.findById(id) + .orElseThrow(() -> new UserNotFoundException("User not found: " + id)); + + return userMapper.toResponse(user); + } +} +``` + +--- + +## Коммуникация и презентации + +### Коммуникация с командой + +#### 1. Technical Communication +```markdown +# Техническая коммуникация + +## Ежедневные стендапы +- Прогресс по архитектурным задачам +- Блокеры и проблемы +- Планы на день + +## Еженедельные технические ревью +- Обзор архитектурных решений +- Обсуждение технических проблем +- Планирование улучшений + +## Ежемесячные архитектурные сессии +- Анализ архитектуры +- Планирование рефакторинга +- Обучение команды +``` + +#### 2. Stakeholder Communication +```markdown +# Коммуникация со стейкхолдерами + +## Бизнес-стейкхолдеры +- Объяснение технических решений простым языком +- Связь технических решений с бизнес-ценностью +- Оценка влияния на бизнес-процессы + +## Технические стейкхолдеры +- Детальное техническое обоснование +- Анализ альтернативных решений +- Оценка рисков и митигаций +``` + +### Презентации архитектуры + +#### 1. Executive Summary +```markdown +# Презентация для руководства + +## Слайд 1: Проблема +- Текущие проблемы системы +- Влияние на бизнес +- Необходимость изменений + +## Слайд 2: Решение +- Предлагаемая архитектура +- Ключевые преимущества +- Бизнес-ценность + +## Слайд 3: Реализация +- План миграции +- Временные рамки +- Ресурсы и бюджет + +## Слайд 4: Риски +- Основные риски +- Планы митигации +- Fallback стратегии +``` + +#### 2. Technical Deep Dive +```markdown +# Техническая презентация + +## Архитектурные принципы +- SOLID принципы +- Паттерны проектирования +- Архитектурные стили + +## Технологический стек +- Выбранные технологии +- Обоснование выбора +- Альтернативы + +## Качественные атрибуты +- Производительность +- Масштабируемость +- Надежность +- Безопасность + +## Реализация +- Детальный план +- Зависимости +- Риски +``` + +--- + +## Практические задания + +### Задание 1: Техническое лидерство +1. Проведите архитектурное ревью +2. Создайте план развития команды +3. Проведите техническую презентацию + +### Задание 2: DevOps процессы +1. Настройте CI/CD пайплайн +2. Реализуйте Infrastructure as Code +3. Настройте мониторинг и алерты + +### Задание 3: Управление техническим долгом +1. Проведите аудит технического долга +2. Создайте план рефакторинга +3. Реализуйте меры по предотвращению долга + +--- + +## Дополнительные ресурсы + +### Книги +- "The Phoenix Project" by Gene Kim +- "The DevOps Handbook" by Gene Kim +- "Team Topologies" by Matthew Skelton +- "Accelerate" by Nicole Forsgren + +### Инструменты +- **CI/CD**: Jenkins, GitLab CI, GitHub Actions +- **Monitoring**: Prometheus, Grafana, ELK Stack +- **Code Quality**: SonarQube, Checkmarx, Veracode +- **Communication**: Slack, Microsoft Teams, Confluence + +--- + +## Контрольные вопросы + +1. Какие обязанности у технического лидера? +2. Как управлять архитектурными процессами? +3. Как интегрировать архитектуру в Agile процессы? +4. Что такое DevOps культура? +5. Как проводить эффективные code review? +6. Как управлять техническим долгом? +7. Как коммуницировать архитектурные решения? +8. Какие инструменты использовать для автоматизации процессов?