Вопрос: Есть ли какие-то best practices как организовывать файлы веб-проекта с докером?

Лучший ответ:
Уж не так все и просто, комрад ;)
Следуя лучшим практикам, вы должны сделать отдельный репозиторий с описанием инфраструктуры вашего проекта. 
Итого, у вас в группе проектов на GitLab (или в организации на GitHub) должно выйти примерно следующее:/project-group (organization):
    /api - сюда можно вынести api вашего проекта.
    /desktop - здесь ваш web-проект для desktop на php, например.
    /mobile - здесь ваш web-проект для мобильной версии сайта, на той-же node.js.
    /admin - здесь ваша админка.
    /infrastructure - ну, или назовите проще, "server". Здесь ваши Dockerfile, docker-compose, ci/cd скрипты и т.п.
Чтобы моч варировать вашу инфраструктуру в зависимости от окружения, вы должны разделить репозиторий инфраструктуры на отдельные сервисы.  Например, не все сервисы проекта должны запускаться в test окружении или на сервере тестовых веток на поддоменах, когда вы делаете домены вида "feature-1.test.project.com", чтобы ваши тестировщики могли тестировать фичи параллельно и независимо друг от друга. Так же, на dev и/или test окружениях может потребоваться сделать сервисы-заглушки на внешние api которые вы используете. Ну, например, на сервисы онлайн оплаты товаров на вашем сайте, которые будут отвечать всегда "OK" или не "OK".
Поэтому, чтобы можно было поднять среду с отличным от prod составом сервисов, нужно разделение.
Таким образом, в репозитории infrastructure должно получиться примерно следующее:/infrastructure
   /api
       service.yml
       build.yml
       deploy.yml
   /mobile
       service.yml
       build.yml
       deploy.yml
   /desktop
       service.yml
       build.yml
       deploy.yml
   /admin
       service.yml
       build.yml
       deploy.yml
   /mysql
       mysql.cnf
       service.yml
   /redis
       redis.conf
       service.yml
   /php-fpm
       Dockerfile
       www-conf
       php.ini
       service.yml
   /nginx
       nginx.conf
       /prod
            site.conf
       /test
            site.conf
       /dev
            site.conf
   /cron
       Dockerfile
       crontab
       service.yml
mobile-dev.sh
desktop-dev.sh
prod.sh
test.sh
В этих самых *.sh файлах, что указаны выше, вы производите запуск нужного состава сервисов. К примеру, разработчику мобильной версии на react не обязательно покупать мощный ПК, чтобы на его машине запускать вообще весь проект, с mysql, redis, admin и другими сервисами. Вам будет достаточно собрать образ мобильного приложения, и написать в mobile-dev.sh примерно следующее:docker-compose -f mobile/service.yml pull;
docker-compose -f mobile/service.yml down;
docker-compose -f mobile/service.yml up --detach;
Ну а в mobile/service.yml может быть что-то следующее:version: "3.7"

services:
  mobile:
    image: keymetrics/pm2:10-alpine
    environment:
      APP_ENV: ${APP_ENV}
      APP_LANG: ${APP_LANG}
      API_URL: ${API_URL}
      BASIC_TOKEN: ${BASIC_TOKEN}
    working_dir: /app
    command: pm2-runtime start config.js --env $APP_ENV
    volumes:
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      - ${APP_DIR}:/app
    restart: always
    container_name: mobile
API_URL при этом, можно приземлить на ваш тестовый или dev сервер. Таким образом, разработчик мобильной версии сайта будет иметь всегда ту версию api, которая по факту используется в проекте. Соответственно, код приложения должен уметь работать с переменными окружения для настройки своих параметров.
Ваш сайт - так же, должен запускаться отдельным service.yml файлом. Service.yml - можно поменять на docker-compose.yml в директории каждого сервиса, но, service здесь смотрится красивее.
Если требуется поднять все сервисы, необходимо передать последовательно все service.yml файлы через -f флаг вашему docker-compose. Это будет громоздкая команда, поэтому и делаются отдельные *.sh файлы для запуска.
Требуется понимать, что не следует хранить Dockerfile в репозитории каждого проекта, если наличие образа потребуется для этого проекта. Не каждый разработчик умеет делать Dockerfile и, например, устанавилвать расширения для PHP в рамках этих Dockerfile. Делегировав ответственность по сборке Dockerfile разработчикам проекта, вы рискуете получить неработающий сервис. Лучшее место для Dockerfile - в нашем примере, это репозиторий infrastructure, находящийся в распоряжении ваших DevOps-инженеров.
Та же история и с описанием build-процессов и процессов CI/CD. Не следует доаверять разработчику сборку его приложения в рамках целой огромной инфраструктуры проекта. Для этого в примере репозитория infrastructure - есть файлы build.yml/deploy.yml, в которых описана сборка и доставка сервисов. Любые CI/CD средства могут подключать фрагменты pipeline с внешних репозиториев, поэтому, когда у вас возникнет потребность сделать автобилд и автодеплой - проблем не будет, вы ведь уже учли все подводные камни совместной работы специалистов в вашей компании. Прошу учесть следующее.
Выше описано, как правильно реализовать ваш проект на docker + docker-compose. Здесь не учтено наличие swarm-mode или kubernetes, тем не менее, данную структуру будет гораздо проще использовать в режиме swarm или c использованием kubernetes. 
ЗЫ. Конечно, есть и другие рабочие практики на эту тему ;) Эта нравится мне больше всего, и работает универсальней остальных.

Худший ответ:
/project1:
    /bin
    /www
    /src (здесь сервисы, контроллеры и тд)
    /docker