AZ-структурирование файлов исходного кода

Дата публикации: 2022-11-16

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, а договорённость команды, заметно помогающая при рефакторинге больших проектов.