Статья ответит на вопрос, который стал причиной потери времени многих программистов: какую структуру каталогов необходимо использовать для будущего или существующего проекта? Какая структура будет наиболее оптимальной не только для текущего зачатка проекта, но и в будущем будет не такой болезненной в плане расширения проекта или его разделения на части?
И так мы взялись за проект. Пусть это будет самый простой проект myapp. Создаём для него каталог в основной папке для разработки (у меня это Devel).
В дальнейшем все действия будут внутри каталога основного проекта
Инициализируем пустой репозиторий
Следующим шагом идёт создание обязательного файла README. В этом файле хранится основное и ключевое описание нашего будущего проекта. Некоторые делают файл в виде text/plain с названием README.txt. На крупных порталах репозитариях принят за стандарт Markdown с названием README.md. Я же предпочитаю html, т.к. мне удобнее делать цветовые выделения, вставлять ссылки, картинки и другие мультимедиа, открывать в браузере, вставлять куски кода в тегах <pre> и <code>, использовать готовые фреймворки типа Bootstrap для оформления и другой магии вне Хогвардса. Соответственно и название README.html
Если проект ведётся несколькими командами, то рекомендую каждой команде иметь свой README файл внутри каждого разрабатываемого независимо модуля, компонента, библиотеки и т.д.
Основной каталог приложения называю так же, как и каталог проекта. В случае этой статьи myapp
Внутри каталога приложения стандартно создаются __init__.py, содержащий код инициализации приложения и подключения всех необходимых частей к приложению. В частности это Blueprints для одноветочных URL или namespace для логики отдельного сервиса, но с разными URL (простым примером может служить создание namespace для сервиса статей блога, где явно имеются разные пути вида /pages и /page/ID)
или
Модель базы данных содержит код инициализации базы данных, а также подключения. И, обязательно, структуру таблиц и связей. Так же желательно выделять таблицы в отдельные файлы на основе бизнес-логики приложения. Описание нескольких классов таблиц и связей в один отдельный файл удобно тем, что можно достаточно легко повторно использовать код, скопировав нужный файл в другой проект или использовать символьные ссылки на файл из общей библиотеки для разных проектов.
Для некоторых приложений (в основном веб-приложений) характерно использование шаблонов для генерации конечных страниц. Так как основной целью является отделение исполняемого кода от представления данных, то данный шаг поможет в работе команды сэкономить много времени, сил и денег, обеспечив возможность параллельной работы программистов и дизайнеров.
Замечу, что в данном случае подкаталог js и css служат не для хранения статических библиотек JavaScript или стилей CSS, а именно для изменяемого кода, кода с параметрами или кода для вставки. Например если имеется компонент прорисовки календаря с подключением допфункционала к кнопкам, то будет гораздо удобнее в js положить компонент календаря, а в html файлах делать включения компонента, но с нужными параметрами. Возможно это покажется кому-то говнокодом, но это гораздо лучше, чем создать готовую статическую библиотеку календаря, а через полгода-год понять, что требуется добавить ещё пару тройку свойств и методов к компоненту (например сделать datepicker не только в виде одного месяца, но и добавить возможность превратить в календарь на год), а какая магия была внутри никто и не вспомнит. Вставки дадут больше прозрачности.
Здесь все основные никогда не меняющиеся (или меняющиеся крайне редко) стили, картинки, звуки, JS библиотеки и фреймворки.
Удобство подключения библиотек зависит в основном от языка и фреймворка в основе приложения. Здесь НЕ лежат библиотеки, подключаемые из репозитариев и поддерживаемые независимыми разработчиками. Здесь лежат ваши собственные вспомогательные функции. Например у меня лежат некоторые функции-декораторы, при обработке маршрута, но до вызова основной функции.
Как хранить настройки, определяющие глобальные параметры приложения? Сколько на этот счет было баталий и не счесть. Без деталей как делаю я: храню в виде Python модуля отдельным каталогом. Внутри файлы для разных режимов запуска.
Почему py? Да потому, что никому не сдался парсинг XML, YAML, INI и другой чепухи, когда под рукой достаточно легко создавать переменные вида:
Подгружаемые в процессе работы файлы, логи и другие данные хранятся в отдельном каталоге data
Тестовые модули и фикстуры
Вся документация по проекту должна храниться отдельно. Я использую для этого каталог doc и храню как статические веб-страницы с входной точкой index.html. Это удобно тем, что я могу расшарить отдельный каталог документации через любой веб-сервер. Или просмотреть прямо из каталога любым веб-браузером (в том числе и консольными типа lynx, elinks).
Здесь всё зависит от задач. И в комментариях (по моему скромному мнению) не особенно нуждается.
Сюда можно складывать логи при тестовых запусках или выводы самого приложения.
В принципе, это все. Почему я так немногословен? Потому что код скажет за меня лучше, чем я сам. Остальные мои комментарии могут либо запутать, либо разжечь споры о том, какой подход будет лучше/хуже.
Корень
И так мы взялись за проект. Пусть это будет самый простой проект myapp. Создаём для него каталог в основной папке для разработки (у меня это Devel).
mkdir myapp
cd myapp
В дальнейшем все действия будут внутри каталога основного проекта
Git
Инициализируем пустой репозиторий
git init
cat << EOF > .gitignore
# Игнорировать байткод
*.pyc
*.pyo
**/__pycache__/
# Игнорировать каталог с данными и логами
**/data/
**/logs/
EOF
Прочти меня
Следующим шагом идёт создание обязательного файла README. В этом файле хранится основное и ключевое описание нашего будущего проекта. Некоторые делают файл в виде text/plain с названием README.txt. На крупных порталах репозитариях принят за стандарт Markdown с названием README.md. Я же предпочитаю html, т.к. мне удобнее делать цветовые выделения, вставлять ссылки, картинки и другие мультимедиа, открывать в браузере, вставлять куски кода в тегах <pre> и <code>, использовать готовые фреймворки типа Bootstrap для оформления и другой магии вне Хогвардса. Соответственно и название README.html
touch README.html
Если проект ведётся несколькими командами, то рекомендую каждой команде иметь свой README файл внутри каждого разрабатываемого независимо модуля, компонента, библиотеки и т.д.
Приложение
Основной каталог приложения называю так же, как и каталог проекта. В случае этой статьи myapp
mkdir myapp
touch myapp/__init__.py
Модули и компоненты
Внутри каталога приложения стандартно создаются __init__.py, содержащий код инициализации приложения и подключения всех необходимых частей к приложению. В частности это Blueprints для одноветочных URL или namespace для логики отдельного сервиса, но с разными URL (простым примером может служить создание namespace для сервиса статей блога, где явно имеются разные пути вида /pages и /page/ID)
mkdir myapp/bp_component
touch myapp/bp_component/__init__.py
или
mkdir myapp/ns_component
touch myapp/ns_component/__init__.py
Модель БД
Модель базы данных содержит код инициализации базы данных, а также подключения. И, обязательно, структуру таблиц и связей. Так же желательно выделять таблицы в отдельные файлы на основе бизнес-логики приложения. Описание нескольких классов таблиц и связей в один отдельный файл удобно тем, что можно достаточно легко повторно использовать код, скопировав нужный файл в другой проект или использовать символьные ссылки на файл из общей библиотеки для разных проектов.
mkdir myapp/models
touch myapp/models/__init__.py
touch myapp/models/page.py
Шаблоны для шаблонизатора
Для некоторых приложений (в основном веб-приложений) характерно использование шаблонов для генерации конечных страниц. Так как основной целью является отделение исполняемого кода от представления данных, то данный шаг поможет в работе команды сэкономить много времени, сил и денег, обеспечив возможность параллельной работы программистов и дизайнеров.
mkdir myapp/templates
mkdir myapp/templates/html
mkdir myapp/templates/js
mkdir myapp/templates/css
Замечу, что в данном случае подкаталог js и css служат не для хранения статических библиотек JavaScript или стилей CSS, а именно для изменяемого кода, кода с параметрами или кода для вставки. Например если имеется компонент прорисовки календаря с подключением допфункционала к кнопкам, то будет гораздо удобнее в js положить компонент календаря, а в html файлах делать включения компонента, но с нужными параметрами. Возможно это покажется кому-то говнокодом, но это гораздо лучше, чем создать готовую статическую библиотеку календаря, а через полгода-год понять, что требуется добавить ещё пару тройку свойств и методов к компоненту (например сделать datepicker не только в виде одного месяца, но и добавить возможность превратить в календарь на год), а какая магия была внутри никто и не вспомнит. Вставки дадут больше прозрачности.
Статика
Здесь все основные никогда не меняющиеся (или меняющиеся крайне редко) стили, картинки, звуки, JS библиотеки и фреймворки.
mkdir myapp/static
mkdir myapp/static/css
mkdir myapp/static/js
mkdir myapp/static/images
Библиотека функций
Удобство подключения библиотек зависит в основном от языка и фреймворка в основе приложения. Здесь НЕ лежат библиотеки, подключаемые из репозитариев и поддерживаемые независимыми разработчиками. Здесь лежат ваши собственные вспомогательные функции. Например у меня лежат некоторые функции-декораторы, при обработке маршрута, но до вызова основной функции.
mkdir myapp/lib
touch myapp/lib/__init__.py
Настройки и конфигурирование
Как хранить настройки, определяющие глобальные параметры приложения? Сколько на этот счет было баталий и не счесть. Без деталей как делаю я: храню в виде Python модуля отдельным каталогом. Внутри файлы для разных режимов запуска.
mkdir config
echo "CONFIG = 'config.devel'" > config/__init__.py
touch config/devel.py
touch config/prod.py
Почему py? Да потому, что никому не сдался парсинг XML, YAML, INI и другой чепухи, когда под рукой достаточно легко создавать переменные вида:
import os
DEBUG = True
TITLE = 'SpecialistOff.NET'
DIR_BASE = '/'.join(os.path.dirname(os.path.abspath(__file__)).split('/')[:-1])
DIR_DATA = DIR_BASE + '/data'
DIR_FILES = DIR_DATA + '/files'
MIMETYPES = {
'gif': 'image/gif',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'txt': 'text/plain'
}
SERVERS = [
{'name': 'server1', 'IP': '8.8.8.8', 'port': '80'}
]
Данные
Подгружаемые в процессе работы файлы, логи и другие данные хранятся в отдельном каталоге data
mkdir data
mkdir data/files
Тестирование
Тестовые модули и фикстуры
mkdir tests
mkdir tests/fixture
touch tests/__init__.py
touch test.py
chmod +x test.py
Документация
Вся документация по проекту должна храниться отдельно. Я использую для этого каталог doc и храню как статические веб-страницы с входной точкой index.html. Это удобно тем, что я могу расшарить отдельный каталог документации через любой веб-сервер. Или просмотреть прямо из каталога любым веб-браузером (в том числе и консольными типа lynx, elinks).
mkdir doc
touch doc/index.html
Развертывание
Здесь всё зависит от задач. И в комментариях (по моему скромному мнению) не особенно нуждается.
mkdir deploy
touch deploy/requirements.txt
touch deploy/build.sh
mkdir deploy/config
touch deploy/config/__init__.py
touch deploy/config/demo.py
mkdir deploy/cron
touch deploy/cron/myapp
mkdir deploy/docker
touch deploy/docker/Dockerfile
touch deploy/docker/docker-compose.yml
mkdir deploy/nginx
touch deploy/nginx/myapp.conf
mkdir deploy/uwsgi
touch deploy/uwsgi/conf.ini
mkdir deploy/uwsgi/conf.d
touch deploy/uwsgi/conf.d/myapp.conf
Логирование
Сюда можно складывать логи при тестовых запусках или выводы самого приложения.
mkdir logs
Вспомогательные скрипты и утилиты
mkdir utils
touch utils/useradd.py
chmod +x utils/useradd.py
Вместо заключения
В принципе, это все. Почему я так немногословен? Потому что код скажет за меня лучше, чем я сам. Остальные мои комментарии могут либо запутать, либо разжечь споры о том, какой подход будет лучше/хуже.
Комментарии (5)
felexlex
31.08.2018 15:28В заголовке стоит указать, что речь идет в первую очередь о проектах на Python
RemiZOffAlex Автор
31.08.2018 15:30Я не взял на себя смелость указывать именно Python, т.к. возможно это применимо и к другим ЯП. Но я не знаю особенностей других ЯП.
evocatus
Это должно считаться истиной в последней инстанции?
Мне кажется, что ответ прост: структура папок проекта отражает структуру зависимостей частей кода друг от друга — более общие и базовые вещи на более верхнем уровне, более специфические — на более нижнем. В случае сомнения — кладём на один уровень. (в случае очень большого проекта, структура папок начинает отражать структуру организации, что не случайно)
Да и это зависит от языка программирования. Тот же Python обладает достаточно мощной системой импорта, которая позволяет сделать почти всё, что угодно, даже циклические зависимости (что очень плохо и чего нужно избегать). Хорошая статья на тему. И ещё одна.
Если код написан хорошо, то его работоспособность не должна зависеть от расположения в папках (а конкретизация отношений происходит в каком-нибудь модуле самого общего уровня) и в итоге такую структуру сравнительно легко менять.
Самая большая проблема иерархии каталогов в том, что она (а также логика импорта и зависимостей в большинстве языков программирования) слишком похожа на одну из самых больших проблем ООП: наследование (которого также следует избегать). Проблема наследования в том, что оно накладывает одну единственную структуру на набор сущностей, тогда как очень часто мы оперируем в рамках нескольких несовместимых структур параллельно.
Например: с одной стороны хорошо, когда все шаблоны лежат в одной папке — мы можем настроить для неё отдельные права доступа, кэширование, отдавать с другого сервера, и т.д., и т.д. Если мы используем хороший движок шаблонов, то мы можем сделать базовые шаблоны, которые будут расширяться более специфичными шаблонами. Мы можем «подготавливать» шаблоны к использованию (например, в Go необходимо явно вызывать ParseFile, которая подгружает шаблон из файла в пустой шаблон, перед использованием).
С другой стороны шаблон тесно связан с V и C в аббревиатуре MVC. Если поменялась модель данных, должны поменяться обработчики и должен поменяться шаблон. А если части крупного веб-проекта делаются разными людьми, то это тем более адекватно, даже с точки зрения систем контроля версий (любопытно, как эти папко-ориентированные программы меняют наш подход к структурированию файлов и каталогов).
Что выбрать? Разве здесь может быть окончательный ответ?
RemiZOffAlex Автор
MVC не единственный шаблон проектирования. Бесспорно, что нужно применять тот шаблон, который наилучшим образом подходит к проекту. Указанная в статье структура по большей части опирается на бизнес-логику проекта и выделение независимых частей в проекте. Такая структура позволила с лёгкостью переносить код, компоненты, целые каталоги между проектами без ущерба основному функционалу проектов. Обсуждение плюсов разработки по шаблону MVC достаточно хорошо описано в других статьях https://habr.com/search/?q=mvc
evocatus
Я понимаю, что не единственный. Мой основной посыл был в том, что часто нельзя однозначно ответить на вопрос о том в какую иерархию лучше уложить модули и соотв. файлы.