En esta serie de entradas de blog, voy a exponer las consideraciones a tener en cuenta al pasar a una base de datos de documentos cuando se tiene una experiencia relacional. En concreto, Microsoft SQL Server en comparación con Servidor Couchbase.

En tres partes, voy a cubrir:

El objetivo es establecer algunas directrices generales que pueda aplicar a la planificación y el diseño de su aplicación.

Si quieres seguirme, he creado una aplicación que demuestra Couchbase y SQL Server lado a lado. Obtenga el código fuente en GitHuby asegúrese de descargar una versión preliminar para desarrolladores de Couchbase Server.

Migrar frente a reescribir

Si estás construyendo una nueva aplicación web, entonces Couchbase Server es una buena opción para utilizar como su sistema de registro. El modelado de datos flexible, el acceso rápido a los datos y la facilidad de ampliación lo convierten en una buena opción.

Couchbase Server puede complementar SQL Server en tu aplicación web existente. Puede ser un almacén de sesiones o un almacén caché. No tienes que reemplazar tu RDMBS para beneficiarte de Couchbase Server. Puedes usarlo como sistema de compromiso.

Sin embargo, si está considerando convertir una base de datos de documentos en su "sistema de registro" para una aplicación existente, entonces necesita planificar qué hacer con esa aplicación (suponiendo que ya haya elaborado un plan de modelado de datos y migración de datos como se ha cubierto en las partes anteriores de esta serie de blogs). En realidad, hay dos opciones:

  • Sustituya su capa de datos/servicios. Si has construido tu aplicación de una manera que la desacopla de la persistencia subyacente, eso te va a beneficiar enormemente cuando cambies de SQL Server a Couchbase. Si estás usando una SOA, por ejemplo, entonces puede que no tengas que hacer muchos cambios en la aplicación web.
  • Reconstruya su aplicación. Si no tiene una arquitectura desacoplada, es probable que tenga que hacer de tripas corazón y reescribir/refactorizar grandes partes de su aplicación. Este puede ser un coste significativo que tendrás que tener en cuenta a la hora de decidir si cambiar o no a una base de datos de documentos. Ojalá pudiera decir que es más fácil, que existe una poción mágica. Pero recuerda, incluso si el coste de una reconstrucción es demasiado grande, todavía puedes usar Couchbase Server en
    con SQL Server.

Las piezas de su pila que podría necesitar reconstruir o sustituir incluyen:

  • ADO.NET - Si estás usando ADO.NET o un micro-OR/M como Dapper, estos pueden ser reemplazados por el SDK .NET de Couchbase.
  • O/M - Entity framework, NHibernate, Linq2SQL, etc. Estos pueden ser reemplazados por Linq2Couchbase
  • Cualquier código que los utilice directamente - Cualquier código que toque ADO.NET, OR/Ms, u otro código SQL Server necesitará ser reemplazado para usar Couchbase (y/o reescrito para introducir otra capa de abstracción).

El resto de esta entrada del blog serán consejos y directrices aplicables a la reescritura, refactorización o inicio de un nuevo proyecto.

Qué se va a cubrir

Las bases de datos de documentos fuerzan la lógica de negocio fuera de la base de datos en mayor medida que las bases de datos relacionales. Por muy bonito que fuera que Couchbase Server tuviera todas las funciones posibles, siempre hay compensaciones.

En esta entrada del blog, vamos a cubrir los cambios en la codificación de aplicaciones que vienen con el uso de Couchbase. A grandes rasgos, esto es lo que trataremos en esta entrada. A la izquierda, una característica de SQL Server; a la derecha, el equivalente más cercano al usar Couchbase Server.

Servidor SQL Servidor Couchbase

tSQL

N1QL

Procedimientos almacenados

Nivel de servicio

Disparadores

Nivel de servicio

Vistas

Vistas Map/Reduce

Autonúmero

Contador

OR/M (mapeador objeto/relacional)

ODM (Modelo de datos de objetos)

ÁCIDO

Documento único ACID

Además, también trataremos estos importantes temas:

  • Serialización
  • Seguridad
  • Concurrencia
  • SSIS, SSRS, SSAS

Uso de N1QL

En N1QL (pronunciado "nickel") es una de mis características favoritas de Couchbase Server. Ya te sientes cómodo con el lenguaje de consulta SQL. Con N1QL, puedes aplicar tus conocimientos a una base de datos de documentos.

He aquí algunos ejemplos para mostrar la similitudes entre N1QL y tSQL:

tSQL N1QL

DELETE FROM [tabla] WHERE val1 = 'foo'

DELETE FROM cubo WHERE val1 = 'foo'

SELECT * FROM [tabla]

SELECT * de cubo

SELECT t1.* , t2.* FROM [tabla1] t1 JOIN [tabla2] t2 ON t1.id = t2.id

SELECT b1.* , b2.* FROM cubo b1 JOIN cubo b2 ON TECLAS b1.mykeys

INSERT INTO [tabla] (clave, col1, col2) VALUES (1, 'val1′,'val2')

INSERTAR EN cubo (KEY, VALUE) VALUES ('1', {"col1″: "val1", "col2″: "val2"})

UPDATE [tabla] SET val1 = 'nuevovalor' WHERE val1 = 'foo'

ACTUALIZACIÓN cubo SET val1 ='newvalue' WHERE val1 = 'foo'

Gracias a N1QLmigrar sus consultas SQL debería ser menos difícil que otras bases de datos documentales. Su modelo de datos será diferente, y no todas las funciones en tSQL están (todavía) disponibles en N1QL. Pero en su mayor parte, su experiencia existente en tSQL puede ser aplicada.

Volviendo a la cesta de la compra, he aquí un ejemplo de una simple consulta tSQL que obtendría la información de la cesta de la compra de un usuario determinado:

En Couchbase, una cesta de la compra podría modelarse como un único documento, por lo que una consulta aproximadamente equivalente sería:

Nótese que mientras N1QL tiene ÚNASE A funcionalidad, no ÚNASE A es necesario en esta consulta de la cesta de la compra. Todos los datos de la cesta de la compra se encuentran en un único documento, en lugar de estar repartidos entre varias tablas y filas.

Los resultados no son exactamente lo mismo: la consulta N1QL devuelve un resultado más jerárquico. Pero la consulta podría modificarse con un UNNEST para aplanar los resultados si es necesario. UNNEST es un unión entre documentosque es una característica necesaria cuando se escribe SQL para JSON.

En muchas bases de datos de documentos que no sean Couchbase, probablemente tendrías que aprender una API para crear consultas, y no podrías aplicar tu experiencia en tSQL para ayudar a mejorar. No estoy diciendo que la traducción vaya a ser siempre un paseo por el parque, pero va a ser relativamente fácil en comparación con las alternativas. Si estás empezando un nuevo te alegrará saber que tus conocimientos de SQL seguirán siendo útiles.

Al escribir C# para interactuar con N1QL, hay un par de conceptos clave que es importante conocer.

Consistencia de la exploración. Cuando se ejecuta una consulta N1QL, hay varias opciones de consistencia de escaneo. La consistencia de escaneo define cómo debe comportarse su consulta N1QL con respecto a la indexación. El comportamiento por defecto es "Not Bounded", y proporciona el mejor rendimiento. En el otro extremo del espectro está "RequestPlus", y proporciona la mejor consistencia. También está "AtPlus", que es un buen término medio, pero requiere un poco más de trabajo. I blog sobre la coherencia de la exploración en junio, y vale la pena repasarlo antes de empezar a escribir N1QL en .NET.

Parametrización. Si está creando consultas N1QL, es importante que utilizar la parametrización para evitar la inyección SQL. Existen dos opciones con N1QL: parámetros posicionales (numerados) y parámetros con nombre.

He aquí un ejemplo tanto de la Consistencia de Exploración como de la Parametrización utilizada en C#:

No voy a sumergirme en la Lenguaje de consulta N1QL más que esto, porque es un tema muy profundo. Pero puede consulta los fundamentos de N1QL y Empieza con el tutorial interactivo de N1QL.

Procedimientos almacenados SQL

No hay equivalente a los procedimientos almacenados (sprocs) en Couchbase. Si todavía no tienes un nivel de servicio, y estás usando sprocs para compartir alguna lógica entre dominios, te recomiendo que crees un nivel de servicio y muevas la lógica allí.

De hecho, no estaba seguro de si los sprocs pertenecían a la parte 2 o a la parte 3 de esta serie de blogs. Niveles típicos en una aplicación empresarial:

  • Web tier (UI - Angular/React/Traditional ASP.NET MVC)
  • Nivel de servicio (ASP.NET WebApi)
  • Base de datos

Los sprocs viven en la base de datos, pero contienen lógica. El nivel de servicio también contiene lógica de negocio. Entonces, ¿cuentan como datos o como funcionalidad?

Hice una encuesta en Twitter para decidirme.

Twitter straw poll on Stored Procedures

Pero mi recomendación es que si ya tienes un nivel de servicio, muevas la lógica sproc a ese nivel. Si no tienes un nivel de servicio, crea uno. Esto vivirá entre la base de datos y la interfaz de usuario.

En el código fuente de esta serie, he creado un único procedimiento almacenado.

Este sproc se puede ejecutar desde Entity Framework de la siguiente manera:

Por cierto, ese código sproc de Entity Framework es feo. ¿Quizás lo hice mal? No estoy tratando de calumniar a EF, pero en general, no he usado OR/Ms y sprocs juntos mucho en mi carrera. Ciertamente un trozo de código ADO.NET o Dapper sería más corto y limpio.

Este es un sproc muy simple, pero introduce una funcionalidad básica de búsqueda. Las ventajas de un sproc así:

  • Reutilización: El mismo sproc puede reutilizarse entre distintas aplicaciones
  • Abstracción: La implementación del sproc puede modificarse o mejorarse. En este caso, un COMO podría cambiarse por una búsqueda de texto completo más robusta.

Cualquier enfoque adoptado con la introducción de un nivel de servicio debe proporcionar los mismos beneficios. He creado un ASP.NET WebApi endpoint para tomar el lugar de la sproc.

Nota: en aras de la simplicidad en el código de ejemplo, este punto final en realidad vive en el mismo proyecto web, pero en la producción, debe ser trasladado a su propio proyecto y desplegado por separado.

Este endpoint contiene una consulta N1QL que es similar en naturaleza al sproc anterior. Veamos si tiene los mismos beneficios:

  • ¿Reutilizar? Sí. Este endpoint puede desplegarse en su propio servidor y reutilizarse desde otras aplicaciones.
  • ¿Abstracción? De nuevo, sí. La aplicación utiliza la ingenua COMO que podríamos mejorar cambiándolo por el uso de Funciones de búsqueda de texto completo de Couchbase sin cambiar la API.

En lugar de llamar a un sproc a través de Entity Framework, este endpoint se llamaría a través de HTTP. Aquí hay un ejemplo que utiliza la biblioteca RestSharp:

Si estás construyendo un nuevo proyecto, te recomiendo que crees un nivel de servicio con la expectativa de que sea utilizado en toda tu empresa. Esto le permite tener el mismo "código compartido" que los sprocs proporcionarían normalmente sin poner ese código en la base de datos.

Lo mismo ocurre con SQL Server funciones, tipos definidos por el usuario, reglas, objetos CLR definidos por el usuario.

Nota: el ejemplo de sproc anterior es un SELECCIONE para simplificar el ejemplo. En este caso, se podría crear una vista MapReduce en su lugar (que se discute más adelante). Sin embargo, una vista MapReduce no puede mutar documentos, por lo que un enfoque de nivel de servicio es una mejor solución general para reemplazar los sprocs.

Triggers SQL

Si los sprocs no fueran ya suficientemente controvertidos, basta con sacar el tema de los triggers en una conversación. Al igual que con los procedimientos almacenados, generalmente recomiendo que muevas la lógica de los disparadores al nivel de servicio, lejos de la base de datos. Si tu proyecto de software depende de muchos triggers, o triggers complejos, o muchos triggers complejos, entonces puede que quieras esperar a otro proyecto en el que intentar usar Couchbase Server.

Dicho esto, se está trabajando en algunas cosas de vanguardia que podrían ser más o menos equivalentes a los activadores. Si te interesa, ponte en contacto conmigo, y también permanece atento a el blog de Couchbase para obtener la información más reciente.

Vistas

En SQL Server, las vistas son una forma de almacenar consultas complejas en el servidor, así como proporcionar algunos beneficios de rendimiento. En Couchbase, las vistas Map/reduce han estado proporcionando una funcionalidad similar durante algún tiempo. En su mayor parte, la funcionalidad proporcionada por las vistas se puede proporcionar de una manera más expresiva con N1QL. Sin embargo, las vistas no van a desaparecer, y a veces hay beneficios al usarlas.

Las vistas map/reduce pueden definirse y almacenarse en el clúster de Couchbase. Para crearlas, se define una función "map" (con JavaScript) y opcionalmente una función "reduce" (también en JavaScript).

En la interfaz de usuario de la consola de Couchbase, ve a Índices → Vistas → Crear vista. Crea un documento de diseño, y crea una vista dentro de ese documento de diseño.

Editing a Map/Reduce view in Couchbase
Figura 1. Captura de pantalla del editor de vistas Map/Reduce en la última consola de Couchbase

En el centro está el código Map/Reduce en el que estás trabajando. También se muestra un documento de ejemplo y sus metadatos para darte algo de ayuda visual, y en la parte inferior tienes algunas opciones para ejecutar tu vista.

Para más información sobre el funcionamiento de las vistas, consulte la página Documentación sobre MapReduce Views.

Como ejemplo rápido, quiero crear una vista que enumere sólo las personas que tienen una edad superior a 21 años.

Esta vista emitiría la clave del documento y el valor del campo "nombre". Si mi cubo contuviera los siguientes documentos

Entonces los resultados de la vista tendrían el siguiente aspecto:

Clave Valor

"foo2"

"Lara Salinas"

"foo3"

"Teresa Johns"

Los resultados de estas vistas se actualizan automáticamente en un intervalo, y también se actualizan de forma incremental a medida que los documentos mutan. Esto significa que, por defecto, los resultados de las vistas son finalmente coherente con los documentos reales. Como desarrollador, puede especificar el nivel de coherencia (o estancamiento) que desee. Esto repercutirá en el rendimiento.

Las vistas map/reduce pueden ser muy útiles cuando se tiene una lógica muy compleja que es más fácil de escribir en JavaScript que en N1QL. También puede ser beneficioso para el rendimiento cuando se trabaja con un sistema de escritura pesada.

Se puede acceder a las vistas desde .NET utilizando ViewQuery.

Alternativamente, puede crear consultas N1QL en lugar de utilizar Vistas. En muchos casos, N1QL será más fácil de escribir, y la diferencia de rendimiento será insignificante. A diferencia de las Vistas, las consultas N1QL vivirían en el nivel de servicio. Actualmente no hay forma de almacenar una "Vista N1QL" en el cluster de Couchbase Server.

Serialización/deserialización

Tanto si utilizas N1QL, Views u operaciones clave/valor, es importante tener en cuenta cómo se serializa y deserializa JSON.

El SDK .NET utiliza Newtonson JSON.NET. Si está familiarizado con esa herramienta (y quién de los desarrolladores .NET no lo está), recuerde que puede utilizar los mismos atributos (como JsonProperty, JsonConverteretc). En algunos casos, puede ser útil crear tu propio serializador personalizado, lo cual es posible con el SDK .NET de Couchbase. Echa un vistazo al documentación sobre serialización y documentos no JSON para más información.

Seguridad

Couchbase tiene control de acceso basado en roles (RBAC) para los administradores.

Couchbase puede integrarse con LDAP para gestionar los administradores de Couchbase y asignar roles a los usuarios. Couchbase también puede crear usuarios de sólo lectura internamente.

Hay algunos cambios más robustos y mejoras que vienen al sistema RBAC de Couchbase, así que mantente atento. De hecho, te recomiendo que empieces consultar las compilaciones mensuales para desarrolladoresya que espero ver pronto algunas mejoras y funciones interesantes en este ámbito.

Concurrencia

La concurrencia es algo con lo que hay que lidiar a menudo, especialmente en una aplicación web. Múltiples usuarios podrían estar tomando acciones que resulten en que el mismo documento sea cambiado al mismo tiempo.

SQL Server utiliza cierre pesimista por defecto. Esto significa que SQL Server espera que las filas estén en contención, por lo que actúa a la defensiva. Se trata de un valor predeterminado razonable para las bases de datos relacionales, ya que los datos desnormalizados se reparten entre varias tablas y varias filas. SQL Server tiene la capacidad de utilizar bloqueo optimista también, a través de una variedad de niveles de transacción.

Couchbase también ofrece dos opciones para gestionar la concurrencia: optimista y pesimista.

Optimista. Esto se llama "optimista" porque funciona mejor cuando es poco probable que un documento esté en disputa muy a menudo. Estás haciendo una suposición optimista. En Couchbase, esto se hace con CAS (Comparar e intercambiar). Cuando se recupera un documento, éste viene con metadatos, incluido un valor CAS (que no es más que un número). Cuando vaya a actualizar ese documento, puede proporcionar el valor CAS. Si los valores coinciden, el optimismo ha merecido la pena y los cambios se guardan. Si no coinciden, la operación falla y tendrás que gestionarla (una fusión, un mensaje de error, etc.). Si no proporciona un valor CAS, los cambios se guardarán pase lo que pase.

Pesimista. Esto se llama "pesimista" porque funciona mejor cuando se sabe que un documento va a mutar mucho. Usted está haciendo una suposición pesimista y está bloqueando el documento a la fuerza. Si utiliza GetAndLock en el SDK .NET, el documento quedará bloqueado, lo que significa que no podrá modificarse. Los documentos se bloquean durante un máximo de 15 segundos. Puede fijar un valor inferior. También puede desbloquear explícitamente un documento, pero para ello debe tener en cuenta el valor CAS.

Para más detalles, consulte la documentación sobre Mutaciones simultáneas de documentos.

Autonúmero

Couchbase Server no ofrece actualmente ningún tipo de generación automática de claves o numeración secuencial de claves.

Sin embargo, puede utilizar el Contador función hacer algo parecido. La idea es que un documento se reserva como un documento contador especial. Este documento se puede incrementar como una operación atómica, y el número se puede utilizar como una clave parcial o total del nuevo documento que se está creando.

Ratnopam Chakrabarti, desarrollador de Ericsson, escribió recientemente un entrada de blog invitado sobre cómo crear claves numeradas secuencialmente con Couchbase Server. Su ejemplo está en Java, pero es bastante fácil de seguir, así que no repetiré su ejemplo aquí.

OR/Ms y ODMs

Si utiliza SQL Server, es posible que esté familiarizado con los OR/M (Object-relational mappers). Entity Framework, NHibernate, Linq2SQL y muchos otros son OR/Ms. Los OR/Ms intentan tender un puente entre los datos estructurados en C# y los datos normalizados en bases de datos relacionales. También suelen proporcionar otras capacidades como proveedores de Linq, unidad de trabajo, etc. Creo que los OR/M siguen la regla del 80/20. Pueden ser muy útiles. Pueden ser muy útiles el 80% de las veces, y un grano en el culo el 20% restante.

En el caso de las bases de datos de documentos, el desajuste de impedancia es mucho menor, ya que los objetos C# pueden serializarse/deserializarse a JSON, y no tienen que dividirse en un conjunto normalizado de tablas.

Sin embargo, las demás funcionalidades que ofrecen los OR/M pueden seguir siendo útiles en las bases de datos de documentos. La herramienta equivalente se denomina ODM (Object Document Model). Estas herramientas te ayudan a definir un conjunto de clases para mapear documentos. Ottoman y Linq2Couchbase son ODMs populares para Couchbase, para Node y .NET respectivamente.

Linq2Base tiene un proveedor Linq. No es un proyecto oficialmente soportado (todavía), pero es uno de los proveedores Linq más completos que he usado, y es usado en producción por clientes de Couchbase.

A continuación se muestra un ejemplo de la documentación de Linq2Couchbase que debería resultar familiar a los usuarios de Entity Framework y NHibernate.Linq:

También utilicé Linq2Couchbase en el código de ejemplo para esta serie de blogs. Aquí hay un ejemplo para Shopping Carts:

Además de ser un gran proveedor de Linq, Linq2Couchbase también tiene una función experimental de seguimiento de cambios. Merece la pena echarle un vistazo. Brant Burnett es uno de los principales contribuidores al proyecto, y también es un Experto en Couchbase. Presentó una sesión en Couchbase Connect 2016 llamada LINQing a los datos: Facilitar la transición desde SQL.

[youtube https://www.youtube.com/watch?v=X__mC2FArp4&w=560&h=315]

Transacciones

Ya he cubierto los bloqueos pesimista y optimista para transacciones sobre un único documento. Debido a esto, podemos decir que Couchbase soporta transacciones ACID a nivel de documento. Couchbase no soporta, en este momento, transacciones ACID entre múltiples documentos.

Pensando en el primera entrada en el blog sobre modelado de datosEn comparación con un modelo relacional, la necesidad de transacciones multi-documento es a menudo reducida o eliminada. Un concepto (como la cesta de la compra) puede requerir filas en múltiples tablas en un modelo relacional, pero un único modelo de documento en Couchbase.

Si sigue un modelo referencial, como en el ejemplo de las redes sociales del primera entrada en el blogSi se trata de un modelo de datos, es posible que le preocupe la falta de transacciones. Esto pone de relieve la importancia de pensar en los casos de uso al crear el modelo de datos. Si las transacciones son vitales para su caso de uso, el modelo de datos a menudo se puede estructurar para adaptarse. Estaremos encantados de ayudarle, ¡sólo tiene que pedírnoslo!

El soporte de transacciones multi-documento puede venir en el futuro si suficientes desarrolladores y clientes de Couchbase lo piden o lo necesitan. Así que, si haces el ejercicio de diseñar un modelo de datos de base de datos de documentos, y las transacciones son todavía una parte vital de tu proyecto, entonces Couchbase puede no ser el mejor "sistema de registro" para al menos parte de tu proyecto. Couchbase todavía puede ser el mejor "sistema de compromiso", capaz de ayudar con el escalado, el almacenamiento en caché, el rendimiento y la flexibilidad cuando sea necesario.

Como nota al margen, puede que merezca la pena echar un vistazo al NDescribir proyectoya que incluye un SDK que funciona sobre el SDK de Couchbase y proporciona un sistema de transacciones. (Ten en cuenta que no es una herramienta soportada oficialmente).

SSIS, SSAS, SSRS

No todo el mundo utiliza SQL Server Integration Services (SSIS), SQL Server Analysis Services (SSAS) y SQL Server Reporting Services (SSRS), pero se trata de potentes funciones que SQL Server ofrece para la integración, la generación de informes y el análisis.

No puedo decirte "usa X en lugar de Y" para esto, porque depende mucho de tu caso de uso. Puedo indicarte algunas de las herramientas disponibles para Couchbase que giran en torno al procesamiento de datos, transformación de datos, informes y análisis.

  • Kafka es una herramienta de flujo de datos de código abierto. Algunas de las casos de uso populares para Kafka incluyen mensajería, seguimiento de la actividad del sitio web, métricas y mucho más.
  • Chispa es un motor de procesamiento de datos, destinado al procesamiento de datos a gran escala y ETL.
  • Hadoop es un marco de macrodatos para el almacenamiento y procesamiento distribuidos.

Couchbase tiene conectores que admiten cada una de estas tres populares herramientas.

Por fin, Análisis de Couchbase se encuentra actualmente en versión preliminar para desarrolladores. Está pensado como un motor de gestión de datos que se ejecuta en paralelo a Couchbase Server. Es una vista previa para desarrolladores, y aún no se recomienda su uso en producción, pero puedes descarga Couchbase Analytics y las extensiones Kafka, Spark, Hadoop (haz clic en la pestaña Extensiones) y pruébalas.

Resumen

Hemos cubierto el modelado de datos, la migración de datos y la migración de aplicaciones a través de la lente de SQL Server. Este es un buen punto de partida para su próximo proyecto, y le dará algo en qué pensar si está considerando migrar.

En Portal para desarrolladores de Couchbase contiene más detalles e información sobre cada aspecto de Couchbase Server.

Quiero que me cuentes qué puede hacer Couchbase para facilitarte la transición, tanto si estás migrando como si empiezas de cero. ¿Me he perdido algo? ¿Tienes alguna herramienta o sistema que recomiendes? ¿Tienes preguntas? Echa un vistazo a la Foros de Couchbaseenvíeme un correo electrónico a matthew.groves@couchbase.com o búscame en Twitter @mgroves.

Autor

Publicado por Matthew Groves

A Matthew D. Groves le encanta programar. No importa si se trata de C#, jQuery o PHP: enviará pull requests para cualquier cosa. Lleva codificando profesionalmente desde que escribió una aplicación de punto de venta en QuickBASIC para la pizzería de sus padres, allá por los años noventa. Actualmente trabaja como Director de Marketing de Producto para Couchbase. Su tiempo libre lo pasa con su familia, viendo a los Reds y participando en la comunidad de desarrolladores. Es autor de AOP in .NET, Pro Microservices in .NET, autor de Pluralsight y MVP de Microsoft.

2 Comentarios

Dejar una respuesta