Рассмотрим этот сценарий …
У вас есть возможность поработать над новым API для вашего клиента. Директива (или ваше решение) заключается в использовании Spring Boot и внедрении зависимостей. Вы начинаете программировать — вдохновленный и взволнованный для нового проекта.
Вам также может понравиться:
Spring Core: инъекция зависимостей
Как разработчик или дизайнер, вы хотите, чтобы ваши услуги были максимально сфокусированными. То есть, если у вас есть CustomerService
класс, он не работает с каким-то виджетом в вашем приложении. Вы решаете, что WidgetService
класс будет обрабатывать такие вещи. Кажется логичным
Ваш дизайн продолжает процветать. Сделав шаг назад, вы можете легко понять ответственность за все ваши классы обслуживания — просто просматривая список имен классов из файловой системы. Вам даже не нужно открывать Interface
классы, чтобы иметь представление об ответственности классов.
Чтобы проиллюстрировать, вот пример:
services/
├─ AccountService.java
├─ CustomerService.java
├─ ...
├─ SalesService.java
├─ UserService.java
└─ WidgetService.java
Вы гордитесь Это приложение будет действительно легко поддерживать и поддерживать.
Тогда все усложняется
Представьте, что список услуг — это больше, чем пять простых классов, указанных выше. Может быть, когда вы закончите первоначальный проект, будет пятьдесят или даже сто классов обслуживания.
Теперь вы подошли к тому моменту, когда вам нужно внедрить сервис, который обеспечивает межсервисную функциональность. Например, какой-то глобальный аспект отчетности в приложении?
Ваш ReportService
класс представлен как интерфейс. Класс реализации может выглядеть так:
@Service
public class ReportServiceImpl implements ReportService {
private AccountService accountService;
private CustomerService customerService;
private DService dService;
private EService dService;
private FService fService;
private HService hService;
private IService iService;
private JService jService;
private MoreServicesService moreServicesService;
private SoMuchTypingService soMuchTyingService;
private SalesService salesService;
private UserService userService;
private WidgetService widgetService;
public ReportServiceImpl(@Lazy AccountService accountService, @Lazy CustomerService customerService,
@Lazy DService dService, @Lazy EService eService, @Lazy FService fService,
@Lazy GService gService, @Lazy HService hService, @Lazy IService idService,
@Lazy JService jService, @Lazy MoreServicesService moreServicesService,
@Lazy SooMuchTypingService soMuchTypingService, @Lazy SalesService salesService
@Lazy UserService userService, @Lazy WidgetService widgetService) {
this.accountService = accountService;
this.customerService = customerService;
this.dService = dService;
this.eService = eService;
this.fService = fService;
this.gService = gService;
this.hService = hService;
this.iService = iService;
this.jService = jdService;
this.moreServicesService = moreServicesService;
this.soMuchTypingService = soMuchTypingService;
this.salesService = salesService;
this.userService = userService;
this.widgetService = widgetService;
}
/**
* {@inheritDoc}
*/
@Override
public List<SomeObject> someReportingServiceMethod() {
// Here we go ....
}
}
В приведенном выше примере происходит довольно много инъекций зависимостей. Это не обязательно должны быть услуги. Фактически, большинство из них могут быть инъекциями в интерфейсы, определенные как расширения JpaRepository
API.
Однако инструменты статического анализа (например, CheckStyle) могут сообщать о слишком большом количестве параметров метода или конструктора . С CheckStyle по умолчанию равно 7, что представляет примерно половину параметров в ReportServiceImpl()
конструкторе.
ReportServiceImpl
Класс будет не строить , если функциональность CheckStyle по умолчанию требуется передать в трубопроводе CI / CD.
Обоснование подхода
Для примера ReportService
предположим, что существует бизнес-потребность охватить все эти аспекты приложения (либо через службу, либо через хранилище), чтобы предоставить ожидаемые результаты клиенту или клиенту.
Было потрачено время, чтобы убедиться, что каждый сервис детализирован и сфокусирован на выполнении аспектов, соответствующих названию сервиса. Опять же, убедившись, что CustomerService
не делает то, что WidgetService
должен делать.
При использовании службы отчетов необходимо использовать каждую из внедренных служб, чтобы получать данные, которые будут отражены в отчетах. Как разработчик, я мог бы размыть линии и сказать, что «все отчетные данные будут храниться в службе отчетов», но мне все равно придется отказаться от внедрения интерфейсов репозитория для доступа к фактическим данным.
При нестандартном подходе альтернативный подход может обременять клиента выполнением необходимых вызовов в каждой службе API и созданием отчета на стороне клиента. Хотя это, безусловно, возможно, некоторые пункты для рассмотрения отмечены ниже:
-
Возможность иметь двойную логику отчетности в нескольких клиентских приложениях, что увеличивает вероятность получения неверных данных.
-
Влияние на сетевой трафик для нескольких вызовов API для получения необходимой информации
-
Влияние на производительность для создания логики клиентского процесса, которая, вероятно, лучше подходит для (серверной) обработки на стороне сервера
In the end, the reporting needs (or whatever the driver) must be provided in order to meet the functional needs of the application. Telling the product owner that requirements need to be changed or removed, because there is a goal to not deviate from default properties of a static analysis tool, just may not be acceptable.
Conclusion
Considering the example above, I feel like the best approach toward meeting the needs of the application is to utilize several dependency injections within the public constructor of the service class.
While it may not be common to see as many dependency injections as noted above, I feel like this is a realistic possibility. What I don’t know is, what will be the impact if say one-hundred services or repositories were to be injected into a single service? Does the dependency injection start to fail at some point?
Certainly interested in feedback to this concept. Hopefully, I have established a scenario that provides an example where several injections are required to meet the needs of the application.
Have a really great day!
Further Reading
Spring Core: Dependency Injection
Spring DI Patterns: The Good, the Bad, and the Ugly
How Dependency Injection Works in Spring Java Application Development