¿Cómo evitar métodos de acceso a datos duplicados que recuperen datos similares?

8

En casi todos los proyectos en los que trabajo con un equipo, el mismo problema parece aparecer. Alguien escribe código de interfaz de usuario que necesita datos y escribe un método de acceso a datos:

AssetDto GetAssetById(int assetId)

Una semana después, alguien más está trabajando en otra parte de la aplicación y también necesita un AssetDto'aprobadores' pero ahora incluye y escribe lo siguiente:

AssetDto GetAssetWithApproversById(int assetId)

Un mes después, alguien necesita un activo, pero ahora incluye las 'preguntas' (o los 'propietarios' o las 'solicitudes en ejecución', etc.):

AssetDto GetAssetWithQuestionsById(int assetId)
AssetDto GetAssetWithOwnersById(int assetId)
AssetDto GetAssetWithRunningRequestsById(int assetId)

Y empeora aún más cuando GetAssetWithOwnerAndQuestionsByIdcomienzan a aparecer métodos como el .

Verá el patrón que emerge: un objeto está unido a un gráfico de objetos grandes y necesita diferentes partes de este gráfico en diferentes ubicaciones.

Por supuesto, me gustaría evitar tener una gran cantidad de métodos que hacen casi lo mismo. ¿Es simplemente una cuestión de disciplina de equipo o hay algún patrón que pueda usar para evitar esto? En algunos casos, puede tener sentido tener métodos separados, es decir, obtener un activo con solicitudes en ejecución puede ser costoso, por lo que no quiero incluirlos todo el tiempo. ¿Cómo manejar tales casos?

Ronald Wildenberg
fuente
1
Puede usar algo como griales que maneja la carga diferida (gorm a través de hibernación) de propiedades, cuando se intenta el acceso. De esta manera, solo necesita llamar a = getAssetById(x)y luego puede llamar a a.preguntas, etc. sin cargarlas específicamente, ya que el sistema ORM subyacente lo carga por usted cuando se intenta el acceso.
techfoobar
1
Eso sería posible, pero requiere que mantenga abierto un contexto de base de datos durante la consulta. Prefiero que este tipo de conocimiento no se filtre en la capa de acceso a datos. Y tiene menos control sobre las consultas que se ejecutan. Pero una opción muy interesante ...
Ronald Wildenberg
Sí, y supongo que todo el punto de separarlo en DTO. Griales va por el camino no DTO de hacer las cosas ..
techfoobar
La ejecución de consultas abiertas desde una única interfaz requeriría que esa interfaz sea un lenguaje de consulta específico del dominio. La respuesta aceptada es similar a esa.
mike30

Respuestas:

4

En cuanto a la sintaxis, crearía un objeto intermedio de construcción de consultas con una interfaz fluida:

// all the basic, cheap to query fields
AssetDto a = AssetRetriever(asset_id).fetch() 

// some common expensive fields
AssetDto a = AssetRetriever(asset_id).withOwner().withQuestion().fetch() 

// numerous less common fields may not command dedicated methods
AssetDto a = AssetRetriever(asset_id).withFields("foo", "bar").fetch() 

// Better yet, use an enum and enjoy static checking
AssetDto a = AssetRetriever(asset_id).withFields(F_OWNER, F_QUESTION).fetch() 

Espero que sea lo suficientemente obvio para implementar. El único método que realmente tocará la base de datos es fetch().

9000
fuente
Realmente me gusta esta solución. Aplica lo que quiero lograr con una sintaxis comprensible y limpia y ofrece muchas posibilidades para la extensión de la interfaz y la optimización de las consultas SQL.
Ronald Wildenberg
2

Cuando se trata de objetos grandes, esto es realmente común. Si bien agregar nuevos métodos aumenta el rendimiento, disminuye significativamente la capacidad de mantenimiento. Y nuevamente debes elegir entre esos dos.

Sugiero que tenga un método que devuelva (no necesariamente el más pequeño) datos de uso común, otro que devuelva todo el objeto y probablemente algunos más para los recursos más caros.

Otro enfoque es tener métodos que devuelvan solo los campos necesarios del objeto, como AssetQuestions GetAssetQuestionsById(int assetId)o Owners GetAssetOwnersById(int assetId).

Junto con esto, debe establecer algunas reglas con respecto a la recuperación de datos. Por ejemplo, si alguien necesita 5 campos del objeto y hay un método existente que devuelve 8, se debe usar el método existente.

superM
fuente
Esperaba que hubiera algún patrón que pudiera aplicar para que el problema no ocurriera, pero me temo que su solución es la mejor. Todo se reduce a más disciplina e investigación cuando escribes un nuevo código de acceso a datos.
Ronald Wildenberg
Me gusta su solución que introduce métodos adicionales para recuperar objetos relacionados. Puede costar un poco de rendimiento ya que ya NO SE UNE a los datos en la base de datos, pero para mantenerlo, probablemente sea una gran mejora.
Ronald Wildenberg
3
Hay una solución "loca" que me viene a la mente: crear un tipo con un campo booleano para cada parámetro, establecer verdadero para los campos que desea recuperar y pasar ese objeto como parámetro)))
superM
Eso también me pasó por la cabeza y la única desventaja es que debe estar preparado para tener en cuenta todas las combinaciones posibles en su código de acceso a datos. Aunque, por supuesto, podría escribir consultas muy eficientes para los casos más comunes ...
Ronald Wildenberg
Supongo que también sería difícil de mantener, pero podría funcionar en algunos casos
superM
1

He pasado por este mismo problema recientemente y adopté la siguiente solución:

Los métodos de acceso a datos deben obtener datos solo de un único recurso (por ejemplo, una tabla de base de datos), y si el proceso necesita objetos relacionados agregados al objeto principal, debe llamar al método responsable de esos objetos respectivos.

De esta manera, si necesita un activo con sus aprobadores, debe crear un método de fachada que una los objetos.

Ejemplo:

public Class AssetFacade {

   public AssetDto getAssetWithQuestionsByAssetId(int assetId) { 

      AssetDto asset = AssetDao.getAssetById(assetId);
      List<QuestionDto> questions = AssetDao.getQuestionsByAssetId(assetId);
      asset.setQuestions(questions);

      return asset;
   };
 }
marcioggs
fuente
1
Esta es una solución posible, pero no me gusta que pierda todas las posibilidades para optimizar el acceso a los datos a través de la base de datos. Es decir, una consulta con una combinación en tres tablas puede ser más rápida que tres consultas separadas.
Ronald Wildenberg
Estoy de acuerdo contigo, pero no pude encontrar una solución mejor en ese momento. ¡Me alegra que hayas creado esta pregunta para que yo también aprenda mejor!
marcioggs
0

¿Es simplemente una cuestión de disciplina de equipo o hay algún patrón que pueda usar para evitar esto?

Sí, se trata de algunas pautas en el patrón de nombres para el equipo. Puede establecer 4 métodos simples como GetEntityById (), GetAllEntities (), SetEntity (), DeleteEntityById ().

Además, puede tener dos dto's con el nombre AssetSimpleDto GetAssetById(assetId)y otro dto detallado llamado as AssetDto GetAssetDetailById(assetId). El primer método y dto se personaliza para brindar un mínimo, mientras que el segundo es brindar toda la información relacionada que su funcionalidad pueda necesitar.

Yusubov
fuente
Desafortunadamente, el caso no es tan fácil como 'obtener simple' o 'obtener todo', por lo que esto realmente no resuelve el problema subyacente. Esto probablemente causaría que todos usen el método 'get all' porque necesitan una parte del gráfico de objeto que no se devuelve con 'get simple'.
Ronald Wildenberg