Documentation
Learn
Service Layer

Service Layer

In this section, we'll dive into the creation of the HauntedHousesService, a crucial component in our Python web application for managing haunted houses. The service layer is responsible for handling the business logic related to haunted houses.

Understanding the Service Layer

The service layer acts as a bridge between the controller (which handles HTTP requests) and the repository (which deals with data storage). It encapsulates the business logic and ensures that the controllers remain lightweight and focused on handling user input.

Haunted Houses Service

We'll walk through the step-by-step process of creating the HauntedHousesService provider. This service class will act as the business logic layer for managing haunted houses in our API.

~/haunted-houses
 touch haunted_houses/houses/haunted_houses_service.py

Importing Dependencies

haunted_houses/houses/haunted_houses_service.py
from .repository import HauntedHousesRepository, HauntedHouse

We begin by importing the required modules. The service relies on the HauntedHousesRepository to interact with the data.

The HauntedHousesService Class

The first thing we need in our service is a reference to the repository. We'll use dependency injection to inject the repository into the service.

haunted_houses/houses/haunted_houses_service.py
from .repository import HauntedHousesRepository, HauntedHouse
 
class HauntedHousesService:
    """The haunted houses service"""
 
    repository: HauntedHousesRepository  # 💉 pest will know how to inject this!

Get a List of Haunted Houses

In our API, we will have a GET /haunted-houses endpoint that returns a list of haunted houses. Let's create a method in our service to serve this purpose.

haunted_houses/houses/haunted_houses_service.py
from .repository import HauntedHousesRepository, HauntedHouse
 
class HauntedHousesService:
    """The haunted houses service"""
 
    repository: HauntedHousesRepository  # 💉 pest will know how to inject this!
 
    def get_houses(self) -> list[HauntedHouse]:
        """Get all haunted houses"""
        return self.repository.get_all_houses()

The get_houses method simply delegates the call to the repository wich returns a list of HauntedHouse objects.

Get a Single Haunted House

We'll also need a method to get a single haunted house by its ID. Let's create a method for that.

haunted_houses/houses/haunted_houses_service.py
from .repository import HauntedHousesRepository, HauntedHouse
 
class HauntedHousesService:
    """The haunted houses service"""
 
    repository: HauntedHousesRepository  # 💉 pest will know how to inject this!
 
    def get_houses(self) -> list[HauntedHouse]:
        """Get all haunted houses"""
        return self.repository.get_all_houses()
 
    def get_house_by_id(self, house_id: int) -> HauntedHouse | None:
        """Get a haunted house by ID"""
        return self.repository.get_house_by_id(house_id)

Create a New Haunted House

In order to provide the necessary business logic for creating a new haunted house, we'll need to create a create_house method in our service.

haunted_houses/houses/haunted_houses_service.py
from .repository import HauntedHousesRepository, HauntedHouse
 
class HauntedHousesService:
    """The haunted houses service"""
 
    repository: HauntedHousesRepository  # 💉 pest will know how to inject this!
 
    def get_houses(self) -> list[HauntedHouse]:
        """Get all haunted houses"""
        return self.repository.get_all_houses()
 
    def get_house_by_id(self, house_id: int) -> HauntedHouse | None:
        """Get a haunted house by ID"""
        return self.repository.get_house_by_id(house_id)
 
    def create_house(self, house: HauntedHouse) -> HauntedHouse:
        """Create a new haunted house"""
        return self.repository.create_house(house)

Update an Existing Haunted House

The update_house method will be responsible for updating an existing haunted house.

haunted_houses/houses/haunted_houses_service.py
from .repository import HauntedHousesRepository, HauntedHouse
 
class HauntedHousesService:
    """The haunted houses service"""
 
    repository: HauntedHousesRepository  # 💉 pest will know how to inject this!
 
    def get_houses(self) -> list[HauntedHouse]:
        """Get all haunted houses"""
        return self.repository.get_all_houses()
 
    def get_house_by_id(self, house_id: int) -> HauntedHouse | None:
        """Get a haunted house by ID"""
        return self.repository.get_house_by_id(house_id)
 
    def create_house(self, house: HauntedHouse) -> HauntedHouse:
        """Create a new haunted house"""
        return self.repository.create_house(house)
 
    def update_house(self, house_id: int, updated_house: HauntedHouse) -> HauntedHouse | None:
        """Update an existing haunted house"""
        return self.repository.update_house(house_id, updated_house)

Delete a Haunted House

Finally, we'll need to be able to delete a haunted house. We'll create a delete_house method for that.

haunted_houses/houses/haunted_houses_service.py
from .repository import HauntedHousesRepository, HauntedHouse
 
class HauntedHousesService:
    """The haunted houses service"""
 
    repository: HauntedHousesRepository  # 💉 pest will know how to inject this!
 
    def get_houses(self) -> list[HauntedHouse]:
        """Get all haunted houses"""
        return self.repository.get_all_houses()
 
    def get_house_by_id(self, house_id: int) -> HauntedHouse | None:
        """Get a haunted house by ID"""
        return self.repository.get_house_by_id(house_id)
 
    def create_house(self, house: HauntedHouse) -> HauntedHouse:
        """Create a new haunted house"""
        return self.repository.create_house(house)
 
    def update_house(self, house_id: int, updated_house: HauntedHouse) -> HauntedHouse | None:
        """Update an existing haunted house"""
        return self.repository.update_house(house_id, updated_house)
 
    def delete_house(self, house_id: int) -> None:
        """Delete a haunted house by ID"""
        self.repository.delete_house(house_id)

Dependency Injection

pest framework uses modular dependency injection. The providers are registered by the modules and injected in controllers and other providers. In this case, we are injecting the repository into the service. This means that we will need to register the repository as a provider in the module.

When we talked about the HauntedHousesModule, we mentioned that we will be comming back to it later. Now is the time to do that.

haunted_houses/houses/haunted_houses_module.py
from pest import module
from .repository import HauntedHousesRepository
 
@module(
    providers=[HauntedHousesRepository],
)
class HauntedHousesModule:
    pass

And now that we are here, we can also register the HauntedHousesService as a provider in the module.

haunted_houses/houses/haunted_houses_module.py
from pest import module
from .haunted_houses_service import HauntedHousesService
from .repository import HauntedHousesRepository
 
@module(
    providers=[HauntedHousesService, HauntedHousesRepository],
)
class HauntedHousesModule:
    pass

We will use our service as a provider for our controller in the next section.

Conclusion

In this section, we've covered the step-by-step process of creating the HauntedHousesService class within the pest framework. This service class acts as the core business logic layer, providing methods for handling haunted house-related operations.

We also learned about dependency injection and how to register providers in the module. In the next section, we'll create the HauntedHousesController and learn how we can use our service in the controller to handle HTTP requests.