The best practice is to inject a service as a dependency into a controller, and then we can utilize the service object to invoke its methods.
What is dependency injection?
Dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. It means we can directly inject the services to the class __construct method, and it will load the dependency runtime when instantiate the class.
Credit - https://www.tothenew.com/blog/services-and-dependency-injection-in-drupal/
Why use dependency injection?
- It improves code reusability.
Services in Drupal enable us to define our code in a manner that promotes reusability across multiple modules. This is achieved by injecting these services as dependencies, allowing them to be utilized by various modules.
- It improves the flexibility and maintainability of code by loose coupling.
Services in drupal are loosely coupled which enhances the independence of a service from other code components. This loose coupling results in increased flexibility and maintainability for the service.
- It improves testability.
Drupal services can easily be used with unit testing making it effortless to write test cases.
Let's come back to our question, the recommended best practice is to inject a service as a dependency into a controller and then utilize its object to invoke its methods.
Okay, let's see how we can do this.
Step 1:- Develop your service
File:- my_module.services.yml
File:- /my_module/src/ModuleHelper.php
Step 2:- Create your controller and inject your service.
File:- my_module.routing.yml
File:- /my_module/src/Controller/TestController.php
Dependency Injection for a Form :- https://www.drupal.org/docs/drupal-apis/services-and-dependency-injection/dependency-injection-for-a-form