https://python-dependency-injector.ets-labs.org/examples/decoupled-packages.html
런타임 의존성 : Service class -> class -> class
소스코드 의존성 : Service class <- class <- class
이렇게 클래스 간의 디커플링을 해보자. 디커플링이 잘 돼있으면 모킹이 편해지고 테스트코드에서 원하는 응답으로 바꿀 수 있어서 다양한 경우에 대해 테스트할 수 있게 된다. 의존성이 줄어들기 때문에 특정 클래스의 코드가 바뀌어도 그걸 사용하는 클래스에선 변경이 적어진다.
services/get_real_estates_on_search_service.py
from real_estate.containers.service_container import (
ServiceContainer,
)
from dependency_injector.wiring import inject, Provide
from real_estate.services.search_real_estates import SearchRealEstates
from real_estate.services.set_real_estates import SetRealEstates
class GetRealEstatesOnSearchService:
@inject
def __init__(
self,
set_real_estate: SetRealEstates = Provide[
ServiceContainer.set_real_estate_real_estates
],
set_region: SetRealEstates = Provide[ServiceContainer.set_real_estate_regions],
search_real_estates=Provide[ServiceContainer.search_real_estates],
) -> None:
self.set_real_estate: SetRealEstates = set_real_estate
self.set_region: SetRealEstates = set_region
self.search_real_estates: SearchRealEstates = search_real_estates
services/set_real_estates.py
from typing import Union
from real_estate.serializers import (
GetRealEstatesOnSearchResponseSerializer,
GetRegionsOnSearchResponseSerializer,
)
class SetRealEstates:
def __init__(self, serializer, key) -> None:
self.serializer: Union[
GetRealEstatesOnSearchResponseSerializer,
GetRegionsOnSearchResponseSerializer,
] = serializer
self.key = key
services/search_real_estates.py
from dependency_injector.wiring import inject, Provide
from manticore.manticore_client import SearchClientInterface
from real_estate.containers.search_container import SearchContainer
class SearchRealEstates:
@inject
def __init__(
self,
search_client: SearchClientInterface = Provide[SearchContainer.search_client],
) -> None:
self.search_client: SearchClientInterface = search_client
containers/service_container.py
from dependency_injector import containers, providers
from manticore.manticore_client import ManticoreClient
from real_estate.containers.search_container import SearchContainer
from real_estate.serializers import (
GetRealEstatesOnSearchResponseSerializer,
GetRegionsOnSearchResponseSerializer,
)
from real_estate.services.search_real_estates import SearchRealEstates
from real_estate.services.set_real_estates import SetRealEstates
class ServiceContainer(containers.DeclarativeContainer):
config = providers.Configuration()
set_real_estate_real_estates = providers.Factory(
provides=SetRealEstates,
serializer=GetRealEstatesOnSearchResponseSerializer,
key="real_estates",
)
set_real_estate_regions = providers.Factory(
provides=SetRealEstates,
serializer=GetRegionsOnSearchResponseSerializer,
key="regions",
)
search_client: providers.Factory[ManticoreClient] = SearchContainer.search_client
search_real_estates = providers.Singleton(
provides=SearchRealEstates,
search_client=search_client,
)
ServiceContainer().wire(
modules=[
"real_estate.services.get_real_estates_on_map_service",
"real_estate.services.get_real_estates_on_search_service",
]
)
containers/search_container.py
from dependency_injector import containers, providers
from manticore.manticore_client import ManticoreClient
class SearchContainer(containers.DeclarativeContainer):
config = providers.Configuration()
search_client = providers.Factory(provides=ManticoreClient)
SearchContainer().wire(
modules=[
"real_estate.views.manticore_view",
]
)
views/__init__.py
# 컨테이너 초기화용 import
from real_estate.containers.service_container import ServiceContainer # noqa
from real_estate.containers.search_container import SearchContainer # noqa
manticore/manticore_client.py
from abc import ABC, abstractmethod
import os
from typing import Any, Dict, List, Optional
import manticoresearch
import manticoresearch
from manticoresearch.api import search_api
class SearchClientInterface(ABC):
@abstractmethod
def run_indexer(self) -> None:
pass
@abstractmethod
def search(
self, index: str, query: dict, limit: int
) -> List[Optional[Dict[str, Any]]]:
pass
class ManticoreClient(SearchClientInterface):
def __init__(self) -> None:
self.configuration = manticoresearch.Configuration(
host=f"http://{os.getenv('HTTP_HOST')}:{os.getenv('HTTP_PORT')}"
)
self.api_client = manticoresearch.ApiClient(self.configuration)
self.search_api = search_api.SearchApi(self.api_client)