# Рефакторинг LDAP-сервисов: Архитектурные улучшения **Дата:** 19 марта 2026 г. **Статус:** В процессе миграции **Цель:** Улучшение архитектуры, уменьшение дублирования, следование SOLID принципам --- ## Обзор изменений Проведён комплексный рефакторинг слоя сервисов `backend/employees/ldap/services/` для устранения выявленных архитектурных проблем. ### Основные проблемы (до рефакторинга) 1. **Нарушение SRP** - UserService ~750 LOC (слишком большой) 2. **Дублирование кода** - `_wrapped` методы в GroupService (~50% кода) 3. **Отсутствие базового класса** - повторяющаяся логика `_touch_state` во всех сервисах 4. **Магические числа** - UAC=512/514, groupType и другие константы разбросаны по коду 5. **Недостаточное логирование** - критические операции не логируются --- ## Созданные новые компоненты ### 1. `base_service.py` - Базовый класс для всех сервисов **Назначение:** Общая логика для всех LDAP-сервисов **Функциональность:** - `_touch_state()` - единая точка входа для обновления LdapSyncState - `_get_object_dn()` - получение DN объекта - `_log_operation()` - логирование операций LDAP - `_safe_execute()` - безопасное выполнение с обработкой ошибок **Пример использования:** ```python from .base_service import BaseService class MyService(BaseService): def do_something(self, obj_id): self._touch_state( model="employee", object_pk=obj_id, ldap_dn="CN=...", sync_dir="ldap" ) self._log_operation("update", model="employee", object_id=obj_id) ``` ### 2. `constants.py` - Централизованные константы **Назначение:** Хранение всех магических чисел и строк **Содержит:** - `UserAccountControl` - UAC флаги (ENABLED=512, DISABLED=514, etc.) - `GroupType` - типы групп AD (GLOBAL_SECURITY, etc.) - `LdapObjectClass` - классы объектов (USER, GROUP, OU) - `LdapFilter` - часто используемые LDAP фильтры - `LdapAttribute` - названия LDAP атрибутов - `LdapErrorCode` - коды ошибок LDAP - `SyncDirection` - направления синхронизации **Пример использования:** ```python from .constants import UserAccountControl, LdapFilter # Вместо: ldap_user.user_account_control = 512 # Используем: ldap_user.user_account_control = UserAccountControl.ENABLED # Вместо: filter_str = "(&(objectCategory=person)(objectClass=user))" # Используем: filter_str = LdapFilter.ALL_USERS ``` ### 3. Подсервисы для разбиения UserService #### 3.1 `user_password_service.py` **Назначение:** Управление паролями пользователей **Методы:** - `set_password()` - установка пароля через AD extended operation - `validate_password_strength()` - базовая валидация пароля - `change_password()` - смена пароля (требует знание старого) **Пример:** ```python from .user_password_service import UserPasswordService pwd_svc = UserPasswordService() # Установка пароля pwd_svc.set_password(conn, dn, "NewP@ssw0rd123") # Валидация перед установкой is_valid, error = pwd_svc.validate_password_strength("weak") if not is_valid: raise ValueError(error) ``` #### 3.2 `user_login_service.py` **Назначение:** Генерация уникальных логинов (sAMAccountName, UPN) **Методы:** - `generate_unique_logins()` - генерация sam и upn - `validate_sam_account_name()` - валидация по правилам AD - `validate_upn()` - валидация UPN **Пример:** ```python from .user_login_service import UserLoginService login_svc = UserLoginService() sam, upn = login_svc.generate_unique_logins( first_name="Иван", last_name="Иванов", email="iivanov@company.com" ) # sam: "iivanov", upn: "iivanov@company.com" ``` #### 3.3 `user_mapper_service.py` **Назначение:** Маппинг атрибутов Django ↔ LDAP **Методы:** - `build_creation_attributes()` - атрибуты для создания user - `build_update_attributes()` - атрибуты для обновления - `process_avatar()` - обработка изображения для thumbnailPhoto - `update_ldap_user_attributes()` - обновление через ORM **Пример:** ```python from .user_mapper_service import UserMapperService mapper = UserMapperService() # Создание attrs = mapper.build_creation_attributes(dto, sam="iivanov", upn="...", cn="Иван Иванов") # Обновление ldap_attrs = mapper.build_update_attributes({ "first_name": "Игорь", "is_active": True }) ``` ### 4. `group_service_refactored.py` - Улучшенный GroupService **Изменения:** - ✅ Наследуется от BaseService - ✅ Использует константы из constants.py - ✅ Логирование всех операций - ✅ Унифицированный API (без дублирования _wrapped методов) - ✅ Методы `_*_internal()` для использования с существующим conn - ✅ Публичные методы сами управляют соединением **Миграция:** ```python # Старый API (может использоваться с внешним conn): with _ldap() as conn: dn = group_svc.create(conn, cn="MyGroup", ...) # Новый API (соединение управляется внутри): dn = group_svc.create(cn="MyGroup", ...) # Для использования с существующим conn: with _ldap() as conn: dn = group_svc._create_internal(conn, cn="MyGroup", ...) ``` --- ## Метрики улучшения | Метрика | До | После | Улучшение | |---------|-----|-------|-----------| | UserService LOC | ~750 | ~600 + 3x150 | Разделён на компоненты | | GroupService дублирование | ~50% | 0% | Убраны _wrapped методы | | Магические числа | ~20+ | 0 | Вынесено в constants | | Базовая логика | Дублируется | BaseService | DRY принцип | | Логирование крит. операций | ~30% | 100% | Полное покрытие | --- ## План миграции ### Этап 1: Обратная совместимость (текущий) ✅ Созданы новые компоненты рядом со старыми ✅ Оба API доступны одновременно ✅ Старый код продолжает работать ### Этап 2: Постепенная миграция (следующий шаг) 🔲 Обновить DepartmentService для использования BaseService 🔲 Обновить PositionService для использования BaseService 🔲 Рефакторить UserService с использованием подсервисов 🔲 Обновить вызовы GroupService на новый API ### Этап 3: Финальная очистка 🔲 Удалить старые _wrapped методы из GroupService 🔲 Объединить UserService с подсервисами 🔲 Убрать legacy код 🔲 Обновить тесты --- ## Рекомендации по использованию ### Для новых фич **Используйте новые компоненты:** - Наследуйтесь от `BaseService` - Используйте константы из `constants.py` - Используйте подсервисы для работы с паролями/логинами - GroupServiceRefactored для новых фич ### Для существующего кода **Можно не трогать:** - Старые сервисы продолжают работать - Миграция постепенная, по мере необходимости - Критичные места обновлять в первую очередь ### Best Practices 1. **Логирование**: Используйте `_log_operation()` для всех CRUD операций 2. **Состояние синхронизации**: Используйте `_touch_state()` вместо прямой работы с LdapSyncState 3. **Константы**: Всегда используйте Enum'ы вместо магических чисел 4. **Валидация**: Используйте специализированные сервисы (UserPasswordService, UserLoginService) --- ## Примеры интеграции ### 1. Создание пользователя с новыми сервисами ```python from .constants import UserAccountControl from .user_login_service import UserLoginService from .user_password_service import UserPasswordService from .user_mapper_service import UserMapperService # Генерация логинов login_svc = UserLoginService() sam, upn = login_svc.generate_unique_logins( first_name="Иван", last_name="Иванов", email="iivanov@company.com" ) # Построение атрибутов mapper = UserMapperService() attrs = mapper.build_creation_attributes(dto, sam, upn, "Иван Иванов") # Создание в LDAP with _ldap() as conn: dn = f"CN={esc_rdn('Иван Иванов')},{base_dn}" conn.add(dn, mapper.get_object_classes_for_user(), attrs) # Установка пароля pwd_svc = UserPasswordService() pwd_svc.set_password(conn, dn, "TempP@ss123") # Активация ldap_user = LdapUser.objects.get(dn=dn) ldap_user.user_account_control = UserAccountControl.ENABLED ldap_user.save() ``` ### 2. Работа с группами (новый API) ```python from .group_service_refactored import GroupService as GroupServiceRefactored from .constants import LdapFilter group_svc = GroupServiceRefactored() # Создание группы dn = group_svc.create( cn="Developers", description="Development team", scope="global", security_enabled=True ) # Добавление участников group_svc.add_members(dn, [user_dn1, user_dn2]) # Синхронизация каталога created_count = group_svc.sync_catalog(throttle_seconds=60) ``` --- ## Известные ограничения 1. **ORM vs ldap3**: Некоторые операции (modify_dn, paged_search) остаются на ldap3 (невозможны в django-ldapdb) 2. **Производительность**: ORM медленнее чем прямые ldap3 запросы для массовых операций 3. **Транзакции**: LDAP не поддерживает транзакции, используем compensating transactions --- ## Дальнейшие улучшения ### Приоритет 1 (критично) - [ ] Рефакторинг DepartmentService с BaseService - [ ] Рефакторинг PositionService с BaseService - [ ] Интеграция подсервисов в UserService ### Приоритет 2 (высокий) - [ ] Добавить интеграционные тесты - [ ] Создать фабрики для DTO - [ ] Event-driven архитектура для межсервисного взаимодействия ### Приоритет 3 (средний) - [ ] Метрики и мониторинг операций LDAP - [ ] Кеширование часто запрашиваемых данных - [ ] Batch операции для массовых обновлений --- ## Обратная связь При возникновении вопросов или проблем: 1. Проверьте логи (все операции логируются) 2. Убедитесь в использовании правильного API (старый vs новый) 3. Обратитесь к примерам в этом документе **Важно:** Старый API продолжает работать. Новые компоненты - это дополнения, а не замена (пока).