Hace poco me preguntaba Alfonso Vargas en twitter sobre patrones para guardar un registro de los cambios realizados en entidades. No conozco ninguna receta mágica para hacerlo, pero sí he usado varias técnicas que a lo mejor pueden resultar útiles a alguien.
Por cierto, esto es algo que os animo a hacer: preguntar. La mayoría estamos dispuestos a ayudar en la medida de nuestras posibilidades.
El concepto de auditoría
Como hice cuando conté que toda aplicación debería tener un log, vamos a empezar por ver a qué nos referimos (o, al menos, a qué me voy a referir yo en este post) con auditoría.
Una aplicación puede guardar mucha información sobre lo que está pasando, pero no toda la información forma parte de la auditoría. La auditoría está formada por acciones de negocio. Son acciones realizadas por usuarios de la aplicación de las cuales queremos guardar una trazabilidad. Por ejemplo, conocer qué usuario activó una promoción. O cuándo se creó un nuevo producto. O quién canceló un pedido.
Aquí el concepto de negocio puede ser un tanto difuso, y dependiendo del tipo de aplicación y del tipo de usuarios que tenga, podemos auditar también aspectos como cambios en la configuración, cambios de versión, etc.
Esto dejaría fuera de la auditoría información como estadísticas de uso, errores, controles de rendimiento o, en general, cosas que interesan más a quién desarrolla la aplicación que a quien la usa.
Partiendo de esta visión de la auditoría como un registro de acciones de negocio, vamos a ver varias formas de enfocar el problema de cómo gestionarla.
No hacer nada
Suena mal, pero muchas veces nos empeñamos en hacer cosas que luego no valen de mucho.
Generar un buen registro de auditoría, que sea fácil de mantener, se consulte con comodidad y ofrezca información valiosa para el negocio no es trivial, por lo que antes de hacerlo porque sí, planteáte si te lo puedes evitar.
Generarla a mano
Sin duda es la opción más tediosa, porque implica que debemos escribir código específico para registrar cada evento de auditoría.
Depende de cómo esté estructurada la aplicación es posible que acabemos teniendo que intercalar código de auditoría con el código puro “de negocio” y se haga todo un poco más difícil de leer, mantener y testear.
Por otra parte, si estás generando la auditoría es porque se supone que es importante para el negocio, por lo que tratarla como parte integral del código de negocio tampoco parece tan mala idea.
La principal ventaja de esta técnica es que, aunque tediosa, es muy simple de implementar y ofrece el máximo control sobre qué auditamos y cómo lo hacemos. Por ejemplo, si en una operación de negocio queremos generar distintos eventos de auditoría en función del resultado o de los parámetros, podemos hacerlo fácilmente. O si necesitamos que una operación de negocio genere más de un evento de auditoría. O que lo haga sólo en determinados casos.
Además del trabajo que conlleva esta técnica, corremos el riesgo de que se nos olvide auditar determinadas acciones. A fin de cuentas, es algo manual y es fácil que cuando implementas una nueva operación se te olvide que hay que auditarla, o que al cambiar los datos generados por una acción se te olvida modificar los datos auditados.
Utilizar Event Sourcing
Si estás usando un diseño basado en Event Sourcing tienes auditoría casi gratis. O al menos es fácil tenerla si tus eventos están bien diseñados, porque deberían representar las acciones de negocio importantes para tu aplicación. Siendo realistas, más que las acciones de negocio son sus consecuencias, por lo que puede que pierdas parte del contexto.
De todas formas, tal vez eso sea demasiado optimista y haya unos cuantos eventos que no son interesantes para la auditoría, pero es relativamente fácil poder marcar de alguna forma aquellos eventos que sí lo son y hacer que pasen a formar parte de la auditoría.
Esta opción suena muy bien, pero su pega principal es que utilizar Event Sourcing introduce una complejidad muy grande en el sistema y no creo que compense introducirlo sólo para facilitar la parte de auditoría.
Utilizar triggers en base de datos
Es una técnica que tradicionalmente se ha usado mucho. Si estás usando una base de datos y la base de datos que estás utilizando permite crear triggers, puedes utilizarlos para detectar los cambios que se van produciendo en las tablas y actuar en consecuencia.
Es fácil construir unos preciosos triggers genéricos que registren todos los cambios a nivel de base de datos, marcando qué usuario modifica cada vez qué tabla e incluso registrando los cambios concretos en cada tabla.
El problema de esta aproximación es que si no haces triggers muy específicos (y te acercas entonces más al caso de la auditoría manual), la información que se va a guardar será demasiado genérica y difícilmente se pueda considerar operaciones de negocio porque tendrán poco valor semántico.
Apoyarse en eventos del ORM
Si subes un poco el nivel de abstracción, en lugar de utilizar triggers en base de datos puedes usar su equivalente en capa de aplicación, que serían los eventos disparados por todos los ORMs (o por la capa de acceso a datos que estés usando).
Esta forma de auditar tiene una ventaja importante sobre hacerlo a nivel de base de datos, y es que en lugar de trabajar con tablas podemos trabajar con entidades. Eso nos aisla (parcialmente) de la estructura de datos y ayuda (probablemente) a que los eventos de auditoría que registramos tengan mayor valor de negocio.
Aun así, sigue sin ser una auditoría tan semántica como en el caso de hacerla a mano y dependemos de que la capa de acceso a datos dispare los eventos necesarios para engancharnos y poder auditar.
Aprovechar los puntos de entrada de un API
Subiendo todavía más el nivel de abstracción, podemos pasar de basarnos en las entidades a basarnos en los puntos de entrada que tengamos en el API de la aplicación. No hace falta que estemos hablando de un API web, tal vez tengas una capa de servicios de aplicación, o de casos de uso, o un mediador gestionando peticiones y respuestas.
Lo bueno de aprovechar este punto para auditar es que se supone que las acciones del API representan operaciones completas contra su tu modelo, por lo que debería corresponderse bastante bien con las acciones de negocio que quieres auditar. Puede que no todas, pero si la mayoría. Mientras que en el caso de Event Sourcing estábamos enganchándonos al resultado de las operaciones, aquí nos podemos enganchar al comando que desencadena la operación.
Si sólo te limitas a auditar en los puntos de entrada puedes perder granularidad, por ejemplo si de una operación necesitas auditar varias cosas, pero podrías mezclar esta aproximación con la de auditoría manual que vimos al principio.
En el caso de que tu API esté tremendamente bien diseñada, podrías incluso utilizar algún sistema declarativo para definir lo que hay que auditar en cada punto de entrada, y usar alguna técnica (decoradores, AOP o similar) para realizar la auditaría. Desgraciadamente, muchas veces no es viable automatizar tanto y la tarea es más manual de lo que nos gustaría.
Dónde almacenar la auditoría
Algunas de las estrategias que he comentado te llevan a almacenar la auditoría junto a los datos de la aplicación, como el caso de los triggers en base de datos, pero en muchas ocasiones en posible utilizar otros tipos de almacenamiento.
Para elegir uno y otro entran en juego varios aspectos.
Si la información que vas a auditar tiene una estructura poco homogénea, utilizar un sistema que permita almacenar documentos (JSON, XML, o lo que sea) puede suponer ventajas frente a una base de datos relacional.
Sin embargo, antes de decidir usar otro sistema de persistencia para la auditoría debes tener en cuenta el nivel de transaccionalidad que necesitas. Si para ti es crítico garantizar que siempre que se produce algo en tu aplicación queda auditado, utilizar dos sistemas de persistencia diferentes te lleva al maravilloso mundo de las transacciones distribuidas, que es un mundo en el que es preferible no estar.
Implicaciones legales
Un último aspecto que me gustaría señalar en esto de la auditoría son las implicaciones legales que puede tener. No soy abogado y no tengo mucha idea sobre el tema, pero hay dos factores a considerar.
En primer lugar, puede haber escenarios en los que la auditoría sea crítica desde un punto de vista normativo/legal. Si desarrollas un software para empresas de control de plagas (por poner un ejemplo), legalmente necesitas tener un control de quién ha usado qué venenos, cuándo y dónde. En estos casos necesitas tratar la auditoría como una parte fundamental del proceso de negocio y asegurarte de que se genera y almacena correctamente (cuidado entonces si tenías pensado tratarla como un subproducto almacenado en mongodb).
Por otra parte, con las normativas de protección de datos cada vez más complejas que existen, tu sistema de auditoría debería poder permitirte eliminar toda la información relativa a un usuario. Esto, dependiendo del tipo de almacenamiento que hayas elegido, puede ser muy sencillo (sobreescribir los datos de usuario de una tabla User) o terriblemente complejo (reescribir eventos en un stream con millones de operaciones).
Posts relacionados:
- ¿De quién son las herramientas de desarrollo?
- Cuánto daño han hecho… Los generadores de aplicaciones
- Cuánto daño han hecho… los arquitectos de software