4. Сервисный слой и адаптер производственного календаря (Adapter, фасады, кэш)
Date: 2026-02-03
Status
Accepted
Context
Модели (Salary, ActualWorkShedule) не должны выполнять HTTP-запросы и парсить ответы API. Нужен слой, изолирующий внешний API и предоставляющий данные календаря и расчёта стоимости дня/часа.
Decision
Adapter — клиент к isdayoff.ru в
app/manufacture/service/production_calendar.py:get_work_days_and_hours(year, month) -> tuple[int, int]— рабочие дни и рабочие часы в месяце (по символам 0, 2, 4; часы: 8 для 0/4, 7 для 2).is_working_day(year, month, day) -> bool— является ли дата рабочим днём (False для кода 1). Реализация через тот же ответ за месяц (символ в позиции day−1).- Кэширование ответа за месяц в Django cache (ключ вида
production_calendar:ru:{year}:{month}, TTL не менее 1 дня).
Сервис расчёта — функция/класс: по
monthly_salary,year,monthи зависимости «календарь» вычисляетcost_per_dayиcost_per_hour(оклад / рабочие дни, оклад / рабочие часы). Защита от деления на ноль и от отсутствия оклада.Фасады для вызова из кода (в т.ч. из модели/расчёта смен):
get_cost_per_hour_for_month(salary, year, month)— возвращает стоимость часа за месяц (или None/0 при ошибке или type≠4).is_working_day(year, month, day)— доступен через адаптер/сервис. Модели вызывают фасады, а не HTTP.
Обоснование
- Разделение ответственности — модель не знает об HTTP; тесты могут подменять фасад/адаптер через mock.patch.
- Кэширование — один запрос к API на месяц, повторные расчёты используют кэш.
- Единая точка входа — фасады упрощают вызов из ActualWorkShedule и сериализатора.
Consequences
- (+) Модели остаются «чистыми», тестируемость высокая.
- (+) Меньше нагрузки на isdayoff.ru за счёт кэша.
- (-) Необходимость обработки ошибок API и граничных случаев (нет даты, пустой оклад) в сервисе/фасаде.
Источник: backend/narmak_v2/app/manufacture/ADR.md (ADR-003)