Как избежать дублирования кода для горизонтально масштабируемого веб-приложения?
==================================================================================

 Разрабатываем приложение (symfony4), у которого с одной стороны есть пульт управления (админка), а с другой backend http restful api. БД общая, планируется сделать master(writes)->slaves(reads) реплики с балансировщиком slaves (haproxy). Админка будет только вертикально масштабируемой, а backend api горизонтально.  
  

Предположим в админке нужно управлять клиентами и банками (CRUD), эти данные улетают в master. Далее идет взаимодействие с backend api, который должен забрать клиенты и банки из slave и отдать в виде json.  
  

На мой взгляд правильно разделить проектируемое приложение на два - админка и backend api. У них разные зависимости и конфиги, а также требования к контейнеризации, что еще больше добавляет уверенности, что делать два независимых приложения верно.  
  

Но в таком случае меня смущает дублирование сущностей клиент и банк. Получается, если нужно добавить поле в одну сущность, то ее придется менять сразу в двух приложениях. Создавать общий бандл с сущностями тоже видится неверным решением. Думал сделать общение между этими приложениями на уровне HTTP запросов, но тогда в админку будет долбиться все backend api, что не согласовывается к требованиям масштабирования.  
  

Подскажите правильный архитектурный подход? 

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

Если нужно эти данные уже как-то обработать, декорируете их уже в кокретном приложении.  
  

Если у вас сейчас эти приложения похожи, то не известно, что будет через несколько лет. А вынос в отдельный репозитарий даст гибкость для архитектуры.  
  

Если выносится слой логики в отдельный репозитарий, очень желетельно иметь прослойку в конечном приложении, чтоб уже данные правильно готовить. Чтоб изменения в репозитарии с логикой, не поломали другое приложение.  
  

Долбится по HTTP выглядит вкусно, но там все недостатки репозитария и еще куча других.  
  

Update:  

[Philipp](http://toster.ru/user/zoonman) , [xmoonlight](http://toster.ru/user/xmoonlight) и я о том же пишем разными словами, насколько я понимаю. По крайней мере ответ [Philipp](http://toster.ru/user/zoonman) это то, что я имею ввиду. 

 Немного перефразирую xmoonlight. Налицо разделение модели данных и различных ее представлений.  

Создайте общий репозитарий с моделями и через наследование сделайте сериализацию с возвращением нужных структур для админки и API.   

Для API представление реализуйте в стиле белого списка. При расширении системы будет сохраняться совместимость.   

Работу с данными реализуйте через общий сервисный слой, в котором будет жить бизнес-логика.  

В таком случае бизнес-процессы будут реализованы вне зависимости от интерфейсов, а значит меньше багов и т.д. 

 Свои пять копеек.  

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

Так сказать инвестиция в развитие.  
  

На протяжении года занимаюсь проектированием собственного фреймворка, есть некие достижения.  

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

В частности я использую Doctrine ORM и ряд других компонентов от Symfony и других.  

Например роутинг свой, загрузка сервисов своя...  
  

Очень много сил потратил, а заказчик средств.  

Фреймворк уже готов.  
  

При разработке столкнулся с рядом проблем, как раз с дублированием кода.  

Да, эта проблема частично решена.  
  

Например:  
  

Сущности, и репозитории сущностей - это общий код для всех окружений.  

Окружений может быть сколько угодно.  
  

На данный момент использую два окружения  

Admin  

Api  
  

[admin.mysite.com](http://admin.mysite.com)  

[api.mysite.com](http://api.mysite.com)   

[mysite.com](http://mysite.com) - например фронт вообще на NUXT  
  

У окружений есть общий конфиг  

Общие языковые пакеты с ленивой загрузкой  
  

Вот так выглядят контроллеры в своих окружениях  
![5d6ae80b31c6d516616002.png](https://habrastorage.org/webt/5d/6a/e8/5d6ae80b31c6d516616002.png)  
  

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

Точек входа всегда ровно количеству окружений, но загрузчик приложения один  
![5d6aeab1c0789688262811.png](https://habrastorage.org/webt/5d/6a/ea/5d6aeab1c0789688262811.png)  

Код файла запуска приложения  

```
<?php

define('ENV', basename(__DIR__));
require '../../engine/bootstrap.php';
```
  
  

В контроллере у каждого окружения могут быть дублирующие методы   
  

Для себя выделил неформальное определение  
  

Функции, модули которые решают одну и туже задачу для всех окружений, выношу в отдельную папку  

Визуально это выглядит так  
  
![5d6aed5540883788430736.png](https://habrastorage.org/webt/5d/6a/ed/5d6aed5540883788430736.png) 

 1. Админка и бэкенд API - это манипуляторы объектов.  

2. Репозиторий объектов (их свойств и взаимосвязей) - должен быть централизованный: обычно - это связи и сущности в БД.  

3. Для управления репозиторием объектов можно создать ещё один манипулятор (со своим API) и обращаться к нему по необходимости из "админки" и/или "бэкенд API".  
  

Главное - **читайте мануал к БД, чтобы использовать все возможности** для манипуляции и хранения нужной Вам структуры объектов. 

 Вот возникла ситуация, когда надо использовать одинаковые вещи во многих местах, логично вынести их в отдельных пакет. Подключаешь туда, подключаешь сюда, и получаешь уверенность в том, что ты работаешь с одинаковыми наборами данных. Круто же? Останется только обновлять пакет.