É má prática criar uma função que retorna uma classe?
No banco de dados, tenho 7 tabelas diferentes. Como o MVC é o único padrão arquitetural com o qual tenho familiaridade, tentei seguir algo que mais ou menos se encaixe nisso (percebi ao longo do desenvolvimento que não seria possível fazer tudo em MVC, então foram feitas algumas adaptações). Sendo assim:
- Tenho 7 classes
Model
para representar os dados do banco no código; - Para cada classe
Model
, há uma classeSchema
, que serve para serializar/desserializar os dados que a API recebe/envia. Essa classe é um padrão do Marshmallow pelo que entendi; - Para cada
Model
, há também uma classeService
, que, no momento, interage com o SQLAlchemy e processa regras de negócio; - Para cada
Service
, há umResource
, que lida com as requisições HTTP e chama os métodos daService
.
Isso significa que, para cada tabela, tenho 4 classes. O problema é que, estruturalmente, as classes Schema
e Resource
são sempre idênticas, apenas adaptando qual outra classe elas vão chamar. Exemplo: todas as classes Resource
possuem o método get
. Porém, a UserResource
chama a UserService
nesse método, enquanto a TeamResource
chama a TeamService
, e por aí vai.
As classes Service
possuem regras distintas, e as Model
têm campos diferentes, então não tem pra onde correr. Mas nos outros casos, me parece repetição de código desnecessária, o que traz todas aquelas questões de manutenibilidade, redundância e por aí vai.
TL;DR: Em uma API, tenho várias classes idênticas para cada tabela que ela pretende acessar. Os grupos mais redundantes de classes são Resource
e Schema
.
No caso da Resource
, usei herança e a situação ficou menos pior. Por outro lado, no caso da Schema
, estou pensando em fazer uma função que retorne uma classe Schema
de acordo com o Model
passado como parâmetro. Seria má prática, considerando que elas possuem estrutura idêntica, com exceção do model?
Segue a estrutura das Schema:
class UserSchema(SQLAlchemyAutoSchema):
class Meta:
model = UserModel
@post_load
def to_model(self, data: dict, **kwargs) -> UserModel:
return UserModel(**data)
O que pretendo fazer é:
def create_schema(model) -> type:
class ModelSchema(SQLAlchemyAutoSchema):
class Meta:
model = model
@post_load
def to_model(self, data: dict, **kwargs) -> model:
return model(**data)
return ModelSchema
Obs.: quanto ao padrão arquitetural, estou ciente que por enquanto parece uma grande mistura. Peço que foquem apenas na pergunta do título.
Olha, criar High Order Classes é uma forma de se atingir herança horizontal em linguagens que suportam isso, inclusive acredito que Python suporte isso nativamente o que torna um pouco sem sentido criar uma High Order Class, e se herança vertical já não é bem vista, a horizontal tem uma reputação ainda pior, inclusive um bom motivo para herança num geral não ter uma boa reputação é quando você consegue utilizar as duas ao mesmo tempo, pois isso pode acabar levando ao que as pessoas chamam de problema do diamante.
A princípio herança seja ela qual for é uma técnica válida de reúso de código, porém com certeza essa não seria a minha recomendação para resolver um problema.
Eu não sou da comunidade python, então não faço ideia de como seria algo idiomático para um programador python, entretanto dei uma olhada nessa lib Marshmellow, e me parece algo como o Zod do JS com umas baterias a mais.
Olhando o exemplo que tem na doc dele me parece que eles tem uma solução oficial para a criação dos Schemas: https://marshmallow-sqlalchemy.readthedocs.io/en/latest/recipes.html#automatically-generating-schemas-for-sqlalchemy-models
Agora pensando em uma boa arquitetura, mesmo que os dados sejam os mesmos entre várias classes, você não deveria ficar tentado a compartilhar eles por qualquer meio que seja, ainda mais herança que vai gerar um acoplamento enorme. Isso só deve acontecer se ambas as classes mudarem pelos mesmos motivos, senão apesar de parecerem iguais no início, conforme o tempo for passando, elas vão terminar muito diferentes uma da outra, e esse acoplamento inicial feito para reaproveitar o código é o que vai estar segurando uma de evoluir independente da outra.
Quanto ao problema de sentir que muitas classes são iguais/repetidas, isso provavelmente se deve a criação de abstrações desnecessárias. Seu projeto provavelmente deve ser tão simples que acaba por ter um modelo anêmico, e portanto ter algumas abstrações extras (um dos 4 esteriotipos de classes) pode acabar por apenas servir para repetir o código enquanto causa alguma indireção, sem agregar algo de fato ao projeto, sendo um problema de overeengenering.
Claro que como arquitetos gostamos de deixar nossas opções abertas para o futuro, mas criar abstrações demais no início pode ser um problema tão grande quanto a falta delas no longo prazo.
Opa amigo, tudo bom? como é quase a mesma coisa, será que você não consegue adaptar ao padrão strategy? ai sim retornar a classe correspondente que você deseja?
Recomendo que p utilize os padrões strategy e factory. procure no refactor.guru. idealmente e visando a separação de código você faria uma fábrica pra cada classe e uma outra pra agregação. Porém precisa de verificar qual a melhor forma de faze-lo. O padrão strategy pode te ajudar com isso.
Não conheço muito de python mas verifique a possibilidade de utilizar polimorfismo.
acho que nao faz muito sentido, apesar de não ser politicamente incorreto.