¿Es posible mantener el código de registro completamente fuera de la lógica empresarial?

12

Con la ayuda de AOP, puedo eliminar el código de registro de mi lógica empresarial. Pero creo que solo se puede usar para registrar cosas simples (es decir, entrada / salida del método de registro y valores de parámetros).

Sin embargo, ¿qué sucede si necesito registrar algo en mi lógica comercial? p.ej

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      Log.Warn("user is not existed");        //<----------------- Log A
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   Log.Info("Step 1 is completed");            //<----------------- Log B

   //Step 2
   while(true)
   {
       //do something
   }
   Log.Info("Step 2 is completed");            //<----------------- Log C

}

El método de muestra anterior puede no ser lo suficientemente claro, lo que quiero mostrar aquí es que el método debe tratarse como la unidad más pequeña desde el punto de vista del dominio. No debe dividirse en piezas más pequeñas.

¿Es posible mover por encima del código de registro 3 fuera del método? ¿Cuál es la mejor práctica para tal situación?

Charlie
fuente
Estoy bastante seguro de que los registros de "Paso 1" y "Paso 2" de su ejemplo deben ser parte de un seguimiento de auditoría de lógica de negocios y el primero de un registro técnico. Primero ordenaría esto ...
tofro

Respuestas:

1

¡Seguro!

Pero en mi experiencia, hay dos tipos generales de registro útil :

Todo registra: registros creados a través de API de creación de perfiles. Bueno para identificar problemas de rendimiento y reportar excepciones. Muy ruidoso.

Registros de eventos empresariales : registros invocados en la lógica empresarial. Cualquier cosa que le interese al negocio. Ruido mínimo. Solo eventos notables, lógicos, "comerciales". Bueno para auditoría y KPI's ...

Por lo tanto, sugeriría dos cosas. En primer lugar, haga lo que hacen otras herramientas de monitoreo, como New Relic, y use la API de creación de perfiles .NET 1 . En segundo lugar, registre eventos empresariales lógicos en su lógica empresarial . Mantener un registro de ciertos eventos es la lógica empresarial.

Y, normalmente no sugeriría AOP para cualquier tipo de registro 2 . En mi experiencia, o quieres todo , lo que significa que estás usando un generador de perfiles, o quieres eventos lógicos / de negocios. Y en el último caso, creo que es más sencillo invocar al registrador en la lógica empresarial.


1. Pero en serio, ahorre miles de horas de esfuerzo y simplemente use una herramienta de perfil existente ...

2. ¡Por supuesto, esto supone que comparte mi opinión de que un aspecto no es un gran lugar para ocultar las reglas de negocios!

svidgen
fuente
Estoy bastante de acuerdo con los "registros de eventos de negocios", y al igual que las respuestas de otros, mantendré el código de registro en la lógica de negocios. Y para la parte "Todo registra", prefiero usar la solución AOP, ya que seguirá el SRP y no contaminará mi lógica empresarial. De todos modos, echaré un vistazo a la API de creación de perfiles en primer lugar.
Charlie
10

Claro, puedes usar AOP fácilmente para esto. Simplemente refactorice las partes

  • Obtener usuario por Id
  • paso 1
  • paso 2

en métodos separados (como debería haber hecho para hacer que su código sea más limpio). Ahora puede configurar fácilmente su marco AOP para registrar las llamadas de método que elija ( como se muestra aquí ). La persona que llama puede registrar la excepción directamente, no es necesario usar AOP para sacar esto de la lógica empresarial.

A tu edición:

Quiero mostrar aquí que el método debe tratarse como la unidad más pequeña desde el punto de vista del dominio. No debe dividirse en piezas más pequeñas.

¿Por qué no debería? Si, en un "contexto de lógica de negocios", desea registrar "algo" que valga la pena registrar, y si a este "algo" se le puede dar un nombre razonable, en la mayoría de los casos tendrá sentido refactorizar el código en un método en su propio. Si desea usar AOP, requerirá que estructure su código de una manera que probablemente debería haberlo estructurado independientemente del requisito de registro. Puede interpretar esto como una desventaja de AOP, o puede interpretar esto como un beneficio, ya que le brinda una retroalimentación donde se puede mejorar la estructura de su código.

Doc Brown
fuente
Es malo que mi ejemplo no sea lo suficientemente claro. Lo que realmente quiero mostrar en el ejemplo es que el método es la unidad más pequeña desde el punto de vista del dominio que no debe dividirse en partes más pequeñas.
Charlie
@ Charlie: el ejemplo es perfectamente claro. Su idea errónea aquí es que probablemente piense que sería una buena idea tener métodos más grandes que pasos. Y eso está mal en mi humilde opinión, no es una buena idea. Tener diferentes pasos que valga la pena registrar es una señal clara de que estos pasos deben tener una abstracción, un nombre en sí mismo, por lo tanto, un método en sí mismo.
Doc Brown
@Charlie nada te impide hacer 3 métodos privados que son llamados por tu unidad o trabajo. De esta manera, desde el exterior se mantuvo igual, pero ahora tiene la abstracción requerida para su registro.
Rémi
Este enfoque está bien si desea manejar su estructura de código registrando inquietudes. Sin embargo, a veces quieres conducirlo por otra cosa.
John Wu
@ JohnWu: la estructura del código debe reflejar las diferentes preocupaciones / pasos, independientemente del requisito de registro. Eso es lo que impulsa la estructura del código aquí. Una vez que se resuelve este problema, el registro puede ser realizado por AOP, que es más un "efecto secundario" de darle una mejor estructura al código. Entonces, creo que no es la preocupación de registro lo que impulsa la estructura del código, es más que el requisito de usar AOP para el registro hace que sea más transparente que el código pierde alguna estructura que tampoco debería tener.
Doc Brown
3

A menos que el registro sea parte de los requisitos comerciales, es mejor, como usted dice, mantenerlo completamente fuera de su código.

Eso significa que realmente no desea registrar cosas como "paso 1 completo". Aunque inicialmente puede ser útil para la depuración, en la producción solo va a generar gigabytes de basura que nunca verá.

Si Step1Complete es un tipo de evento de negocios que requiere más acción, entonces puede exponerse a través de un buen evento a la antigua sin obligarlo a inyectar un ILogger o similar en su clase

Ewan
fuente
Eso es lo que estaba pensando. No se me ocurre un caso razonable para iniciar sesión en un POCO de dominio / modelo de negocio. El registro es algo que tiende a encajar naturalmente fuera de los modelos comerciales centrales, IMO.
jleach
2

Con la ayuda de algún patrón común, puede extraer el código de registro de su lógica empresarial. Sin embargo, es posible que no valga la pena hacerlo.

Por ejemplo, mediante el uso de listener (artesanía uno o usando bus de eventos, etc.), su código se verá así

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      listener.OnUserNotFound(userId);
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   listener.OnStep1Finished(......);

   ...

}

Al implementar el registro en el oyente, la lógica de registro ya no está en su lógica empresarial.

Sin embargo, puede encontrar que esto no siempre es realista, ya que es posible que no siempre pueda definir un evento significativo de su lógica.

Otro enfoque es a través de mecanismos como Dtrace en Solaris que le permiten inyectar en procesos en ejecución (creo que hay una manera de hacer algo similar en C #?) Para que el registro y las reuniones estadísticas se puedan definir en tiempo de ejecución. Todavía hay otros inconvenientes.

Adrian Shum
fuente
Un problema que AOP está tratando de resolver es el problema de que el código sea ilegible para el código no comercial (como el registro de llamadas) entretejido con el "código comercial". Reemplazar un "registrador" por un "oyente" no resuelve esto, la legibilidad del código no cambia,
Doc Brown
2

Otro enfoque es separar el registro de negocios y el registro técnico. Luego, podemos llamar al "registro de negocios" "Auditoría" y aplicar un conjunto específico de reglas comerciales, como el término de almacenamiento y las reglas de procesamiento, como el Monitoreo de la actividad comercial.

Por otro lado, el registro técnico, o simplemente "Registro", es un medio de último recurso para dejar un rastro de problema técnico. Debe ser asíncrono, rápido, tolerante a la falta de persistencia del mensaje de registro. Además, los mensajes de registro deben pasar por el menor número posible de servidores proxy para estar cerca de la fuente del problema.

La lógica de The Logging es bastante variable y está estrechamente relacionada con la implementación, entonces, ¿realmente necesita separarlo del código?

La lógica de The Audit debe considerarse una lógica de dominio y manejarse en consecuencia.

Por ejemplo, en la arquitectura hexagonal puede haber un puerto de auditoría junto con puertos de clientes, almacenamiento y MQ (y, posiblemente, métricas y control). Sería un puerto secundario, es decir, la actividad en este puerto es activada por el núcleo del negocio, en lugar de por sistemas externos.

iTollu
fuente
Estoy muy de acuerdo en que hay dos tipos de registro. Pero no entiendo Logic of The Logging es bastante variable y está estrechamente relacionado con la implementación , ¿te refieres al registro técnico aquí? Para el registro técnico, creo que se usa para registrar la entrada / salida del método y los valores de los parámetros, que es mejor sentarse fuera del método.
Charlie
@ Charlie Sí, por "El registro" me refiero al registro técnico. El registro de valores de entrada / salida / parámetro es suficiente en el caso de funciones puras. Luego, o por supuesto, podría usar un aspecto o mónada Logger. Pero las funciones puras son geniales porque son comprobables. Por lo tanto, es probable que los problemas que el registrador debe rastrear se resuelvan durante el desarrollo / depuración. Con funciones impuras, donde el registro tecnológico es de mayor utilidad, querrá registrar cada parámetro / resultado de llamada con efecto secundario, cada excepción.
iTollu
1

Formas de evitar iniciar sesión directamente en una clase o método:

  1. Lanza una excepción y haz tu registro en un bloque catch más arriba en el árbol de llamadas. Si necesita capturar un nivel de registro, puede lanzar una excepción personalizada.

  2. Realice llamadas a métodos que ya están instrumentados para el registro.

Robert Harvey
fuente
1
¿Ha sido el registro donde se ha demostrado que es un problema, e incluso vale la pena "arreglarlo"?
whatsisname
1

¿Es realmente necesario separar su registro de su lógica empresarial? El registro realizado se corresponde con la lógica comercial escrita y, por lo tanto, tiene sentido estar en la misma clase / función. Más importante aún, ayuda a facilitar la lectura del código.

Sin embargo, en caso de que realmente quiera segregar el registro de su lógica comercial, debería considerar lanzar excepciones personalizadas y entregar esas excepciones para el registro.

usuario88748
fuente
0

No, no en c #

OP, la respuesta a su pregunta específica es no, no en c #. Puede haber otros lenguajes de AOP más nativos, pero todos los enfoques de AOP en C # que he visto solo pueden aplicar comportamientos esperados en el contexto de un punto de unión , lo que significa que debe haber un flujo de control entre un bloque de código y otro. Los comportamientos esperados no se ejecutarán en medio de un método, excepto, por supuesto, llamando a otro método.

Podrías "apsect-ize" ciertos bits de registro

Dicho esto, puede extraer ciertas preocupaciones relacionadas con el registro, pero no con la escritura del registro. Por ejemplo, un punto de corte que se ejecuta al ingresar a un método podría establecer un contexto de registro y generar todos los parámetros de entrada, y al salir podría detectar excepciones o confirmar un registro para el almacenamiento permanente, ese tipo de cosas.

La escritura de registros no es un aspecto, de todos modos

De todos modos, agregaría que la escritura de registros no es realmente una preocupación transversal. Al menos no depurar el registro. Mi evidencia de esto es que no podría escribir un requisito transversal que explique completamente lo que haría este aspecto; es específico para cada caso, porque el propósito de escribir el registro es reflejar lo que está sucediendo con el lógica, y la lógica en cada método debe ser razonablemente única (ver SECO ).

En otras palabras, existe una dependencia lógica inextricable entre la escritura de registros y el material sobre el que se escribe. No puedes generalizarlo.

Pero la auditoría es

Si tiene algún tipo de requisito de registro funcional (por ejemplo, el registro de auditoría en apoyo de un requisito de no repudio ), entonces algunos argumentarán (y yo estaría de acuerdo) que si necesita ejecutar estas escrituras de registro en medio de un método, no ha estructurado su código de manera consistente con el pensamiento orientado a aspectos. Si esto ocurre, debe extraer el código en métodos separados hasta que obtenga el nivel de granularidad que necesita.

John Wu
fuente