feat(ServiceFactory): Criação de importação de classes de forma dinâmica mais otimizada

This commit is contained in:
Keven 2025-12-17 09:56:19 -03:00
parent 2223db9d75
commit 433d889060
4 changed files with 86 additions and 26 deletions

View file

@ -0,0 +1,64 @@
import importlib
import re
from functools import lru_cache
from typing import Type, TypeVar, Optional
from actions.config.config import Config
from actions.env.env_config_loader import EnvConfigLoader
# Genérico para garantir que o retorno respeite o Protocolo
T = TypeVar("T")
class ServiceFactory:
def __init__(self, package: str, table: str):
# Instancia o loader com o prefixo correto
env = EnvConfigLoader(".env")
# Ex: "packages.v1"
self.base_root = "packages.v1"
self.package = package
self.table = table
# Carrega config apenas uma vez
self.app_config = Config.get("app.json")
# Define a UF da aplicação
self.current_state = env.ORIUS_CLIENT_STATE
@lru_cache(maxsize=32)
def make(self, class_name: str, interface: Type[T]) -> T:
"""
Instancia um serviço dinamicamente com comportamento de Autoload.
"""
# 1. Converte CamelCase para snake_case (Autoload style)
# Ex: TAtoIndexService -> t_ato_index_service
module_name = re.sub(r"(?<!^)(?=[A-Z])", "_", class_name).lower()
# 2. Monta o caminho completo
# Ex: packages.v1.servicos.balcao.services.t_ato.SP.t_ato_index_service
import_path = (
f"{self.base_root}.{self.package}.services."
f"{self.table}.{self.current_state}.{module_name}"
)
try:
# 3. Importação Dinâmica
module = importlib.import_module(import_path)
# 4. Pega a classe do módulo
clazz = getattr(module, class_name)
# 5. Retorna a INSTÂNCIA da classe (já com () )
# Se seus serviços precisam de argumentos no __init__, altere aqui.
return clazz()
except ImportError as e:
raise ImportError(
f"FATAL: Não foi possível carregar o serviço '{class_name}' para o estado '{self.current_state}'.\nCaminho tentado: {import_path}\nErro: {e}"
)
except AttributeError:
raise AttributeError(
f"FATAL: O arquivo '{module_name}.py' existe, mas a classe '{class_name}' não foi encontrada dentro dele."
)

View file

@ -0,0 +1,10 @@
from typing import Protocol, Any, runtime_checkable
@runtime_checkable
class ServiceProtocolsInterface(Protocol):
"""
Contrato que garante que todo serviço tenha um método execute.
"""
def execute(self, schema: Any) -> Any: ...

View file

@ -1,34 +1,20 @@
from actions.dynamic_import.dynamic_import import DynamicImport
from packages.v1.servicos.balcao.schemas.t_ato_schema import TAtoIndexSchema
from packages.v1.servicos.balcao.services.t_ato.t_ato_index_service import (
TAtoIndexService,
)
from actions.dynamic_import.service_factory import ServiceFactory
from interfaces.service_protocols import ServiceProtocolsInterface
class TAtoController:
"""
Controller responsável por orquestrar as operações CRUD da tabela T_PESSOA_CARTAO,
utilizando carregamento dinâmico de serviços via DynamicImport.
"""
def __init__(self):
# ----------------------------------------------------
# Inicialização do DynamicImport
# ----------------------------------------------------
self.dynamic_import = DynamicImport()
self.dynamic_import.set_package("servicos.balcao")
self.dynamic_import.set_table("t_ato")
# ----------------------------------------------------
# Lista todos os registros de T_PESSOA_CARTAO
# ----------------------------------------------------
def index(self, t_ato_index_schema: TAtoIndexSchema):
# Configura o escopo deste controller
self.factory = ServiceFactory(package="servicos.balcao", table="t_ato")
# Instância da classe service
self.index_service = TAtoIndexService()
def index(self, schema):
# Execução da listagem
return {
"message": "Registros de T_ATO localizados com sucesso.",
"data": self.index_service.execute(t_ato_index_schema),
}
# Instânciamento da classe
service = self.factory.make("TAtoIndexService", ServiceProtocolsInterface)
# O VS Code sabe que .execute() existe por causa do IService!
result = service.execute(schema)
return {"message": "Sucesso", "data": result}