Injectable Providers (Services)
Providers are a fundamental concept of the framework. Many of Ascender Framework's classes are providers, some of them are just values, most of them are services or even repositories. The most common thing you may use in them are services. A service is usually a class with it's specific well-defined purpose. It can carry specific business logic tailored for specific purpose. In basic Ascender Framework application it used to supply your controller and routes with a specific business logic for it.
Now in previous chapters we discussed about basics of @Injectables, let's dive deeper into them in this chapter.
Services examples
Let's define an example service which works as file manager and stores text in files when required. We'll call it, DataService
@Injectable(
    provided_in="root"
)
class DataService:
    def save_file(self, path: str, content: str) -> None:
        with open(f"{path}/content.txt", "w") as f:
            f.write(content)
        print(f"Created/Updated file in {path}/content.txt")
    def read_file(self, path: str) -> str:
        with open(f"{path}/content.txt", "r") as f:
            content = f.read()
        return content
@Injectable() decorator marks the DataService as an injectable and tells Ascender Framework's DI to that it can be injected or inject dependencies. Also the parameter of decorator provided_in="root" specifies that it DataService will be available and provided in the application as a singletone.
Creating Service
You don't have to manually define these services as it time consuming. Ascender Framework CLI tries to maximumly optimize your time by creating these structures for you so you can concentrate on important things instead of this routine.
For example, to generate a new ExampleService in src/examples:
- Run Ascender Framework CLIcommand below:
You can also specify where to provide this service by specifying --module. Let's imagine we have an AscModule in example_module.py:
ExampleService with path src/examples/example_service.py
from ascender.core import Injectable
@Injectable()
class ExampleService:
    def __init__(self):
        ...
and AscModule in src/examples/example_module.py
Injecting Services
To inject service as dependency into controller or other services you can use their class __init__ method. Ascender Framework's DI determines and detects annotations of their __init__ method to supply it with dependencies it needs.
Unlike TypeScript, which loses type information during transpilation to JavaScript, Python retains its type annotations at runtime. This removes restrictions with resolving Type based dependencies and using @Injectable in some cases is not required, but strongly recommended to be used!
from examples.example_service import ExampleService
@Controller(
    name="example", 
    standalone=False
)
class ExampleController
    def __init__(self, example_service: ExampleService):
        self.example_service = example_service
And here's the service depends on service injection example: