AZ-структурирование — мой способ организовать исходные файлы в проектах, где код хранится в файловой системе: Java, PHP, JavaScript/Node.js. Его цель — сделать рефакторинг предсказуемее.
Почему это нужно
Код чаще меняют, чем пишут с нуля. В веб-разработке требования уточняются по ходу дела: мы видим направление, но не все детали. Поэтому на каждой итерации добавляем, удаляем и меняем код. Это не недостаток, а нормальный режим работы.
Главная сложность рефакторинга — понять границы распространения изменений. Инкапсуляция в ООП решает именно эту задачу.
Обычные границы
Большой проект состоит из пакетов и файлов. В ES-модуле код без export
можно менять свободно внутри файла. Экспортируемый код потенциально
используют где угодно в проекте. У npm-пакета публичную поверхность
задают main или exports; всё остальное — внутреннее дело пакета.
Но иногда границы нужны меньше пакета. Исходники обычно раскладывают по роли или по предметной области. Я сочетаю оба подхода: сначала роль, затем функция.
src/
Back/Mod/RDb/
Front/Mod/Store/Ui/
Lib/Route/Home.mjs
Shared/Dto/
Первый уровень устойчивее и зависит от фреймворка; второй меняется вместе с бизнес-задачами. У файла могут быть глобальные границы — его используют во всём пакете, или локальные — изменения не затрагивают другие файлы.
A-структура
Сложность побеждают декомпозицией. Предположим, крупная модель лежит в
Mod/Settings.mjs. Её части логично вынести рядом:
Mod/
Settings/
Payments.mjs
Profile.mjs
Security.mjs
Settings.mjs
Пара папки Settings/ и файла Settings.mjs уже подсказывает, что это
одна модель с деталями. Но если структура и так занята несколькими
моделями, я помечаю приватные фрагменты подкаталогом A/:
Mod/
Settings/
A/
Snippet1.mjs
Snippet2.mjs
Payments.mjs
Profile.mjs
Settings.mjs
Файлы из Settings/A/ — части Settings.mjs. Корневой файл можно
импортировать в пакете, а содержимое A/ предназначено только для него.
Так уровень файлов получает собственную private-область, а границы
изменений становятся очевиднее. A-структура может вкладываться:
Route/Settings/A/Profile/A/Password/Change/A/Evt/Change.mjs.
Z-структура
Иногда вспомогательный код относится не к одному файлу, а к группе
файлов — это «библиотека для группы». Для неё я использую каталог Z/:
Cli/
Data/
Z/ListTables.mjs
Export.mjs
Import.mjs
Init.mjs
Изменения в Cli/Data/Z/ListTables.mjs не должны выходить за границы
Cli/Data/.
Вывод
Если обозначать private-области прямо структурой файлов, легче видеть,
куда распространяется изменение. A/ означает детали одного корневого
файла, Z/ — общие детали небольшой группы. Это не правило языка и не
замена хорошему API, а договорённость команды, заметно помогающая при
рефакторинге больших проектов.