Должен ли я использовать слой между службой и хранилищем для чистой архитектуры - Spring

9

Я работаю в архитектуре, она собирается предложить API отдыха для веб-клиента и мобильных приложений. Я использую Spring (Spring MVC, Spring данных JPA, ... и т. Д.). Модель предметной области закодирована со спецификацией JPA.

Я пытаюсь применить некоторые концепции чистой архитектуры ( https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html ). Не все, потому что я собираюсь сохранить модель домена jpa.

Фактический поток через слои таков:

Внешний интерфейс <-> Служба API -> Служба -> Репозиторий -> БД

  • Внешний интерфейс: веб-клиент, мобильные приложения
  • API Service : Rest Controllers, здесь я использую конвертеры и сервисы dto's и call
  • Сервис : Интерфейсы с реализациями, и они содержат бизнес-логику
  • Репозиторий : интерфейсы репозитория с автоматическими реализациями (выполняемыми Spring data jpa), которые содержат операции CRUD и, возможно, некоторые SQL-запросы.

Мое сомнение: я должен использовать дополнительный слой между службой и хранилищем?

Я планирую этот новый поток:

Внешний интерфейс <-> Служба API -> Служба -> Постоянство -> Репозиторий -> БД

Зачем использовать этот постоянный слой? Как говорится в статье о чистой архитектуре, я хотел бы иметь реализацию службы (бизнес-логика или сценарий использования), которая имеет доступ к слою постоянства. И никакие изменения не потребуются, если я решу использовать другой шаблон «доступа к данным», например, если я решу прекратить использование репозитория.

class ProductServiceImpl implements ProductService {
    ProductRepository productRepository;
    void save(Product product) {
        // do business logic
        productRepository.save(product)
    }
}

Так что я думаю использовать постоянный слой, подобный этому:

class ProductServiceImpl implements ProductService {
    ProductPersistence productPersistence;
    void save(Product product) {
        // do business logic
        productPersistence.save(product)
    }
}

и реализация персистентного слоя, как это:

class ProductPersistenceImpl implements ProductPersistence {
    ProductRepository productRepository;
    void save(Product product) {
        productRepository.save(product)
    }
}

Поэтому мне нужно только изменить реализации уровня персистентности, оставив сервис без изменений. В сочетании с тем, что репозиторий связан с фреймворком.

Что вы думаете? Спасибо.

Alejandro
источник
3
хранилище - это уровень абстракции. добавление еще одного не помогает
Ewan
О, но мне бы пришлось использовать интерфейс, предложенный Spring, я имею в виду имена методов репозитория. И если я захочу поменять репозиторий, мне придется сохранить имена пользователей, нет?
Алехандро
мне кажется, что хранилище пружин не заставляет вас выставлять пружинные объекты. Просто используйте его для реализации интерфейса агностика
Ewan

Ответы:

6

Внешний интерфейс <-> Служба API -> Служба -> Репозиторий -> БД

Правильно. Это базовый дизайн по разделению проблем, предложенных Spring Framework. Значит, вы находитесь на « весне правильным путем ».

Несмотря на то, что репозитории часто используются в качестве DAO, правда в том, что разработчики Spring взяли понятие Repository от DDD Эрика Эванса. Интерфейсы репозитория часто выглядят очень похожими на DAO из-за методов CRUD и потому, что многие разработчики стремятся сделать интерфейсы репозиториев настолько обобщенными, что, в конце концов, они не имеют различий с EntityManager (истинный DAO здесь) 1, но поддерживают запросы и критерии.

В переводе на компоненты Spring ваш дизайн похож на

@RestController > @Service > @Repository >  EntityManager

Репозиторий уже является абстракцией между сервисами и хранилищами данных. Когда мы расширяем интерфейсы репозитория Spring Data JPA, мы реализуем этот проект неявно. Когда мы делаем это, мы платим налог: тесная связь с компонентами Spring. Кроме того, мы разбиваем LoD и YAGNI, наследуя несколько методов, которые нам могут не понадобиться или которые мы не хотим иметь. Не говоря уже о том, что такой интерфейс не дает нам какой-либо ценной информации о потребностях домена, которые они обслуживают.

При этом расширение репозиториев Spring Data JPA не является обязательным, и вы можете избежать этих компромиссов, внедрив более простую и настраиваемую иерархию классов.

    @Repository
    public class MyRepositoryImpl implements MyRepository{
        private EntityManager em;

        @Autowire
        public MyRepository (EntityManager em){    
             this.em = em;
        }

        //Interface implentation
        //...
    }

Изменение источника данных теперь требует новой реализации, которая заменяет EntityManager другим источником данных .

    //@RestController > @Service > @Repository >  RestTemplate

    @Repository
    public class MyRepositoryImpl implements MyRepository{
        private RestTemplate rt;

        @Autowire 
        public MyRepository (RestTemplate rt){    
             this.rt = rt;
        }

        //Interface implentation
        //...
    }
    //@RestController > @Service > @Repository >  File

    @Repository
    public class MyRepositoryImpl implements MyRepository{

        private File file; 
        public MyRepository (File file){    
            this.file = file;
        }

        //Interface implentation
        //...
    }
    //@RestController > @Service > @Repository >  SoapWSClient

    @Repository
    public class MyRepositoryImpl implements MyRepository{

        private MyWebServiceClient wsClient; 

        @Autowire
        public MyRepository (MyWebServiceClient  wsClient){    
               this.wsClient = wsClient;
        }

        //Interface implentation
        //...
    }

и так далее. 2

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


1: В отличие от многих разработчиков, интерфейсы репозитория могут полностью отличаться друг от друга, потому что каждый репозиторий обслуживает разные потребности домена. В Spring Data JPA роль DAO играет EntityManager . Он управляет сессий, доступ к DataSource , отображения и т.д.

2: аналогичное решение улучшает интерфейсы репозитория Spring, смешивая их с пользовательскими интерфейсами. Для получения дополнительной информации ищите BaseRepositoryFactoryBean и @NoRepositoryBean . Тем не менее, я нашел этот подход громоздким и запутанным.

LAIV
источник
3

Лучший способ доказать гибкость дизайна - это согнуть его.

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

Хорошо, давайте проверим, хорошо ли этот шунтирующий слой. Создайте слой плоского файла, который сохранит ваши продукты в файлах. Теперь, где этот новый слой идет в этом дизайне?

Ну, это должно быть в состоянии пойти, где БД. В конце концов, нам больше не нужна БД, поскольку мы используем плоские файлы. Но для этого также не требовалось хранилище.

Подумайте, может быть, репозиторий - это деталь реализации. В конце концов я могу общаться с БД без использования шаблона хранилища.

Front end <--> API Service -> Service -> Repository -> DB

Front end <--> API Service -> Service -> Repository -> Files

Front end <--> API Service -> Service -> Persistence -> DB

Front end <--> API Service -> Service -> Persistence -> Files

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

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

candied_orange
источник