Об организации

2024-01-06

Об организации кода. Code layout.

Попадается тебе проект, которому лет 10. Открываешь его. И что ты видишь? config, dto, integration, model, rest, вездесущий util. Какие-то части веб приложения? Ну так мы и до этого знали, что это веб приложение. Что оно делает-то?

Если копнуть глубже, появляется какое-то понимание:

  • dto
    • audit
    • campaign
    • fee
    • invoice
    • ...
  • model
    • audit
    • campaign
    • invoice
    • ...
  • rest
    • audit
    • campaign
    • invoice
    • ...

И тут, допустим, прилетает задача. Отчёты по аудиту стали слишком тяжёлыми. А нужны они только аудиторам. Надо бы вынести их на другой сервер, чтобы не мешали остальным пользователям пользоваться приложением.

Но ведь у нас монолит. Как это, вынести на другой сервер?

Наверное, стоит выделить наши dto.audit, model.audit, rest.audit и, наверняка, еще с десяток разных *.audit в отдельное приложение. Наверняка окажется, что оно всё зависит от пары-тройки различных util. И ещё от чего-нибудь.

Если вы хорошо писали код, стараясь минимизировать зависимости и вообще старались следовать SOLID, то, скорее всего, у вас получится. Но было бы значительно проще, если бы весь код, относящийся к аудиту, не был бы размазан ровным слоем по всему проекту. А был бы в своём пакете, в своей отдельной папочке.

А если у вас монолит действительно сильно монолитный, то, возможно, вам придётся просто взять ваш монолит как есть, и просто запустить ещё одну копию на другом сервере. Пусть там используется только audit часть. Грязно? А то!

Откуда берётся эта мода группировать файлы исходного кода по выполняемым им техническим функциям, а не по бизнес функциям?

Полагаю, потому что слишком многие туториалы для начинающих либо вообще не подымают тему организации исходного кода, либо предлагают группировку по архитектурным слоям как единственный возможный вариант. Сходу нагуглил: 1, 2. И слишком часто это называют Best Practices.

by layer layout

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

Я призываю группировать по фичам, бизнес функциям. Всё равно делить (на микросервисы, ага) придётся поперёк слоёв, чтобы в каждом (микро)сервисе остались все слои. Так давайте же сразу складывать всё так, чтобы потом удобнее было бы пилить. А пилить потом всё равно придётся. Рано или поздно, так или иначе.

как группировать

Делайте вот так:

  • audit
    • dto
    • model
    • rest
  • campaign
    • dto
    • model
    • rest
  • fee
    • dto
  • invoice
    • dto
    • model
    • rest
  • util
  • ...

Тогда и разделить будет проще. В том числе и потому, что вы, возможно, инстинктивно постараетесь уменьшить зависимости между разными частями приложения.

В идеале зависимостей между разными фичами вообще быть не должно. Могут быть зависимости от util. Ну что ж, на то он и утиль, чтобы предоставлять какие-то мелкие сервисы всем. Для начала пусть будет отдельным пакетом. А потом подумайте выделить его в отдельную библиотеку или модуль. Один приватный модуль с дурацким названием shared всё же лучше, чем дублирование кода.

В чём-то это противопоставление похоже на размещение софта в файловой системе.

Есть Filesystem Hierarchy Standard (FHS) для Unix систем. Где исполняемые файлы кладутся в /bin, настройки в /etc, а изменяемые файлы в /var (логи в /var/log). Соображение тут в том, что разные типы файлов требуют разного места на диске, разной скорости доступа, разной надёжности хранения. И удобнее примонтировать разные виды файловых систем поближе к корню, и совместно использовать их разными приложениями. Но, ручной контроль того, какая программа куда что положила, становится почти невозможным. Контролируют это с помощью различных пакетных менеджеров. Которые следят, что куда складывается, и подчищают нужные файлы при удалении софта.

С другой стороны, в Windows принято, чтобы каждая программа всё тащила с собой. А в Mac каждая программа — это вообще маленький образ диска. В этих ОС программы свои настройки хранят централизованно (реестр Windows), и логи тоже пишут централизованно. Не через файлы, а через сервисы операционной системы.

P.S. Не я один так считаю. Что исходный код надо организовывать по фичам (by feature), а не по слоям (by layer).