Eliminación suave en la API REST

imagen

Para que el usuario no sienta dolor por la pérdida irremediable de datos, vale la pena pensar en la eliminación suave. Con la eliminación suave, el registro no se elimina físicamente de la base de datos, sino que solo se marca como eliminado. Esto facilita la recuperación de datos al restablecer la bandera.

Recientemente implementé la eliminación suave en uno de nuestros servicios REST. Los que estén interesados ​​en lo que hice, los invito a gato.

Entrada requerida


El debate sobre si se debe usar una extracción leve es muy antiguo. Solo mira los holivars largos aquí y aquí .

Lo más razonable es la posición según la cual todo depende de la situación. Hay casos en que la eliminación suave es conveniente o incluso necesaria; hay casos en que los argumentos de los opositores de la eliminación suave merecen atención. Por cierto, un argumento importante contra la eliminación suave es la respuesta que vino de 2018: si estamos hablando de cuentas de usuario, entonces la eliminación suave es contraria al GDPR .

Decidimos que en nuestro servicio para el almacenamiento de documentos, es necesaria la eliminación suave.

Enfoque de descanso


Si queremos implementar la eliminación suave en un servicio, debemos entender cómo debería verse desde el punto de vista de la interfaz. Una búsqueda en Internet mostró que una pregunta típica que las personas tienen es si usar DELETE {resource} como antes, o es mejor usar el método PATCH en su lugar con un cuerpo que incluye algo como {status: 'eliminado'} .

Aquí la opinión de la gente es inequívoca: es necesario usar DELETE como antes. Desde el punto de vista del cliente, la eliminación también es una eliminación en África. Nada debería cambiar: si se elimina un recurso, se vuelve inaccesible; Si el cliente desea eliminar el recurso, sabe que el método DELETE HTTP es para este propósito. No es necesario dedicar al cliente a los detalles de cómo exactamente el servicio implementa la eliminación.

Pero además de esto, me preocupaba la cuestión de cómo recuperar recursos eliminados. Por supuesto, este problema se resuelve administrando la base de datos. Sin embargo, me gustaría poder hacer esto a través de la API REST. Y aquí entramos en conflicto. ¿Resulta que el cliente todavía necesita estar dedicado a los detalles de implementación?

La búsqueda no arrojó resultados durante mucho tiempo, hasta que encontré un buen artículo de Dan Yoder . El artículo examina la semántica de diferentes solicitudes HTTP y sugiere que, en lugar de la eliminación física, mueva recursos remotos al archivo . Además, será bueno si DELETE devuelve un enlace al recurso archivado. El usuario siempre puede restaurar el recurso eliminado enviando una solicitud POST al archivo.

Diseño


Nuestro servicio REST se basa en la API web ASP.NET utilizando Entity Framework. Como dije, hago una eliminación suave para un recurso llamado documento.

Entonces, primero debe agregar las columnas a la tabla correspondiente. Como indicador, uso una marca de tiempo llamada Suprimido. Si el valor no es NULL, el recurso se considera eliminado. Además, es útil tener información sobre quién eliminó el recurso.

ALTER TABLE Documents ADD Deleted datetime NULL, DeletedBy int NULL GO 

La acción ELIMINAR en el controlador ahora simplemente establecerá los valores de estos campos en lugar de eliminar físicamente el registro. Además, DELETE devolverá un cuerpo con una referencia estándar al documento archivado:

 { "links": { "archive": "documents/{id}/deleted" } } 

De hecho, este es un punto importante: el enlace ayuda al cliente a comprender que el documento no se elimina, sino que se mueve .

El nuevo controlador para documentos archivados debe proporcionar los siguientes métodos:
OBTENER documentos / eliminadoObtiene una colección de todos los documentos eliminados.
OBTENER documentos / {id} / eliminadoDevuelve el documento eliminado.
Documentos POST / {id} / eliminadoRecupera el documento eliminado;
no requiere un cuerpo; devuelve 201 Creado
BORRAR documentos / {id} / eliminadoElimina físicamente un documento.

Implementación


Inicialmente, planeé agregar dos vistas a mi base de datos:

 CREATE VIEW DeletedDocuments AS SELECT * FROM Documents WHERE Deleted IS NOT NULL GO CREATE VIEW AvailableDocuments AS SELECT * FROM Documents WHERE Deleted IS NULL GO 

Me pareció que esto sería menos problemático: en lugar de establecer condiciones en el código, solo obtengo dos propiedades DbSet diferentes en mi contexto de base de datos. Es cierto que debe tener dos entidades idénticas en el modelo, pero esa es la propiedad de los objetos POCO en el contexto de EF: cada tabla corresponde exactamente a una entidad.

Por cierto, las representaciones en SQL pueden ser útiles para Entity Framework en otros aspectos: con su ayuda, por ejemplo, puede hacer referencia a tablas de otra base de datos si no desea crear varios contextos de base de datos.

Sin embargo, en mi caso, el número con las vistas no pasó. Durante la autorización, debe trabajar con todos los documentos, porque los usuarios tienen los mismos derechos para eliminar los documentos que los existentes.

Por lo tanto, decidí tener solo un documento DbSet en DbContext, y en el código cada vez que descubro qué se necesita exactamente en este momento:

 var availableDocuments = DbContext.Documents.Where(d => d.Deleted == null); var deletedDocuments = DbContext.Documents.Where(d => d.Deleted != null); var allDocuments = DbContext.Documents; 

Recursos relacionados


Un documento es un recurso con el que están asociados otros recursos. Por ejemplo, tenemos un alias de documento. Es decir, puede obtener un documento no solo por la ruta de documentos / {id} , sino también por la ruta de documentos / {alias} , donde alias es una cadena única.

Después de eliminar un documento, todos los alias asociados con él deberían volverse "invisibles": si antes el cliente recibió una lista de todos los alias usando documentos / alias GET, luego de eliminar el documento, sus alias de la lista desaparecerán.

¡Pero permanecieron en la base de datos! Queremos proporcionar la capacidad de restaurar el documento en el estado en que se eliminó. Esto puede causar confusión al cliente: está tratando de agregar un nuevo alias para otro documento, la lista devuelta por los documentos / alias GET no contiene esa línea, y el servicio se niega a agregarla.

No creo que este sea un problema grave. Sin embargo, si necesita resolverlo, puede agregar el punto final GET documentos / eliminado / alias . Entonces todo encaja: el servicio no puede agregar un alias, ya que dicho valor ya lo utiliza el documento remoto.

Puede surgir la pregunta: ¿vale la pena tirar un alias de la lista devuelta por documentos / alias ? ¡Que se queden! No creo que tal decisión sea correcta. Luego resulta que la lista de alias contendrá enlaces rotos, porque el servicio devolverá 404 No encontrado si el cliente intenta obtener el documento eliminado por alias. Si se trata de los recursos secundarios asociados con el documento, el comportamiento debería ser exactamente el mismo que si estuviéramos eliminando el documento físicamente.

Limpieza de archivos


La eliminación suave, además de poder recuperar datos fácilmente, tiene varias ventajas más. La operación de eliminación en bases de datos relacionales es una operación costosa. Y si incluso eliminar un registro conduce a la eliminación en cascada de registros en otras tablas, entonces esto está lleno de puntos muertos. Por lo tanto, la eliminación suave es más rápida y confiable que la eliminación física.

Pero hay un inconveniente significativo. La base está empezando a crecer.

Por lo tanto, en la etapa final, debe encargarse de la limpieza automática del archivo. Por supuesto, puede limpiar la base manualmente, pero es mejor automatizar este proceso. Si establecemos directamente la fecha de vencimiento de un objeto remoto, por ejemplo, 30 días, el cliente puede mostrar la página de archivo en la que se resaltarán los elementos cuya vida útil está cerca del final.

Mis manos aún no han alcanzado esta tarea. Planeamos agregar a nuestro sistema de tareas una tarea que una vez al día ejecute una consulta SQL simple que elimine todos los objetos sucios del archivo. Como parámetro, la tarea debe tener una fecha de vencimiento. Será necesario asegurarse de que el valor actual de este parámetro se almacene en algún lugar en un lugar. Entonces será posible implementar un método en el servicio que devuelva este valor al cliente.

Source: https://habr.com/ru/post/440886/


All Articles