RSS Telegram YouTube Apple Яндекс Spotify Google Amazon Почта

13. Project layout

19.08.2023

Скачать

К списку выпусков

Сегодня я предлагаю поговорить о файловой структуре проекта, о том, какие использовать файлы и директории для веб сервиса.

С одной стороны, какую бы структуру я не выбрал, это качественно не изменит мое приложение, не добавит новый функционал. Оранизация проекта - это про оформление кода, не про содержание. Тем не менее я считаю, что эта тема важна, т.к. файловая структура может повысить удобство кодовой базы для программиста, а именно удобство поиска нужной логики в программе.

Структура из одного файла

Самая простая файловая структура проекта - когда мы используем один файл. Такое приложение не лишено структуры. Его логика в одном файле может быть разделена на функции.

Преимущество одного файла в том, что из любого места в коде мне доступны все package-level переменные, функции и типы. Мне не нужно их импортировать, чтобы начать ими пользоваться. Для упрощения навигации по коду, можно использовать IDE. Например, в Goland IntellijIdea есть окно Structure, в котором есть список всех package-level переменных, функций и типов.

Еще одно преимущество - это легкость добавления нового кода, тк достаточно перейти в конец файла и начать писать код. В случае, если мы используем сложную файловую структуру, то для добавления, например, новой функции, нужно найти подходящую директорию и файл. Например, если я написал утилиту, то я могу добавить ее в util package, либо использовать в том package который ее использует в данный момент.

Этот пример показывает, что усложнение файловой структуры проекта - это не бесплатное действие. Поэтому усложнять структуру проекта стоит в том случае, если приемущества перевешивают недостатки.

В чем недостатки одного файла?

Если весь код будет состоять из одного package main, то могут быть проблемы с именованием. Хэндлеры между собой очень похожи, им часто нужна структура request, response, функция validate. Тк нельзя использовать одно имя несколько раз, то для каждой функции validate, нужно будет придумывать уникальное имя. Например, для этого можно использовать префикс, по которому можно будет понять, к какому хэндлеру отноиться validate. Проект из одного файла будет содержать длинные имена.

Предположим нам надо что-то изменить, например валидацию в каком-либо хэндлере. Поиск этой логики в проекте начнем с функции мэйн, которая будет в начале файла. Потом переходим в раутер, где для каждого пути есть название функции хэндлера. Потом мы переходим в нужную функцию и ищем ту часть, которая отвечает за валидацию, например вызывается функция validate. И только потом мы можем чтото поправить.

Кроме того можно легко потерять нужную строку в коде. Случайно проскролил вверх или вниз и ты уже не знаешь как вернуться назад. И надо заново искать. Также нельзя в IDE в нескольких окнах открыть один и тот же файл, что в каждом окне можно было видеть его разные части.

Проблемы с неймингом и поиском можно решить усложняя структуру проекта.

Аналогии по усложнению структуры

Разбиение проекта на части можно представить так. Предположим что вся логика представлена в виде одного файла. И те его части который связаны логически мы можем пометить ярлыками. И в следующий раз, когда нам нужно найти нужную логику, мы будем использовать эти ярлыки.

Еще одна аналогия - индексы в бд. Они замедляют добавление нового кода - тебе нужно разбивать его на части, добавлять в разные файлы, создавать новые файлы, именовать новые файлы. Нужно многое сделать, но зато потом, проще искать нужную часть кода в проекте.

Вывод - главная цель файловой организации это ускорения поиска кода. Как можно понять, полезен ли ваш project layout? Очень просто, если вы можете быстро найти нужную часть логики в приложении, то да. Про организацию проекта я бы в первую очередь говорил не о том, как делать а о том как лучше не делать. Например, не стоит делать глубокую иерархию, потому что по ней сложно искать. Возможно не стоит делать сильный декаплинг, когда логически связанный код разбросан по разным файлам и папкам. Мне кажется, что логически связанные части кода должны быть расположены ближе друг к другу, а не разнесены по дальним местам проекта. Не стоит создавать папки с одним файлом. Не стоит создавать папки с сотней файлов. Т.е. всегда когда возникают проблемы с поискаом кода, можно задуматься о упрощении поиска через изменение структуры проекта.

Нужен ли src?

Поговорим про простые способы организации. Зачем например в корне приложения добавляют диркеторию для исходного кода, например, src? Многие считают, что это удобно, тк. Мы отделяем исходники от всего остального. Но с тем же успехом можно же отдельную директорию сделать для этих файлов, а не для исходников. Если мы чаще открываем исходники, то не проще ли их держать в корне, а не помещать в src? Я например поступаю иначе, я создаю диркеторию docs, а в корне у меня исходники. Мне кажется, что те файлы, с которыми мы работаем чаще всего, должны быть ближе к корню проекта.

Консьюмеры и продьюсеры

Условно код можно поделить на потребителей и производители. Есть функции, которые вызывают и которых вызывают. Когда у нас все в main package, то проблем никаких с пакетами нет. Когда мы выделяем в отдельный пакет продьюсеров, то проблем тоже нет. В мейне можно испортировать любой продьюсер без последствий. Но трудности возникают когда мы из мэйна выносим в отдельный пакет консьюмеров. Особенно если у этих консьюмеров есть зависимости от каких-либо переменных в мейне, например, от флагов или от переменных, который инициализированы в мейн функции из переменных окружения. В этом случае, перенос в отдельнный пакет будет невозможен, т.к. Из мейн пакета нельзя будет импортировать зависимости. Для переноса придеться все зависимости добавить в отдельный пакет, из которого будет возможно их импортировать.

Мой project layout

Мой проджект лэйаут довольно простой. Обычно все хэндлеры я пишу в корне проекта в директории handler. В ней есть отдельный пакет для каждого хэндлера. В нем обычно есть файлы db.go, validate.go, handle.go. Часто у меня есть в корне директория docs и db. В мэйн файле в корне у меня старт сервера и раутер. За счет того, что для каждого хэндлера отдельный пакет, у меня нет проблем с неймингом. Я могу повторять имена функций или структур в каждом пакете, тем самым имена лаконичные читабельный и есть однообразие и предсказуемость в проекте.

Кроме того, каждый хэндлер как отдельное приложение, не зависит от других частей кода. С таким кодом комфортно работать, когда ты понимаешь, что все что тебе нужно находиться в нескольких файлах в одном package, а не разбросано по разным частям. Искать по такому коду очень просто.

К списку выпусков