Compartir a través de


Este artículo proviene de un motor de traducción automática.

.NET Framework

Capas de acceso adaptables + inserción de dependencias = productividad

Ulrik Born

Uno de los desafíos más difíciles cuando administrar y desarrollar el código fuente de una solución empresarial complejo son asegurar el código permanece constante, intuitiva y altamente comprobable estando mantenido y ampliado por varios equipos dentro de un departamento de desarrollo de software grandes. El problema central es que los desarrolladores tienen típicamente una serie de normas y directrices a seguir y un conjunto de bibliotecas de la utilidad de usar, que tienden a provocar sus soluciones para convertirse en el mayor y más complejo.Esto es porque terminan afinando la implementación de lógica de negocio ideal, intuitiva para adherirse a las reglas y ajuste fijas APIs. Esto generalmente significa más trabajo para los desarrolladores, más errores, menos estandarización, menos reutilización y reduce la productividad y calidad general.

Soy un desarrollador senior para un banco de inversión en línea líder y han observado cómo estos retos pueden limitar la productividad. Este artículo es un estudio de caso que presenta cómo nuestro equipo de desarrollo ha analizado y superar los retos por uso innovador de la generación de código en tiempo de ejecución y la inyección de dependencia (DI). Tal vez no está de acuerdo con algunas de las opciones de diseño de nuestro equipo, pero creo que estará de acuerdo que representan una manera fresca y eficiente de abordar algunos retos arquitectónicos comunes.

Mi empresa tiene un departamento de desarrollo de software en casa grande (trabajando en dos continentes) que constantemente mantiene y amplía nuestra enorme base de código de Microsoft .NET Framework. Nuestro código se centra en numerosos servicios de misión crítica Windows que conforman el sistema de comercio de alto rendimiento y baja latencia alojado en nuestros centros de datos. Tenemos un número de equipos de plataforma que protegen el medio ambiente codebase y tiempo de ejecución, además de los equipos que continuamente (en paralelo), mejoran y ampliar el sistema de muchos proyectos.

He trabajado durante varios años en equipos de plataforma y experimentó la desventaja de una inconsistente y excesivamente complejo codebase durante numerosos comentarios y apoyar los casos. Hace dos años, hemos decidido tratar estos temas, y he encontrado los siguientes problemas:

  • Tuvimos muchas soluciones a los mismos problemas fundamentales. Un buen ejemplo es que la mayoría de nuestros servicios de Windows tenían su propia manera única de combinar las APIs de varios en un simple servicio con un soporte adecuado para el registro, seguimiento, acceso a base de datos y así sucesivamente.
  • Nuestras implementaciones de lógica de negocio eran simple —­pero no puede probar la unidad y demasiado ingenuo, no adhiere a las directrices — o excesivamente complejo debido a un montón de códigos de plomería. Un ejemplo común: código simple que trabajó directamente en el .NET SQL Server API versus código complejo que gastó mucho más líneas en plomería trivial para apoyar reintentos automáticos, almacenamiento en caché y así sucesivamente que en la lógica de negocio real.
  • Tuvimos las bibliotecas utilidad apoyando la mayoría de nuestros principios arquitectónicos y reglas de codificación, pero fueron implementadas en varios estilos y evolucionaron independientemente. Incluso cuando se les utiliza como dictados por las directrices, cada característica solución terminó teniendo una huella enorme en términos de conjuntos de referenciadas y la exposición hacia cambios en la API. Esto a su vez hizo una tarea compleja en sí mismo para poner una nueva característica en la producción y también hizo difícil actualizar las bibliotecas de utilidad.
  • El conjunto total de directrices y normas que se aplican y utilidades para usar simplemente era tan grande que sólo los más experimentados desarrolladores tuvo una oportunidad justa de entender todo y la barrera de entrada para nuevos desarrolladores era extremadamente alta. Esto significó que un montón de código no estándar fue escrito, tampoco a ser desechada más tarde o para alcanzar la producción con mayor inconsistencia.
  • Varios de nuestros servicios principales de Windows tenían central "puntos de registro" donde todos los equipos del proyecto tuvieron que tocar el mismo código — por ejemplo, interruptor declaración enviar comandos o puestos de trabajo. Este hecho no triviales para combinar código en nuestra rama principal.

Natrually, estos problemas no eran nuevas ni único para nosotros, y un número de patrones de diseño bien establecida describe cómo resolver dichos problemas:

  • El patrón fachada esconde todos los detalles de acceso a un recurso complejo detrás de una interfaz de capa de acceso simple. Esto facilita implementaciones de lógica de negocio limpio y comprobable donde recursos externos pueden ser fácilmente burlados por en las pruebas.
  • DI — o contenedores de inversión de Control (IoC) — permite libremente acoplados y por lo tanto más fácil de ampliar, mantener y combinar componentes. Esta técnica también hace que sea fácil burlarse de los componentes seleccionados y así aumentar la capacidad de prueba.
  • Biblioteca de utilidades bien diseñados APIs no fuerce el código de consumo para ser ajustados; en cambio, apoyan la aplicación intuitiva.

Nos había sido conscientes de estos patrones durante años y también había aplicado en diversas formas a lo largo de nuestro código. Pero algunos problemas fundamentales habían limitado significativamente el éxito de estos patrones. En primer lugar, el patrón fachada no elimina la necesidad de un montón de códigos de plomería — sólo lo mueve en otra clase y generalmente sólo significa más trabajo para el desarrollador. En segundo lugar, a menos que el envase DI automáticamente descubre sus componentes en tiempo de ejecución (por ejemplo, a través de atributos), todavía será necesario un registro central y en realidad sólo introducirá una capa adicional en la ejecución. Y, finalmente, es costoso y muy difícil diseñar e implementar APIs que son intuitivos, flexible y útil al mismo tiempo.

Por eso creamos adaptativas acceder a capas

Después de una serie de sesiones de reflexión, nos ocurrió con una única solución potente y flexible a todos estos problemas. La idea básica es ocultar todas las API detrás de interfaces de acceso atribuido-capa y construir un motor de ejecución que puede implementar esas interfaces en tiempo de ejecución de una manera que sigue todas las reglas y directrices. Llamamos esta técnica de capas de acceso adaptativo (AAL) porque cada solución define las interfaces de capa de acceso que necesita de una manera altamente flexible. Hemos combinado el motor de ejecución AAL con el open source, impulsada por atributo Autofac DI contenedor y logrado un marco flexible de servicio de Windows que hace que la opción más sencilla implementaciones limpias, intuitivas y comprobables. Figura 1 ilustra cómo el tamaño, la huella y la complejidad de una solución se reducen drásticamente cuando AAL se utiliza para separar la aplicación lógica de núcleo de todas las bibliotecas y APIs circundante. Los cuadros azules representan las huellas de una única solución, implementado con AAL (a la izquierda) y sin (a la derecha).

Adaptive Access Layers (Depicted on the Left) Dramatically Reduce Solution Complexity and Footprint
Figura 1 acceso adaptativa capas (mostradas a la izquierda) dramáticamente reducen la huella y la complejidad de la solución

Una característica clave de la técnica de AAL es que nos da un lugar central común para implementar nuestras mejores prácticas y directrices sin contaminar el código de lógica de negocio. En ese sentido, AAL es similar a la programación orientada a aspectos (AOP), diversas técnicas de proxy y características de interceptación. La principal diferencia es que AAL oculta las API subyacentes desde el consumo código de lógica de negocio, mientras que las otras técnicas todavía exponen y así aumentan el espacio de solución significativamente.

Para ilustrar la idea, analizaré una capa de acceso simple entre una lógica empresarial y el registro de sucesos de Windows estándar. Considere un servicio de orden de registro que registra pedidos entrantes en una base de datos. Si la base de datos llamada falla, el servicio debe escribir un error en el registro de eventos.

En el enfoque clásico, esto podría implicar una llamada al método EventLog.WriteEntry .NET y puede verse como el código de figura 2. Este enfoque no es óptimo por dos razones. En primer lugar, no es adecuado para la unidad de pruebas, como la prueba tendría que inspeccionar el registro de eventos en la máquina en marcha las pruebas unitarias para validar que una entrada con el texto correcto en realidad fue escrita. Y segundo, cuatro líneas de plomería trivial código será "contaminar" una parte fundamental de la lógica de negocio.

Figura 2 sucesos clásico acceso

public OrderConfirmation RegisterOrder(Order order)
{ 
  try 
  {  
    // Call database to register order and return confirmation 
  } 
  catch (Exception ex) 
  {   
    string msg = string.Format("Order {0} not registered due to error: {1}",     
      order.OrderId,     
      ex.Message);   
    _eventLog.WriteEntry(msg, 1000, EventLogEntryType.Error); 
  }
}

Estos dos problemas se abordan mediante la introducción de una interfaz AAL entre la lógica de negocio y la clase EventLog subyacente. Tan una capa se ilustra en el siguiente código:

[EventLogContract("OrderService")]
public interface IOrderServiceEventLog{
    [EventEntryContract(1000, EventLogEntryType.Error,
        "Order {0} not reg due to error: {1}"]
    void OrderRegistrationFailed(int orderId, string message);
}

La capa es definida por la interfaz atribuida IOrderServiceEventLog que se implementa mediante una clase dinámica por el motor de ejecución en tiempo de ejecución. La propia interfaz tiene el atributo [EventLogContract] para permitir que el motor de ejecución a reconocerla como una capa de acceso de registro de eventos. El único parámetro es el nombre del registro de sucesos que al objetivo. No existen restricciones en el nombre de la interfaz o el número de métodos en él. Cada método debe devolver void (no es ningún valor significativo retorno al escribir información en el registro de eventos) y el atributo [EventEntryContract]. El atributo toma toda la entrada de metadatos fijo (id, severidad y formateo) como parámetros que esto ya no necesita estar en la lógica de negocio.

Usando la capa de acceso a la interfaz, la lógica de negocio de figura 2 se convierte en mucho más pequeño y más clara:

public OrderConfirmation RegisterOrder(Order order)
{
    try  
    {
        // Call database to register order and return confirmation
    }
    catch (Exception ex)  
    {
        _logLayer.OrderRegistrationFailed(order.Id, ex.Message);   
    }
}

El método RegisterOrder muestra ahora es simple, altamente legibles y mucho más comprobable, porque validación ya no requiere inspección de los sucesos, sino sólo una pequeña clase simulacro que implementa la interfaz de capa de acceso. Otra ventaja es que la interfaz IOrderServiceEventLog puede asignar la interacción completa sucesos por el servicio de toda la orden y así proporcionar un simple pero completo Resumen de qué entradas del registro de sucesos del sistema escribe.

(Nota: Como un breve aparte, la reciente semántica Logging Application Block [losa] sobre la nueva clase de .NET 4.5 EventSource abarca las mismas ideas de mudarse atributos de metadatos de código y exponer los métodos de registro modificado para requisitos particulares, inflexible en vez de unos métodos generales. Para utilizar losa, los desarrolladores deben implementar una clase personalizada derivada de la clase EventSource y utilizar esta clase a lo largo de la base de código. Creo que nuestro enfoque es tan poderoso como losa pero fácil de usar, ya que sólo requiere de desarrolladores definir la interfaz y no la implementación de la clase. Una característica clave de la clase EventSource es que es compatible con registro de eventos estructurados mediante un conjunto configurable de fregaderos. Nuestra aplicación de capa de acceso no es actualmente compatible con registro estructurado pero podría ampliarse fácilmente para hacerlo porque tiene acceso a la información estructurada mediante los parámetros del método de la capa de acceso).

Todavía no lo he considerado el cuerpo real del método RegisterOrder, es decir la llamada a una base de datos del SQL Server procedimiento almacenado para persistir la orden para su posterior procesamiento. Si mi equipo esta implementado usando la API de .NET SqlClient, sería por lo menos 10 líneas de código trivial para crear una instancia de SqlConnection y SqlCommand instancia, pueblan el comando con los parámetros de las propiedades de orden, ejecute el comando y Lee el conjunto de resultados. Si tuviéramos que cumplir con requisitos adicionales tales como reintentos automáticos en caso de bloqueos de la base de datos o los tiempos de espera, fácilmente podríamos terminar con 15 a 20 líneas de código para hacer una simple llamada. Y todo esto sería necesario porque el objetivo de la convocatoria pasó a ser un procedimiento almacenado en lugar de un método de .NET en proceso. Desde una perspectiva lógica empresarial, no hay absolutamente ninguna razón por nuestra implementación del núcleo debe ser tan desordenados y complejos porque cruza el procesamiento de un sistema a otro.

Mediante la introducción de una capa de acceso a base de datos adaptable similar a la capa de acceso de registro de eventos, podemos implementar el cuerpo como una simple y comprobables:

public OrderConfirmation RegisterOrder(Order order)
{
  try
  {
    return _ordersDbLayer.RegisterOrder(order);
  }
  catch (Exception ex)
  {
    _logLayer.OrderRegistrationFailed(order.Id, ex.Message);
   }
}

Hasta ahora, he ilustrado las ideas, flexibilidad y poder de AAL. Ahora a seguir adelante con una inspección más detallada de las capas de acceso hemos desarrollado y útil. Voy a empezar con la capa de acceso a dicha base de datos.

Base de datos de las capas de acceso acceso de base de datos es una parte central de los sistemas empresariales más, incluyendo el nuestro. Siendo un facilitador clave de comercio en línea de instrumentos financieros graves, tenemos que cumplir con un estricto cumplimiento y los requisitos de seguridad exigidos por nuestros clientes y las autoridades financieras y por lo tanto se ven obligados a salvaguardar nuestras bases de datos con cuidado. Generalmente hacemos esto haciendo sólo acceso de base de datos mediante procedimientos almacenados, ya nos permite aplicar las reglas de seguridad de grano fino y revisar todas las consultas de base de datos de rendimiento y servidor de carga antes de que lleguen nuestros sistemas de producción.

Hemos evaluamos cuidadosamente si herramientas de mapeo objeto-relacional (ORM) tales como el Entity Framework podría ayudarnos a lograr código más simple y más comprobable sin moverse lejos de procedimientos almacenan. Nuestra conclusión fue que el Entity Framework es una solución muy atractiva, pero depende fuertemente de ser capaz de componer y ejecutar sentencias SQL complejas en tiempo de ejecución. Pueden asignar los procedimientos almacenados, pero cuando se limitan a mapeo sólo procedimientos almacenados, se pierde la mayor parte de sus beneficios. Por esa razón, hemos decidido aplicar nuestra propia base de datos-acceso marco como una capa de acceso de base de datos adaptable.

Nuestra aplicación admite llamadas a procedimientos almacenados, selecciona en vistas e insertos masas efectivas mediante la funcionalidad de copia de SQL Server a granel. Se pueden asignar datos de entrada directamente en las propiedades de la clase de objeto de transferencia de datos (DTO) a los parámetros del procedimiento almacenado y además pueden asignar columnas ajustado resultado en propiedades de la clase. Esto facilita una sintaxis clara y directa al hacer el acceso de base de datos en código .NET.

El código siguiente muestra una capa simple que se adapte al servicio de registro de pedido de muestra:

[DatabaseContract("Orders")]
public interface IOrdersDatabase{
  [StoredProcedureContract("dbo.RegisterOrder",
    Returns=ReturnOption.SingleRow)]
   OrderConfirmation RegisterOrder(Order order);
 }

Este código asigna un único procedimiento almacenado y convierte la fila en el resultado en una instancia de OrderConfirmation inicializada desde las columnas del conjunto de resultado. Se establecen los parámetros del procedimiento almacenado asignado de las propiedades de la instancia de orden dada. Este comportamiento de asignación se define en el [StoredProcedure­contrato] atribuyen y por lo tanto ya no es necesaria en la implementación de lógica de negocio, haciendo que clara y legible.

Hemos implementado algunas características muy avanzadas en la capa de acceso de base de datos porque llegamos a la conclusión que es una manera simple y eficiente de ofrecer funcionalidad estándar para nuestros desarrolladores sin restringir su libertad para aplicar su lógica de negocio de la manera más natural e intuitiva.

Una de las características soportadas es soporte transparente para insertar filas mediante la funcionalidad de copia SQL a granel a granel. Nuestro soporte permite que nuestros desarrolladores definir un método sencillo que toma una colección enumerable de una clase DTO que representa las filas para insertar como entrada. La capa de acceso encarga de todos los detalles y tal modo alivia la lógica de negocio de 15 a 20 líneas de código complejo, centrada en la base de datos. Este soporte de copia masiva es un ejemplo perfecto de una operación de expresiones simple — para insertar filas en una tabla de manera eficiente, que normalmente termina siendo bastante complejo para aplicar simplemente porque la clase SqlBulkCopy .NET Framework subyacente pasa a trabajar en un IDataReader y no directamente en nuestra clase DTO.

La capa de acceso de base de datos fue el primero que hemos implementado, y ha sido un gran éxito desde el principio. Nuestra experiencia es que escribimos más simples y menos líneas de código con él y nuestras soluciones naturalmente ser altamente unidad comprobable. Basado en estos resultados positivos, rápidamente nos dimos cuenta de que podríamos beneficiarnos de introducir AAL entre nuestro código de lógica empresarial y varios otros recursos externos.

Servicio de acceso a las capas nuestra implementación sistema comercial es altamente orientada al servicio y sólida comunicación entre servicio es esencial para nosotros. Nuestro protocolo estándar es Windows Communication Foundation (WCF) y tenemos un montón de código enfocado en hacer llamadas WCF.

La mayoría de estas implementaciones sigue el mismo patrón general. En primer lugar, las direcciones de los extremos se resuelven (típicamente corremos nuestros servicios ya sea en configuraciones activo / activo o activo / pasivo). A continuación, se utiliza la clase ChannelFactory .NET para crear una implementación de la clase de canal en el cual se invoca el método deseado. Si el método tiene éxito, el canal está cerrado y eliminarse, pero si fracasa, la excepción debe ser inspeccionado. En algunos casos tiene sentido intentar el método en el mismo extremo, mientras que en otros escenarios es mejor hacer un failover automático y vuelva a intentarlo en uno de los otros extremos disponibles. Además de esto, muchas veces queremos poner en cuarentena un extremo fallado por un corto tiempo con el fin de no sobrecargar con intentos de conexión y en su defecto las llamadas a métodos.

Está lejos de ser trivial para escribir una correcta aplicación de este patrón, y fácilmente puede tomar 10 a 15 líneas de código. Y otra vez, esta complejidad se introduce sólo porque la lógica de negocio que necesitamos llamar pasa a ser alojados en otro servicio y no en proceso. Hemos implementado una capa de acceso a servicio adaptable para eliminar esta complejidad y hacerla tan simple y segura para llamar a un método remoto como es para llamar a un método en el proceso.

Los principios y flujo de trabajo son idénticas a la de la capa de acceso de base de datos. El desarrollador escribe una interfaz atribuida que sólo los métodos a que tiene que llamar los mapas, y nuestro motor de ejecución crea un tipo de tiempo de ejecución que implementa la interfaz con el mejor comportamiento de práctica como se especifica en los atributos.

El código siguiente muestra una capa de acceso pequeño servicio que asigna un único método:

[ServiceAccessLayer(typeof(IStatisticsSvc), 
  "net.tcp", "STATISTICS_SVC"]
public interface ISalesStatistics{
  [ServiceOperationContract("GetTopSellingItems")]
  Product[] GetTopProducts(int productCategory);
}

El atributo de interfaz identifica el subyacente llano [ServiceContract] atribuido interfaz (para ser utilizado con la llamada interna a ChannelFactory), el protocolo a usar y el id del servicio para llamar. El último se utiliza como clave en nuestro localizador de servicios para resolver las direcciones endpoint real en el tiempo. La capa de acceso por defecto utilizará el enlace WCF predeterminado para el protocolo determinado, pero esto puede personalizarse mediante el establecimiento de las propiedades adicionales en el atributo [ServiceAccessLayer].

El único parámetro para el ServiceOperationContract es el verbo de acción que identifica el método asignado en el contrato de servicio WCF. Otros parámetros opcionales para el atributo especifican si servicio llamada resultados deberán ser guardados y sea siempre seguro para failover automáticamente la operación incluso si fracasa la primera llamada WCF extremo después de código ha sido ejecutado en el servicio de destino.

Otras capas de acceso también hemos construido AAL similar para ficheros de traza, contadores de rendimiento y nuestro bus de mensajes. Todos se basan en los mismos principios ilustrados por los ejemplos anteriores, es decir, para permitir que la lógica de negocio expresar su acceso a los recursos en el más sencillo posible camino moviendo todos los metadatos en atributos.

Integración de la inyección de dependencia

Con capas de acceso, nuestros desarrolladores ya no es necesitan implementar un montón de códigos de plomería trivial, pero aún debe haber una manera de invocar el motor de ejecución AAL para obtener una instancia del tipo ejecución aplicado cuando el recurso externo asignado debe ser llamado. El motor de ejecución se puede llamar directamente, sino que iría en contra de nuestros principios de mantener la lógica de negocio limpio y comprobables.

Nos hemos abordado este problema registrando nuestro motor de aplicación como fuente de registro dinámico con Autofac tal que eso se llama cada vez que Autofac no se puede resolver una dependencia con cualquiera de los registros estáticos. En este caso, Autofac pedirá el motor de ejecución si puede resolver una determinada combinación de tipo de id. El motor inspeccionará el tipo y le ofrecerá una instancia del tipo si el tipo es una interfaz de acceso-capa atribuido.

En este lugar, hemos establecido un ambiente donde las implementaciones de lógica de negocio simplemente pueden declarar su acceso-capa tipos de interfaz y toman como parámetros (por ejemplo, en los constructores de clase) y luego la confianza que el contenedor DI será capaz de resolver estos parámetros al invocar el motor de ejecución detrás de las escenas. Estas implementaciones naturalmente trabajará en interfaces y ser fácil de probar como tarda solamente unas cuantas clases simulacros para implementar estas interfaces.

Implementación

Todas nuestras capas de acceso se implementan utilizando las mismas técnicas. La idea general es implementar todas las funciones en código C# llano en una clase base abstracta y luego utilice sólo emiten para generar una fina clase que se deriva de la clase base e implementa la interfaz. El cuerpo emitido de cada método de interfaz simplemente remite la ejecución de un método de ejecución general de la clase base.

La firma de este método general es:

object Execute(Attribute, MethodInfo, object[], TAttributeData)

El primer parámetro es el atributo método el método de la interfaz de capa de acceso de la cual se llama al método Execute. Generalmente contiene todos los metadatos (por ejemplo, nombre del procedimiento almacenado, reintentar especificación y así sucesivamente) necesarios para que el método Execute proporcionar el comportamiento correcto de tiempo de ejecución.

El segundo parámetro es la instancia MethodInfo reflejada por el método de interfaz. Contiene información completa sobre el método implementado — incluyendo los tipos y nombres de los parámetros del método — y es utilizado por el método Execute para interpretar el tercer parámetro. Contiene los valores de todos los parámetros de la llamada al método actual interfaz. El método Execute remite normalmente estos valores en el recurso subyacente API, por ejemplo, como parámetros de procedimiento almacenado.

El cuarto parámetro es un tipo personalizado que contiene datos fijada que se utilizará en cada invocación del método con el fin de que sea lo más eficiente posible. Se inicializan los datos fijos una vez (por un método en la clase base abstracta) cuando el motor implementa la clase de tiempo de ejecución. Nuestras capas de acceso de base de datos utilizan esta función para inspeccionar los procedimientos almacenados sólo una vez y preparar una plantilla de SqlCommand lista para su uso cuando se invoca el método.

Los parámetros de atributo y MethodInfo pasados al método Execute son también refleja sólo una vez y reutilizados en cada invocación de método, para minimizar la sobrecarga por llamada.

El valor devuelto de ejecución se utiliza como el valor devuelto por el método de interfaz implementada.

Esta estructura es muy simple y resultó ser flexible y potente. Nosotros lo hemos reutilizado en todas las capas de nuestro acceso a través de una clase base abstracta, común de la AccessLayerBase. Implementa toda la lógica necesaria para inspeccionar una interfaz atribuido y conducir el proceso de emisión de una nueva clase de tiempo de ejecución. Cada categoría de capa de acceso tiene su propia clase base abstracta especializado derivado de AccessLayerBase. Contiene la implementación real de acceso a los recursos externos, por ejemplo, haciendo un procedimiento almacenado llamada según todas nuestras mejores prácticas. Figura 3 muestra la jerarquía de la clase de implementación para una interfaz de capa de acceso de base de datos de muestra. La sección azul es el marco AAL; la sección roja es la interfaz atributos definida por la solución de característica de lógica de negocio; y la sección verde es la clase de ejecución emitida por el motor de ejecución AAL.

An Access-Layer Implementation Outline
Figura 3 un esquema de aplicación de capa de acceso

Figura 3 también ilustra cómo hemos dejado las clases base de implementar un conjunto de interfaces públicas (derivados de IAccessLayer) para exponer la información clave del comportamiento. Esto no está destinado a ser utilizado por las implementaciones de lógica de negocio sino por lógica de infraestructura — por ejemplo realizar un seguimiento cada vez que la invocación de un procedimiento almacenado falla.

Estas interfaces de acceso-capa son también útiles en los pocos casos especiales donde la demanda empresarial o los requerimientos técnicos que el acceso a los recursos subyacentes detrás de la capa de acceso se hace de una manera completamente no apoyada por AAL. Con estas interfaces, nuestros desarrolladores pueden utilizar AAL pero la intercepción y ajustar las operaciones subyacentes para cumplir requisitos especiales. Un buen ejemplo de esto es el evento IDatabaseAccessLayer.ExecutingCommand. Esto se produce justo antes de un SqlCommand se ejecuta y permite personalizarlo modificando cosas tales como los valores de tiempo de espera o parámetros.

Presentación de informes y verificación

Las interfaces AAL le atribuyen una solución lógica de negocio también nos permiten reflejar los binarios compilados en el tiempo y extracción un número de informes útiles. Nuestro equipo ha incorporado esto en nuestras estructuras Team Foundation Server (TFS) tal que cada construcción salida ahora incluye algunos informativos, pequeños archivos XML.

Hora de compilar informes la capa de acceso de base de datos informa la lista completa de todos los procedimientos, vistas y a granel inserta está accediendo. Usamos esto para simplificar comentarios y compruebe que todos los necesarios objetos de base de datos han sido correctamente implementados y configurado antes de que liberamos a la lógica de negocio.

Asimismo, nuestra capa de acceso de registro de eventos informa la lista completa de las entradas de registro de eventos que puede generar un servicio. Nuestros pasos posteriores a la generación toman esta información y transforman en un paquete de administración para nuestra vigilancia de entorno de producción de Microsoft System Center Operations Manager. Esto es inteligente porque asegura que Operations Manager siempre está actualizado con información adecuada sobre cómo manejar mejor los problemas de producción.

Automatizado de paquetes de Microsoft Installer nosotros hemos aplicado las mismas técnicas de reflexión para cosechar valioso aporte a los paquetes de Microsoft Installer (MSI) que generamos para nuestros servicios de Windows como el paso final en nuestro TFS construye. Un punto clave para estos paquetes es instalar y configurar el registro de eventos y contadores de rendimiento para asegurar concuerdan con la lógica de negocio está implementa. La compilación extrae los sucesos nombres y definiciones de contador de rendimiento de los binarios y genera automáticamente un paquete MSI que instala estos nombres y definiciones.

Comprobación de tiempo de ejecución uno de los errores más comunes registrados desde nuestro entorno de producción antes que un servicio había tratado de llamar a un procedimiento almacenado que no existe o existió con la firma equivocada sobre la base de datos de producción. Estos tipos de errores ocurrió porque perdimos el despliegue de todos los objetos de base de datos requerida cuando implantamos un servicio de Windows a la producción. La cuestión crítica aquí era el despliegue falta, ya que podría fijarse fácilmente, pero más el hecho de que el error no sucedió durante el despliegue, pero típicamente más adelante cuando el procedimiento almacenado fue el primero llamado durante el horario comercial. Hemos usado la lista basada en la reflexión de todos los objetos de base de datos se accede para abordar este problema permitiendo que nuestros servicios Windows validar la existencia y validez de todos los objetos durante el inicio del servicio. El servicio simplemente recorre la lista de objetos y luego consulta la base de datos para cada uno comprobar que será capaz de obtener acceso al objeto cuando sea necesario. De esta manera, nos hemos movido todos estos errores de horas de trabajo a tiempo de implementación, cuando es mucho más seguro y fácil arreglarlos.

Que he enumerado estos usos adicionales para ilustrar un beneficio clave de AAL. Con casi completa información sobre el comportamiento de servicio fácilmente disponible a través de reflexión, se abre una nueva dimensión de inteligente reporting, construcción, automatización y monitoreo. Nuestro equipo ha cosechado hasta ahora algunos de estos beneficios, pero vemos una serie de aplicaciones interesantes adicionales más adelante.

Productividad y calidad

AAL, diseñado y aplicado en los últimos dos años, ha demostrado para ser un facilitador extremadamente de gran alcance de la mayor productividad del desarrollador y solución mayor calidad para el equipo de desarrollo de nuestra empresa. Hemos reducido nuestros costos para la preparación de un nuevo servicio de Windows de semanas a horas y para ampliar los servicios existentes de días a minutos. Esto ha mejorado nuestra agilidad y así lo hizo más barato para que desarrollemos nuestras ofertas al cliente.

Nuestras capas de acceso son adecuadas cuando se implementa la mayoría de nuestras soluciones de negocio. Sin embargo, tenemos unos pocos casos especiales donde no quedan bien — típicamente en escenarios altamente configurables, como cuando es leído desde una tabla de configuración y en tiempo de compilación no se sabe el nombre del procedimiento almacenado para llamar. Nuestro equipo ha elegido deliberadamente no para apoyar tales casos para evitar la complejidad adicional de marco que se introduzcan. En cambio, permitimos que nuestros desarrolladores a usar las API de .NET llano en tales casos aislados.

La propia solución AAL no es grande y se ha desarrollado dentro de unos meses/persona durante un período de dos años. Por lo tanto, nuestra inversión inicial no ha sido muy alto y ya ha alcanzado el estatus tramitarlos mediante muchas horas de desarrollo y soporte de guardado.

Por supuesto, el desafío de contar con una plataforma altamente versátil y ampliamente utilizada es que puede convertirse en un punto único de falla. Nos hemos mitigado esto por tener cobertura completa pruebas unitarias de la solución AAL y desplegando nuevas versiones de forma por-servicio de gobernados. También se podría argumentar que el enfoque AAL en sí mismo introduce la complejidad adicional en nuestro sistema y las fuerzas de nuestros desarrolladores para aprender una nueva capa de abstracción. Pero creemos que esto es más que compensada por el aumento de la productividad general y la calidad.

Es otro motivo de preocupación que somos conscientes de que debemos mantener el foco en diseño y arquitectura general y no sólo hacer servicios de Windows para todo simplemente porque ese enfoque se ha vuelto tan fácil y barato. A veces una solución de terceros o un IIS alojado Windows proceso de activación de servicio (WAS) ofrece una mejor solución global, aunque se añade a la diversidad de nuestro entorno de producción.

Ulrik Born tiene un grado de maestría en Ciencias en tecnología de la información de la Universidad técnica de Dinamarca y ha estado trabajando durante los últimos 15 años como desarrollador y arquitecto en la plataforma Windows. Es un desarrollador líder de Saxo Bank, un banco de inversión basados en Internet.

Gracias a los siguientes expertos técnicos por su ayuda en la revisión de este artículo: Jonas Gudjonsson (Saxo Bank), James McCaffrey (Microsoft), Grigori Melnick (Microsoft) y Fernando Simonazzi (Microsoft)
Jonas Gudjonsson, VP y enterprise architect, es parte de la oficina del director técnico responsable de la arquitectura general de Saxo Bank, incluyendo la estrategia, principios y directrices de los cuatro dominios arquitectónicos

Dr. James McCaffrey trabaja para Microsoft en el campus de Redmond, Washington. Ha colaborado en el desarrollo de varios productos de Microsoft como, por ejemplo, Internet Explorer y MSN Search. Él es el autor de ".NET Test Automation recetas" (Apress, 2006) y puede ser contactado en jammc@microsoft.com.

Dr. Grigori Melnik es un administrador de programa Principal sobre los patrones de Microsoft & equipo de prácticas. Estos días conduce el Microsoft Enterprise Library, unidad, CQRS viaje y NUI patrones proyectos. También promueve el diseño para ello eficiencia. Antes de eso, era un ingeniero investigador y software hace tiempo suficiente para recordar la alegría de la programación en Fortran. Grigori habla todo el mundo sobre los temas de reutilización de código, cloud computing, ágil métodos y pruebas de software. También es miembro de la Junta Consultiva de IEEE Software. Blog en https://blogs.msdn.com/agile

Fernando Simonazzi es un desarrollador y arquitecto con más de 15 años de experiencia profesional. Ha sido colaborador de los patrones de Microsoft & proyectos de prácticas, incluyendo varias versiones de la Enterprise Library, unidad, CQRS viaje y Prism. Fernando es un asociado de Clarius Consulting.