[{"content":"A medida que los sistemas de software crecen, aparece una tensión difícil de evitar: cada aplicación necesita resolver sus propios problemas, pero muchas de las capacidades que necesita ya existen en algún lugar de la organización. Una aplicación de ventas necesita consultar clientes, una aplicación de facturación necesita conocer esos mismos clientes y diferentes procesos necesitan consultar productos, contratos o pedidos. Si cada aplicación implementa de nuevo esas capacidades, terminamos multiplicando lógica, datos y reglas que deberían mantenerse coherentes. El problema deja de ser únicamente cómo construir una aplicación y pasa a ser cómo construir capacidades que puedan ser utilizadas por diferentes aplicaciones sin que cada una tenga que conocer cómo están implementadas.\nLa arquitectura orientada a servicios (Service-Oriented Architecture, SOA) surge precisamente como una forma de abordar este problema. Su idea central consiste en organizar el software alrededor de servicios que representan capacidades concretas y que pueden ser utilizados por diferentes consumidores. Un servicio encapsula una responsabilidad y expone una interfaz mediante la cual otros sistemas pueden utilizarla sin depender directamente de su implementación. La propia definición de servicio en el contexto de la arquitectura de software enfatiza precisamente su carácter reutilizable y la ocultación de su implementación respecto al consumidor. servicio en arquitectura de sistemas\nEsto cambia nuestra forma de pensar sobre una aplicación. En lugar de imaginarla como un bloque que contiene toda la funcionalidad necesaria para resolver un problema concreto, podemos verla como una composición de capacidades proporcionadas por diferentes servicios. Para que esta idea funcione aparecen varias cuestiones fundamentales: qué debe convertirse en servicio, qué contrato debe ofrecer, cómo se combinan varios servicios y cómo se implementan sin que los consumidores queden acoplados a sus detalles internos.\nDel software como aplicación al software como conjunto de capacidades En una aplicación tradicional es habitual que las funcionalidades se organicen pensando principalmente en las necesidades de esa aplicación. Si necesitamos gestionar clientes, creamos un módulo de clientes; si necesitamos facturar, creamos un módulo de facturación. Esta organización puede funcionar perfectamente mientras tengamos una única aplicación o mientras cada funcionalidad tenga un único consumidor. El problema aparece cuando varias aplicaciones necesitan resolver las mismas capacidades.\nImaginemos una empresa con tres aplicaciones: un portal comercial, una aplicación interna para empleados y una aplicación móvil. Las tres necesitan consultar información de clientes. Una posibilidad sería que cada aplicación tuviera su propio código para acceder a los datos y aplicar las reglas correspondientes. Otra sería crear una capacidad común de gestión de clientes que pudiera ser consumida por las tres. En este segundo caso, el servicio de clientes se convierte en una pieza reutilizable de la arquitectura y las aplicaciones dejan de necesitar conocer directamente cómo se almacenan o procesan esos datos.\n┌──────────────────┐ │ Portal comercial │ └────────┬─────────┘ │ ┌────────▼─────────┐ │ Servicio clientes│ └────────┬─────────┘ │ ┌────────▼─────────┐ │ Datos y reglas │ │ de clientes │ └──────────────────┘ ▲ │ ┌──────────────┴──────────────┐ │ │ ┌────────┴─────────┐ ┌─────────┴────────┐ │ App empleados │ │ Aplicación móvil │ └──────────────────┘ └──────────────────┘ La arquitectura orientada a servicios lleva esta idea más lejos que la simple reutilización de código. El objetivo es identificar capacidades con una responsabilidad propia, establecer una frontera alrededor de ellas y permitir que diferentes aplicaciones las utilicen mediante una interfaz conocida. La tecnología utilizada para exponer esa interfaz puede variar; lo importante es la relación entre consumidores y proveedor.\nQué convierte una funcionalidad en un servicio No toda función o método de una aplicación debería convertirse en un servicio. Si hacemos que cada pequeña operación sea accesible remotamente, podemos acabar construyendo una arquitectura excesivamente fragmentada, en la que realizar una operación sencilla requiere múltiples comunicaciones entre componentes. El servicio debe representar una capacidad que tenga sentido como unidad de responsabilidad y que pueda ser utilizada independientemente de una aplicación concreta.\nPor ejemplo, CalcularPrecioConDescuento puede ser una operación interna del servicio de pedidos. En cambio, GestionarClientes puede representar una capacidad más amplia que tenga consumidores en diferentes aplicaciones. Del mismo modo, en una empresa logística podríamos disponer de servicios relacionados con clientes, contratos, envíos o facturación. La frontera no viene determinada por el número de métodos ni por el tamaño del código, sino por la responsabilidad que la capacidad representa.\nEsto también permite distinguir un servicio de una base de datos compartida. Si tres aplicaciones acceden directamente a las mismas tablas, están compartiendo datos, pero cada una puede interpretar esos datos y aplicar reglas diferentes. Cuando utilizamos un servicio, en cambio, podemos encapsular tanto los datos como las reglas necesarias para operar sobre ellos. El consumidor solicita una capacidad y no necesita conocer cómo se obtiene el resultado.\nEl contrato de servicio: acordar qué se ofrece Si un servicio va a ser utilizado por diferentes consumidores, necesitamos establecer qué ofrece y cómo se utiliza. Aquí aparece uno de los conceptos fundamentales de SOA: el contrato de servicio. El contrato define la interfaz que permite a consumidores y proveedores ponerse de acuerdo sobre la comunicación: operaciones disponibles, datos intercambiados, errores y otras condiciones necesarias para utilizar el servicio.\nPodemos imaginarlo como un acuerdo entre dos partes. El consumidor necesita saber qué puede pedir y qué recibirá, mientras que el proveedor se compromete a mantener ese comportamiento aunque cambie su implementación interna. El contrato establece la frontera entre lo que el servicio promete y la forma concreta en que consigue cumplirlo.\nPor ejemplo, un servicio de clientes podría exponer una operación conceptual como ObtenerCliente. El consumidor necesita conocer qué identificador debe proporcionar y qué estructura recibirá, pero no debería necesitar saber si el servicio consulta una base de datos SQL, un sistema antiguo, una API externa o una combinación de varias fuentes.\nEsta separación resulta especialmente importante cuando el servicio evoluciona. Si diez aplicaciones dependen de él, modificar su comportamiento deja de ser un cambio local. Por eso los contratos necesitan evolucionar de forma controlada y, cuando sea necesario, incorporar mecanismos de versionado. La reutilización aumenta el valor de un servicio, pero también hace más importante mantener estable aquello que sus consumidores esperan.\nAbstracción: ocultar la implementación El contrato nos lleva directamente a la abstracción. Cuando una aplicación consume un servicio, no debería necesitar conocer todos los detalles de su implementación. Si utilizamos un servicio de facturación, por ejemplo, el consumidor necesita saber cómo solicitar la creación de una factura y qué resultado obtendrá, pero no debería conocer las tablas donde se almacenan las facturas, el algoritmo utilizado para calcular los impuestos o el mecanismo empleado para comunicarse con otros sistemas.\nEsta separación permite cambiar la implementación sin obligar necesariamente a cambiar a los consumidores. Podemos migrar una base de datos, sustituir un componente interno o introducir una nueva regla manteniendo intacto el contrato externo, siempre que el comportamiento esperado siga siendo compatible. La abstracción convierte la implementación en un detalle del proveedor y el contrato en el punto de encuentro entre las partes.\nSin embargo, abstraer no significa esconderlo todo. Un contrato demasiado genérico puede resultar difícil de utilizar y uno demasiado específico puede terminar exponiendo detalles internos. El nivel adecuado es aquel que proporciona al consumidor la información necesaria para utilizar la capacidad sin convertir la estructura interna del proveedor en parte de la interfaz.\nComposición de servicios: construir algo mayor Una vez que tenemos servicios con contratos definidos aparece una posibilidad especialmente interesante: combinarlos. La composición de servicios consiste en utilizar diferentes servicios para construir una capacidad o proceso de mayor nivel. Una aplicación no tiene por qué implementar desde cero cada una de las capacidades que necesita si puede aprovechar otras que ya existen.\nImaginemos un proceso de compra. Para completarlo podemos necesitar consultar el cliente, comprobar el stock, calcular el precio, registrar el pedido, realizar el pago y solicitar el envío. En lugar de concentrar toda esa lógica en una única aplicación, podemos disponer de diferentes servicios especializados y combinarlos para representar el proceso completo.\n┌─────────────────┐ │ Proceso de │ │ compra │ └────────┬────────┘ │ ┌─────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ Servicio Servicio Servicio Clientes Inventario Pagos │ │ │ └─────────────┼──────────────┘ │ ▼ Servicio de envío La composición puede ser sencilla, como una aplicación que llama secuencialmente a varios servicios, o puede involucrar mecanismos específicos para coordinar el proceso. En ambos casos, una capacidad compleja puede construirse a partir de capacidades más pequeñas que ya existen en la organización.\nEsto no elimina la complejidad, sino que cambia su naturaleza. Si una operación depende de cinco servicios, ahora existen cinco posibles puntos de fallo y diferentes comunicaciones que gestionar. La arquitectura debe decidir qué ocurre cuando una dependencia no responde, cómo se recupera una operación y qué parte del proceso es responsable de coordinar el conjunto.\nOrquestación y coreografía: cómo se coordina un proceso Cuando varios servicios participan en un proceso aparece una cuestión adicional: ¿quién decide qué servicio debe ejecutarse y en qué orden? Una posibilidad es utilizar una orquestación, donde existe un componente responsable de coordinar el proceso. El orquestador conoce la secuencia general: primero consulta al servicio de clientes, después solicita la reserva al servicio de inventario, posteriormente solicita el pago y finalmente inicia el envío.\n┌─────────────────┐ │ Orquestador │ └───────┬─────────┘ │ ┌───────────────┼────────────────┐ │ │ │ ▼ ▼ ▼ Clientes Inventario Pagos │ │ │ └───────────────┼────────────────┘ │ ▼ Envíos Otra posibilidad es la coreografía (choreography), donde los servicios reaccionan ante determinadas situaciones y desencadenan la siguiente parte del proceso sin que exista necesariamente un coordinador central que conozca toda la secuencia. Este modelo tiene relación con la comunicación basada en eventos, aunque SOA y arquitectura basada en eventos no son conceptos equivalentes. Un sistema orientado a servicios puede utilizar llamadas síncronas, eventos o una combinación de ambos mecanismos.\nLa orquestación hace que el flujo global sea más explícito, pero concentra conocimiento del proceso en un componente. La coreografía reduce esa centralización, aunque puede dificultar la comprensión del proceso cuando intervienen muchos participantes. La elección depende de cómo queramos representar y gobernar el proceso, no de una regla universal.\nImplementación de servicios: convertir el contrato en una capacidad real Detrás del contrato existe una implementación concreta. El servicio necesita ejecutar la lógica de negocio, acceder a los datos que controla, comunicarse con otros sistemas cuando sea necesario y exponer el contrato a sus consumidores.\nUna implementación podría estar compuesta por una API HTTP, una capa de aplicación, componentes de dominio y una base de datos. Pero SOA no exige una estructura interna única. Un servicio puede estar construido con .NET, Java u otra tecnología; puede utilizar una base de datos relacional o NoSQL; puede ejecutarse en un servidor tradicional, una máquina virtual o un contenedor. La arquitectura de servicios define principalmente la frontera y la forma de colaboración, no una receta concreta para el código interno.\nCONTRATO DEL SERVICIO │ ▼ ┌─────────────┐ │ API / │ │ interfaz │ └──────┬──────┘ │ ▼ ┌─────────────┐ │ Lógica de │ │ negocio │ └──────┬──────┘ │ ┌─────┴─────┐ ▼ ▼ Datos Otros servicios Esta separación explica por qué compartir una base de datos entre aplicaciones no equivale a compartir un servicio. Si varias aplicaciones acceden directamente a las mismas tablas, conocen detalles internos del sistema que las posee. Si acceden mediante un servicio, la estructura interna puede cambiar siempre que el contrato siga siendo compatible. La frontera arquitectónica está en la responsabilidad y en la interfaz, no simplemente en dónde reside físicamente el código.\nUn ejemplo completo: construir una aplicación comercial a partir de servicios Supongamos que una empresa dispone de varios sistemas independientes y quiere construir una nueva aplicación comercial. Esta aplicación necesita mostrar información de clientes, consultar productos, calcular precios, crear pedidos y solicitar envíos. En lugar de implementar todas esas capacidades dentro de la nueva aplicación, la organización decide aprovechar servicios existentes.\nLa aplicación solicita información al servicio de clientes, consulta el catálogo y utiliza el servicio de precios para calcular el importe correspondiente. Cuando el usuario confirma la operación, utiliza el servicio de pedidos y posteriormente solicita el pago y el envío.\nAPLICACIÓN COMERCIAL │ ┌─────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ Servicio de Servicio de Servicio de Clientes Catálogo Precios │ │ │ └─────────────────┼──────────────────┘ │ ▼ Servicio de Pedidos │ ▼ Servicio de Pagos │ ▼ Servicio de Envíos La nueva aplicación no necesita conocer las bases de datos de cada sistema ni implementar de nuevo las reglas que esos servicios ya proporcionan. Su responsabilidad consiste en componer las capacidades disponibles para construir la experiencia que necesita el usuario. Una segunda aplicación, como una aplicación móvil, podría reutilizar buena parte de esos mismos servicios.\nSi el servicio de precios cambia su implementación interna y pasa, por ejemplo, de consultar una tabla de precios a utilizar un nuevo motor de promociones, la aplicación comercial puede seguir funcionando mientras el contrato conserve el comportamiento esperado. El ejemplo muestra así la diferencia entre reutilizar código y reutilizar capacidades: lo que se comparte no es la implementación, sino una responsabilidad accesible mediante un contrato.\nServicios, APIs y microservicios: conceptos relacionados, pero diferentes En la práctica, estos conceptos suelen mezclarse. Una API puede ser el mecanismo mediante el cual exponemos un servicio, pero una API y un servicio no son exactamente lo mismo. La API describe una interfaz de acceso; el servicio representa una capacidad que puede utilizarse. Podemos tener diferentes mecanismos de acceso a una misma capacidad sin cambiar necesariamente la responsabilidad que representa.\nAlgo similar ocurre con los microservicios. La arquitectura de microservicios comparte muchas ideas con SOA, pero SOA y microservicios no son sinónimos. Microsoft describe SOA como un enfoque en el que una aplicación se estructura mediante diferentes servicios y señala que los microservicios derivan de SOA, aunque presentan diferencias importantes en aspectos como autonomía, despliegue y organización. Arquitectura orientada a servicios — Microsoft Learn\nLos microservicios suelen llevar más lejos la independencia de cada servicio: cada uno puede tener su propio ciclo de vida, desplegarse de manera independiente y, habitualmente, controlar sus propios datos. SOA es un concepto más amplio y menos prescriptivo. Puede existir una arquitectura orientada a servicios sin que cada servicio sea un microservicio ni que sea necesario adoptar todas las prácticas asociadas actualmente a este estilo.\nLa distinción es importante porque evita utilizar una tecnología o estilo de despliegue como sustituto del razonamiento arquitectónico. Primero identificamos las capacidades y sus fronteras; después decidimos cómo exponerlas y qué tecnología utilizar para implementarlas.\nEl papel de la gobernanza Cuando tenemos pocos servicios, mantenerlos coordinados puede ser relativamente sencillo. Cuando una organización tiene decenas o cientos de servicios utilizados por múltiples aplicaciones, aparecen otras preguntas: quién es responsable de cada servicio, cómo se versionan los contratos, qué consumidores existen, qué dependencias hay y cuándo puede retirarse una versión.\nAquí aparece la gobernanza de servicios. Su objetivo es mantener una visión coherente del ecosistema: identificar servicios, responsabilidades, propietarios, contratos, consumidores y ciclos de vida. La gobernabilidad de arquitectura orientada a servicios aborda precisamente aspectos relacionados con la supervisión y control de los servicios y sus interacciones.\nLa necesidad de gobernanza aparece porque un servicio compartido deja de ser una pieza puramente local. Un cambio que sería trivial en una aplicación aislada puede afectar a muchas otras cuando existe un contrato compartido. La arquitectura distribuida necesita mecanismos para conocer y controlar esas relaciones, especialmente cuando el número de servicios y consumidores crece.\nEl precio de distribuir una capacidad Una llamada dentro del mismo proceso es muy diferente de una llamada que atraviesa una red. Pueden producirse problemas de latencia, errores de comunicación, indisponibilidad, cambios de versión o problemas de autenticación. Una aplicación que antes realizaba una operación local puede necesitar ahora gestionar todos estos escenarios.\nAdemás, cuando un proceso depende de varios servicios, la disponibilidad global puede verse afectada por sus dependencias. Si una operación necesita consultar clientes, precios, inventario y pagos, debemos decidir qué ocurre cuando uno de ellos no responde, si podemos reintentar, si existe una alternativa o si la operación debe detenerse.\nPor eso convertir una capacidad en servicio siempre implica asumir un coste de distribución. Ese coste puede estar justificado por la reutilización, la integración entre sistemas, la necesidad de evolución independiente o una frontera clara de responsabilidad, pero no debería introducirse simplemente porque separar el código parezca más elegante.\nCuándo tiene sentido una arquitectura orientada a servicios La pregunta práctica puede formularse de una manera bastante sencilla: ¿existe una capacidad que tenga sentido ofrecer independientemente de una aplicación concreta? Si la respuesta es afirmativa, podemos analizar si existen consumidores adicionales, una necesidad de evolución independiente, sistemas heterogéneos que deban integrarse o una razón clara para ocultar la implementación.\nPor el contrario, una funcionalidad pequeña, utilizada por un único consumidor y que siempre evoluciona junto con el resto de la aplicación puede beneficiarse más de permanecer integrada. No existe una obligación arquitectónica de convertirla en servicio solo porque técnicamente sea posible hacerlo.\nLa decisión puede resumirse en cuatro preguntas:\n1. ¿Es una capacidad claramente identificable? │ Sí ▼ 2. ¿Necesita ser utilizada o evolucionar independientemente? │ Sí ▼ 3. ¿Podemos definir un contrato estable? │ Sí ▼ 4. ¿El beneficio supera el coste de distribuirla? │ ┌──────┴──────┐ Sí No │ │ ▼ ▼ Considerar Mantener integrado un servicio o revisar el diseño Este recorrido no pretende convertirse en una fórmula matemática. Sirve para separar tres decisiones que a menudo se mezclan: primero decidimos si existe una capacidad que merece una frontera propia; después definimos el contrato; finalmente elegimos cómo implementarla y desplegarla. Empezar por la tecnología —por ejemplo, decidir primero que queremos crear una API— puede llevarnos a diseñar servicios alrededor de detalles técnicos en lugar de alrededor de responsabilidades reales.\nTambién debemos recordar que la decisión puede cambiar con el tiempo. Una capacidad puede comenzar como un módulo dentro de un monolito y convertirse posteriormente en un servicio cuando aparezcan nuevos consumidores o una necesidad real de independencia. Diseñar con fronteras de responsabilidad claras permite retrasar el coste de la distribución hasta que exista una razón para asumirlo.\nUna matriz de decisión resumida Podemos condensar el análisis anterior en una matriz que sirva como referencia rápida. No pretende sustituir al diseño arquitectónico, sino ayudarnos a identificar los factores que hacen más razonable una separación.\nCriterio Pregunta Si la respuesta es «sí» Si la respuesta es «no» Capacidad ¿Representa una responsabilidad clara del negocio? Candidato a servicio Mantener integrado o revisar la frontera Reutilización ¿Existen varios consumidores actuales o previsibles? Favorece la creación del servicio No hay una necesidad clara de reutilización Independencia ¿Necesita evolucionar o desplegarse de forma independiente? Favorece separar la capacidad Puede permanecer dentro de la aplicación Encapsulación ¿Queremos ocultar datos y reglas internas? El servicio proporciona una frontera útil La separación puede aportar poco valor Contrato ¿Podemos definir una interfaz estable y comprensible? Existe una base adecuada Refinar primero la responsabilidad Integración ¿Debe ser consumido por sistemas heterogéneos? Favorece exponer una capacidad común La separación puede aportar poco valor Escalabilidad ¿Tiene necesidades de escalado diferentes? Puede justificar una implementación independiente No existe una necesidad técnica evidente Coste ¿El beneficio compensa la complejidad remota? La separación puede estar justificada Mantenerlo integrado puede ser preferible La matriz permite distinguir entre señales que favorecen una frontera y señales que aconsejan mantener la funcionalidad integrada. No todos los criterios tienen el mismo peso en todos los sistemas, y algunos pueden ser suficientes por sí mismos en determinados contextos. Por ejemplo, una fuerte necesidad de integración con sistemas externos puede justificar una frontera aunque solo exista inicialmente un consumidor.\nTambién ayuda a evitar un criterio poco útil: el tamaño del código. Un componente pequeño puede justificar un servicio si representa una capacidad compartida que necesita evolucionar independientemente, mientras que otro mucho mayor puede funcionar mejor dentro del mismo proceso si pertenece a una única responsabilidad. La cuestión no es cuánto código queremos separar, sino qué relación queremos establecer entre esa capacidad y el resto del sistema.\nUna forma diferente de pensar la reutilización La arquitectura orientada a servicios propone, en última instancia, una forma diferente de mirar el software. Una aplicación deja de ser necesariamente la unidad alrededor de la cual organizamos todas las capacidades y pasa a ser uno de los consumidores de un conjunto más amplio de servicios. Una capacidad como clientes, pagos, pedidos o envíos puede ser utilizada por diferentes aplicaciones siempre que exista un contrato que defina cómo interactuar con ella.\nLos tres conceptos fundamentales se entienden entonces como partes de una misma idea. El contrato define qué ofrece un servicio; la implementación determina cómo consigue ofrecerlo; y la composición permite combinar varios servicios para construir capacidades y procesos más complejos. La reutilización aparece cuando estas piezas están suficientemente bien diseñadas para que diferentes consumidores puedan utilizarlas sin depender de sus detalles internos.\nLa arquitectura orientada a servicios tampoco consiste simplemente en dividir una aplicación en varios servidores o en crear una colección de APIs. Su aportación está en identificar capacidades que merecen una frontera propia, definir contratos que permitan consumirlas y organizar su colaboración de manera que diferentes aplicaciones puedan aprovecharlas.\nEn este sentido, podemos establecer una conexión con la arquitectura basada en eventos. En SOA, una pregunta habitual es «¿qué capacidad necesito y qué servicio puede proporcionármela?». En una arquitectura basada en eventos, la pregunta cambia hacia «¿qué ha ocurrido y quién necesita reaccionar?». Ambas pueden convivir: podemos utilizar servicios para solicitar capacidades de manera explícita y eventos para comunicar acontecimientos que otros componentes necesitan conocer.\nAl final, una arquitectura orientada a servicios intenta conseguir algo más profundo que reutilizar código: convertir las capacidades del software en piezas explícitas, consumibles y combinables, de manera que construir una nueva aplicación no implique necesariamente volver a construir todo lo que ya sabe hacer la organización. Cuando los servicios tienen responsabilidades claras, los contratos son comprensibles y las implementaciones permanecen ocultas, el software deja de ser únicamente un conjunto de aplicaciones independientes y empieza a comportarse como un ecosistema de capacidades que pueden combinarse para crear nuevas soluciones.\n","id":0,"tag":"Arquitectura","title":"Arquitectura orientada a servicios","url":"https://metrosetenta.es/blog/arquitectura-orientada-a-servicios/"},{"content":"Cuando una aplicación crece, uno de los problemas que antes o después aparece no está necesariamente en la lógica de negocio ni en la cantidad de código, sino en la forma en que sus diferentes partes se comunican. Un componente necesita avisar a otro de que algo ha sucedido, ese segundo componente necesita informar a un tercero y, con el tiempo, una operación aparentemente sencilla termina recorriendo una cadena de llamadas entre módulos, servicios y sistemas. El problema no es que exista comunicación, sino que cada nueva dependencia directa hace que los componentes conozcan más cosas unos de otros y que cualquier cambio tenga un radio de impacto mayor.\nEl modelo tradicional de comunicación suele apoyarse en una interacción de tipo petición-respuesta: un componente necesita algo y llama directamente a otro. Es un modelo sencillo, explícito y muy útil, pero no siempre representa bien la naturaleza de todos los procesos que ocurren en un sistema. Hay situaciones en las que un componente no necesita realmente pedirle algo a otro, sino simplemente comunicar que ha ocurrido un acontecimiento que puede ser relevante para diferentes partes del sistema. Es precisamente en este punto donde aparece la arquitectura basada en eventos (Event-Driven Architecture, AEA): una forma de diseñar sistemas en la que los acontecimientos relevantes se convierten en elementos de comunicación y otros componentes pueden reaccionar ante ellos sin que exista necesariamente una relación directa entre quien produce el acontecimiento y quien lo consume.\nDel «haz esto» al «ha ocurrido esto» La diferencia fundamental puede entenderse comparando dos tipos de mensajes. Un comando expresa una intención: «crear este pedido», «cancelar esta reserva» o «cambiar la dirección de este cliente». Un evento, en cambio, representa un hecho que ya ha ocurrido: «pedido creado», «reserva cancelada» o «dirección del cliente actualizada». La diferencia parece pequeña, pero cambia considerablemente la relación entre los componentes: un comando pide que algo suceda; un evento comunica que algo ha sucedido.\nImaginemos una aplicación de comercio electrónico. Cuando un cliente confirma un pedido, una implementación tradicional podría hacer que el servicio de pedidos llamase directamente al servicio de inventario para reservar los productos, al servicio de pagos para cobrar el importe y al servicio de notificaciones para enviar un correo. El servicio de pedidos conoce entonces la existencia de esos tres componentes y, además, necesita conocer cómo comunicarse con ellos. En un diseño basado en eventos, el servicio de pedidos puede limitarse a completar su propia operación y publicar PedidoCreado; a partir de ahí, inventario, pagos, notificaciones u otros componentes pueden reaccionar ante ese acontecimiento según sus propias responsabilidades.\nLa diferencia no consiste simplemente en sustituir una llamada HTTP por un mensaje. Lo importante es cambiar el significado de la relación entre los componentes. En el primer caso, el productor está diciendo «haz algo por mí» a un componente concreto; en el segundo, está diciendo «esto ha ocurrido» y deja que los componentes interesados decidan si necesitan reaccionar.\nLos eventos como acontecimientos relevantes No cualquier cambio interno de una aplicación debería convertirse en un evento. Que una fila de una base de datos haya cambiado, que se haya ejecutado un método o que una variable haya adoptado un nuevo valor no significa necesariamente que exista un acontecimiento relevante para el resto del sistema. Un evento adquiere valor cuando representa algo que otros componentes pueden necesitar conocer para realizar su propia actividad.\nPor ejemplo, una aplicación puede modificar internamente diez campos cuando un cliente cambia su dirección. La mayoría de esos cambios son detalles de implementación que no necesitan salir de ese componente. Sin embargo, el hecho de que la dirección del cliente haya sido actualizada sí puede ser relevante para otros sistemas, por lo que podemos representarlo mediante un evento como DireccionClienteActualizada. El evento establece así una frontera entre lo que ocurre dentro de un componente y aquello que ese componente considera suficientemente importante como para comunicar al exterior.\nEn la práctica, la palabra «evento» puede utilizarse tanto para referirse al acontecimiento conceptual como al mensaje que lo representa dentro del sistema. Conviene mantener presente la diferencia: primero existe un hecho significativo —por ejemplo, que un pedido ha sido creado— y después decidimos cómo representar y transportar esa información para que otros componentes puedan reaccionar ante ella.\nEventos de dominio y eventos de integración Esta distinción adquiere especial importancia cuando introducimos el concepto de diseño guiado por el dominio (Domain-Driven Design, DDD), una aproximación que pone el conocimiento del negocio y sus conceptos en el centro del diseño del software. Dentro de un dominio podemos identificar acontecimientos que tienen significado para la propia lógica de negocio. Por ejemplo, en un sistema de pedidos podemos considerar eventos de dominio PedidoCreado, ProductoReservado o PagoConfirmado. Estos eventos permiten que diferentes partes de ese dominio reaccionen ante cambios relevantes sin tener que establecer necesariamente llamadas directas entre ellas.\nPero cuando ese acontecimiento necesita atravesar la frontera de un dominio, servicio o aplicación, aparece otro concepto: el evento de integración. Su propósito es comunicar a otro componente o sistema que se ha producido algo relevante, utilizando para ello un contrato que ambos puedan entender. No tiene por qué coincidir exactamente con la representación interna utilizada dentro del dominio. Por ejemplo, nuestro dominio puede trabajar internamente con PedidoCreado, mientras que para un sistema externo podemos publicar PedidoCreadoParaFacturacion con únicamente la información necesaria para que el sistema de facturación pueda realizar su trabajo.\nLa diferencia puede resumirse de una manera sencilla: el evento de dominio expresa el acontecimiento desde la perspectiva del dominio que lo produce; el evento de integración expresa aquello que necesitamos comunicar fuera de esa frontera. Esta separación evita que el modelo interno de una aplicación se convierta accidentalmente en su contrato de integración. En sistemas pequeños ambos conceptos pueden acabar representados físicamente mediante el mismo mensaje, pero mantener la distinción conceptual resulta útil porque nos obliga a preguntarnos quién necesita conocer el acontecimiento y qué información necesita realmente.\nPodemos visualizarlo así:\nDOMINIO DE PEDIDOS │ │ Evento de dominio ▼ PedidoCreado │ │ transformación ▼ Evento de integración PedidoCreadoParaFacturacion │ ▼ SISTEMA DE FACTURACIÓN Esta separación también permite que un único acontecimiento de dominio dé lugar a diferentes comunicaciones externas. El hecho PedidoCreado podría generar un evento de integración para facturación, otro para analítica y otro para un sistema de logística, cada uno con un contrato adaptado a su consumidor. El dominio expresa qué ha ocurrido; la integración decide qué parte de ese acontecimiento debe hacerse visible a cada frontera.\nLas fuentes de eventos: dónde comienza el acontecimiento Todo evento tiene un origen. Puede producirse dentro de una aplicación cuando una operación modifica el estado del negocio, en un dispositivo físico que detecta una situación, en otro sistema empresarial o incluso como consecuencia de una interacción humana. Lo importante es que en algún punto existe una fuente capaz de detectar que ha ocurrido algo que merece ser comunicado.\nPensemos en una plataforma logística. Un lector situado en la entrada de un almacén puede detectar que un paquete ha llegado. La aplicación que recibe esa información puede convertirla en un acontecimiento de negocio como PaqueteRecibidoEnAlmacen. A partir de ahí, diferentes componentes podrían actualizar el inventario, informar al sistema de seguimiento, iniciar una preparación de envío o registrar información para analítica. La fuente conoce que el paquete ha llegado, pero no necesita conocer necesariamente todas las reacciones que ese acontecimiento provocará.\nLa misma idea aparece en otros dominios: PedidoEnviado, PagoConfirmado, ContratoFirmado o UsuarioRegistrado son acontecimientos que pueden tener consecuencias en diferentes partes de una organización. El valor de una arquitectura basada en eventos aparece precisamente cuando somos capaces de identificar estos acontecimientos relevantes y convertirlos en puntos de comunicación entre responsabilidades diferentes.\nPublicadores: comunicar sin conocer a los consumidores El componente que pone un evento a disposición de otros componentes actúa como publicador (publisher). Su responsabilidad es comunicar el acontecimiento, no decidir necesariamente quién va a reaccionar ante él. Esta idea está estrechamente relacionada con el patrón Publish/Subscribe, en el que los productores publican mensajes y los consumidores se suscriben a los acontecimientos que les interesan.\nLa diferencia frente a una llamada directa es importante. Si el servicio de pedidos llama directamente al servicio de inventario, ambos están relacionados explícitamente: el primero necesita conocer al segundo y saber cómo solicitarle la operación. Si publica PedidoCreado, puede existir un canal intermedio a través del cual diferentes consumidores reciban el evento sin que el productor tenga que conocerlos individualmente. El publicador comunica el hecho, mientras que la decisión de reaccionar ante él queda en manos de los consumidores.\nEsto no significa que las dependencias desaparezcan por completo. Simplemente cambian de naturaleza. Ya no dependemos necesariamente de la existencia de un servicio concreto, pero sí dependemos del significado y del contrato del evento. Si publicamos PedidoCreado, los consumidores necesitan saber qué significa ese evento y qué información contiene. El desacoplamiento, por tanto, no consiste en eliminar toda dependencia, sino en evitar dependencias directas entre implementaciones y sustituirlas por contratos de comunicación más estables.\nSuscriptores: reaccionar ante lo que sucede Al otro lado encontramos los suscriptores o consumidores. Un consumidor recibe aquellos eventos que le interesan y decide qué hacer con ellos de acuerdo con sus propias responsabilidades. Un mismo acontecimiento puede tener múltiples consumidores y cada uno puede reaccionar de una manera completamente diferente.\nVolvamos al evento PedidoCreado. El servicio de inventario puede utilizarlo para reservar productos; el servicio de facturación puede generar una factura; el servicio de notificaciones puede enviar un correo y un sistema analítico puede registrar el pedido para sus métricas. Ninguno necesita necesariamente conocer la implementación interna de los demás. Cada consumidor interpreta el evento desde el punto de vista de su propio dominio o responsabilidad y decide qué debe hacer con la información recibida.\nEsta característica hace que una arquitectura basada en eventos pueda evolucionar de una forma interesante. Podemos incorporar un nuevo consumidor que necesite reaccionar ante PedidoCreado sin modificar necesariamente el componente que produjo el evento. Por ejemplo, meses después de poner en producción la aplicación podemos incorporar un sistema de análisis de fraude que quiera observar los pedidos creados. Si el evento ya forma parte del contrato de comunicación, el nuevo consumidor puede suscribirse sin introducir una nueva llamada en el servicio de pedidos.\nEl intermediario: el punto de encuentro Entre publicadores y suscriptores suele aparecer un intermediario de mensajes (message broker) o event broker. Su función es proporcionar el mecanismo mediante el cual los mensajes pueden publicarse, almacenarse, distribuirse y entregarse a los consumidores correspondientes. Dependiendo de la tecnología y de la configuración, puede encargarse también de aspectos como colas, persistencia, enrutamiento, confirmaciones o reintentos. Broker de mensajería en Wikipedia\nPodemos imaginarlo como una oficina de correos. El productor entrega un mensaje indicando qué ha ocurrido y no necesita ir puerta por puerta buscando quién podría estar interesado. El intermediario se encarga de poner ese mensaje a disposición de los consumidores según las reglas establecidas. El intermediario se convierte así en uno de los mecanismos que hacen posible el desacoplamiento entre quienes producen acontecimientos y quienes reaccionan ante ellos.\n┌──────────────┐ │ Productor │ │ PedidoCreado │ └──────┬───────┘ │ ▼ ┌───────────────────┐ │ Event / Message │ │ Broker │ └─────┬─────┬──────┘ │ │ ▼ ▼ ┌─────────┐ ┌──────────────┐ │Inventario│ │ Notificaciones│ └─────────┘ └──────────────┘ La existencia de un intermediario no convierte automáticamente una aplicación en una arquitectura basada en eventos. Lo importante sigue siendo la forma en que utilizamos la comunicación. Podemos tener un broker y seguir diseñando componentes fuertemente acoplados; del mismo modo, podemos implementar comunicación basada en eventos con mecanismos mucho más sencillos. La tecnología facilita el patrón, pero no sustituye la decisión arquitectónica.\nLos manejadores de comandos: iniciar una operación Aunque una arquitectura basada en eventos pone el foco en los acontecimientos, los comandos siguen teniendo un papel importante. Alguien tiene que iniciar muchas de las operaciones que provocan esos acontecimientos. Un usuario puede solicitar CrearPedido, otro sistema puede enviar CancelarReserva o una aplicación puede pedir CambiarDireccionCliente.\nEl componente encargado de procesar esa intención suele denominarse manejador de comandos (command handler). Recibe el comando, valida lo necesario, ejecuta la lógica correspondiente y modifica el estado de la aplicación. Como consecuencia de esa operación puede producirse uno o varios eventos. El flujo podría ser el siguiente:\nUsuario │ │ CrearPedido ▼ ┌──────────────────┐ │ Manejador comando│ └────────┬─────────┘ │ ▼ Base de datos │ │ Pedido creado ▼ PedidoCreado │ ▼ Broker / \\ / \\ ▼ ▼ Inventario Notificaciones Aquí vuelve a aparecer la diferencia fundamental entre comandos y eventos. CrearPedido representa una intención que estamos solicitando al sistema; PedidoCreado representa el resultado o hecho que el sistema está comunicando una vez que la operación ha ocurrido. Los comandos expresan lo que queremos que suceda; los eventos expresan lo que ya ha sucedido. El manejador de comandos, además, no es exclusivo de las arquitecturas basadas en eventos: es un mecanismo de procesamiento de intenciones que puede utilizarse también en arquitecturas que no emplean eventos.\nCómo encajan todas las piezas Cuando observamos el conjunto, podemos empezar con una interacción aparentemente convencional. Un usuario solicita crear un pedido, la aplicación recibe el comando y un manejador ejecuta la operación correspondiente. Una vez que el pedido ha sido creado, el sistema publica PedidoCreado. El intermediario distribuye ese acontecimiento a los consumidores interesados y cada uno ejecuta la reacción que corresponda a su responsabilidad.\nEl flujo completo puede representarse de una manera simplificada:\nUsuario │ │ Comando: CrearPedido ▼ Aplicación de pedidos │ │ modifica estado ▼ Pedido creado │ │ Evento: PedidoCreado ▼ Broker ├──────────────► Inventario ├──────────────► Facturación ├──────────────► Notificaciones └──────────────► Analítica Lo interesante es que la arquitectura deja de organizarse únicamente alrededor de las preguntas «¿a qué servicio tengo que llamar?» y «¿qué método tengo que invocar?». Empieza a organizarse también alrededor de preguntas como «¿qué acontecimientos son importantes en nuestro negocio?», «¿quién necesita conocerlos?» y «¿qué responsabilidad debe asumir cada consumidor cuando ocurren?». Ese cambio de perspectiva es probablemente más importante que cualquiera de las tecnologías concretas utilizadas para implementarlo.\nUn ejemplo completo: desde que el cliente compra hasta que el pedido llega al almacén Podemos ver todos estos conceptos juntos mediante un ejemplo sencillo de comercio electrónico. Supongamos que un cliente compra un producto y pulsa el botón «Confirmar pedido». La aplicación recibe el comando CrearPedido, que es procesado por el manejador correspondiente. Este valida la información, registra el pedido y, si la operación se completa correctamente, el dominio genera el evento PedidoCreado. Hasta este punto estamos principalmente dentro del dominio de pedidos: hemos pasado de una intención del usuario a un nuevo hecho de negocio.\nCliente │ │ CrearPedido ▼ ┌───────────────────┐ │ Servicio de │ │ pedidos │ └─────────┬─────────┘ │ │ Pedido creado ▼ PedidoCreado Ahora podemos introducir la distinción entre evento de dominio y evento de integración. Dentro del dominio puede ser suficiente trabajar con PedidoCreado, pero si necesitamos comunicar el acontecimiento a otros sistemas, podemos transformar ese hecho en un evento de integración como PedidoCreadoParaInventario. Este evento puede contener, por ejemplo, el identificador del pedido y las líneas que necesitan ser procesadas, sin exponer toda la estructura interna del agregado o del modelo de pedidos. De esta forma, el contrato de integración se diseña para las necesidades del consumidor y no como una copia accidental del modelo interno del productor.\nDOMINIO DE PEDIDOS │ │ PedidoCreado ▼ Evento de integración PedidoCreadoParaInventario │ ▼ Broker │ ▼ SISTEMA DE INVENTARIO El sistema de inventario recibe el evento y comprueba si existe stock suficiente. Si puede reservar las unidades solicitadas, realiza su propia operación y genera otro acontecimiento: StockReservado. Ese nuevo evento no tiene por qué ser una respuesta directa al servicio de pedidos. Es un hecho independiente que puede interesar a otros consumidores. Por ejemplo, el sistema de preparación de envíos puede reaccionar ante StockReservado y comenzar a preparar el paquete.\nAl mismo tiempo, otros consumidores pueden reaccionar ante acontecimientos anteriores. Facturación puede recibir un evento relacionado con la confirmación del pedido y generar la factura; notificaciones puede enviar un correo al cliente; analítica puede registrar el pedido. Ninguno de estos componentes necesita que el servicio de pedidos realice una llamada directa a cada uno. La cadena de comportamiento se construye mediante acontecimientos que van apareciendo como consecuencia de las responsabilidades de cada componente.\nCrearPedido │ ▼ PedidoCreado │ ▼ ┌───────────────┐ │ Broker │ └─┬─────┬─────┬─┘ │ │ │ ▼ ▼ ▼ Stock Fact. Notif. │ ▼ StockReservado │ ▼ Preparación de envío │ ▼ PedidoPreparado │ ▼ Envío │ ▼ PedidoEnviado Este ejemplo muestra también algo importante: una arquitectura basada en eventos no consiste únicamente en publicar un evento y ejecutar inmediatamente varias acciones independientes. Los propios consumidores pueden producir nuevos eventos, creando una cadena de acontecimientos. PedidoCreado puede provocar StockReservado; este puede provocar PedidoPreparado; posteriormente puede aparecer PedidoEnviado. El sistema empieza a describirse como una secuencia de hechos y reacciones en lugar de como una única cadena de llamadas entre servicios.\nTambién aparecen aquí algunas de las dificultades que veremos más adelante. ¿Qué sucede si el consumidor de inventario recibe PedidoCreado pero falla antes de reservar el stock? ¿Qué ocurre si procesa el mensaje y, justo después, se produce un error antes de confirmar que lo ha procesado? ¿Puede recibir el mismo evento dos veces? ¿Qué ocurre si facturación tarda unos segundos más que inventario? Estas preguntas no son detalles secundarios: cuando sustituimos llamadas directas por acontecimientos asíncronos, estamos aceptando un modelo de comunicación con nuevas propiedades y, por tanto, nuevas responsabilidades de diseño.\nSincronía y asincronía: aceptar que no todo ocurre al mismo tiempo Una de las características habituales de las arquitecturas basadas en eventos es el uso de comunicación asíncrona. En una llamada síncrona, el componente que realiza la petición espera normalmente una respuesta antes de continuar. En una comunicación asíncrona, el productor puede publicar el acontecimiento y continuar sin esperar a que todos los consumidores hayan terminado de reaccionar. Comunicación asíncrona en Wikipedia\nEsto introduce una diferencia importante en la percepción del estado del sistema. El pedido puede haber sido creado a las 10:00:00, mientras que el sistema de analítica procesa el evento a las 10:00:01 y el sistema de notificaciones termina su trabajo a las 10:00:02. Desde el punto de vista del usuario, el pedido existe; desde el punto de vista de los diferentes consumidores, la información puede tardar un tiempo en propagarse. Esta situación se relaciona con la consistencia eventual: distintos componentes pueden observar temporalmente estados diferentes hasta que las actualizaciones terminan de propagarse. Consistencia eventual en Wikipedia\nNo debemos interpretar esto como un defecto automático. En muchos procesos es perfectamente aceptable que una notificación llegue unos instantes después de crear un pedido o que una métrica aparezca en el sistema analítico con cierto retraso. Lo importante es identificar qué partes del negocio necesitan una respuesta inmediata y cuáles pueden procesarse posteriormente. La arquitectura basada en eventos nos obliga a hacer explícita una decisión que en un sistema completamente síncrono puede quedar oculta: qué cosas necesitan ocurrir ahora y cuáles pueden ocurrir cuando el sistema esté preparado para procesarlas.\nEl precio de desacoplar: mensajes duplicados, fallos y reintentos El desacoplamiento tiene un precio. Cuando los componentes dejan de comunicarse mediante llamadas directas y empiezan a trabajar de forma distribuida, aparecen nuevos escenarios de fallo. Un consumidor puede estar temporalmente caído, un mensaje puede necesitar ser reintentado o una operación puede completarse justo antes de que se produzca un error de comunicación.\nImaginemos que el consumidor de inventario recibe PedidoCreado, reserva correctamente el stock y guarda el cambio en su base de datos. Inmediatamente después, antes de confirmar al intermediario que el mensaje ha sido procesado, el proceso se detiene. Cuando vuelva a estar disponible, el mismo mensaje podría entregarse de nuevo. Si el consumidor no está preparado para este escenario, podría intentar reservar dos veces las mismas unidades.\nPor eso aparece el concepto de idempotencia: una operación idempotente puede ejecutarse más de una vez con el mismo resultado efectivo que si se hubiera ejecutado una sola vez. Idempotencia en Wikipedia En una arquitectura basada en eventos, la posibilidad de recibir un mensaje más de una vez debe formar parte del diseño del consumidor, no tratarse como una anomalía imposible. Para conseguirlo podemos utilizar identificadores únicos de eventos, registros de mensajes procesados, operaciones idempotentes, reintentos controlados o mecanismos como las colas de mensajes no procesables.\nEsto nos lleva también a cuestiones de observabilidad. Cuando una operación atraviesa varios componentes, necesitamos poder reconstruir qué ocurrió: qué evento se publicó, qué consumidor lo recibió, cuánto tardó, si falló, cuántas veces se reintentó y qué otros acontecimientos produjo. La arquitectura puede reducir el acoplamiento entre componentes, pero aumenta la importancia de disponer de trazabilidad y mecanismos adecuados para diagnosticar el comportamiento distribuido.\nEventos no significa utilizar una tecnología concreta Llegados a este punto puede surgir una asociación casi automática: hablar de eventos parece llevarnos inmediatamente a tecnologías como Apache Kafka o RabbitMQ. Sin embargo, arquitectura, patrón y tecnología son niveles diferentes. Una arquitectura basada en eventos define una forma de organizar la comunicación; los patrones concretan cómo se relacionan los componentes; y una tecnología proporciona los mecanismos necesarios para implementar esas decisiones.\nAdemás, Kafka y RabbitMQ no son simplemente dos nombres diferentes para la misma cosa. Kafka está especialmente orientado a escenarios de event streaming, donde los eventos pueden mantenerse en un registro distribuido y ser consumidos de acuerdo con diferentes modelos. RabbitMQ se centra en el intercambio de mensajes mediante un broker, con conceptos como colas, intercambios y enrutamiento. Ambos pueden utilizarse en sistemas basados en eventos, pero sus modelos y características hacen que determinadas soluciones encajen mejor con unas necesidades que con otras.\nLa decisión arquitectónica determina cómo queremos organizar la comunicación; el patrón determina cómo se relacionan productores y consumidores; y la tecnología proporciona los mecanismos concretos para llevar esa decisión a la práctica. Confundir estos tres niveles suele llevar a empezar por la herramienta antes de haber identificado el problema que queremos resolver.\nEventos y microservicios: relacionados, pero no equivalentes Otra asociación frecuente es identificar automáticamente arquitectura basada en eventos con microservicios. Ambas ideas suelen aparecer juntas porque los eventos son una forma especialmente útil de comunicar servicios que queremos mantener desacoplados, pero describen problemas diferentes.\nLos microservicios representan principalmente una forma de dividir y desplegar capacidades de una aplicación de manera independiente. La arquitectura basada en eventos, en cambio, describe una forma de comunicar acontecimientos entre componentes. Los microservicios describen principalmente una forma de dividir y desplegar capacidades, mientras que la arquitectura basada en eventos describe una forma de comunicar acontecimientos. Un monolito puede utilizar eventos internamente y una arquitectura de microservicios puede utilizar APIs síncronas basadas en HTTP sin emplear eventos.\nPor tanto, no deberíamos introducir eventos simplemente porque estamos construyendo microservicios. La pregunta adecuada sigue siendo qué tipo de comunicación necesita nuestro sistema. Si una operación requiere una respuesta inmediata y existe una dependencia directa que resulta natural y controlada, una llamada síncrona puede ser perfectamente válida. Si, por el contrario, existen múltiples reacciones independientes ante un mismo acontecimiento, la comunicación mediante eventos puede representar mucho mejor el problema.\nEl sistema empieza a parecerse más a una red de acontecimientos Cuando un sistema utiliza eventos de forma extensa, la arquitectura empieza a adquirir una forma diferente. Ya no tenemos solamente una serie de servicios conectados mediante llamadas, sino una red en la que los acontecimientos provocan nuevas reacciones y, en algunos casos, nuevos acontecimientos.\nPedidoCreado │ ├──────────────► FacturaGenerada │ ├──────────────► NotificacionEnviada │ └──────────────► StockReservado │ ▼ PedidoPreparado │ ▼ PedidoEnviado Esto puede resultar muy potente, pero también hace que el comportamiento global sea más difícil de seguir. En una llamada directa podemos abrir el código y observar que A llama a B y B devuelve una respuesta. En una arquitectura distribuida mediante eventos, la relación puede ser más indirecta: A publica un acontecimiento, B reacciona y produce otro, C consume ese nuevo evento y genera otro más. Este estilo de coordinación suele relacionarse con la coreografía (choreography), en la que los participantes reaccionan ante acontecimientos sin existir necesariamente un componente central que dirija toda la secuencia.\nA medida que crece esta red, la observabilidad deja de ser una preocupación secundaria. Los identificadores de correlación, los registros estructurados, las métricas, los trazados distribuidos y una buena documentación de los contratos de eventos se convierten en herramientas fundamentales para comprender qué está ocurriendo. El desacoplamiento reduce determinadas dependencias, pero aumenta la necesidad de comprender el comportamiento global del sistema.\nCuándo tiene sentido utilizar una arquitectura basada en eventos No todas las aplicaciones necesitan una arquitectura basada en eventos. Si tenemos una aplicación pequeña, con pocas dependencias y operaciones que necesitan respuestas inmediatas, introducir un broker, consumidores independientes, reintentos y procesamiento asíncrono puede añadir más complejidad de la que resuelve. La arquitectura debe responder a las necesidades del sistema, no convertirse en un objetivo por sí misma.\nLos eventos empiezan a resultar especialmente interesantes cuando un mismo acontecimiento puede provocar varias reacciones independientes, cuando esas reacciones no necesitan completarse de forma síncrona, cuando queremos reducir dependencias directas entre componentes o cuando diferentes sistemas necesitan reaccionar ante información producida por otro. También resultan útiles cuando prevemos que aparecerán nuevos consumidores a lo largo del tiempo y queremos evitar que cada incorporación obligue a modificar el componente que origina el acontecimiento.\nUna buena pregunta arquitectónica no sería «¿deberíamos usar Kafka?», sino «¿qué dependencias directas queremos evitar y qué acontecimientos necesitan conocer otros componentes?». A partir de ahí podremos determinar si necesitamos eventos, qué eventos tienen significado real, qué consumidores existen, qué garantías de entrega necesitamos y qué tecnología resulta apropiada.\nUna forma diferente de pensar la comunicación La arquitectura basada en eventos no consiste simplemente en sustituir llamadas HTTP por mensajes. Su verdadera aportación está en cambiar la forma en que pensamos sobre la comunicación entre las partes de un sistema. En lugar de organizar toda interacción alrededor de la pregunta «¿a quién tengo que llamar para que haga esto?», podemos empezar a pensar también en términos de «¿qué ha ocurrido y quién necesita saberlo?».\nEse cambio permite construir sistemas en los que los componentes tienen responsabilidades más independientes y pueden reaccionar ante acontecimientos sin conocerse directamente. Pero esa independencia tiene un coste: aparecen contratos de eventos, procesamiento asíncrono, consistencia eventual en determinados escenarios, reintentos, duplicados y una mayor necesidad de observabilidad. No estamos eliminando la complejidad de la comunicación; estamos trasladando parte de esa complejidad desde las dependencias directas hacia la gestión explícita de acontecimientos y sus consecuencias.\nAl final, la cuestión no es utilizar eventos porque sean una tecnología moderna ni porque formen parte habitual de determinadas arquitecturas de microservicios. La cuestión es reconocer aquellos lugares donde un sistema no necesita decirle directamente a otro qué hacer, sino comunicar que algo importante ha sucedido y permitir que las partes interesadas reaccionen de manera independiente. Cuando ese modelo representa mejor el negocio y las relaciones entre componentes, el sistema deja de organizarse exclusivamente alrededor de llamadas y empieza a organizarse alrededor de acontecimientos.\n","id":2,"tag":"Arquitectura","title":"Arquitectura basada en eventos","url":"https://metrosetenta.es/blog/arquitectura-basada-en-eventos/"},{"content":"Durante mucho tiempo, construir una aplicación significaba reunir buena parte de su funcionalidad en un único programa. Esa forma de organizar el software puede funcionar muy bien, especialmente cuando el sistema es pequeño o cuando el equipo necesita avanzar rápidamente. El problema aparece cuando la aplicación crece: cada nueva funcionalidad empieza a relacionarse con muchas otras, los cambios afectan a zonas que aparentemente no deberían estar conectadas y desplegar una pequeña modificación puede implicar poner en riesgo todo el sistema.\nEn ese contexto aparece la arquitectura basada en microservicios, una forma de diseñar sistemas como un conjunto de servicios relativamente pequeños, independientes y especializados que colaboran para proporcionar una funcionalidad completa. La idea parece sencilla, pero sus consecuencias son mucho más profundas que separar una aplicación en varios proyectos. Dividir el software también significa dividir responsabilidades, datos, despliegues, fallos y, sobre todo, las decisiones que cada parte del sistema puede tomar de manera independiente.\nLa pregunta importante, por tanto, no es simplemente cómo crear varios servicios. Es cómo decidir dónde termina uno y empieza otro, cómo conseguir que colaboren sin volver a quedar acoplados y cómo operar todo ese conjunto como un único sistema.\nDel sistema único al conjunto de servicios Imaginemos una aplicación para gestionar una empresa de transporte. En ella podemos encontrar clientes, vehículos, conductores, rutas, pedidos, facturación y seguimiento de entregas. En una aplicación tradicional, toda esa funcionalidad podría vivir dentro de un mismo sistema y compartir una única base de datos.\nMientras el sistema es manejable, esta organización tiene una ventaja evidente: cambiar una parte del programa es relativamente sencillo porque todas las piezas están cerca unas de otras. Una funcionalidad puede llamar directamente a otra clase, consultar una tabla o reutilizar una misma biblioteca. La aplicación se comporta como una unidad.\nPero el crecimiento introduce un problema diferente. Las dependencias empiezan a convertirse en una red difícil de controlar. Una modificación en la gestión de clientes puede afectar a facturación; un cambio en pedidos puede obligar a modificar el seguimiento; una actualización de la base de datos puede tener consecuencias sobre prácticamente toda la aplicación.\nEl problema no es necesariamente que el código esté mal escrito. Puede existir una arquitectura interna razonablemente organizada y, aun así, el sistema haber alcanzado un tamaño en el que resulta difícil evolucionar determinadas partes de forma independiente.\nUna alternativa consiste en dividir el sistema en varios servicios. El servicio de clientes podría encargarse de los clientes; otro, de los pedidos; otro, del seguimiento. Cada uno tendría una responsabilidad claramente delimitada y podría evolucionar y desplegarse de forma independiente.\nAquí aparece una idea fundamental: un microservicio no es simplemente una aplicación pequeña, sino una unidad de software que intenta encapsular una capacidad del negocio y mantener bajo control sus propias dependencias.\nEsta distinción es importante porque existe una tentación habitual: coger una aplicación grande, dividir sus clases entre varios proyectos y llamar a cada proyecto \u0026ldquo;microservicio\u0026rdquo;. Eso puede producir muchos procesos, pero no necesariamente una arquitectura basada en microservicios. La división física es relativamente sencilla; la verdadera dificultad está en encontrar fronteras que tengan sentido para el negocio y que permitan mantener cierta autonomía.\nLas fronteras de servicio: decidir dónde dividir La primera gran decisión de una arquitectura de microservicios consiste precisamente en decidir qué constituye un servicio.\nPodríamos dividir una aplicación atendiendo a criterios puramente técnicos: un servicio para acceder a la base de datos, otro para autenticación, otro para generar informes y otro para enviar correos electrónicos. Algunas de esas separaciones pueden ser perfectamente razonables, pero no existe una regla que diga que cada componente técnico debe convertirse en un microservicio.\nLa pregunta más interesante es otra: ¿qué parte del problema empresarial queremos que pueda evolucionar con un cierto grado de independencia?\nVolvamos a nuestra empresa de transporte. La gestión de pedidos tiene unas reglas, unos datos y unas decisiones propias. El seguimiento de entregas tiene otras. Facturación tiene otras diferentes. Esas áreas pueden interactuar, pero no necesariamente necesitan compartir toda su lógica interna.\nEsta idea conduce al concepto de frontera de servicio: el límite dentro del cual un servicio es responsable de una determinada capacidad y fuera del cual debe relacionarse con otros servicios mediante mecanismos explícitos de comunicación.\nLa frontera es importante porque determina qué conoce cada servicio y qué puede cambiar sin afectar directamente a los demás. Cuanto más clara sea la frontera, mayor será la posibilidad de modificar una parte del sistema sin tener que modificar simultáneamente todas las demás.\nPero las fronteras no son eternas ni evidentes. En un sistema real, podemos descubrir que dos servicios están demasiado relacionados y deberían haberse considerado una única unidad, o que uno de ellos contiene responsabilidades que pertenecen a dominios diferentes. La arquitectura evoluciona a medida que comprendemos mejor el problema.\nPor eso, diseñar microservicios no debería empezar con la pregunta \u0026ldquo;¿cuántos servicios queremos tener?\u0026rdquo;. Debería empezar por comprender qué capacidades existen, qué reglas pertenecen a cada una y qué relaciones deben existir entre ellas.\nLas APIs: la frontera se convierte en un contrato Una vez que hemos separado dos partes del sistema, aparece inmediatamente otro problema. Antes podían comunicarse mediante llamadas directas a métodos o mediante objetos compartidos. Ahora están separadas.\n¿Cómo solicita un servicio de pedidos información a un servicio de clientes?\nAquí entran en juego las API (Application Programming Interfaces). Una API define cómo un software puede interactuar con otro. En una arquitectura de microservicios, la API se convierte en una de las principales formas de expresar el contrato entre dos servicios.\nPor ejemplo, el servicio de pedidos podría ofrecer una operación que permita consultar un cliente:\nGET /customers/123 El servicio de pedidos no necesita conocer cómo almacena internamente el cliente sus datos. Puede recibir una respuesta como:\n{ \u0026#34;id\u0026#34;: 123, \u0026#34;name\u0026#34;: \u0026#34;Ana García\u0026#34;, \u0026#34;status\u0026#34;: \u0026#34;active\u0026#34; } Esta separación parece trivial, pero representa un cambio importante respecto a una arquitectura donde todos los componentes comparten directamente las mismas estructuras internas. El consumidor conoce el contrato que necesita utilizar, pero no debería necesitar conocer la implementación que existe detrás de ese contrato.\nLas APIs pueden adoptar diferentes tecnologías y estilos. Una API HTTP basada en REST es una posibilidad habitual, pero también existen otras alternativas, como gRPC o mecanismos basados en mensajería. Lo importante arquitectónicamente no es tanto la tecnología concreta como la existencia de una frontera explícita.\nSin embargo, una API también introduce un coste. Cuando dos componentes están separados, una llamada que antes era local puede convertirse en una comunicación de red. Aparecen latencia, errores de conexión, timeouts y problemas de disponibilidad que no existían dentro del mismo proceso.\nPor eso convertir una llamada interna en una llamada de red no es una mejora arquitectónica automática. La comunicación entre servicios debe justificarse por la independencia que proporciona la separación.\nComponer servicios para construir una funcionalidad completa Un sistema de microservicios no resulta útil porque tenga muchos servicios aislados. Su propósito es que esos servicios puedan colaborar.\nSupongamos que un usuario realiza un pedido. Para completar la operación quizá necesitemos consultar al servicio de clientes, comprobar el inventario, registrar el pedido y solicitar el pago.\nNingún servicio tiene por qué realizar toda la operación. El sistema puede construir una funcionalidad más compleja mediante la composición de servicios independientes.\nUna posibilidad consiste en que un servicio coordine varias llamadas:\nAplicación | v Servicio de Pedidos | +--\u0026gt; Servicio de Clientes | +--\u0026gt; Servicio de Inventario | +--\u0026gt; Servicio de Pagos Pero esta composición introduce una cuestión fundamental: ¿quién es responsable de coordinar el proceso?\nEn operaciones sencillas, un servicio puede realizar varias llamadas secuenciales. En procesos más complejos, la coordinación puede requerir mecanismos específicos, especialmente cuando una operación afecta a varios servicios y no podemos utilizar una única transacción de base de datos para garantizar que todo se complete simultáneamente.\nAquí aparecen conceptos como eventos, mensajería y procesamiento asíncrono. En lugar de que un servicio tenga que esperar inmediatamente la respuesta de otro, puede publicar un evento indicando que algo ha ocurrido:\nPedido creado | +--\u0026gt; Facturación | +--\u0026gt; Notificaciones | +--\u0026gt; Seguimiento Cada consumidor reacciona de forma independiente.\nEsta forma de colaboración permite reducir determinadas dependencias temporales, pero tampoco es gratuita. La comunicación asíncrona puede aumentar la autonomía de los servicios a cambio de introducir una mayor complejidad en la coordinación y en la comprensión del estado global del sistema.\nPor ejemplo, si el pago se procesa unos segundos después de crear el pedido, el sistema debe asumir que existe un periodo durante el cual el pedido está creado pero todavía no pagado. Esa situación debe formar parte del diseño, no ser tratada como una anomalía inesperada.\nLa independencia también afecta a los datos Existe otra frontera especialmente importante: la de los datos.\nSi varios microservicios comparten indiscriminadamente las mismas tablas, podemos tener servicios separados físicamente pero unidos por una dependencia muy fuerte. El servicio de pedidos podría conocer las tablas internas del servicio de clientes; facturación podría modificar directamente las tablas de pedidos; cualquier cambio en el esquema podría afectar a todos.\nLa independencia de un servicio resulta difícil de conseguir si otros servicios dependen directamente de sus datos internos.\nPor eso, una arquitectura de microservicios suele intentar que cada servicio tenga autoridad sobre sus propios datos. No significa necesariamente que cada servicio deba utilizar una tecnología de base de datos diferente. Puede haber varias bases de datos del mismo tipo. Lo importante es quién es responsable de cada información y mediante qué contrato pueden acceder a ella los demás.\nEsta decisión tiene consecuencias importantes. En una aplicación monolítica podemos consultar varias tablas dentro de una misma transacción. En microservicios, una operación que afecta a varios servicios puede requerir coordinación distribuida.\nEl resultado es una de las paradojas de esta arquitectura: ganamos independencia local precisamente porque aceptamos una mayor complejidad en las relaciones entre las partes.\nDesplegar servicios de forma independiente La independencia arquitectónica tiene sentido especialmente cuando también podemos desplegar los servicios de manera independiente.\nSupongamos que el equipo encargado de notificaciones realiza una modificación. Si la arquitectura está realmente desacoplada, debería poder construir y desplegar una nueva versión del servicio de notificaciones sin tener que reconstruir toda la aplicación.\nEste es uno de los principales atractivos de los microservicios. La unidad de despliegue puede coincidir con una unidad de responsabilidad del sistema, permitiendo que diferentes partes evolucionen a ritmos distintos.\nPero aquí aparece otro cambio importante. Una aplicación monolítica puede consistir en un único proceso que desplegamos en unas pocas máquinas. Un sistema formado por veinte servicios necesita gestionar veinte aplicaciones, sus configuraciones, sus versiones, sus redes, sus logs y sus mecanismos de monitorización.\nLa complejidad que antes estaba dentro de una aplicación empieza a aparecer alrededor de ella.\nPor ejemplo, si tenemos diez servicios y cada uno dispone de tres instancias para garantizar disponibilidad, ya no estamos operando diez procesos, sino alrededor de treinta instancias que deben iniciarse, detenerse, actualizarse y supervisarse correctamente.\nLa arquitectura necesita entonces mecanismos que permitan automatizar esta operación.\nLa orquestación: cuando administrar servicios se convierte en un problema propio Aquí entra en escena la orquestación. Orquestar significa coordinar de forma automatizada la ejecución de múltiples servicios o contenedores: dónde se ejecutan, cuántas instancias existen, qué ocurre si una falla, cómo reciben configuración o cómo se realiza una actualización.\nTecnologías como Kubernetes han adquirido una gran importancia precisamente porque permiten automatizar buena parte de estas tareas.\nPodemos imaginar un servicio que deja de responder. En un sistema sencillo, alguien podría tener que reiniciarlo manualmente. En un entorno orquestado, la plataforma puede detectar que una instancia ha desaparecido y crear otra para mantener el estado deseado.\nLa idea es especialmente poderosa cuando el número de servicios crece. Los microservicios no eliminan la complejidad operativa: la trasladan a una capa que debe automatizarse y gestionarse explícitamente.\nEsto explica también por qué contenedores y orquestadores aparecen frecuentemente asociados a los microservicios. Un contenedor facilita empaquetar y ejecutar una aplicación de manera reproducible, mientras que un orquestador permite administrar grandes cantidades de esas unidades.\nPero conviene evitar una confusión habitual: Kubernetes no convierte una aplicación en microservicios. Podemos ejecutar una aplicación monolítica en Kubernetes y podemos construir microservicios sin Kubernetes. La arquitectura y la plataforma de ejecución son decisiones relacionadas, pero no son la misma decisión.\nMicroservicios no significa dividir por dividir Llegados a este punto aparece una pregunta inevitable: si dividir permite obtener independencia, ¿por qué no dividir cualquier sistema en decenas o cientos de servicios?\nPorque cada frontera tiene un precio.\nCada nuevo servicio necesita despliegue, configuración, monitorización, seguridad, gestión de errores y posiblemente almacenamiento propio. Además, cada comunicación entre servicios introduce latencia y nuevos puntos de fallo.\nImaginemos que una operación aparentemente sencilla necesita atravesar seis servicios:\nA → B → C → D → E → F Si cualquiera de ellos falla, la operación completa puede verse afectada. El sistema distribuido puede ser mucho más complejo que la aplicación que pretendíamos mejorar.\nEste fenómeno conduce a una idea esencial: el objetivo no es maximizar el número de microservicios, sino encontrar fronteras que proporcionen suficiente autonomía como para justificar la complejidad adicional que introducen.\nUn servicio demasiado grande puede parecer un monolito dentro del sistema distribuido. Pero uno demasiado pequeño puede acabar convirtiendo una operación sencilla en una cadena de llamadas de red difíciles de comprender y mantener.\nPor eso el tamaño físico del servicio es un criterio secundario. Mucho más importante es su cohesión: qué responsabilidades contiene, qué reglas gobierna y qué razones existen para que esas responsabilidades cambien juntas.\nUna arquitectura distribuida cambia la naturaleza de los errores En una aplicación que vive dentro de un único proceso, muchas cosas pueden darse por garantizadas. Una llamada a una función puede fallar, pero la comunicación en sí misma no suele depender de una red.\nEn microservicios, la red pasa a formar parte del comportamiento cotidiano del sistema.\nUn servicio puede estar temporalmente indisponible. Una petición puede tardar demasiado. Un mensaje puede procesarse más tarde. Una instancia puede reiniciarse mientras una operación está en curso.\nEsto obliga a introducir mecanismos como timeouts, reintentos, circuit breakers, idempotencia o trazabilidad distribuida.\nLa idempotencia, por ejemplo, permite diseñar una operación de manera que repetirla no produzca efectos adicionales no deseados. Resulta especialmente importante cuando un cliente no sabe si una petición ha llegado correctamente y decide volver a enviarla.\nLa observabilidad también adquiere una importancia mucho mayor. En una aplicación monolítica podemos revisar un log y seguir una operación dentro del mismo proceso. En un sistema distribuido, una única petición del usuario puede atravesar varios servicios y generar decenas de acontecimientos que necesitamos relacionar para entender qué ha ocurrido.\nDe ahí la importancia de métricas, logs centralizados y trazas distribuidas.\nLa historia explica por qué esta arquitectura resulta atractiva La arquitectura de microservicios no apareció de forma aislada. Es, en buena medida, una respuesta a la evolución de los sistemas y de las organizaciones de desarrollo.\nDurante años, las aplicaciones empresariales tendieron a crecer como grandes unidades. Posteriormente, prácticas como Service-Oriented Architecture (SOA) popularizaron la idea de construir sistemas a partir de servicios. La experiencia adquirida con estos sistemas, junto con la expansión de la computación en la nube, la automatización del despliegue y los contenedores, creó las condiciones para una aproximación más ligera y orientada a servicios pequeños.\nEl término \u0026ldquo;microservices\u0026rdquo; comenzó a popularizarse durante la década de 2010, especialmente en torno a experiencias de organizaciones que necesitaban desplegar grandes sistemas con equipos capaces de trabajar de forma relativamente autónoma.\nLa evolución resulta interesante porque los microservicios no surgieron simplemente porque dividir software fuese una idea nueva, sino porque determinadas organizaciones necesitaban que equipos y partes del sistema pudieran evolucionar a una velocidad y con un nivel de independencia que una arquitectura monolítica dificultaba.\nEsto también explica por qué los microservicios están estrechamente relacionados con prácticas como integración continua, entrega continua, automatización y equipos responsables de sus servicios. La arquitectura tiene una dimensión técnica, pero también organizativa.\nMicroservicios y arquitectura monolítica no son enemigos A veces se presenta la arquitectura monolítica como una etapa que necesariamente debe superarse. Esa interpretación puede llevar a decisiones poco justificadas.\nUn monolito bien diseñado puede ser una solución excelente para determinados sistemas. Puede ser más sencillo de desarrollar, probar, desplegar y operar. Una única base de datos puede simplificar las transacciones y determinadas consultas.\nLos microservicios empiezan a resultar especialmente interesantes cuando necesitamos independencia de evolución, despliegues separados, escalado diferenciado o autonomía entre determinadas partes del sistema.\nPero si esas necesidades no existen, introducir una arquitectura distribuida puede aportar más complejidad que beneficios.\nDe hecho, existe una estrategia intermedia especialmente útil: comenzar con un monolito bien modularizado y establecer fronteras claras dentro de él. Si posteriormente alguna de esas fronteras necesita convertirse en un servicio independiente, la separación conceptual ya existe y la extracción resulta más sencilla.\nLa decisión arquitectónica deja así de ser \u0026ldquo;monolito o microservicios\u0026rdquo; y pasa a ser una cuestión más concreta: ¿qué grado de independencia necesita cada parte del sistema y cuánto estamos dispuestos a pagar en complejidad para conseguirlo?\nQué cambia realmente cuando adoptamos microservicios Visto desde fuera, una arquitectura basada en microservicios parece consistir en cambiar una aplicación por muchas aplicaciones pequeñas. Pero esa descripción se queda corta.\nCambia la manera de definir responsabilidades. Cambia la forma en que los componentes se comunican. Cambia la relación con los datos. Cambia el modelo de despliegue. Cambia la gestión de errores. Cambia incluso la forma de trabajar de los equipos.\nEn un sistema monolítico, muchas decisiones pueden permanecer implícitas porque todos los componentes están dentro del mismo espacio. En un sistema distribuido, las fronteras obligan a hacer explícitos contratos, dependencias y responsabilidades que antes podían permanecer ocultos dentro del código.\nEse es probablemente uno de los aspectos más importantes de esta arquitectura. Los microservicios no son principalmente una técnica para conseguir programas más pequeños. Son una forma de construir sistemas donde determinadas partes puedan tener un ciclo de vida más independiente.\nUna forma diferente de entender la arquitectura Una arquitectura basada en microservicios puede resumirse visualmente como varios servicios colaborando:\n┌─────────────────┐ │ Cliente │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ API / Gateway │ └────────┬────────┘ │ ┌──────────────────┼──────────────────┐ ▼ ▼ ▼ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ Clientes │ │ Pedidos │ │ Pagos │ │ │ │ │ │ │ │ Datos propios │ │ Datos propios │ │ Datos propios │ └────────────────┘ └────────────────┘ └────────────────┘ │ │ │ └──────────────────┼──────────────────┘ ▼ Mensajería / Eventos │ ▼ ┌─────────────────┐ │ Notificaciones │ └─────────────────┘ Pero el diagrama no debería hacernos perder de vista la idea fundamental. La arquitectura basada en microservicios no consiste en dibujar muchos cuadrados, sino en establecer fronteras significativas entre capacidades que necesitan evolucionar con cierto grado de independencia.\nLas APIs hacen explícita la comunicación entre esas fronteras. La composición permite que varios servicios colaboren para proporcionar funcionalidades mayores. La separación de datos evita que la independencia sea solamente superficial. El despliegue independiente permite que cada parte evolucione a su propio ritmo. Y la orquestación proporciona la infraestructura necesaria para operar todo ese conjunto sin convertir la administración en una tarea manual e inmanejable.\nEl precio es claro: distribuimos también los problemas. Aparecen latencia, fallos de red, consistencia eventual, observabilidad distribuida y una infraestructura considerablemente más compleja.\nPor eso entender microservicios no consiste en aprender a crear servicios pequeños. Consiste en comprender cuándo una frontera aporta suficiente independencia como para compensar el coste de introducirla. Cuando esa decisión está bien fundamentada, los microservicios pueden convertirse en una herramienta para controlar la evolución de sistemas grandes. Cuando la división responde únicamente a una moda tecnológica o al deseo de tener \u0026ldquo;muchos servicios\u0026rdquo;, el resultado puede ser simplemente un sistema distribuido que conserva la complejidad del monolito y añade todos los problemas de la red.\nLa verdadera arquitectura, como tantas veces ocurre en ingeniería de software, no está en la tecnología que elegimos, sino en las decisiones sobre responsabilidades, dependencias y límites que hacemos visibles mediante esa tecnología.\n","id":3,"tag":"Arquitectura ; Microservicios","title":"Arquitectura basada en microservicios: dividir un sistema para hacerlo evolucionar","url":"https://metrosetenta.es/blog/arquitectura-basada-en-microservicios/"},{"content":"Una aplicación puede estar perfectamente programada y, sin embargo, representar mal el negocio para el que fue construida. Puede tener una arquitectura ordenada, pruebas automatizadas, una base de datos bien diseñada y un código limpio, pero utilizar conceptos que no coinciden con la realidad que sus usuarios necesitan gestionar. Cuando esto ocurre, el problema no suele encontrarse en una función concreta, sino en algo más profundo: el software y el negocio están utilizando modelos mentales diferentes para describir la misma realidad.\nEsta tensión es especialmente visible en sistemas empresariales. Una aplicación para gestionar estudios clínicos, por ejemplo, puede hablar de centros, promotores, estudios, contratos o intermediaciones. Pero que esas palabras aparezcan en las tablas de una base de datos o en las clases de una aplicación no significa necesariamente que el sistema haya comprendido qué representan. ¿Qué hace que algo sea realmente un centro? ¿Puede cambiar su identidad? ¿Qué información forma parte del centro y cuál pertenece a una relación concreta con un estudio? ¿Quién puede modificarla? ¿Qué reglas deben cumplirse cuando cambia su estado?\nEl diseño guiado por el dominio, conocido habitualmente como Domain-Driven Design o DDD, parte precisamente de esta preocupación. Su propuesta fundamental no consiste en utilizar determinadas clases, patrones o estructuras de carpetas, sino en hacer que el diseño del software nazca de una comprensión profunda del dominio empresarial que el sistema intenta representar.\nDel código al problema que queremos resolver Cuando comenzamos un proyecto de software es tentador empezar pensando en tecnología. ¿Qué lenguaje utilizaremos? ¿Qué framework? ¿Qué base de datos? ¿Será una API REST? ¿Utilizaremos microservicios? Son preguntas legítimas, pero existe una pregunta anterior que puede resultar mucho más importante: ¿cómo funciona realmente el negocio?\nImaginemos una empresa que necesita gestionar la relación entre estudios clínicos y centros en los que se realizan determinadas actividades. Desde una perspectiva puramente técnica podríamos empezar diseñando tablas llamadas Study, Center y Contract, crear algunas relaciones entre ellas y posteriormente construir una API.\nEl problema es que crear estructuras de software a partir de nombres del negocio no significa necesariamente que hayamos modelado el negocio. El verdadero trabajo comienza cuando intentamos comprender qué significan esos conceptos, qué reglas los relacionan y qué comportamientos debe preservar el sistema.\nPor ejemplo, un centro puede estar activo o inactivo. Un centro puede participar en determinados estudios, pero no necesariamente en todos. Una relación entre un centro y un estudio puede tener su propio estado y unas fechas determinadas. Un contrato puede condicionar qué operaciones son posibles. El significado de cada concepto aparece cuando empezamos a comprender las reglas que lo rodean.\nDDD propone precisamente dedicar una atención especial a esa comprensión. El software deja de ser una traducción directa de tablas o pantallas y pasa a convertirse en una representación explícita de los conceptos y reglas importantes del negocio.\nUn lenguaje común para entender el dominio Para poder construir ese modelo necesitamos primero hablar sobre el problema. Y aquí aparece una de las ideas más importantes de DDD: el lenguaje ubicuo (ubiquitous language).\nEn cualquier organización existen palabras que tienen un significado concreto para las personas que trabajan en ella. El problema aparece cuando los desarrolladores utilizan unas palabras, los responsables de negocio otras y el sistema termina utilizando una tercera interpretación.\nSi un usuario habla de una “intermediación”, por ejemplo, no deberíamos asumir que sabemos exactamente qué significa. Tendríamos que descubrir qué representa, cuándo comienza, cuándo termina, qué estados puede tener y qué condiciones determinan que una intermediación sea válida.\nEl lenguaje utilizado por el negocio debería convertirse progresivamente en parte del lenguaje utilizado para diseñar y construir el software.\nEsto tiene una consecuencia práctica importante. Si durante una conversación con los expertos del dominio aparece una palabra que describe algo importante, esa palabra puede terminar apareciendo en el código. Una clase, un método o una operación pueden utilizar el mismo concepto que utilizan los usuarios.\nNo se trata de introducir términos empresariales en el código de forma artificial. Se trata de evitar que el código tenga que traducir constantemente entre dos modelos diferentes de la realidad.\nEl dominio no es necesariamente uno solo A medida que profundizamos en un negocio aparece otro problema. Una misma palabra puede significar cosas diferentes dependiendo del contexto en el que se utilice.\nPensemos, por ejemplo, en una empresa que trabaja con clientes. Para el departamento comercial, un cliente puede ser una organización con determinados datos fiscales y comerciales. Para facturación, ese mismo cliente puede tener condiciones de pago, facturas y saldos. Para soporte, puede representar una cuenta con incidencias, contactos y acuerdos de servicio.\nTodos están hablando de un “cliente”, pero no necesariamente están hablando del mismo concepto.\nAquí aparece el concepto de contexto delimitado, o bounded context. Un contexto delimitado establece el límite dentro del cual un determinado modelo del dominio tiene un significado coherente.\nEsto significa que no siempre debemos intentar construir un único modelo empresarial universal que represente toda la organización. Puede ser más útil reconocer que diferentes partes del negocio necesitan modelos diferentes.\nUn cliente en el contexto comercial puede no tener exactamente las mismas propiedades, reglas o responsabilidades que un cliente en el contexto de facturación. Aunque ambos conceptos puedan estar relacionados, cada contexto puede tener su propio modelo y su propio lenguaje.\nEsta idea resulta especialmente importante cuando los sistemas crecen. DDD no intenta eliminar la complejidad del negocio haciendo que todo comparta el mismo modelo, sino controlar esa complejidad estableciendo límites dentro de los cuales cada modelo puede mantenerse coherente.\nModelar significa decidir qué importa Una vez delimitado un contexto podemos empezar a construir un modelo. Pero modelar no significa intentar representar absolutamente todo lo que existe en el mundo real.\nUn modelo es una simplificación. Selecciona determinados aspectos de la realidad porque son relevantes para el problema que queremos resolver.\nSi estamos construyendo un sistema para gestionar centros de estudios clínicos, probablemente no necesitemos representar todo lo que sabemos sobre un centro. Quizá nos interesen su identificador, nombre, ubicación, estado y determinadas capacidades. En otro sistema podrían ser relevantes otros aspectos completamente diferentes.\nEl modelo de dominio no pretende ser una copia completa de la realidad, sino una representación de aquella parte de la realidad que importa para el problema que estamos resolviendo.\nEsta distinción ayuda a entender por qué DDD no consiste simplemente en convertir cada sustantivo del negocio en una clase. Una clase Center no constituye por sí misma un modelo de dominio. El modelo aparece cuando esa estructura expresa conceptos, relaciones, comportamientos y reglas que realmente tienen significado dentro del negocio.\nEntidades: objetos cuya identidad importa Algunos conceptos del dominio necesitan mantener una identidad a lo largo del tiempo. Para ellos DDD utiliza el concepto de entidad.\nImaginemos un centro identificado por C123. Podemos modificar su nombre, dirección o estado, pero seguimos hablando del mismo centro. Sus atributos han cambiado; su identidad permanece.\nEsto es lo que diferencia a una entidad de un simple conjunto de datos. Una entidad se reconoce principalmente por quién es, no únicamente por los valores que contiene en un momento determinado.\nEsta característica tiene consecuencias en el diseño. Si cambiamos el nombre de un centro, no queremos que el sistema interprete que hemos eliminado un centro y creado otro. La identidad permite comprender que se trata del mismo elemento del dominio a lo largo de su ciclo de vida.\nLas entidades, además, suelen tener comportamiento. Si un centro no puede pasar directamente de “inactivo” a determinados estados sin cumplir ciertas condiciones, esa regla pertenece al dominio y puede formar parte del comportamiento de la entidad o de otros elementos del modelo.\nLa cuestión importante es evitar convertir una entidad en una simple estructura de datos con getters y setters. Una entidad de dominio resulta valiosa cuando ayuda a proteger las reglas que determinan qué estados y comportamientos son válidos.\nObjetos de valor: cuando importa el significado, no la identidad No todo concepto necesita una identidad propia. Hay valores cuyo significado depende exclusivamente de sus propiedades.\nPensemos en una dirección, una cantidad monetaria o un intervalo de fechas. Si dos direcciones contienen exactamente los mismos datos relevantes, normalmente no necesitamos distinguirlas porque sean “la misma dirección” con un identificador independiente.\nEstos conceptos pueden representarse mediante objetos de valor, o value objects.\nUn objeto de valor se define por los valores que contiene y no por una identidad propia. Una dirección podría estar formada por calle, número, código postal y ciudad. Una cantidad monetaria podría combinar importe y moneda.\nEsta distinción parece sencilla, pero puede cambiar considerablemente la calidad de un modelo. En lugar de trabajar con cadenas y números sin significado, podemos representar explícitamente conceptos del dominio.\nPor ejemplo, una propiedad llamada PostalCode como simple string permite almacenar cualquier texto. Un objeto de valor CodigoPostal puede encapsular las reglas que determinan qué constituye un código postal válido.\nLos objetos de valor permiten que conceptos aparentemente simples adquieran significado dentro del modelo y concentren las reglas que les corresponden.\nAdemás, suelen ser inmutables: en lugar de modificar internamente un objeto de valor, se crea otro que representa el nuevo valor. Esta característica facilita razonar sobre ellos y reduce determinados problemas derivados de compartir estados mutables.\nAgregados: establecer límites de consistencia A medida que nuestro modelo crece aparece una nueva pregunta: si tenemos muchas entidades relacionadas, ¿debemos tratarlas todas como una única unidad?\nSupongamos que un estudio tiene varios centros asociados y que cada relación entre estudio y centro posee información y reglas propias. Podríamos crear un modelo en el que todas las entidades estén directamente conectadas entre sí. Pero cuanto más grande sea esa red, más difícil será determinar qué podemos modificar conjuntamente y qué reglas deben cumplirse en cada operación.\nAquí aparece el concepto de agregado, uno de los elementos más importantes del diseño táctico de DDD.\nUn agregado es un conjunto de objetos del dominio que se trata como una unidad a efectos de determinadas reglas de consistencia. Dentro de él existe una raíz del agregado (aggregate root) que actúa como punto de entrada para acceder a los elementos que forman parte de esa unidad.\nLa idea fundamental no es simplemente agrupar clases. Un agregado establece un límite dentro del cual determinadas invariantes del negocio deben mantenerse consistentes.\nImaginemos que un Estudio es la raíz de un agregado que contiene determinada información que debe mantenerse coherente con sus relaciones internas. En lugar de permitir que cualquier parte del sistema modifique directamente cualquiera de sus objetos internos, las operaciones pasan por la raíz del agregado, que puede comprobar las reglas correspondientes.\nEsto también ayuda a limitar las transacciones y el acoplamiento. No todo el modelo tiene que cargarse y modificarse como una única estructura gigante.\nUn error habitual consiste en interpretar los agregados como simples agrupaciones jerárquicas. Si una entidad contiene otras entidades, eso no significa automáticamente que todas formen un agregado. El límite del agregado debería surgir de las reglas de consistencia que el negocio necesita proteger, no de cómo nos resulte cómodo organizar las clases.\nLos intermediarios: coordinar sin convertir todo en una entidad No todas las operaciones del dominio encajan naturalmente dentro de una entidad o de un objeto de valor.\nImaginemos una operación que necesita coordinar varios conceptos del dominio pero que no representa una propiedad o comportamiento natural de una entidad concreta. Si intentamos introducirla dentro de una entidad únicamente para tener un lugar donde colocarla, podemos terminar creando objetos con responsabilidades que realmente no les pertenecen.\nAquí aparecen los servicios de dominio (domain services), que podemos entender como intermediarios que encapsulan una operación o regla del dominio cuando su responsabilidad no pertenece naturalmente a una única entidad u objeto de valor.\nPor ejemplo, podríamos tener una regla que determina si un centro puede ser asociado a un determinado estudio teniendo en cuenta información procedente de varios conceptos. Esa operación podría no pertenecer naturalmente ni al centro ni al estudio.\nUn servicio de dominio permite expresar esa regla sin atribuírsela artificialmente a uno de los objetos.\nEsto no significa que cualquier lógica de negocio deba convertirse en un servicio. Si una regla pertenece claramente a una entidad, normalmente resulta más coherente mantenerla dentro de ella.\nLos servicios de dominio deberían utilizarse cuando existe una operación significativa para el dominio que no encaja naturalmente dentro de una única entidad o un objeto de valor.\nLa misma idea de intermediación aparece también en otras partes de una arquitectura. Los servicios de aplicación pueden coordinar casos de uso, los repositorios pueden mediar entre el dominio y la persistencia y otros componentes pueden conectar el modelo con sistemas externos. Pero sus responsabilidades no son intercambiables: uno de los objetivos de DDD es mantener claras las fronteras entre el dominio y los mecanismos que lo rodean.\nUn ejemplo completo: desde una necesidad de negocio hasta el modelo Podemos reunir ahora todas estas ideas en un ejemplo completo. Imaginemos una empresa que gestiona estudios clínicos y necesita una aplicación para coordinar qué centros participan en cada estudio. El objetivo inicial parece sencillo: registrar estudios, registrar centros y permitir que un usuario establezca una relación entre ambos. Si empezáramos directamente por la base de datos, probablemente acabaríamos rápidamente con tablas como Study, Center y StudyCenter, pero todavía no sabríamos si esas estructuras representan realmente las reglas del negocio.\nEl primer paso sería hablar con las personas que conocen el proceso. Durante esas conversaciones descubrimos que un estudio tiene un ciclo de vida, que un centro puede estar activo o inactivo y que un centro no puede participar en un estudio si no cumple determinadas condiciones. También descubrimos que la relación entre estudio y centro no es una simple relación técnica: representa una intermediación, tiene su propio estado, puede comenzar en una fecha determinada y puede finalizar por diferentes motivos. Ya no estamos simplemente recopilando nombres para crear tablas; estamos descubriendo el lenguaje y las reglas del dominio.\nA partir de esas conversaciones podemos comenzar a construir un lenguaje común. Decidimos que utilizaremos términos como Estudio, Centro, Intermediación y Estado de la intermediación tanto en las conversaciones como en el diseño del software. Si aparece una palabra ambigua, no la convertimos automáticamente en una clase: primero preguntamos qué significa, quién utiliza ese concepto y qué reglas lo acompañan. De esta forma, el lenguaje empieza a convertirse en una herramienta para descubrir el modelo y no simplemente en una lista de nombres que después trasladaremos al código.\nA continuación analizamos los contextos en los que aparecen esos conceptos. El área encargada de gestionar estudios necesita saber qué estudios existen, su estado y qué centros participan en ellos. Sin embargo, otro contexto de la organización puede gestionar la información maestra de los centros, donde un centro representa una organización con identidad, dirección, estado y otros datos generales. El mismo centro puede ser utilizado por ambos contextos sin que eso obligue a que ambos tengan exactamente el mismo modelo de centro. En el contexto de estudios nos interesa aquello que necesitamos para gestionar la participación; en el contexto de datos maestros puede interesarnos la identidad y administración general del centro.\nDentro del contexto de gestión de estudios podemos entonces empezar a decidir qué conceptos necesitan identidad. Un Estudio tiene claramente una identidad: aunque cambien su nombre o determinadas propiedades, sigue siendo el mismo estudio. La Intermediación también necesita identidad porque representa una relación concreta que tiene un ciclo de vida propio. Un Centro puede ser identificado mediante un CenterId, aunque en este contexto no necesariamente seamos responsables de toda su información.\nTambién encontramos conceptos que no necesitan identidad propia. El estado de una intermediación puede representarse como un concepto explícito, pero otros valores como un intervalo de fechas o determinados datos de configuración pueden modelarse como objetos de valor. Por ejemplo, podríamos representar el periodo de participación mediante un PeriodoParticipacion formado por FechaInicio y FechaFin, encapsulando la regla de que la fecha final no puede ser anterior a la inicial. La ventaja no está en crear una clase por cada par de propiedades, sino en convertir en conceptos explícitos aquellos valores que tienen significado y reglas propias.\nDespués debemos decidir dónde colocar las reglas. Supongamos que una intermediación solo puede pasar de Propuesta a Aceptada si el centro está habilitado y el estudio se encuentra en un estado que permite incorporar nuevos centros. No tendría demasiado sentido permitir que cualquier controlador o servicio de aplicación cambie directamente el estado de la intermediación. La operación debería pasar por el modelo de dominio para que las reglas que determinan una transición válida queden protegidas.\nPodríamos tener entonces una entidad Intermediacion con operaciones como Aceptar() o Cancelar(). La propia entidad puede comprobar las reglas que dependen de su estado y de la información que le pertenece. Si para determinar si puede aceptarse necesita coordinar información que no pertenece naturalmente a la entidad, podemos utilizar un servicio de dominio, por ejemplo ValidadorDeIntermediacion, que reúna esa regla sin convertir a Intermediacion en responsable de todo el dominio.\nAhora aparece la cuestión de los agregados. Supongamos que una Intermediacion debe mantener internamente consistente su estado, sus fechas y determinada información propia. Podemos decidir que Intermediacion sea la raíz de un agregado. El resto del sistema no modificaría directamente sus propiedades internas, sino que utilizaría operaciones de la raíz para ejecutar acciones significativas como aceptar, rechazar o cancelar la intermediación.\nEl agregado no tendría por qué contener todo el estudio, todos sus centros ni toda la información relacionada. Precisamente una de sus funciones es establecer hasta dónde necesitamos consistencia inmediata. Si modificar una intermediación no requiere cargar y modificar simultáneamente todo el estudio, no tendría sentido convertir el estudio y todas sus intermediaciones en una única estructura transaccional gigantesca.\nCon el modelo definido podemos pasar a la aplicación. Imaginemos que un usuario pulsa el botón “Aceptar intermediación”. La interfaz envía una solicitud a la aplicación. La capa de aplicación recupera la Intermediacion, obtiene la información necesaria para ejecutar la operación y solicita al dominio que realice la aceptación. El dominio comprueba las reglas correspondientes y, si son válidas, cambia el estado. Finalmente, la aplicación persiste el agregado y devuelve el resultado al usuario.\nEl flujo conceptual podría representarse así:\nUsuario │ ▼ Aplicación de gestión de estudios │ │ \u0026#34;Aceptar intermediación\u0026#34; ▼ Servicio de aplicación │ ▼ Intermediación │ ├── comprueba su estado ├── valida sus reglas └── cambia a \u0026#34;Aceptada\u0026#34; │ ▼ Repositorio de Intermediaciones │ ▼ Base de datos Pero el proceso no termina necesariamente ahí. Supongamos que, cuando una intermediación es aceptada, otro sistema necesita saberlo para continuar el proceso. En lugar de introducir directamente esa responsabilidad dentro de la entidad, podemos hacer que la aplicación publique un evento como IntermediacionAceptada. Otro componente puede reaccionar ante ese hecho sin que el modelo de dominio tenga que conocer quién lo consumirá.\nEl resultado final es muy diferente de nuestro diseño inicial basado simplemente en tres tablas. Hemos pasado de una estructura técnica formada por Study, Center y StudyCenter a un modelo en el que existen conceptos con significado propio, reglas explícitas, límites de consistencia y responsabilidades diferenciadas. El código no se ha limitado a almacenar información sobre el negocio: está expresando decisiones sobre cómo funciona el negocio.\nY, sobre todo, el ejemplo muestra que DDD no consiste en aplicar todos sus patrones desde el principio. Primero descubrimos el lenguaje, después delimitamos el contexto, posteriormente identificamos los conceptos y sus reglas y finalmente decidimos qué entidades, objetos de valor, agregados y servicios necesitamos. El modelo surge del problema y de las reglas del dominio; las estructuras técnicas se deciden después para dar soporte a ese modelo.\nCómo encajan todas las piezas Llegados a este punto podemos ver que los conceptos de DDD no son una colección independiente de patrones.\nPrimero intentamos comprender el negocio y construir un lenguaje común. Después descubrimos que ese lenguaje puede tener diferentes significados según el contexto y establecemos contextos delimitados. Dentro de cada contexto construimos un modelo que representa los conceptos relevantes.\nAlgunos de esos conceptos necesitan identidad y se convierten en entidades. Otros se definen únicamente por sus valores y pueden representarse como objetos de valor. Cuando varias entidades y objetos necesitan formar una unidad coherente de consistencia, establecemos un agregado. Y cuando una operación de dominio no pertenece naturalmente a una entidad concreta, podemos expresarla mediante un servicio de dominio.\nLa verdadera fuerza de estos conceptos aparece cuando se utilizan conjuntamente para construir un modelo coherente del negocio, no cuando se aplican como una colección de patrones de programación.\nPodemos imaginarlo como diferentes niveles de decisión. El contexto delimitado establece dónde tiene sentido nuestro modelo. Las entidades y objetos de valor representan los conceptos que contiene. Los agregados determinan determinadas fronteras de consistencia. Los servicios permiten expresar operaciones que atraviesan varios conceptos cuando no pertenecen naturalmente a uno solo.\nEl resultado debería ser un software en el que leer el código permita reconocer, al menos parcialmente, cómo funciona el negocio.\nDDD no significa crear muchas clases Existe una tentación cuando se descubre DDD: transformar inmediatamente cada concepto empresarial en una clase, añadir entidades, objetos de valor, agregados y servicios, y considerar que ya estamos aplicando diseño guiado por dominio.\nPero eso puede producir exactamente el problema contrario al que DDD intenta solucionar. Podemos terminar con un modelo excesivamente complejo, lleno de abstracciones que no aportan significado real.\nDDD no consiste en utilizar muchas clases ni en reproducir literalmente todos sus patrones tácticos. La cuestión fundamental es que las estructuras del software representen de forma útil las reglas y conceptos que realmente importan en el dominio.\nUn sistema puede tener muchas entidades y, sin embargo, tener un modelo de dominio pobre. También puede tener un modelo relativamente sencillo y representar extraordinariamente bien un problema concreto.\nLa complejidad debe aparecer cuando el negocio es complejo, no porque la técnica utilizada para modelarlo lo sea.\nDDD y la arquitectura del sistema DDD tampoco determina por sí solo una arquitectura completa. Un sistema diseñado siguiendo DDD puede utilizar una aplicación monolítica, una arquitectura distribuida o diferentes estilos de integración.\nLo que sí aporta es una forma de decidir dónde deben situarse determinadas responsabilidades y qué partes del sistema pertenecen realmente al dominio.\nEsto resulta especialmente interesante cuando se combina con arquitecturas que buscan aislar la lógica empresarial de los mecanismos externos. El dominio puede expresar sus reglas sin depender directamente de una base de datos concreta, de un framework web o de un proveedor externo.\nLa arquitectura deja entonces de ser únicamente una cuestión de organización técnica y empieza a reflejar también las fronteras conceptuales del negocio.\nEn sistemas grandes, los contextos delimitados pueden incluso convertirse en una herramienta para decidir cómo dividir aplicaciones o servicios. Pero aquí también conviene evitar una asociación automática: un contexto delimitado no equivale necesariamente a un microservicio, porque el límite conceptual del dominio y el límite físico de un despliegue son decisiones diferentes.\nEl verdadero trabajo está fuera del código Quizá la consecuencia más importante de DDD sea que desplaza parte del trabajo esencial del ingeniero de software fuera del editor de código.\nComprender un dominio requiere hablar con las personas que conocen el negocio, observar cómo trabajan, cuestionar términos ambiguos, descubrir excepciones y comprender qué reglas utilizan realmente para tomar decisiones.\nEsto puede resultar incómodo para quien está acostumbrado a pensar que desarrollar software consiste principalmente en implementar requisitos ya definidos. En dominios complejos, los requisitos escritos rara vez contienen todo el conocimiento necesario.\nEl modelo de dominio se construye mediante un proceso de aprendizaje compartido entre las personas que conocen el negocio y las personas que construyen el software.\nPor eso DDD tiene una dimensión técnica y otra de comunicación. El código es importante, pero también lo son las conversaciones que permiten descubrir qué debe representar ese código.\nCuándo aporta valor y cuándo puede resultar excesivo DDD resulta especialmente interesante cuando el dominio contiene reglas complejas, conceptos que evolucionan, diferentes áreas de negocio con significados distintos o decisiones empresariales que deben quedar reflejadas con precisión en el software.\nNo todos los sistemas tienen ese nivel de complejidad. Una aplicación sencilla de gestión de información puede no necesitar un modelo de dominio sofisticado. Introducir agregados, servicios y múltiples abstracciones donde no existe una complejidad real puede hacer que el sistema sea más difícil de entender.\nPor eso el objetivo no debería ser “aplicar DDD” como una obligación metodológica. El objetivo debería ser utilizar las herramientas de DDD cuando ayudan a gestionar la complejidad real del dominio.\nEsta perspectiva también evita otro error frecuente: confundir DDD con una arquitectura determinada. DDD es, ante todo, una forma de abordar el diseño del software a partir del conocimiento del dominio. Los patrones y las técnicas son herramientas para conseguirlo, no el objetivo final.\nUna forma diferente de pensar el software La principal aportación de DDD no es un nuevo tipo de clase ni una estructura concreta de proyecto. Es un cambio de perspectiva.\nEn lugar de comenzar preguntando qué tablas necesitamos, qué endpoints debemos crear o qué clases debemos implementar, comenzamos preguntando cómo funciona el negocio, qué conceptos utiliza, qué reglas lo gobiernan y dónde aparecen diferentes interpretaciones de una misma realidad.\nA partir de ahí, el software puede empezar a adquirir una estructura que refleje ese conocimiento.\nLos contextos delimitados nos ayudan a controlar dónde tiene sentido cada modelo. Las entidades representan conceptos cuya identidad importa. Los objetos de valor permiten modelar valores con significado propio. Los agregados establecen fronteras de consistencia y los servicios de dominio permiten expresar operaciones que no pertenecen naturalmente a una única entidad.\nPero todos ellos son piezas de una idea más amplia. Diseñar guiado por el dominio significa intentar que el software y el conocimiento del negocio evolucionen juntos, de manera que el modelo utilizado por el sistema sea una expresión útil de cómo funciona realmente el problema que queremos resolver.\nAl final, DDD no consiste en hacer que el código se parezca más al negocio simplemente porque utiliza sus palabras. Consiste en conseguir algo más profundo: que las decisiones importantes del software estén fundamentadas en una comprensión explícita del dominio y que esa comprensión pueda mantenerse, discutirse y evolucionar junto con el propio sistema.\n","id":4,"tag":"Arquitectura ; DDD","title":"Diseño guiado por el dominio","url":"https://metrosetenta.es/blog/dise%C3%B1o-guiado-por-el-dominio/"},{"content":"La historia del desarrollo de software puede entenderse como la búsqueda constante de mejores formas de afrontar un problema que apareció casi al mismo tiempo que los propios ordenadores: construir sistemas cada vez más grandes y complejos sin perder el control sobre ellos. En los primeros años de la informática, programar consistía principalmente en conseguir que una máquina ejecutara correctamente una serie de instrucciones. A medida que los sistemas crecieron, esa visión dejó de ser suficiente, porque ya no bastaba con conseguir que el código funcionara: también era necesario encontrar formas sistemáticas de organizar, desarrollar y mantener sistemas cada vez más complejos.\nDe programar a hacer ingeniería Durante las décadas de 1950 y 1960, el software comenzó a adquirir una importancia creciente en sistemas científicos, industriales y militares. Los proyectos aumentaban de tamaño y, con ellos, también lo hacían los problemas: retrasos, costes superiores a los previstos, errores difíciles de corregir y sistemas que resultaban cada vez más difíciles de mantener.\nEste fenómeno se conoció como la crisis del software, una expresión utilizada para describir las dificultades que encontraba la industria para desarrollar software fiable y económicamente viable a medida que aumentaba su complejidad. El problema no era únicamente que los programadores cometieran errores, sino que las técnicas utilizadas para construir software no evolucionaban al mismo ritmo que los sistemas que se pretendía construir.\nA partir de esta situación comenzó a consolidarse la idea de la ingeniería de software como una disciplina que debía aplicar métodos sistemáticos al desarrollo y mantenimiento de software. El cambio de perspectiva es importante. Ya no se trata únicamente de preguntarnos cómo escribir determinado código, sino de comprender qué problema estamos intentando resolver, qué características debe tener la solución, cómo vamos a construirla, cómo comprobaremos que funciona y cómo conseguiremos mantenerla cuando cambien las circunstancias.\nPor eso, aunque la programación sea una parte fundamental de la ingeniería de software, no constituye todo el proceso. Un buen desarrollo requiere tomar decisiones antes de escribir código y continuar tomando decisiones después de que ese código haya llegado a producción.\nUn proceso que comienza antes del código Podemos imaginar el desarrollo de software como un proceso que comienza con una necesidad y continúa durante toda la vida del sistema. Primero debemos comprender qué se necesita; después decidir cómo vamos a resolverlo; posteriormente implementar esa solución y comprobar que se comporta como esperamos. Una vez puesta en producción, tendremos que observarla, corregirla y adaptarla a nuevas necesidades.\nEste recorrido suele dividirse conceptualmente en actividades como requisitos, diseño, implementación, pruebas, evaluación y mantenimiento. No debemos interpretar estas actividades como compartimentos completamente separados ni como una secuencia rígida en la que una termina definitivamente antes de comenzar la siguiente.\nEn la práctica, estas actividades se mezclan y se repiten, porque al diseñar podemos descubrir que un requisito no estaba suficientemente definido, durante la implementación podemos descubrir una limitación técnica que obliga a revisar el diseño y las pruebas pueden revelar que nuestra interpretación del requisito era incorrecta.\nEsta característica resulta especialmente importante porque el conocimiento sobre el sistema no aparece todo de golpe. Se construye progresivamente mientras el equipo comprende el problema, prueba soluciones y recibe información de los usuarios y del propio sistema.\nLos requisitos: comprender qué necesitamos construir Antes de decidir cómo construir una solución necesitamos comprender qué debe resolver. Esta actividad pertenece a la ingeniería de requisitos, que se ocupa de identificar, analizar, documentar y gestionar las necesidades que debe satisfacer un sistema.\nUn requisito puede expresar una funcionalidad concreta que el sistema debe proporcionar. Por ejemplo, una aplicación podría necesitar permitir que un usuario consulte los centros asociados a un determinado estudio. Pero también existen requisitos relacionados con características como el rendimiento, la seguridad, la disponibilidad o la facilidad de mantenimiento.\nPodemos distinguir así entre requisitos funcionales, que describen comportamientos o capacidades que debe proporcionar el sistema, y requisitos no funcionales, que describen propiedades o restricciones bajo las cuales debe funcionar.\nSin embargo, hablar de requisitos únicamente como una lista de funcionalidades puede resultar engañoso. Detrás de cada requisito existe una necesidad de negocio o de usuario que debemos comprender. Una aplicación puede implementar exactamente lo que está escrito y, aun así, no resolver correctamente el problema que originó su desarrollo.\nPor eso, una parte importante del trabajo de ingeniería consiste en hacer preguntas, detectar ambigüedades, identificar restricciones y entender quién utilizará el sistema y con qué propósito. Cuanto mejor comprendamos el problema, más posibilidades tendremos de construir una solución adecuada.\nEl diseño: decidir cómo resolver el problema Una vez comprendido qué necesitamos conseguir aparece una nueva pregunta: cómo vamos a construir la solución.\nEl diseño de software consiste en tomar las decisiones necesarias para transformar los requisitos en una solución construible. Algunas de esas decisiones afectan a la estructura general del sistema y forman parte de su arquitectura de software; otras son más concretas y determinan cómo se organizarán determinadas funcionalidades, componentes o estructuras de datos.\nEn esta etapa aparecen decisiones como dónde almacenar la información, cómo se comunicarán los diferentes componentes, dónde debe situarse una determinada responsabilidad o qué dependencias tendrá una funcionalidad respecto a otras partes del sistema.\nEstas decisiones no son puramente técnicas. Una arquitectura puede facilitar el crecimiento futuro del sistema, pero introducir mayor complejidad inicial. Una solución sencilla puede ser suficiente para las necesidades actuales, pero resultar más difícil de ampliar posteriormente. Diseñar software implica, por tanto, trabajar con compromisos entre diferentes necesidades y restricciones.\nUna consecuencia importante es que las decisiones de diseño tienen una vida más larga que muchas líneas concretas de código. El código puede cambiar con relativa facilidad, mientras que una decisión arquitectónica puede condicionar durante años la evolución de una aplicación.\nLa implementación: convertir las decisiones en software La implementación es probablemente la actividad que más asociamos tradicionalmente con el desarrollo de software: escribir código, configurar componentes y construir el sistema que hemos diseñado.\nSin embargo, contemplar la implementación de forma aislada puede llevarnos a una visión demasiado limitada de la profesión. El código es el resultado visible de muchas decisiones anteriores, y cuando un desarrollador implementa una funcionalidad está materializando requisitos y decisiones de diseño que deberían haber sido comprendidos previamente.\nEsto tampoco significa que el diseño deba completarse por entero antes de escribir una sola línea de código. En muchos proyectos descubriremos durante la implementación que determinadas decisiones necesitan ser revisadas. Precisamente por eso los procesos modernos tienden a favorecer ciclos cortos de construcción y aprendizaje.\nAquí adquiere especial importancia la calidad interna del software. Dos programas pueden proporcionar exactamente el mismo resultado al usuario y, sin embargo, tener costes de mantenimiento completamente diferentes. La estructura del código, la claridad de sus responsabilidades, la gestión de dependencias y la facilidad para modificarlo condicionan directamente la evolución futura del sistema.\nCuando estas decisiones se posponen constantemente para resolver problemas inmediatos, puede aparecer la denominada deuda técnica: decisiones que permiten avanzar más rápidamente en el presente pero que pueden incrementar el coste de modificar el sistema posteriormente.\nLas pruebas: comprobar que el sistema hace lo que debe Una vez construido el software necesitamos obtener evidencias de que funciona correctamente. Para ello utilizamos las pruebas de software, que permiten comprobar el comportamiento del sistema bajo diferentes condiciones.\nLas pruebas pueden realizarse a diferentes niveles. Podemos comprobar una pequeña unidad de código, la interacción entre varios componentes o el comportamiento de todo el sistema desde la perspectiva del usuario. También podemos comprobar distintos aspectos del sistema, desde sus funcionalidades hasta características como el rendimiento o la seguridad.\nPero existe una distinción especialmente importante entre verificación y validación. La verificación se centra en comprobar que estamos construyendo correctamente el producto según las especificaciones establecidas. La validación intenta responder a una pregunta diferente: si aquello que estamos construyendo es realmente la solución adecuada para la necesidad que pretendíamos resolver.\nLas pruebas permiten comprobar que el sistema se comporta de acuerdo con los requisitos, pero esta comprobación no garantiza por sí sola que hayamos definido correctamente esos requisitos.\nEsta diferencia explica por qué un sistema puede superar todas sus pruebas técnicas y, aun así, no resolver correctamente el problema que originó su desarrollo. Podemos construir correctamente algo que no era realmente lo que necesitábamos construir.\nLa evaluación: mirar el sistema desde una perspectiva más amplia La evaluación amplía todavía más nuestra perspectiva. Un sistema puede funcionar técnicamente y cumplir sus requisitos, pero todavía debemos preguntarnos si está proporcionando el resultado esperado.\nPodemos evaluar aspectos como el rendimiento, la seguridad, la fiabilidad, la facilidad de mantenimiento, los costes de operación o la capacidad del sistema para adaptarse a nuevas necesidades. También podemos analizar si realmente está aportando el valor que justificó su construcción.\nUn sistema puede funcionar correctamente desde el punto de vista técnico y, aun así, dejar de ser adecuado para la necesidad que debe resolver.\nEsta evaluación no tiene por qué producirse únicamente al final de un proyecto. En un desarrollo iterativo podemos utilizar la información obtenida en cada ciclo para revisar nuestras decisiones y determinar si debemos continuar por el mismo camino o modificarlo.\nDe esta manera, evaluación y aprendizaje se convierten en partes naturales del proceso de ingeniería. El equipo no sólo construye software: construye conocimiento sobre el problema y utiliza ese conocimiento para mejorar la siguiente decisión.\nDel modelo en cascada al desarrollo iterativo La forma de organizar estas actividades también ha evolucionado a lo largo de la historia. Uno de los modelos clásicos fue el modelo en cascada, en el que las diferentes fases del desarrollo se organizaban de manera secuencial.\nEste modelo resulta sencillo de comprender y puede ser adecuado cuando los requisitos son relativamente estables y conocidos. Sin embargo, muchos proyectos de software no tienen esas características. Cuando los requisitos cambian, cuando existen incertidumbres técnicas o cuando el usuario necesita descubrir progresivamente qué solución necesita realmente, resulta difícil definirlo todo por adelantado.\nLos enfoques iterativos y, posteriormente, las metodologías de desarrollo ágil de software, ponen mayor énfasis en construir pequeñas partes del sistema, obtener información y utilizarla para orientar las siguientes decisiones.\nEn lugar de realizar cada actividad una única vez, podemos recorrer repetidamente el ciclo de comprender una necesidad, diseñar una solución, implementarla, probarla y evaluarla.\nEsto no significa que desaparezcan los requisitos, el diseño, la implementación o las pruebas. Lo que cambia es la forma de relacionarlos. El proceso pasa a entenderse como una sucesión de ciclos en los que el conocimiento adquirido permite revisar las decisiones anteriores.\nEl mantenimiento: cuando comienza la verdadera vida del software Cuando una aplicación llega a producción podría parecer que el proyecto ha terminado. En realidad, desde el punto de vista de la ingeniería de software, comienza una etapa especialmente importante.\nEl mantenimiento de software incluye la corrección de errores, la adaptación a nuevos entornos, la incorporación de funcionalidades y todas aquellas modificaciones necesarias para mantener el sistema útil a lo largo del tiempo.\nEl mundo que rodea al software cambia. Cambian las necesidades de los usuarios, las regulaciones, los sistemas operativos, las infraestructuras, las tecnologías y las integraciones con otros sistemas. Incluso cuando los requisitos de negocio permanecen estables, las condiciones técnicas pueden obligarnos a modificar el software.\nEl software debe seguir adaptándose después de su puesta en producción, porque las necesidades y el entorno en el que funciona también evolucionan.\nPor eso conceptos como mantenibilidad, simplicidad, modularidad y bajo acoplamiento tienen una importancia que va más allá de la estética del código. Una decisión que parece pequeña durante el desarrollo puede determinar cuánto esfuerzo será necesario para realizar un cambio dentro de varios años.\nIngeniería de software frente a programación Llegados a este punto, podemos establecer una diferencia fundamental. Programar consiste en escribir instrucciones que una máquina puede ejecutar. La ingeniería de software engloba un problema mucho mayor: comprender una necesidad, definir qué debe hacer el sistema, diseñar una solución, implementarla, verificarla, validarla, ponerla en funcionamiento y mantenerla durante su vida útil.\nEsto no reduce la importancia de la programación. Al contrario, un buen conocimiento de programación sigue siendo una de las herramientas fundamentales del ingeniero de software. Lo que cambia es el contexto en el que se utiliza.\nLa programación es una actividad fundamental dentro de la ingeniería de software, pero la ingeniería de software abarca todo el proceso necesario para construir y evolucionar un sistema.\nEsta distinción también ayuda a comprender por qué, a medida que aumenta la experiencia profesional, las preguntas que se plantean los desarrolladores suelen cambiar. Al principio es natural concentrarse en cómo implementar una funcionalidad. Posteriormente aparecen preguntas sobre dónde debería vivir esa funcionalidad, qué otras partes del sistema afecta, qué requisitos existen realmente o cómo evolucionará la solución dentro de unos años.\nLa progresión no consiste simplemente en aprender más lenguajes o más herramientas. Consiste también en ampliar el nivel de abstracción desde el código hacia el sistema y, finalmente, hacia el problema que el sistema intenta resolver.\nUna disciplina centrada en gestionar complejidad y cambio Vista en conjunto, la historia de la ingeniería de software puede interpretarse como la evolución de las técnicas utilizadas para gestionar dos problemas fundamentales: la complejidad y el cambio.\nLa complejidad aparece porque los sistemas modernos están formados por numerosas piezas que interactúan entre sí. El cambio aparece porque las necesidades, las tecnologías y el entorno nunca permanecen completamente estáticos.\nRequisitos, diseño, implementación, pruebas, evaluación y mantenimiento no son, por tanto, simples etapas administrativas de un proyecto. Son diferentes actividades que nos permiten comprender el problema, construir una solución, comprobarla y mantenerla a medida que cambian las circunstancias.\nDesde esta perspectiva, aprender ingeniería de software significa aprender a tomar decisiones conscientes sobre todo el ciclo de vida de un sistema. Significa comprender el problema antes de resolverlo, reconocer que las decisiones técnicas tienen consecuencias, comprobar nuestras hipótesis mediante pruebas y evaluación y diseñar pensando en un futuro que inevitablemente traerá nuevos requisitos.\nAprender ingeniería de software significa pasar de pensar únicamente en cómo escribir código a comprender cómo construir, evaluar y evolucionar sistemas completos.\n","id":5,"tag":"Arquitectura","title":"Historia y fundamentos de la ingenieria de software","url":"https://metrosetenta.es/blog/historia-y-fundamentos-de-la-ingenieria-de-software/"},{"content":"Cuando empecé a estudiar arquitectura de software, una de las cosas que más me costaba era entender qué significaba exactamente cada una de las cajas que aparecían en los diagramas. Una caja podía representar un módulo, un componente, un servicio, un proceso o incluso un servidor. Con las flechas ocurría algo parecido: podían representar una dependencia, una llamada, un mensaje o una relación entre el software y la infraestructura.\nDespués de leer Software Architecture in Practice, de Len Bass, Paul Clements y Rick Kazman, encontré una clasificación que me resulta bastante útil para poner un poco de orden en todo esto. Los autores hablan de tres categorías de estructuras arquitectónicas: estructuras de módulos, estructuras de componentes y conectores y estructuras de asignación.\nLa idea me parece interesante porque no intenta definir una única forma de representar una arquitectura. Una arquitectura de software puede observarse desde diferentes perspectivas, y cada estructura nos ayuda a responder preguntas diferentes sobre el mismo sistema.\nEstructuras de módulos Las primeras son las module structures, que describen el sistema desde el punto de vista de sus unidades de implementación. Un módulo es una unidad de software que tenemos que construir, adquirir o mantener y, dependiendo del sistema, puede ser una clase, un paquete, una capa, un proyecto o cualquier otra unidad que utilicemos para organizar el código.\nPor ejemplo, si estoy construyendo una aplicación de comercio electrónico, podría dividirla de esta manera:\nECommerce │ ├── Catalog ├── Orders ├── Payments └── Users En este caso estoy describiendo una estructura de módulos. Lo que me interesa saber es cómo he dividido el software y qué relación existe entre esas divisiones. Puedo preguntarme qué responsabilidad tiene cada módulo, de qué otros módulos depende, qué módulos puede utilizar o qué partes tendré que modificar cuando cambie una funcionalidad.\nEsto último es especialmente interesante porque las estructuras de módulos resultan muy útiles para razonar sobre la modificabilidad del sistema. Si mañana tengo que cambiar la forma en la que calculo los impuestos, por ejemplo, quiero poder identificar rápidamente qué módulos están relacionados con esa funcionalidad y qué otras partes dependen de ellos.\nUna estructura de módulos me permite entender cómo está organizado el software que tengo que construir y mantener.\nCuando trabajo con módulos estoy describiendo fundamentalmente una estructura estática. Puedo estar hablando de código que todavía ni siquiera se está ejecutando. Un proyecto de .NET, por ejemplo, puede formar parte de una estructura de módulos independientemente de que en ese momento haya un proceso ejecutándolo.\nPor eso, cuando estés mirando una estructura de módulos, la pregunta que te interesa hacerte es bastante sencilla: ¿cómo está organizado el software que tengo que construir?\nEstructuras de componentes y conectores La segunda categoría cambia de perspectiva. Las component-and-connector structures, que normalmente se abrevian como estructuras C\u0026amp;C, describen elementos que tienen comportamiento en tiempo de ejecución y las interacciones entre ellos.\nAquí hay una distinción que me parece importante dejar clara porque es fácil interpretarla mal. El libro no presenta módulos, componentes y conectores como tres categorías independientes. Componentes y conectores forman conjuntamente una única categoría de estructuras arquitectónicas: las estructuras component-and-connector.\nLos componentes representan los elementos que participan en la ejecución y los conectores representan los mecanismos mediante los cuales esos elementos interactúan. Por ejemplo, podría tener una arquitectura como esta:\n┌───────────────┐ │ Web App │ └───────┬───────┘ │ HTTPS │ ▼ ┌───────────────┐ │ API │ └───────┬───────┘ │ SQL │ ▼ ┌───────────────┐ │ Database │ └───────────────┘ Aquí puedo identificar tres componentes: la aplicación web, la API y la base de datos. Entre ellos tengo conectores que representan HTTPS y SQL. Pero lo importante no es únicamente identificar las cajas y las flechas, sino entender qué pregunta estoy intentando responder.\nMientras que una estructura de módulos me muestra cómo está organizado el software, una estructura C\u0026amp;C me muestra qué elementos participan en la ejecución y cómo interactúan.\nLa diferencia se ve todavía mejor cuando comparamos módulos y componentes. Imagina que tengo tres módulos llamados Orders, Payments y Users. Puedo terminar ejecutándolos todos dentro de un único proceso:\n┌─────────────────────┐ │ Backend │ │ │ │ Orders │ │ Payments │ │ Users │ └─────────────────────┘ Desde el punto de vista de módulos tengo tres elementos, pero desde el punto de vista de componentes puedo tener uno solo. Un módulo no tiene por qué convertirse en un componente independiente durante la ejecución.\nEsto es algo que te conviene tener presente cuando leas un diagrama arquitectónico. No intentes averiguar qué es una caja únicamente por su nombre. Antes de interpretar el elemento, intenta descubrir qué estructura está representando.\nLos conectores también son importantes porque nos permiten describir cómo se relacionan los componentes. Un conector puede representar una llamada, un mensaje, un evento, una comunicación mediante una cola, un mecanismo de publicación y suscripción, el acceso a un repositorio compartido o un protocolo de comunicación.\nPor ejemplo, en una arquitectura basada en eventos podríamos tener:\nComponent A │ │ publish ▼ Broker │ │ subscribe ▼ Component B Aquí estamos describiendo una estructura C\u0026amp;C porque estamos hablando de elementos que participan en la ejecución y de las interacciones que se producen entre ellos. Esta perspectiva resulta especialmente útil cuando queremos razonar sobre cuestiones como la comunicación, la concurrencia, el rendimiento o la disponibilidad.\nLos componentes y los conectores nos permiten describir la estructura del sistema mientras está funcionando, no simplemente cómo está organizado su código.\nEstructuras de asignación La tercera categoría es probablemente la que más dudas puede generar cuando intentamos traducirla al español. En el libro se utiliza el término allocation y podemos encontrar traducciones como localización, asignación o distribución. Después de darle unas vueltas, creo que asignación describe mejor el concepto que se quiere expresar.\nLa razón es que una allocation structure no consiste simplemente en indicar dónde está algo. El concepto es más amplio: describe relaciones entre elementos software y elementos que no son software.\nPor ejemplo:\nOrders │ │ allocated to ▼ Application Server 01 Aquí estoy asignando un elemento software a un elemento de infraestructura. Pero no tengo por qué estar hablando exclusivamente de hardware. También puedo relacionar elementos software con sistemas de archivos, redes, equipos de desarrollo u otros elementos del entorno.\nPor ejemplo, podría tener una relación como esta:\nMódulo Orders │ ▼ Equipo de desarrollo de Pedidos En este caso no estoy diciendo dónde se ejecuta el módulo. Estoy indicando qué equipo tiene asignada su responsabilidad de desarrollo.\nPor eso, para mí, «localización» se queda demasiado corto para expresar todo lo que engloba el término allocation; «asignación» resulta una traducción más apropiada.\nAllocation no es lo mismo que deployment Aquí aparece otra distinción que conviene tener presente. Cuando hablamos de allocation es bastante habitual pensar inmediatamente en despliegue, porque una de las situaciones más evidentes en las que asignamos software a elementos externos es precisamente cuando decidimos dónde se ejecutará.\nPodríamos tener, por ejemplo:\n┌─────────────────────┐ │ Servidor Web │ │ │ │ Web App │ └──────────┬──────────┘ │ HTTPS │ ▼ ┌─────────────────────┐ │ Servidor API │ │ │ │ Backend │ └──────────┬──────────┘ │ SQL │ ▼ ┌─────────────────────┐ │ Servidor BD │ │ │ │ Database │ └─────────────────────┘ Aquí estoy haciendo una asignación de elementos software a elementos de infraestructura. Sin embargo, deployment y allocation no son sinónimos: el despliegue es un caso particular de una estructura de asignación.\nEsto es importante porque, si reduzco allocation a «dónde se despliega cada cosa», estoy perdiendo una parte importante del concepto. Las estructuras de asignación pueden utilizarse también para representar relaciones con otros elementos del entorno de desarrollo o mantenimiento.\nPor eso, cuando vuelvas a encontrarte con el término allocation, intenta pensar en algo más amplio que simplemente «dónde se despliega».\nLas tres perspectivas juntas Llegados a este punto, podemos resumir las tres categorías mediante tres preguntas:\nEstructura Pregunta Module ¿Cómo está organizado el software? Component-and-connector ¿Qué elementos participan en la ejecución y cómo interactúan? Allocation ¿Cómo se relacionan los elementos software con su entorno? Imagina que tenemos una aplicación de comercio electrónico. Desde el punto de vista de módulos podríamos tener:\nECommerce │ ├── Catalog ├── Orders ├── Payments └── Users Desde el punto de vista de componentes y conectores, la misma aplicación podría representarse así:\nWeb App │ HTTPS │ ▼ Backend │ SQL │ ▼ Database Y desde el punto de vista de asignación podríamos indicar que el backend se ejecuta en un determinado servidor:\nBackend │ │ allocated to ▼ Application Server Es el mismo sistema, pero estamos haciendo preguntas diferentes sobre él. Lo que cambia entre las tres estructuras no es necesariamente el sistema que estamos describiendo, sino la perspectiva desde la que lo estamos observando.\nEsto explica también por qué dos diagramas de una misma aplicación pueden parecer completamente diferentes y, sin embargo, los dos ser correctos. Uno puede estar describiendo módulos y otro componentes y conectores. Otro puede estar describiendo el despliegue. No están compitiendo entre sí ni tienen por qué contener exactamente las mismas cajas.\nNo hay un único diagrama que explique toda la arquitectura Esta es probablemente la idea que más me interesa recordar.\nCuando digo que una aplicación tiene una determinada arquitectura, puedo estar hablando de muchas cosas diferentes. Puedo estar interesado en cómo está organizado el código, en qué elementos participan en la ejecución, en cómo se comunican, en qué servidores se ejecutan o incluso en qué equipo de desarrollo es responsable de cada parte.\nIntentar meter toda esa información en un único diagrama suele terminar creando un dibujo difícil de entender.\nCada estructura arquitectónica sirve para responder preguntas diferentes sobre el sistema.\nUna estructura de módulos puede ayudarme a entender la organización del código y razonar sobre la modificabilidad. Una estructura C\u0026amp;C puede ayudarme a entender cómo funciona el sistema durante la ejecución y a razonar sobre comunicación, concurrencia o rendimiento. Una estructura de asignación puede ayudarme a analizar cuestiones relacionadas con el despliegue, la infraestructura o la organización del trabajo.\nNo están compitiendo entre sí. Están describiendo diferentes aspectos de la misma arquitectura.\nEl error de hacer coincidir las cajas Una consecuencia práctica de entender esto es que ya no tengo que buscar una correspondencia uno a uno entre las diferentes estructuras.\nPuedo tener cuatro módulos:\nOrders Payments Users Catalog y que todos formen parte de un único componente:\n┌───────────────────┐ │ Backend │ └───────────────────┘ También puedo tener varios componentes que se ejecuten en el mismo servidor:\n┌─────────────────────────┐ │ Server 01 │ │ │ │ Web App │ │ API │ │ Worker │ └─────────────────────────┘ Y puedo tener un componente asignado a una infraestructura completamente diferente:\nAPI │ ▼ Cloud Instance No necesito que las cajas de las diferentes estructuras tengan una correspondencia uno a uno.\nEsta idea puede parecer obvia cuando la ves así, pero creo que es bastante fácil olvidarla cuando empiezas a diseñar una arquitectura y quieres que todos los diagramas encajen perfectamente entre sí.\nPor eso, cuando diseñes o leas una arquitectura, intenta no preguntarte únicamente «¿qué es esta caja?». Antes de eso, pregúntate:\n¿Qué estructura estoy mirando?\nUna vez que sabes qué tipo de estructura tienes delante, resulta mucho más sencillo interpretar qué significa cada caja y cada flecha.\nUna forma sencilla de recordarlo Si dentro de unos meses vuelves a este artículo y ya no recuerdas exactamente cómo funcionaban las tres categorías, creo que puedes quedarte con tres preguntas.\nCuando estés mirando una estructura de módulos, pregúntate:\n¿Cómo he dividido el software que tengo que construir?\nCuando estés mirando una estructura de componentes y conectores, pregúntate:\n¿Qué elementos participan en la ejecución y cómo interactúan?\nY cuando estés mirando una estructura de asignación, pregúntate:\n¿Cómo relaciono esos elementos software con el entorno en el que se desarrollan, ejecutan o mantienen?\nPara mí, esa es la parte realmente útil de esta clasificación.\nNo se trata de memorizar tres términos, sino de disponer de tres formas diferentes de mirar el mismo sistema.\nY cuando dibujes una arquitectura, antes de añadir otra caja o flecha, intenta preguntarte qué estás intentando explicar con ella. Quizá descubras que el problema no era que te faltase otro diagrama, sino que estabas intentando responder tres preguntas diferentes con uno solo.\n","id":6,"tag":"Arquitectura","title":"Las tres categorías de estructuras de una arquitectura de software","url":"https://metrosetenta.es/blog/las-tres-categorias-de-estructuras-de-una-arquitectura-de-software/"},{"content":"En desarrollo de software es habitual asumir que las releases forman parte natural del proceso de Scrum; que ocurren al final de un sprint, que agrupan todas las historias planificadas o que representan el último estado del flujo de trabajo. Sin embargo, esta asociación es más cultural que real. Aunque lo parezca, las releases no dependen de Scrum, ni del estado de tus historias, ni de lo que hayas decidido planificar en un sprint.\nUna release es, ante todo, una decisión de entrega. Es el momento en el que decides poner una versión concreta de tu software en manos de usuarios o en producción. Scrum, en cambio, es un framework de gestión del trabajo. Te ayuda a estructurar cómo desarrollas el producto, a inspeccionar el progreso y a mejorar continuamente, pero scrum no define cuándo ni cómo debes entregar software.\nCuando trabajas con Scrum, cada sprint produce un incremento potencialmente liberable. Esa expresión es clave. Potencialmente significa que puede liberarse, no que deba liberarse. Que una funcionalidad esté terminada desde el punto de vista del equipo no implica que tenga que formar parte de una release inmediata, ni siquiera de la siguiente. Puedes tener software terminado que no se libera, o que se libera junto con otros cambios desarrollados en momentos completamente distintos.\nEl flujo de estados de las historias de usuario tampoco define las releases. Ese flujo existe para darte visibilidad y control del trabajo, no para gobernar la entrega. Una historia puede llegar a Done y quedarse ahí. Otra puede liberarse sin que el resto del sprint esté completo. Incluso puedes liberar cambios que no están asociados a una historia visible para negocio, como refactors, mejoras técnicas o correcciones internas. El tablero describe el trabajo; no decide la publicación.\nTampoco existe una obligación de sincronizar las releases con el cierre del sprint. Aunque muchas organizaciones lo hacen por comodidad o tradición, no hay nada en Scrum que lo exija. Puedes liberar a mitad del sprint, varias veces dentro del mismo sprint o después de varios sprints sin liberar nada. El sprint marca el ritmo de trabajo; la release responde a necesidades de negocio, riesgo o contexto operativo. Son relojes distintos y forzarlos a coincidir suele generar fricción innecesaria.\nEsta separación se entiende con especial claridad cuando trabajas con entrega continua (Continuous Delivery). En este modelo, cada cambio que supera el pipeline de CI/CD queda listo para ser liberado. Si completas una historia el tercer día del sprint y pasa todas las pruebas automatizadas, puedes ponerla en producción inmediatamente, sin esperar al Sprint Review ni al cierre del sprint. El sprint continúa, el equipo sigue trabajando y la release ocurre cuando aporta valor, no cuando lo dicta el calendario.\nEn el desarrollo de backend esta independencia es todavía más evidente. Es habitual hacer múltiples releases al día, desplegar cambios invisibles para el usuario final o liberar funcionalidades protegidas por feature flags. Se utilizan técnicas como compatibilidad hacia atrás, despliegues progresivos o rollbacks rápidos. Nada de esto necesita alinearse con un sprint ni con el estado global del tablero.\nScrum sí juega un papel importante, pero no como mecanismo de entrega. Te ayuda a producir software de calidad, a mantener un ritmo sostenible y a tener claridad sobre qué está realmente terminado. Eso facilita decidir cuándo liberar, pero no te obliga a hacerlo de una forma concreta.\nEntender esta diferencia te permite dejar de forzar releases artificiales, evitar acoplamientos innecesarios y asumir una idea fundamental: liberar software es una decisión consciente e independiente. Scrum te ayuda a construirlo bien; la release decide cuándo ese valor llega al mundo real.\n","id":7,"tag":"Product owner","title":"Las releases no están ligadas a Scrum","url":"https://metrosetenta.es/blog/las-releases-no-estan-ligadas-a-scrum/"},{"content":"La batalla entre producción y calidad no es exclusiva del desarrollo de software; se da en todas las industrias. En la manufactura, por ejemplo, Henry Ford revolucionó la producción con la línea de ensamblaje, permitiendo fabricar automóviles a una velocidad sin precedentes. Sin embargo, este enfoque sacrificaba la personalización y, en algunos casos, la durabilidad de los vehículos. En la industria de la moda, las marcas de fast fashion logran lanzar nuevas colecciones en semanas, pero a menudo a costa de la calidad de los materiales y la sostenibilidad.\nEn el desarrollo de software, esta tensión también existe: algunos desarrolladores priorizan la entrega rápida de funcionalidades, mientras que otros se enfocan en la solidez y estabilidad del código. Como Product Owner, tu papel es comprender estos dos enfoques y gestionarlos adecuadamente para el éxito de tu proyecto.\nPerfil 1: El Desarrollador Enfocado en la Velocidad Este tipo de desarrollador se caracteriza por su rapidez en la implementación de historias de usuario. Su principal objetivo es entregar funcionalidad lo antes posible, asegurándose de que el \u0026ldquo;happy path\u0026rdquo; funcione correctamente. Sin embargo, puede omitir aspectos cruciales como:\nGestión de errores y excepciones. Manejo de casos extremos o inusuales. Calidad del código y buenas prácticas. Pruebas exhaustivas, incluyendo pruebas unitarias y de integración. Pros: Rapidez en la entrega de nuevas funcionalidades. Capacidad de reacción ágil ante cambios y prioridades del negocio. Prototipado rápido de ideas y conceptos. Contras: Mayor riesgo de errores en producción. Código difícil de mantener y extender. Necesidad de retrabajo frecuente para corregir problemas no considerados inicialmente. Perfil 2: El Desarrollador Enfocado en la Robustez Este perfil prioriza la estabilidad y la calidad del código. Asegura que la solución no solo funcione en el \u0026ldquo;happy path\u0026rdquo;, sino que también contemple excepciones, validaciones y pruebas rigurosas. Es meticuloso con la arquitectura y sigue buenas prácticas de desarrollo.\nPros: Código más estable, mantenible y escalable. Menos fallos en producción. Reducción del costo de mantenimiento a largo plazo. Contras: Mayor tiempo de implementación. Posible sobreingeniería si no se gestiona adecuadamente. Riesgo de retraso en la entrega de funcionalidades críticas para el negocio. La \u0026ldquo;Best Simple Way\u0026rdquo; y su Relación con el Desarrollo Ágil Una noción interesante que puede influir en este debate es el concepto de la \u0026ldquo;best simple way\u0026rdquo;. Este enfoque, discutido en este artículo, propone que el mejor sistema es el más sencillo que puedas construir, dado el contexto y los requisitos actuales. Esto no debe confundirse con dejar \u0026ldquo;corner cases\u0026rdquo; detectados sin gestionar. La clave está en crear una solución que funcione de manera efectiva para el presente sin sobrecomplicar el sistema, pero siempre considerando las posibles fallas y casos extremos. A medida que el producto evoluciona, puedes ir añadiendo robustez y refinamiento según lo demanden los usuarios y las circunstancias.\nTensiones entre Ambos Perfiles y Cómo Aliviarlas La coexistencia de estos dos perfiles en un mismo equipo puede generar fricciones. Los desarrolladores orientados a la velocidad pueden frustrarse con los procesos de validación y pruebas que consideran innecesarios, mientras que los enfocados en la robustez pueden sentir que se sacrifica la calidad en favor de una entrega apresurada. Esta tensión puede derivar en conflictos, reprocesos y un ambiente de trabajo menos colaborativo.\nPara aliviar estas tensiones, puedes tomar las siguientes medidas:\nFomentar la empatía y el entendimiento mutuo: Organiza sesiones donde cada perfil exponga su enfoque y los beneficios que aporta al equipo. Establecer estándares comunes: Define reglas claras sobre cuándo priorizar rapidez y cuándo es imprescindible profundizar en la robustez. Asignación equilibrada de tareas: Combina ambos perfiles en proyectos o tareas específicas para aprovechar sus fortalezas y mitigar sus debilidades. Revisiones de código colaborativas: Impulsa revisiones entre desarrolladores con enfoques distintos para asegurar un balance entre velocidad y calidad. Feedback continuo: Mantén una comunicación abierta donde ambos perfiles puedan expresar preocupaciones y proponer mejoras. Cómo Gestionar Ambos Perfiles desde el Rol de Product Owner Antes de definir estrategias concretas, es fundamental reconocer que, aunque ciertas prácticas de desarrollo deben ser innegociables (como la seguridad, las pruebas críticas o la gestión de excepciones), siempre es conveniente acordar con el negocio, los clientes y los interesados cuál será el equilibrio adecuado entre calidad y velocidad de producción. Encontrar este balance garantizará que el software cumpla con los objetivos estratégicos sin comprometer su estabilidad a largo plazo.\nEl equilibrio entre velocidad y robustez es clave para el éxito de un producto digital. Como Product Owner, puedes tomar ciertas acciones para gestionar ambos perfiles dentro del equipo:\nDefinir criterios de aceptación claros: Asegura que las historias de usuario incluyan requisitos funcionales y no funcionales, como gestión de errores y validaciones. Priorizar el código de calidad: Fomenta revisiones de código, pair programming y buenas prácticas para que los desarrolladores que priorizan la velocidad no sacrifiquen la calidad. Promover una cultura de testing: Incluye pruebas unitarias y de integración como parte de la definición de \u0026ldquo;done\u0026rdquo;. Fomentar la colaboración: Haz que los desarrolladores con mentalidades opuestas trabajen juntos en tareas específicas para equilibrar rapidez con robustez. Asegurar entregas incrementales: Implementa historias de usuario en pequeños incrementos que permitan validar funcionalidades sin comprometer la estabilidad del sistema. Revisar incidentes en producción: Si detectas fallos recurrentes debido a código apresurado, es momento de reforzar la importancia de la calidad en el equipo. Equilibrar la planificación: Establece expectativas realistas sobre el tiempo necesario para desarrollar correctamente una funcionalidad, sin frenar excesivamente a los desarrolladores rápidos ni presionar demasiado a los meticulosos. Conclusión En un equipo de desarrollo, tanto la velocidad como la robustez son cualidades valiosas. Tu desafío como Product Owner es fomentar un balance entre ambos enfoques, asegurando que el producto se entregue a tiempo sin comprometer su calidad. A través de una gestión efectiva, revisiones de código y criterios de aceptación sólidos, puedes aprovechar lo mejor de cada perfil y construir un software estable, escalable y alineado con las necesidades del negocio.\n","id":8,"tag":"Product owner","title":"La batalla entre producción y calidad","url":"https://metrosetenta.es/blog/la-batalla-entre-produccion-y-calidad/"},{"content":"En un proyecto de software, las decisiones de alto nivel las puedes documentar en un formato estándar conocido como ADR (Architecture Decision Record). Los ADR son documentos cortos y claros que capturan información clave sobre decisiones arquitectónicas y de diseño importantes. A continuación, te explico cómo puedes documentar y compartir estas decisiones:\nFormato típico de un ADR Título: Escribe un título descriptivo que resuma la decisión. Contexto: Explica el problema o situación que te llevó a tomar la decisión. Incluye antecedentes técnicos, restricciones y objetivos del proyecto. Decisión: Describe la solución o enfoque que seleccionaste, de manera clara y concisa. Razonamiento: Justifica la decisión, incluyendo pros y contras, alternativas evaluadas y las razones por las que las descartaste. Consecuencias: Detalla los efectos de la decisión, incluyendo beneficios, riesgos y posibles desafíos futuros. Fecha: Registra la fecha en que tomaste la decisión. Estado: Indica el estado actual de la decisión (por ejemplo, propuesta, aceptada, rechazada o reemplazada). Buenas prácticas para documentar ADRs Centraliza la información: Guarda los ADR en un repositorio accesible para todo el equipo, como un repositorio de código (por ejemplo, Git) o una herramienta de documentación (por ejemplo, Confluence o Notion). Usa un formato consistente: Esto facilita que todos los interesados lean y comprendan los documentos. Mantén los ADR actualizados: Si cambias una decisión, crea un nuevo ADR que documente el cambio y haga referencia al anterior. Hazlos visibles: Proporciona enlaces desde herramientas de gestión de proyectos o bases de conocimiento para que los interesados los encuentren fácilmente. Evita documentos excesivamente largos: Asegúrate de que los ADR sean breves y directos; si es necesario, incluye enlaces a documentación más detallada. Aquí puedes consultar información sobre plantillas de ADRs.\nHerramientas para gestionar ADRs Repositorios de código (Git): Crea un directorio docs/adr/ o similar para mantener los ADR versionados junto con el código. Herramientas colaborativas: Usa herramientas como Confluence, Notion o Google Docs para facilitar el acceso y la edición colaborativa. Plantillas: Usa plantillas prediseñadas para asegurar la uniformidad en todos los ADR. Este enfoque te asegura que todos los interesados, incluidos desarrolladores, QA y stakeholders, tengan acceso a un historial claro y bien documentado de las decisiones clave del proyecto.\nGestión de ADRs con Azure DevOps En un proyecto gestionado con Azure DevOps, puedes integrar los ADRs (Architecture Decision Records) de manera efectiva aprovechando las capacidades de documentación, repositorios de código y la integración con herramientas de colaboración que ofrece la plataforma. Aquí tienes un enfoque recomendado:\n1. Usar un repositorio Git en Azure DevOps Ubicación: Crea un directorio específico para los ADRs dentro del repositorio principal del proyecto, por ejemplo, docs/adr/. Estructura: Guarda cada ADR como un archivo Markdown (.md) nombrado de manera secuencial y descriptiva, como 001-elegir-base-de-datos.md. Ventajas: Tienes versionado automático con Git. Puedes enlazar a commits o pull requests que implementan la decisión. Es compatible con revisiones a través de pull requests. 2. Wiki de Azure DevOps Descripción: Usa la funcionalidad de Wiki de Azure DevOps para documentar los ADR en una sección específica. Acceso: Asegúrate de que el Wiki esté accesible para todos los miembros del equipo y que tenga permisos de lectura para los stakeholders que no son desarrolladores. Organización: Crea una página principal llamada \u0026ldquo;ADRs\u0026rdquo; que enlace a páginas individuales para cada decisión. Organiza las decisiones por fecha o categorías. Ventajas: Ofreces una interfaz amigable para no desarrolladores. Facilitas la búsqueda y navegación. 3. Incorporar ADRs en Work Items Enlace a Work Items: Vincula los ADR relevantes a Work Items de Azure DevOps (por ejemplo, Epics, Features, o User Stories). Usa el campo de descripción o los comentarios para incluir enlaces directos a los ADR en el repositorio o Wiki. Beneficio: Esto permite que los desarrolladores y QA comprendan el contexto de decisiones arquitectónicas mientras trabajan en tareas específicas. 4. Automatizar con Azure Pipelines Validación y consistencia: Configura Azure Pipelines para automatizar revisiones de ADR, como: Verificar que los archivos estén en el formato correcto. Asegurar que cada ADR tenga los campos requeridos (por ejemplo, contexto, decisión, razones, consecuencias). Notificaciones: Configura notificaciones para el equipo cada vez que crees o modifiques un ADR. 5. Integración con Dashboards Widgets personalizados: Usa los dashboards de Azure DevOps para mostrar: Un resumen de los ADR más recientes. Enlaces directos al repositorio o al Wiki. Estado de las decisiones: Implementa un widget que muestre el estado de las decisiones clave (aceptado, en revisión, rechazado). 6. Políticas de mantenimiento Actualización de ADRs: Define un proceso claro para actualizar ADR cuando cambien las decisiones. Revisión periódica: Asigna responsables para revisar regularmente la relevancia y precisión de los ADR. Ejemplo de flujo en Azure DevOps: Decisión inicial: Crea un ADR en docs/adr/001-elegir-base-de-datos.md. Discusión y aprobación: Revisa el documento mediante un pull request en Azure Repos. Implementación: Vincula el ADR a los Work Items relacionados. Documentación accesible: Refleja la decisión final en la Wiki y enlázala desde los dashboards o pipelines relevantes. Este enfoque te asegura que los ADR estén centralizados, versionados y fácilmente accesibles para todos los miembros del proyecto, desde desarrolladores hasta stakeholders.\n","id":9,"tag":"Fundamentos arquitectura","title":"Registro de decisiones arquitectónicas","url":"https://metrosetenta.es/blog/registro-de-decisiones-arquitectonicas/"},{"content":"Web 3.0, la tercera generación de internet, está revolucionando la manera en que interactuamos en línea, proporcionando un ecosistema más descentralizado, seguro y transparente. A medida que esta tecnología avanza, surgen nuevas aplicaciones que aprovechan las características únicas de Web 3.0, cambiando radicalmente diversas industrias y servicios. A continuación, exploramos algunos de los casos de uso más destacados de Web 3.0, tanto actuales como futuros.\nAplicaciones Actuales de Web 3.0 Finanzas Descentralizadas (DeFi): DeFi es uno de los casos de uso más prominentes de Web 3.0, permitiendo la creación de servicios financieros sin intermediarios tradicionales como bancos o corredores. A través de contratos inteligentes, plataformas DeFi facilitan préstamos, intercambios, staking y otros servicios financieros de manera automatizada y descentralizada. Ejemplos de aplicaciones DeFi incluyen Uniswap, Aave y Compound.\nOrganizaciones Autónomas Descentralizadas (DAOs): Las DAOs son organizaciones gestionadas y gobernadas por contratos inteligentes en la blockchain, sin necesidad de una autoridad central. Esto permite una gobernanza más transparente y democrática, donde los miembros pueden votar sobre decisiones clave. DAOs como MakerDAO y Uniswap son ejemplos de cómo se puede gobernar una organización de manera descentralizada.\nAplicaciones Descentralizadas (DApps): DApps son aplicaciones que funcionan en una red blockchain, lo que significa que no dependen de servidores centralizados. Estas aplicaciones son más seguras y resistentes a la censura. Un ejemplo popular de una DApp es OpenSea, un mercado descentralizado para la compra y venta de NFTs.\nIdentidad Digital Descentralizada: Web 3.0 está impulsando la creación de identidades digitales que permiten a los usuarios controlar sus datos personales sin depender de grandes corporaciones. Proyectos como Sovrin y uPort están desarrollando sistemas de identidad digital descentralizada que permiten a los usuarios gestionar sus datos de manera segura y privada.\nNFTs (Tokens No Fungibles): Los NFTs son tokens únicos que representan la propiedad de un activo digital, como obras de arte, música o bienes virtuales en videojuegos. Estos tokens han transformado industrias creativas al proporcionar una forma de verificar la autenticidad y la propiedad de los activos digitales. Ejemplos de aplicaciones incluyen marketplaces como OpenSea y juegos como Axie Infinity.\nAplicaciones Futuras de Web 3.0 Interoperabilidad entre Blockchains: Uno de los desafíos actuales es la falta de comunicación entre diferentes blockchains. El futuro de Web 3.0 verá un aumento en la interoperabilidad, permitiendo que diferentes redes blockchain interactúen entre sí. Esto abrirá nuevas posibilidades para aplicaciones que puedan operar a través de múltiples blockchains, aumentando su funcionalidad y alcance.\nMetaverso Descentralizado: El concepto del metaverso, un universo virtual compartido, está ganando terreno gracias a Web 3.0. A medida que la tecnología blockchain evoluciona, el metaverso descentralizado permitirá a los usuarios poseer y comerciar activos digitales, como tierras virtuales y objetos de juegos, en un entorno completamente interoperable y descentralizado.\nRedes Sociales Descentralizadas: Las redes sociales actuales están controladas por grandes corporaciones que manejan y monetizan los datos de los usuarios. Las redes sociales descentralizadas prometen un cambio radical, permitiendo a los usuarios tener el control total sobre sus datos y la monetización de su contenido. Proyectos como Mastodon y Diaspora están liderando el camino hacia una nueva generación de redes sociales.\nSupply Chain Management (Gestión de la Cadena de Suministro): La tecnología blockchain puede ser utilizada para mejorar la transparencia y la trazabilidad en las cadenas de suministro. Empresas en todo el mundo están explorando cómo Web 3.0 puede permitir un seguimiento más eficiente y transparente de los productos desde la fabricación hasta el consumidor final, asegurando la autenticidad y reduciendo el fraude.\nGobierno Digital Descentralizado: En el futuro, Web 3.0 podría facilitar la creación de gobiernos digitales más transparentes y eficientes, utilizando blockchain para la gestión de registros públicos, votaciones electrónicas y la distribución de servicios sociales de manera justa y verificable.\nConclusión Web 3.0 está redefiniendo la forma en que interactuamos con la tecnología y cómo se gestionan los servicios digitales. Desde las finanzas hasta la identidad digital, las aplicaciones actuales y futuras de Web 3.0 están transformando industrias y creando nuevas oportunidades. A medida que esta tecnología evoluciona, el potencial para nuevas innovaciones sigue siendo enorme, y aquellos que se adentren en este mundo ahora tendrán la ventaja de estar a la vanguardia de la próxima gran ola de la evolución digital.\n","id":10,"tag":"Web3 ; Blockchain","title":"Casos de uso de Web 3.0: aplicaciones actuales y futuras","url":"https://metrosetenta.es/blog/casos-uso-web-3-0/"},{"content":"La descentralización es uno de los pilares más importantes de Web 3.0. A diferencia de los modelos centralizados de Web 2.0, donde los datos y el poder se concentran en manos de unas pocas entidades, Web 3.0 se basa en la distribución del control a través de una red de nodos independientes. Pero, ¿por qué es tan crucial la descentralización para la próxima generación de internet?\n¿Qué es la Descentralización? La descentralización en el contexto de Web 3.0 significa que ninguna entidad única controla toda la red. En lugar de depender de servidores centralizados que son vulnerables a ataques, censura y fallas, Web 3.0 utiliza redes descentralizadas que están compuestas por muchos nodos independientes. Cada nodo mantiene una copia del registro de datos y participa en el proceso de verificación, lo que garantiza la integridad y la disponibilidad de la red.\nRazones por las que la Descentralización es Importante en Web 3.0 Resistencia a la Censura: En una red descentralizada, no hay una sola autoridad que pueda censurar o controlar el flujo de información. Esto es especialmente importante en regiones donde la libertad de expresión está limitada. La descentralización permite a los usuarios compartir información sin temor a ser silenciados por una entidad central.\nMayor Seguridad: Las redes centralizadas son objetivos atractivos para los hackers porque concentran muchos datos valiosos en un solo lugar. La descentralización distribuye esos datos a través de una red de nodos, haciendo que sea mucho más difícil y costoso para los atacantes comprometer la red en su totalidad.\nConfianza sin Intermediarios: En Web 2.0, los usuarios deben confiar en intermediarios, como bancos o plataformas de redes sociales, para manejar sus datos y transacciones. Web 3.0 elimina la necesidad de estos intermediarios mediante el uso de contratos inteligentes y tecnología blockchain, permitiendo transacciones directas entre usuarios con total transparencia.\nPropiedad de los Datos: En las plataformas centralizadas, las corporaciones suelen tener control sobre los datos del usuario y pueden utilizarlos para su propio beneficio, a menudo sin el consentimiento explícito del usuario. En un sistema descentralizado, los usuarios tienen el control total sobre sus datos y deciden cómo y con quién compartirlos.\nFomento de la Innovación y la Inclusión: La descentralización abre la puerta a la innovación al permitir que cualquier persona contribuya a la red. Los desarrolladores pueden crear aplicaciones sin necesidad de permisos de una autoridad central, fomentando un ecosistema más inclusivo y diverso.\nConclusión La descentralización es fundamental para la visión de Web 3.0 porque empodera a los usuarios, mejora la seguridad y promueve la transparencia y la inclusión. Al distribuir el control y la propiedad, Web 3.0 crea una web más justa y democrática, alineada con los principios de apertura y libertad que guían la evolución de internet. Al entender la importancia de la descentralización, podemos apreciar cómo Web 3.0 está configurando un nuevo paradigma para el futuro de la web.\n","id":11,"tag":"Web3 ; Descentralización ; Blockchain","title":"Importancia de la Descentralización en Web 3.0","url":"https://metrosetenta.es/blog/importancia-de-la-descentralizacion-web-3-0/"},{"content":"Web 3.0, la evolución más reciente de internet, se basa en varios principios fundamentales que buscan transformar la forma en que interactuamos con la web. Estos principios no solo redefinen la estructura técnica de internet, sino que también promueven un cambio en cómo se gestionan los datos, la privacidad y la seguridad en línea.\nPrincipios Fundamentales de Web 3.0 Descentralización: En Web 3.0, el poder y el control están distribuidos en lugar de centralizados. Esto significa que, en lugar de depender de grandes corporaciones o entidades únicas para gestionar y almacenar datos, estos se distribuyen a través de una red de nodos. Esto no solo mejora la resistencia a la censura, sino que también aumenta la seguridad, ya que no hay un único punto de falla.\nPropiedad del Usuario: Uno de los cambios más significativos en Web 3.0 es el retorno del control de los datos al usuario. A diferencia de Web 2.0, donde las empresas almacenan y gestionan nuestros datos, en Web 3.0, cada usuario posee y controla sus propios datos. Esto se logra mediante el uso de tecnologías de identidad descentralizada y almacenamiento en blockchain, lo que asegura que los datos del usuario estén seguros y solo sean accesibles con su consentimiento.\nInteroperabilidad: Web 3.0 promueve un entorno en el que diferentes aplicaciones y plataformas pueden interactuar sin problemas entre sí. Gracias a las tecnologías de blockchain y estándares abiertos, los datos y activos pueden moverse fácilmente entre diversas aplicaciones sin que los usuarios pierdan el control o la propiedad. Esto facilita una experiencia de usuario más unificada y eficiente.\nSeguridad y Privacidad Mejoradas: La seguridad es un pilar clave de Web 3.0. Con el uso de criptografía avanzada y almacenamiento distribuido, los datos de los usuarios están mejor protegidos contra hackers y violaciones de datos. Además, la privacidad se mejora significativamente, ya que los datos no son controlados por una sola entidad y los usuarios tienen la capacidad de decidir quién tiene acceso a su información.\nTransparencia y Confianza: En Web 3.0, todas las transacciones y actividades en la red son transparentes y verificables. La tecnología blockchain permite que todas las acciones sean registradas en un libro mayor inmutable, accesible para cualquier persona en la red. Esto no solo aumenta la confianza, sino que también reduce la necesidad de intermediarios, ya que todos pueden verificar directamente la autenticidad de las transacciones.\nConclusión Web 3.0 representa un cambio radical en cómo percibimos y utilizamos internet. Al centrarse en la descentralización, la propiedad del usuario, la interoperabilidad, la seguridad y la transparencia, Web 3.0 está configurando un futuro digital más equitativo y seguro. Comprender estos principios fundamentales es crucial para aprovechar al máximo las oportunidades que ofrece esta nueva era de internet.\n","id":12,"tag":"Web3 ; Blockchain ; Descentralización","title":"Principios Fundamentales de Web 3.0","url":"https://metrosetenta.es/blog/principios-fundamentales-web-3-0/"},{"content":"A medida que la tecnología evoluciona, también lo hace la forma en que interactuamos con internet. Web 2.0 y Web 3.0 representan dos etapas distintas en el desarrollo de la web, cada una con características y filosofías únicas que las diferencian.\n¿Qué es Web 2.0? Web 2.0, a menudo conocida como la \u0026ldquo;web social\u0026rdquo;, es la versión de internet que conocemos hoy en día. Esta etapa se caracteriza por la creación y el intercambio de contenido generado por los usuarios a través de plataformas centralizadas como redes sociales, blogs y sitios web de colaboración. Las empresas como Facebook, Google y Twitter dominan este espacio, proporcionando servicios gratuitos a cambio de los datos personales de los usuarios y mostrando anuncios personalizados.\nDiferencias Clave entre Web 2.0 y Web 3.0 Centralización vs. Descentralización:\nWeb 2.0: Los datos y servicios están centralizados en grandes plataformas que controlan la infraestructura y el acceso a la información. Web 3.0: Los datos y servicios se distribuyen a través de una red descentralizada, utilizando tecnología blockchain. Esto elimina la necesidad de intermediarios, permitiendo a los usuarios interactuar directamente entre sí. Propiedad de los Datos:\nWeb 2.0: Las plataformas centralizadas poseen y controlan los datos de los usuarios. Los datos se almacenan en servidores privados, lo que a menudo lleva a problemas de privacidad y seguridad. Web 3.0: Los usuarios tienen control total sobre sus datos, que están almacenados de forma segura en blockchain. La privacidad y la seguridad son mayores, ya que solo los propietarios de los datos pueden decidir quién tiene acceso a ellos. Modelo de Monetización:\nWeb 2.0: El modelo de negocio predominante es la publicidad. Las plataformas recopilan datos de los usuarios para crear perfiles detallados que se utilizan para mostrar anuncios dirigidos. Web 3.0: En lugar de depender de la publicidad, Web 3.0 se basa en modelos de negocio más justos, como pagos directos entre usuarios y desarrolladores o recompensas en tokens. Los usuarios pueden ser recompensados por su participación y contribución a la red. Interactividad y Automatización:\nWeb 2.0: La interactividad se limita principalmente a las acciones facilitadas por las plataformas centralizadas, como hacer \u0026ldquo;me gusta\u0026rdquo; o compartir contenido. Web 3.0: Utiliza contratos inteligentes que se ejecutan automáticamente cuando se cumplen ciertas condiciones, permitiendo una mayor automatización y nuevas formas de interacción, como las finanzas descentralizadas (DeFi) y los tokens no fungibles (NFTs). Interoperabilidad:\nWeb 2.0: Las aplicaciones y servicios suelen estar aislados entre sí, lo que dificulta la integración y el intercambio de datos entre plataformas diferentes. Web 3.0: Está diseñada para ser interoperable, facilitando la comunicación y el intercambio de información entre diferentes redes y aplicaciones, independientemente de la plataforma o blockchain subyacente. Conclusión Web 2.0 y Web 3.0 son representaciones de cómo la web ha evolucionado para satisfacer las necesidades cambiantes de los usuarios. Mientras que Web 2.0 se centró en la conectividad y la creación de contenido social, Web 3.0 está construyendo un entorno más descentralizado, seguro y equitativo. Al entender estas diferencias, podemos ver hacia dónde se dirige el futuro de internet y cómo los desarrolladores pueden aprovechar estas nuevas tecnologías para crear aplicaciones más innovadoras y justas.\n","id":13,"tag":"Web2 ; Web3 ; Internet","title":"Diferencias entre Web 2.0 y Web 3.0","url":"https://metrosetenta.es/blog/diferencias-web-2-0-y-web-3-0/"},{"content":"Web 3.0, también conocida como la tercera generación de internet, es una evolución de la web tradicional hacia un entorno más descentralizado, seguro y transparente. A diferencia de Web 2.0, que se centra en la creación de contenido y la interacción social a través de plataformas centralizadas, Web 3.0 utiliza tecnologías de blockchain y contratos inteligentes para crear aplicaciones y servicios que no dependen de una autoridad central.\nCaracterísticas Principales de Web 3.0 Descentralización: A diferencia de las aplicaciones tradicionales que almacenan datos en servidores centralizados, Web 3.0 utiliza una red distribuida de nodos para almacenar información, lo que reduce el control de las grandes corporaciones y mejora la seguridad y la privacidad de los usuarios.\nPropiedad de los Datos: En Web 3.0, los usuarios tienen mayor control sobre sus datos. A través de sistemas de identidad descentralizados, los individuos pueden gestionar quién tiene acceso a su información personal y cómo se utiliza.\nInteroperabilidad: Las aplicaciones y servicios en Web 3.0 están diseñados para ser interoperables entre diferentes plataformas y blockchains, facilitando la comunicación y el intercambio de valor entre distintas redes.\nContratos Inteligentes: Los contratos inteligentes son programas que se ejecutan automáticamente cuando se cumplen ciertas condiciones. Esto permite la automatización de acuerdos y transacciones sin necesidad de intermediarios, reduciendo costes y mejorando la eficiencia.\nTransparencia y Seguridad: Gracias al uso de la tecnología blockchain, todas las transacciones y operaciones en Web 3.0 son transparentes y pueden ser verificadas por cualquier usuario, lo que aumenta la confianza y la seguridad en el ecosistema digital.\n¿Por Qué Es Importante Web 3.0? Web 3.0 representa un cambio fundamental en cómo interactuamos con la tecnología y los servicios en línea. Al empoderar a los usuarios con mayor control sobre sus datos y permitir la creación de aplicaciones descentralizadas, Web 3.0 está construyendo un internet más justo y equitativo, donde la privacidad, la seguridad y la transparencia son primordiales.\n","id":14,"tag":"Web3 ; Descentralización ; Blockchain","title":"¿Qué es Web 3.0?","url":"https://metrosetenta.es/blog/que-es-web-3-0/"},{"content":"¿Te ha pasado en alguna ocasión que no quieres compartir modificaciones de código? Te cuento mi caso.\nEn el proyecto donde colaboro actualmente usamos el puerto 5000 para desplegar la aplicación, pero un software de Intel instalado en mi equipo usa por defecto dicho puerto y, claro, al desplegar el proyecto en mi máquina recibo el siguiente mensaje:\nUnhandled exception. System.IO.IOException: Failed to bind to address http://127.0.0.1:5000: address already in use. La solución más sencilla pasa por actualizar el puerto donde despliego la aplicación en mi máquina, y para esto debo hacer una modificación en un archivo nombrado como launchSettings.json pero, como el resto de colaboradores seguirá usando el puerto 5000 no quiero compartir dicha modificación del puerto de despliegue, únicamente la usaré yo en mi equipo local.\nConseguir esto pasa por estar atento a excluir dicho archivo de todas las subidas de código que realizo, lo cual es muy tedioso dado que soy propenso a usar el siguiente comando antes de hacer una confirmación en git cuando estoy satisfecho con mi código.\ngit add . Añadir una sentencia a mi configuración de git para ignorar el archivo launchSettings.json tampoco sería una solución ideal, pues debería generar una confirmación para que git deje de rastrearlo y, si algún colaborador quisiera en el futuro hacer alguna modificación en este archivo de configuración, no podría compartirla. Finalmente, la solución que mejor se adapta a esta circunstacia es pedirle a git que asuma que dicho archivo no tiene cambios.\nCon el siguiente comando podemos configurar archivos que están siendo rastreados, y no que queremos dejen de estarlo, para que git asuma que no han sido cambiados:\ngit update-index --assume-unchanged src/project.api/properties/launchSettings.json Puedes listar los archivos ignorados así:\ngit ls-files -v | grep \u0026#39;^h\u0026#39; Y, por último, para dejar de asumir que dicho archivo no tiene cambios, usa el siguiente comando:\ngit update-index --no-assume-unchanged src/project.api/properties/launchSettings.json ","id":15,"tag":"Git","title":"Código que no quieras compartir","url":"https://metrosetenta.es/blog/codigo-que-no-quieras-compartir/"},{"content":"En ocasiones podemos disponer de una buena documentación, una wiki del proyecto o cualquier plataforma de colaboración que nos permita consultar y compartir información técnica, configuraciones, procesos de gestión\u0026hellip; Pero seamos sinceros, documentar no es lo que más nos gusta a los ingenieros de desarrollo de software y estas opciones no siempre están disponibles para nosotros. Por otro lado, es posible que quieras disponer de algunas notas personales, scripts de automatización o incluso configuraciones personales para el proyecto donde estás colaborando.\nPara poder disponer de un lugar donde guardar este tipo de información tengo el hábito de crear en la carpeta principal del proyecto, siempre que esté versionado con git, una carpeta para mis notas personales del proyecto. Es cierto que podría disponer de esta información en cualquier otro lugar de mi equipo, pero ubicándola en el propio proyecto consigo mayor orden y facilidad de acceso a mis pequeños tesoros. Ahora bien ¿Cómo se me ocurre añadir al proyecto una carpeta de caracter personal y que incluso podría contener información sensible? Esto es lo mejor, gracias a git puedo usar dicha ubicación, ordenada y accesible, sin enturbiar el proyecto. Te explico a continuación como lo hago.\nEn mis carpetas personales de configuración, mis dotFiles, dispongo de un archivo de configuración de .gitignore_global que contiene la siguiente línea:\n# Ignore my personal project notes personal-project-notes/ Ahora se trata de configurar git, a nivel global, para que use dicho archivo de configuración. Si no usaste .gitignore_global para nombrar tu archivo no olvides modificarlo en el siguiente script.\ngit config --global core.excludesfile ~/documents/dotFiles/.gitignore_global ¡Y listo, ya puedes crear en la carpeta raiz tu propia carpeta personal-project-notes sin molestar al resto de colaboradores!\nSi en algún momento quieres consultar la ubicación del archivo que estas usando en la configuración global de git para ignorar carpetas o archivos puedes ejecutar el siguiente comando:\ngit config --get core.excludesfile ","id":16,"tag":"Git","title":"Notas personales del proyecto","url":"https://metrosetenta.es/blog/notas-personales-del-proyecto/"},{"content":"El doble gasto (double spending) es un concepto crítico en el entorno de blockchain y las criptomonedas. Se refiere al acto de gastar el mismo token digital, por ejemplo una criptomoneda, dos veces o más. En un sistema de pago tradicional como el dinero en efectivo, en la compra se transfiere directamente el bien utilizado como modo de pago, y al dejar de poseerlo es imposible volver a gastarlo. Por otro lado, en las transacciones bancarias centralizadas hay una entidad que registra y controla todos los movimientos, cuidando así de evitar el doble gasto, pero en las redes blockchain descentralizadas, y por tanto donde no existe dicha entidad, evitarlo es un desafío significante.\nPara dar solución al problema del doble gasto las redes blockchain utilizan una tecnología llamada mecanismo de consenso con el cual se pueden validar y registrar las transacciones. Los participantes de la red, los nodos verifican la autenticidad de las transacciones y garantizan que no se haya gastado la misma criptomoneda en más de una transacción. Uno de los algoritmos de consenso más comunes utilizados en criptomonedas como Bitcoin es la prueba de trabajo (proof of work), que requiere que los mineros resuelvan complejos rompecabezas matemáticos para agregar transacciones al registro público, lo que hace que duplicar un gasto sea extremadamente difícil y más costoso que el mismo gasto a duplicar.\n","id":17,"tag":"Blockchain","title":"El motivo de los mecanismos de consenso en Blockchain","url":"https://metrosetenta.es/blog/el-motivo-de-los-mecanismos-de-consenso-en-blockchain/"},{"content":"Blockchain es una tecnología descentralizada que permite almacenar y compartir información de forma segura.\nAtendiendo a su diseño no existe un único actor con la capacidad de acceder a la información y gestionarla, por tanto, se debe establecer un mecanismo de consenso entre todos los actores capaces para realizar dichas acciones. Esto es lo que le otorga a Blockchain su caracter descentralizado.\nCuando se alcanza una determinada cantidad de información a almacenar, esta se empaqueta en un bloque junto un hash del contenido del último bloque existente. Este modo de ordenar y almacenar datos es lo que le otorga el nombre de cadena de bloques. También confiere seguridad pues, al modificar el contenido de cualquier bloque, el hash que hace referencia a dicho contenido en el bloque posterior será incorrecto y, por tanto, se detectará una manipulación no deseada.\n","id":18,"tag":"Blockchain","title":"Breve descripción de Blockchain","url":"https://metrosetenta.es/blog/breve-descripci%C3%B3n-de-blockchain/"},{"content":"El compromiso del equipo de React, desde la aparición de los componentes funcionales, siempre ha sido hacer que estos fueran más eficientes que los componentes basados en clases. De hecho, si le preguntas a cualquier desarrollador que use React, seguro que afirma convencido que son mucho más rápido y eficientes, aunque la realidad es que, en el momento de escribir estas líneas, apenas existe una diferencia de segundos entre componentes funcionales y componentes basados en clases. Teniendo en cuenta que esto es inapreciable para el usuario final, no deberías temer por el rendimiento de tu aplicación si necesitas usar algún componente basado en clases, a pesar de que los componentes funcionales son técnicamente más rápidos y eficientes.\n","id":19,"tag":"React","title":"Rendimiento de los componentes funcionales en React","url":"https://metrosetenta.es/blog/rendimiento-de-los-componentes-funcionales-en-react/"},{"content":"Si has usado React para desarrollar aplicaciones web seguro que ya sabes que cuando un componente es modificado, ésta biblioteca de código de Javascript crea un DOM virtual para compararlo con el DOM actual y, de este modo, calcular si es necesario actualizarlo. A este proceso de actualizar el DOM actual cuando es necesario, para mantenerlo sincronizado con el DOM virtual, se le llama Reconciliación.\nSi usáramos un algoritmo de vanguardia para calcular el número mínimo de operaciones necesarias para transformar un árbol de nodos como el DOM en otro, la complejidad sería del orden de O(n³), donde n es el número de elementos en el árbol. Esto nos lleva a que mostrar 1000 elementos se complica hasta el orden de unos mil millones de operaciones, lo que afectaría drásticamente al rendimiento y, por este motivo, se usa un algoritmo heurístico, el algoritmo diferencial, que se basa en dos suposiciones:\nDos elementos de diferentes tipos producirán diferentes árboles. El desarrollador puede insinuar qué elementos secundarios pueden ser estables en diferentes renders con una propiedad key. ","id":20,"tag":"React","title":"Reconciliación y algoritmo diferencial en React","url":"https://metrosetenta.es/blog/reconciliacion_y_algoritmo_diferencial_en_react/"},{"content":"Creo que no es la primera vez que te cuento que, habitualmente, en las empresas del sector los equipos se plataforman con sistemas operativos Windows. En Windows las líneas de código finalizan con un retorno de carro y un salto de línea, CRLF, pero en Linux y Mac, dichos finales se representan únicamente con saltos de línea, LF. En la gestión de un proyecto donde los programadores usan distintos sistemas operativos, o simplemente deseas mantener un tipo de fin de línea distinto al del sistema operativo que estas usando en el desarrollo, puede ser muy frustrante gestionar los cambios entre versiones de código. Esto se debe a que Git interpretará cambios en cada línea del código, pues donde había simplemente LF, tu editor colocó CRLF, y no podrás discernir los cambios reales. Git, sin embargo, dispone de una configuración para gestionar estos casos relativos a los finales de línea, core.autocrlf.\nPara ver la configuración actual en este parámetro debes usar el siguiente comando de Git:\ngit config --get core.autocrlf Puedes ajustar esta configuración con los valores true, input o false. A continuación te muestro como ajustarla al valor input.\ngit config core.autocrlf input Déjame que te indique a continuación como se comportan los comandos git checkout y git add con cada uno de los tres valores que puedes usar en esta configuración:\nSi lo configuras a true\ngit checkout convierte LF a CRLF git add convierte CRLF a LF Si lo configuras a input\ngit checkout no realiza ninguna conversión git add convierte CRLF a LF Si lo configuras a false\ngit checkout no realiza ninguna conversión git add no realiza ninguna conversión Como habrás deducido, este último valor, false, es el ajuste correcto para proyectos que únicamente se gestionarán en sistemas Windows.\n","id":21,"tag":"Git","title":"Gestión de final de línea en Git","url":"https://metrosetenta.es/blog/gestion_de_final_de_linea_en_git/"},{"content":"Para empezar, y como curiosidad, te cuento que en la abreviatura i18n el número 18 hace referencia a las letras que se encuentran entre la primera y última letra de la palabra internacionalitation.\nLa internacionalización de un proyecto de software es algo en cuya implementación no se suele prestar toda la atención requerida. Habitualmente sólo se gestiona el aspecto más básicos que permita mostrar los textos en distintos idiomas, y hay otros aspectos, como la gestión de la pluralización, que quedan olvidados.\nEn esta entrada, y alguna más que le seguirá, usaré Vue I18n para añadir la gestión de la internacionalización a un proyecto Vue. Como primer paso, por tanto, deberías acceder al directorio donde se encuentra tu proyecto Vue y añadir Vue I18n.\ncd proyecto-vue vue add i18n Tendrás que atender unas consultas en la consola; La primera de estas consultas es la sobre configuración regional que usará por defecto el proyecto. Luego debes indicar la configuración regional alternativa, el directorio donde se guardarán los archivos con las traducciones y si deseas activar cierta compatibilidad.\n? The locale of project localization. es ? The fallback locale of project localization. en ? The directory where store localization messages of project. It\u0026#39;s stored under `src` directory. locales ? Enable legacy API (compatible vue-i18n@v8.x) mode ? No Después vas a poder comprobar que hay varios cambios en el proyecto, por ejemplo, en el archivo main.js deberías ver algo parecido a esto:\nimport i18n from \u0026#39;./i18n\u0026#39; const app = createApp(App).use(i18n) Y el archivo vue.config.js debería contener algo parecido a esto:\nconst { defineConfig } = require(\u0026#39;@vue/cli-service\u0026#39;) module.exports = defineConfig({ pluginOptions: { i18n: { locale: \u0026#39;es\u0026#39;, fallbackLocale: \u0026#39;en\u0026#39;, localeDir: \u0026#39;locales\u0026#39;, enableLegacy: false, runtimeOnly: false, compositionOnly: false, fullInstall: true } } }) Comprueba también que ya dispones de una carpeta llamada locales en la carpeta src de tu proyecto. Aquí es donde guardaremos los archivos con las traducciones que utilizará nuestra aplicación.\n","id":22,"tag":"Vue","title":"Internacionalización de un proyecto Vue","url":"https://metrosetenta.es/blog/internacionalizacion-de-un-proyecto-vue/"},{"content":"En ocasiones te puede tocar trabajar en un proyecto cuyo repositorio cuenta ya con varios años de historia. Clonar este tipo de proyectos puede terminar en un error debido a la cantidad de datos a ser transferidos y, por tanto, es preferible hacer un clonado superficial. Un clonado superficial de un repositorio Git consiste en clonar únicamente parte de la historia del repositorio, y lo puedes hacer con una instrucción similar a la que sigue:\ngit clone urlProyectoAClonar --depth 1 Con este comando clonarías el último commit del repositorio en la url indicada. Esto supone un gran ahorro de espacio y el proyecto será más ligero de gestionar en local, pero es posible que no puedas resolver alguna mezcla que relacione a un commit anterior, ya que no está en tu local. Debido a esto último, es una buena idea clonar, al menos, los 50 o 100 últimos commits.\ngit clone urlProyectoAClonar --depth 50 Otra opción sería extraer los commits deseados:\ngit fetch --depth 50 Por último, tienes que saber que siempre dispones de la opción de deshacer el clonado superficial del repositorio.\ngit fetch --unshallow ","id":23,"tag":"Git","title":"Clonado Superficial de repositorio Git","url":"https://metrosetenta.es/blog/clonado-superficial-de-repositorio-git/"},{"content":"En muchas empresas es habitual plataformar únicamente el sistema operativo Windows en los equipos facilitados a los empleados, de modo que, si quieres disponer de las bondades de Linux para desarrollar tu trabajo, la opción más factible es usar el susbsitema Linux que Windows incluye, WSL. Desde su segunda versión WSL se ejecuta en una máquina virtual que intenta ser lo más liviana posible, pero que usa un archivo vhdx que crece con el uso de dicho subsistema y, aunque liberes espacio en el subsistema Linux, la realidad es que no vas a recuperar espacio de tu disco duro si no manipulas directamente el archivo vhdx. Lo primero que necesitas es saber dónde se encuentra dicho archivo. En el caso de que estés usando una distribución Ubuntu, la dirección será similar a esto:\nC:\\Users\\metroSetenta\\AppData\\Local\\Packages\\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\\LocalState\\ext4.vhdx A continuación abre la aplicación Command Prompt o Powershell asegurándote de hacerlo con privilegios de administrador. Lo más sencillo sería ejecutar el siguiente comando desde el directorio donde se encuentre el archivo ext4.vhdx:\noptimize-vhd -Path .\\ext4.vhdx -Mode full Pero podrias encontrarte con un mensaje como el que sigue:\nOptimize-Vhd : The term \u0026#39;Optimize-Vhd\u0026#39; is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. No hay problema, en ese caso, ejecuta la siguiente secuencia de comandos sin olvidar usar la dirección donde se encuentra tu archivo ext4.vhdx:\nwsl --shutdown diskpart # open window Diskpart select vdisk file=\u0026#34;C:\\Users\\metroSetenta\\AppData\\Local\\Packages\\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\\LocalState\\ext4.vhdx\u0026#34; attach vdisk readonly compact vdisk detach vdisk exit ","id":24,"tag":"WSL","title":"Liberar espacio de WSL","url":"https://metrosetenta.es/blog/liberar-espacio-de-wsl/"},{"content":"En informática, este término hace referencia a un contenedor abstracto de identificadores únicos. Los lenguajes que gestionan espacios de nombres especifican las reglas que determinan a que espacio de nombre pertenece un identificador, de modo que pueden manejar grandes cantidades de estos sin que haya colisión entre ellos, es decir, asegurándose de que son identificadores únicos, a pesar de que algunos de ellos usen la misma designación.\nTe explico mejor con un ejemplo: Dos personas, Rebeca y Andrés, que pertenecen a empresas distintas, pueden tener un mismo número de empleado, digamos el 456. En este caso la empresa representaría el espacio de nombre, y 456, el número de empleado, representaría el identificador único. Aunque se trata de la misma designación para ambos empleados, 456, no existe colisión porque Rebeca es el empleado 456 de la empresa A, y Andrés es también el 456, pero en la empresa B.\n","id":25,"tag":"Conceptos básicos","title":"Espacio de nombres","url":"https://metrosetenta.es/blog/espacio-de-nombres/"},{"content":"En el ámbito de los sistemas de información, el dominio del problema es el conjunto de conceptos, interrelacionados entre sí, que se necesita conocer para comprender el negocio del cliente. Es decir, son todos aquellos conocimientos que están implicados en la correcta comprensión de la necesidad del cliente y, por tanto, son necesarios para proponer una correcta solución. Un buen ejemplo podría ser el dominio del problema al desarrollar un aplicación para una clínica médica privada, donde habría de manejar correctamente conceptos como póliza, paciente, cita, diagnóstico\u0026hellip;\n","id":26,"tag":"Fundamentos arquitectura","title":"Dominio del problema","url":"https://metrosetenta.es/blog/dominio-del-problema/"},{"content":"Es habitual, cuando se aprende un lenguaje de programación, escribir una pequeña aplicación mostrando el mensaje \u0026ldquo;¡Hola mundo!\u0026rdquo;. En el caso de Deno, vamos a hacer algo un poco diferente para ejecutar tu primer programa con esta tecnología. Ya te conté aquí que Deno ofrece un servicio de repositorios a través de https://deno.land/x/, y vamos a usar un programa ejemplo que tienen publicado con el nombre de welcome. ¡Para que Deno lo descarge, lo compile y lo ejecute, sólo necesitamos escribir una línea!:\n$ deno run https://deno.land/std@0.87.0/examples/welcome.ts Download https://deno.land/std@0.87.0/examples/welcome.ts Compile https://deno.land/std@0.87.0/examples/welcome.ts Welcome to Deno! Por supuesto que ejecutar código de manera arbitraria desde cualquier URL de internet no es una práctica recomendable, pero no olvides que con Deno disponemos de un entorno aislado del resto del sistema operativo donde es posible ajustar distintos tipos de permisos para los ejecutables, es decir, Deno dispone de un sandbox integrado.\nEl programa que hemos ejecutado es realmente sencillo, tan solo una llamada a console.log().\nconsole.log(\u0026#39;Welcome to Deno 🦕\u0026#39;) Si abres en el navegador la URL que hemos utilizado no verás el código fuente, porque la web de Deno reconoce tu navegador y sirve dicho código de un modo más amable para nosotros. Puedes descargarlo con, por ejemplo wget, que solita la versión text/plain en lugar de la versión text/html.\n$ wget https://deno.land/std@0.87.0/examples/welcome.ts --2021-02-13 23:36:09-- https://deno.land/std@0.87.0/examples/welcome.ts Loaded CA certificate \u0026#39;/etc/ssl/certs/ca-certificates.crt\u0026#39; Resolving deno.land (deno.land)... 104.21.18.123, 172.67.181.211, 2606:4700:3031::ac43:b5d3, ... Connecting to deno.land (deno.land)|104.21.18.123|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 33 [application/typescript] Saving to: ‘welcome.ts’ welcome.ts 100%[===============================\u0026gt;] 33 --.-KB/s in 0s 2021-02-13 23:36:10 (34.3 MB/s) - ‘welcome.ts’ saved [33/33] Si volvieras a ejecutar el programa, como es algo que ya hiciste anteriormente, y Deno cacheó dicho programa, verás que en esta ocasión no lo descargará.\n$ deno run https://deno.land/std@0.87.0/examples/welcome.ts Welcome to Deno! En todo caso, si quieres que lo vuelva a descargar para asegurarte de que ejecuta la última versión publicada, podrías usar la opción \u0026ndash;reload o -r.\n$ deno run --reload https://deno.land/std@0.87.0/examples/welcome.ts Download https://deno.land/std@0.87.0/examples/welcome.ts Compile https://deno.land/std@0.87.0/examples/welcome.ts Welcome to Deno! Deno run tiene, además de esta, muchísimas opciones más. Para conocerlas ejecutar el comando deno run \u0026ndash;help.\n$ deno run --help deno-run Run a program given a filename or url to the module. By default all programs are run in sandbox without access to disk, network or ability to spawn subprocesses. deno run https://deno.land/std/examples/welcome.ts Grant all permissions: deno run -A https://deno.land/std/http/file_server.ts Grant permission to read from disk and listen to network: deno run --allow-read --allow-net https://deno.land/std/http/file_server.ts Grant permission to read whitelisted files from disk: deno run --allow-read=/etc https://deno.land/std/http/file_server.ts USAGE: deno run [OPTIONS] \u0026lt;SCRIPT_ARG\u0026gt;... OPTIONS: -A, --allow-all Allow all permissions --allow-env Allow environment access --allow-hrtime Allow high resolution time measurement --allow-net=\u0026lt;allow-net\u0026gt; Allow network access --allow-plugin Allow loading plugins --allow-read=\u0026lt;allow-read\u0026gt; Allow file system read access --allow-run Allow running subprocesses --allow-write=\u0026lt;allow-write\u0026gt; Allow file system write access --cached-only Require that remote dependencies are already cached --cert \u0026lt;FILE\u0026gt; Load certificate authority from PEM encoded file -c, --config \u0026lt;FILE\u0026gt; Load tsconfig.json configuration file -h, --help Prints help information --importmap \u0026lt;FILE\u0026gt; UNSTABLE: Load import map file Docs: https://deno.land/std/manual.md#import-maps Specification: https://wicg.github.io/import-maps/ Examples: https://github.com/WICG/import-maps#the-import-map --inspect=\u0026lt;HOST:PORT\u0026gt; activate inspector on host:port (default: 127.0.0.1:9229) --inspect-brk=\u0026lt;HOST:PORT\u0026gt; activate inspector on host:port and break at start of user script --lock \u0026lt;FILE\u0026gt; Check the specified lock file --lock-write Write lock file. Use with --lock. -L, --log-level \u0026lt;log-level\u0026gt; Set log level [possible values: debug, info] --no-remote Do not resolve remote modules -q, --quiet Suppress diagnostic output By default, subcommands print human-readable diagnostic messages to stderr. If the flag is set, restrict these messages to errors. -r, --reload=\u0026lt;CACHE_BLACKLIST\u0026gt; Reload source code cache (recompile TypeScript) --reload Reload everything --reload=https://deno.land/std Reload only standard modules --reload=https://deno.land/std/fs/utils.ts,https://deno.land/std/fmt/colors.ts Reloads specific modules --seed \u0026lt;NUMBER\u0026gt; Seed Math.random() --unstable Enable unstable APIs --v8-flags=\u0026lt;v8-flags\u0026gt; Set V8 command line options. For help: --v8-flags=--help ARGS: \u0026lt;SCRIPT_ARG\u0026gt;... script args ","id":27,"tag":"Javascript","title":"Hola mundo al estilo Deno","url":"https://metrosetenta.es/blog/hola-mundo-al-estilo-deno/"},{"content":"Hay varios motivos por los cuales podrías querer traer la información de un repositorio de código, versionado con Subversion, a tu repositorio de código versionado con Git, desde sincronizar ambos repositorios durante un periodo de transición de gestor de versiones hasta, simplemente, aprovechar las características de git para la gestión de ramas antes de subir nuevo código al repositorio de Subversion. Estos motivos son los que justifican la existencia de git-svn, una característica de Git que nos proporciona comandos para gestionar un flujo bidireccional entre Git y Subversion.\nLo más común es tirar de todo el árbol de Subversion con el siguiente comando:\n$ git svn clone https://miRepositorio.com/repositorios/projecto -T trunk -b branches -t tags Pero lo que te quiero mostrar es como puedes hacer cuando no quieres traer toda la información, sino que quieres traer y trabajar únicamente con una rama del repositorio Subversion. Para ello, debes saber que Git almacena una lista de los repositorios remotos en el archivo .git/config, incluyendo aquellos que has configurado con el comando que te acabo de mostrar arriba, y puedes verlos, una vez que has usado el comando fetch, usando este otro comando que muestras todas tus ramas, tanto locales como remotas.\n$ git branch -a Sabiendo esto, puedes deducir que añadiendo la configuración que nos interesa en dicho archivo, podremos gestionar, desde git, la rama que nos interesa del repositorio Subversion sin tener que hacerle seguimiento a todas. Abajo te muestro un ejemplo para configurar la rama entregas, pero ten en cuenta que la URL que tú necesites dependerá de la estructura de tu repositorio Subversion.\n[svn-remote \u0026#34;entregas\u0026#34;] url = https://miRepositorioSubversion.com/repositorios/projecto/ramas/entregas fetch = :refs/remotes/subversion/entregas Con esto añades a Git una configuración para gestionar una rama de un repositorio remoto Subversion, con un referencia, entregas, sobre la que poder usar el comando git svn fetch.\n$ git svn fetch entregas Si vuelves a comprobar la lista de ramas con git branch -a deberías ver algo similar esto:\n$ git branch -a master netlify * writting remotes/gitlab/master remotes/gitlab/netlify remotes/gitlab/writting remote/subversion/entregas ¡Observa que nuestra nueva rama remota está en la última línea de la salida del comando!\n","id":28,"tag":"Git","title":"Configurar en Git un repositorio remoto Subversion","url":"https://metrosetenta.es/blog/configurar-en-git-un-repositorio-remoto-subversion/"},{"content":"Son muchísimos los proyectos que, en el momento de escribir este artículo, utilizan Maven para gestionar su gestión de dependencias su construcción. Maven permite, además, la encriptación y gestión de contraseñas, lo cual es muy útil para poder acceder a repositorios privados, como los gestionados con Nexus, que pueden requerir autorización. Para esto, únicamente necesitas crear un par de archivos en la carpeta ${user.home}/.m2, y cuyos nombres serán settings-security.xml y settings.xml.\nVamos a comenzar con settings-security.xml, que es el que va a contener una contraseña maestra de nuestra elección. Podemos crearla con el comando mvn de Maven.\nmvn --encrypt-master-password \u0026lt;password\u0026gt; Omite el parámetro password si estás usando una versión de Maven superior a la 3.2.1. Una vez ejecutado el comando obtendrás una versión encriptada de tu contraseña maestra. Debería tener un aspecto parecido a este:\n{jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ$} Es el momento de almacenar esta contraseña en ${user.home}/.m2/settings-security.xml.\n\u0026lt;settingsSecurity\u0026gt; \u0026lt;master\u0026gt;{jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ$}\u0026lt;/master\u0026gt; \u0026lt;/settingsSecurity\u0026gt; Y una vez que tenemos esto configurado, ya podemos guardar las credenciales que usaremos para identificarnos en el servidor privado de artefactos. Para hacer esto, usaremos el comando que se muestra a continuación, y en donde el parámetro password tiene la misma peculiaridad que en comando anterior.\nmvn --encrypt-password \u0026lt;password\u0026gt; De igual manera que antes recibiremos una versión encriptada de nuestras credenciales.\n{COQLCE6DU6GtcS5P$} En esta ocasión, la vamos a usar dentro del archivo ${user.home}/.m2/settings.xml.\n\u0026lt;settings\u0026gt; \u0026lt;servers\u0026gt; \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;mi_servidor\u0026lt;/id\u0026gt; \u0026lt;username\u0026gt;mi_usuario\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;{COQLCE6DU6GtcS5P$}\u0026lt;/password\u0026gt; \u0026lt;/server\u0026gt; \u0026lt;/servers\u0026gt; \u0026lt;/settings\u0026gt; ","id":29,"tag":"Maven","title":"Configurar credenciales de repositorios privados en Maven","url":"https://metrosetenta.es/blog/configurar-credenciales-de-repositorios-privados-en-maven/"},{"content":"Ya sabes que Ionic se integra perfectamente con los tres principales frameworks de front en el momento de escribir este artículo, Angular, React y, recientemente, con Vue.js. Centrándonos en Ionic React, habría que destacar varios aspectos. En definitiva se trata de React, y esto significa que usa los estándares abiertos de la web y las capacidades integradas en los navegadores, por tanto es compatible com millones de bibliotecas de código web. Ionic React cotinene más de una centena de componentes de interfaz de usuario optimizados para ser utilizados con React, usa la pila estandar de herremientas de React, como react-dom o react-router, y puedes preparar tu aplicación para ser utilizada en iOS, Android, Electron y PWA.\nCon únicamente tres comandos puedes instalar Ionic, crear un proyecto React y servirlo en tu máquina local:\n$ npm install -g @ionic/cli $ ionic start myApp tabs --type react $ ionic serve ¡Toda la documentación necesaria para continuar la tienes aquí!\n","id":30,"tag":"Ionic ; JavaScript ; React","title":"Ionic React","url":"https://metrosetenta.es/blog/ionic-react/"},{"content":"Ionic se define a sí mismo como un entorno de trabajo con el que puedes crear aplicaciones móviles y aplicaciones web progresivas de alto rendimiento. Su filosofía está muy basada en la web, pues entiende que es el entorno de ejecución más probado y estable que existe, de modo que cualquier desarrollador que tenga conocimientos de HTML, JavaScript y CSS pude desarrollar aplicaciones para distintas plataformas y dispositivos. Es más, Ionic puede ser usado con los entornos de trabajo de Javascript más utilizados, Angular, React y, recientemente, con Vue.js. La posibilidad de usar las capacidades nativas de un dispositivo, como podría ser la cámara del teléfono inteligente, se añaden gracias al uso de Apache Cordova o Capacitor.\nSu competidor más directo, posiblemente, sea Flutter, aunque este segundo tiene una filosofía muy distinta, pues las aplicaciones de Flutter se escriben con Dart, que posteriormente puede ser compilado a Javascript.\n","id":31,"tag":"Ionic ; JavaScript","title":"Qué es Ionic","url":"https://metrosetenta.es/blog/que-es-ionic/"},{"content":"El operador Elvis es una abreviatura del operador ternario. Un caso en el que suele utilizarse de manera habitual es para devolver un valor que es sensible por defecto cuando una expresión se resuelve como false. Un sencillo ejemplo:\n// Con el operador ternario, tienes que repetir el valor a asignar mostrarNombre = usuario.nombre? usuario.nombre: \u0026#39;Anónimo\u0026#39; //Con el operador de Elvis, el valor que se prueba y se usa si no es falso mostrarNombre = usuario.nombre?: \u0026#39;Anónimo\u0026#39; El uso del operador Elvis reduce la verbosidad de tu código y los riesgos de errores, en caso de refactorizaciones, al eliminar la necesidad de duplicar la expresión que se prueba, tanto en la condición, como en el valor de retorno positivo.\n","id":32,"tag":"Groovy","title":"El operador Elvis de Groovy","url":"https://metrosetenta.es/blog/el-operador-elvis-de-groovy/"},{"content":"Una imagen Docker es una plantilla o esquema a partir del cual podemos generar un contenedor Docker, es decir, podemos generar un entorno aislado que está diseñado para ejecutar una o más apliaciones de forma óptima. La imágenes se crean a partir de un archivo, al que se denomina Dockerfile, usando el comando docker build. Una vez que has generado tu imagen puedes almacenarla en repositorio de imágenes Docker. En Docker Hub, por ejemplo, puedes almacenar tus imágenes de manera privada o pública. Si visitas Docker Hub verás que puedes usar muchísimas imágenes de forma gratuita sin tener que crear tus propias imágenes. Muchas de estas imágenes son oficiales, desarrolladas y distribuidas por equipos desarrolladores de una tecnología específica. Desde bases de datos a herramientas DevOps, pasando por servicios de mensajería e, incluso, sistemas operativos, tienen una imagen preparada para ser descargada y usada en Docker Hub. Puedes disponer rápidamente de una instancia de Jenkins descargando su imagen oficial\u0026hellip;\n$ docker pull jenkins \u0026hellip;y luego creando el contenedor con el comando docker run, que se encargará de, usando la imagen Docker para Jenkins recién descargada, crear el sistema de archivos, iniciar las dependencias necesarias y todas las demas gestiones necesarias para ejecutar jenkins en un contenedor Docker.\n","id":33,"tag":"Docker","title":"Qué es una imagen Docker","url":"https://metrosetenta.es/blog/que-es-una-imagen-docker/"},{"content":"¿Te imaginas una clase de Groovy a la que le puedes añadir, en tiempo de ejecución, propiedades y métodos? Pues eso es lo que te ofrece su clase Expando, cuyo uso es muy similar al modo en el que, en JavaScript, se usan objetos instanciando la variable Object.\n// Define la classe Expando con dos propiedades: def blog = new Expando(titulo : \u0026#34;metroSetenta\u0026#34;, subtitulo : \u0026#34;Arquitectura y diseño de software\u0026#34;) // Añade una nueva propiedad a Expando: blog.url=\u0026#34;metrosetenta.es\u0026#34; // Incluye un método con un cierre: blog.toString={ return \u0026#34;\u0026#34;\u0026#34; Título del blog: ${titulo} Subtítulo del blog: ${subtitulo} URL del blog: ${url} \u0026#34;\u0026#34;\u0026#34; } println (blog) Observa en el pequeño ejemplo de arriba que el constructor usa una notación similar a los mapas Groovy y, que los métodos, son implementados usando cierres (closures). Puedes probar este pequeño ejemplo guardándolo en un archivo para ejecutarlo con el comando groovy.\n$ groovy expandoText.groovy Título del blog: metroSetenta Subtítulo del blog: Arquitectura y diseño de software URL del blog: metrosetenta.es Este pequeño artículo se lo quiero dedicar con cariño a mi compañero Fran, que fue quien me mostró la clase Expando de Groovy.\n","id":34,"tag":"Groovy","title":"La clase Expando de Groovy","url":"https://metrosetenta.es/blog/la-clase-expando-de-groovy/"},{"content":"Los patrones de diseño creacionales son aquellos que nos proponen soluciones para problemas relacionados con la instanciación de objetos. Automatizar el proceso de construcción de un objeto complejo, limitar la instaciación de una clase a un único objeto al que se pueda acceder de manera global o usar un objeto como prototipo para crear otros objetos similares son algunos de los usos de los patrones de diseño creacionales.\nFactory Method (Método de fabricación): Si necesitamos un objeto de un tipo, pero desconocemos cual es el objeto concreto que vamos a necesitar en el momento del diseño, este es nuestro patrón. Donde dije tipo puedes poner, por ejemplo, conexión a base de datos, y puedes sustituir objeto concreto por conexión a Oracle Database, conexión a MySQL, conexión a PostgreSQL\u0026hellip;\nAbstract Factory (Fábrica abstracta): Si agrupamos los objetos que tienen una cualidad común obtenemos una familia de objetos. Pues bien, este patrón nos ayuda cuando necesitamos instaciar toda una familia de objetos pero no sabemos cual es la familia concreta en el momento del diseño. Un buen ejemplo de familia de objetos son los elementos de una interfaz gráfica (ventana, menú, botón\u0026hellip;), y podemos diseñar una familia concreta para cada sistema operativo que queramos soportar.\nSingleton (Instancia única): Sirve para limitar una clase a una única instanca a la que es posbible acceder de forma global. Piensa en la configuración de una aplicación, que es necesaria utilizar desde varias clases, y ya no hay que instanciar cada vez que se consulte. Se crea una única instancia de la configuración y a compartir.\nBuilder (Constructor virtual): Abstrae el proceso de creación de un objeto complejo. Cuando tienes que crear muchas veces un tipo de objeto, cuando dicho tipo de objeto tiene muchisimos atributos que ajustar o cuando gestionar sus muchos métodos constructores es difícil, este patrón aplica perfectamente.\nPrototype (Prototipo): Se trata de crea nuevos objetos clonándolos de una instancia ya existente. El patrón perfecto para crear objetos con sutiles diferencias con otro ya creado, como copiar y pegar un círculo en un programa de dibujo.\nObject Pool (Grupo de objetos): Se trata de crear un almacén de objetos reutilizables, de un determinado tipo. Puede contar con varias opciones de gestión, como limitar el número de objetos disponibles, la cantidad de usos, la operativa a seguir al solicitar un objeto del almacén si todos están siendo utilizados\u0026hellip; Este patrón no se encuentra entre los patrones del GoF.\nModel View Controller o MVC (Modelo Vista Controlador). Este patrón es de arquitectura, no de diseño, y plantea una estructura de tres capas. La primera, la capa modelo, es la que representa los objetos que gestiona la aplicación. El usuario usa la segunda capa, la vista, para visualizar dichos objetos y para hacer peticiones respecto a ellos, interactuando con la aplicación. La tercera y última capa, la capa controlador, recibe y realiza lo que el usuario quiere hacer, y es el encargado de comunicar las dos capas anteriores, ejecutando en el modelo las peticiones que le llegan desde la vista y, posteriormente, informando al usuario, mediante la capa vista, del resultado de sus peticiones.\n","id":35,"tag":"Patrones creacionales ; Patrones de diseño ; Patrones de arquitectura","title":"Patrones creacionales","url":"https://metrosetenta.es/blog/patrones-creacionales/"},{"content":"En ocasiones necesitas conocer la evolución de una petición o tarea en Redmine, en la que no estás involucrado, mediante notificaciones por correo electrónico. Es posible hacerlo porque Redmine te facilita la posibilidad de monitorizar peticiones o tareas. Para ello, dirígete a la petición a la que deseas hacer seguimiento y, en la parte inferior, al final del cuerpo de la petición, pulsa en el enlace Monitorizar que se encuentra junto al icono de la estrella.\nEs importante que en tu cuenta, en la configuración de notificaciones, selecciones objetos de los cuales eres seguidor.\n","id":36,"tag":"Redmine","title":"Monitorizar peticiones en Redmine","url":"https://metrosetenta.es/blog/monitorizar-peticiones-en-redmine/"},{"content":"Hoy voy a ampliar los artículos sobre Deno que hay en el blog comentandote los subcomandos disponibles en esta herramienta. Si ya has instalado y configurado Deno, y dispones del comando deno en tu consola, el primer subcomando que debes probar es help.\n$ deno help Entre otras cosas, te va a facilitar un listado de subcomandos similar al que sigue:\nbundle Bundle module and dependencies into single file cache Cachea las dependencias completions Genera autocompletados de la consola de comandos doc Muestra la documentación para un módulo eval Evalúa un script fmt Formatea archivos fuente help Imprime la ayuda general o la de un subcommando dado info Muestra información sobre el caché o información relacionada con un archivo fuente install Instalar script como un ejecutable lint Aplicar resaltado de código a archivos fuente repl Bucle leer, evaluar e imprimir run Ejecuta un programa desde un nombre de archivo o una URL. Usa \u0026#39;-\u0026#39; como nombre de archivos para leer de la entrada estandar test Ejecuta las pruebas types Imprime las declaraciones de TypeScripts en tiempo de ejecución upgrade Actualiza el ejecutable deno a una versión dada Algo más sobre el subcomando help es que si lo usas con cualquiera de los subcomandos listados más arriba, te facilitará información más detallada sobre dicho subcomando y su sobre uso. Por ejemplo, si queremos saber algo más sobre el subcomando eval, podemos ejecutar el siguiente comando:\n$ deno help eval Y Deno nos facilitará una información más detallada sobre el subcomando eval.\ndeno-eval Evalua JavaScript desde la línea de comandos. deno eval \u0026#34;console.log(\u0026#39;¡Hola mundo!\u0026#39;)\u0026#34; Para evaluar como TypeScript: deno eval -T \u0026#34;const v: string = \u0026#39;hello\u0026#39;; console.log(v)\u0026#34; Este comando tiene acceso implícito a todos los permisos (--allow-all). USO: deno eval [OPCIONES] \u0026lt;código\u0026gt; OPCIONES: --cert \u0026lt;ARCHIVO\u0026gt; Cargar autoridades certificadoras desde un archivo PEM codificado -h, --help Imprimir información de ayuda --inspect=\u0026lt;HOST:PORT\u0026gt; Activar inspector en host:port (por defecto: 127.0.0.1:9229) --inspect-brk=\u0026lt;HOST:PORT\u0026gt; Activar inspector en host:port host:port con break al comienzo del script del usuario -L, --log-level \u0026lt;nivel log\u0026gt; Ajustar el nivel de log [Valores posibles: debug, info] -p, --print Imprimir resultados a la salida estandar -q, --quiet Suprimir salida de diagnóstico -T, --ts Tratar la entrada dada a eval como Typescript --unstable Habilitar APIs inestables --v8-flags=\u0026lt;opciones V8\u0026gt; Ajustar las opciones V8 de la línea de comandos. Para más ayuda: --v8-flags=--help ARGUMENTOS: \u0026lt;código\u0026gt; ","id":37,"tag":"JavaScript ; Deno","title":"Subcomandos de Deno","url":"https://metrosetenta.es/blog/subcomandos-de-deno/"},{"content":"Docker es, posiblemente, la herramienta que revolucionó el modo en el que desplegamos y distribuimos aplicaciones. Asegurarnos de que el entorno en el que se van a ejecutar nuestras aplicaciones es óptimo para ellas ha sido siempre un esfuerzo asociado al desarrollo de software. El uso de máquinas virtuales supuso el acercamiento más satisfactorio antes de Docker, pero el rendimiento se veía comprometido por el esfuerzo que suponía, para el sistema operativo huesped, levantar y gestionar un sistema operativo completo en el que ajustar el entorno deseado y ejecutar nuestra aplicación. Con Docker, ejecutamos nuestras aplicaciones en entornos aislados, contenedores, que si bien no son máquinas virtuales, se asemejan muchísimo, pero con la ventaja de aprovechar nuestro sistema operativo todo lo posible en lugar de tener que gestionar un completo sistema operativo inquilino.\nLa idea principal detrás de Docker consiste en diseñar un esquema de un entorno ideal para nuestra aplicación y el modo en el que ésta se ejecuta en él usando los recursos mínimos necesarios. A este esquema lo llamamos imagen, y cualquier desarrollador que disponga de nuestra imagen puede usar Docker para instanciarla, como un contenedor, del modo que hemos mencionado en el párrafo anterior. Esto significa que podemos distribuir una imagen de nuestra aplicación con la certeza de que funcionará en cualquier máquina que disponga de Docker.\nLos beneficios son numerosos, desde poder ejecutar varias versiones de la misma aplicación en nuestro sistema operativo a evitar tener que dar soporte a todos los servidores, o configuraciones posibles, que los usuarios de nuestras aplicaciones puedan utilizar. Simplemente hay que poner una imagen de Docker a la disposición de los usuarios de nuestras apliaciones, en un repositorio propio o en un repositorio de imágenes de Docker. Esto abre, además, nuevas posibilidades de distribución.\nDocker es el asesino de la frase \u0026ldquo;Pues en mi máquina no funciona\u0026hellip;\u0026rdquo;\n","id":38,"tag":"Docker","title":"¿Qué es Docker?","url":"https://metrosetenta.es/blog/que-es-docker/"},{"content":"Los patrones de diseño son un concepto que empezó a manejar el arquitecto britano-americano Christopher Alexander en 1966. Mucho más tarde, en 1994, con la publicación de Design Patterns: Elements of Reusable Object-Oriented Software, dicho concepto comenzó a ganar popularidad en el mundo del desarrollo de software. Este libro fué escrito por cuatro autores que posteriormente serían conocidos como la pandilla de los cuatro (Gang of Four): Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides. Los patrones de diseño aportan soluciones generales y reutilizables para problemas comunes. No se trata de porciones de código, sino de algo más genérico: La descripción de un problema y de una solución, que ha sido probada, a ese problema. Comprender un patrón, identificar cuando es útil, aplicarlo y adaptarlo a un caso concreto es tarea de aquel que lo estudia. El uso adecuado de estos patrones, por tanto, puede acelerar el proceso de diseño y desarrollo de software, pues al aportar paradigmas que han sido probados como eficaces evitan invertir tiempo en buscar una solución partiendo desde cero.\nOriginalmente, los patrones de diseño fueron categorizados en 3 grupos:\nPatrones creacionales: Tratan la creación de objetos de un modo controlado basandose en unos criterios requeridos. Patrones estructurales: Tratan la organización de distintas entidades software para crear estructuras mayores, extensibles y desacopladas entre sí. Patrones de comportamiento: Tratan la asignación de responsabilidades entre distintas entidades software y como éstas se comunican entre sí. Hay que mencionar también que existen patrones de diseño para dominios específicos, como pueden ser la visualización de información, la seguridad, el diseño web\u0026hellip; El grupo Hillside viene realizando, desde hace años, unas conferencias anuales sobre los patrones de diseño de software, dónde se contemplan estos patrones específicos para un dominio.\n","id":39,"tag":"Patrones de diseño","title":"Patrones de diseño","url":"https://metrosetenta.es/blog/patrones-de-diseno/"},{"content":"Al espacio de memoria donde Java almacena objetos se denomina heap. El heap se crea cuando la maquina virtual de java, JVM, se inicia, y puede aumentar o disminuir de tamaño durante la ejecución de la aplicación. Cuando se llena este espacio de memoria Java inicia el proceso de recolección de basura, Garbage Collector. Durante la recogida de basura, los objetos que ya no se utilizan son eliminados, creando así espacio para nuevos objetos. La JVM puede iniciarse con unos parámetros para gestionar la memoria. Con Xmx configuramos el tamaño máximo del heap y con Xms el tamaño mínimo. La JVM tiene, también, un espacio permanente para alojar objetos, el Permanent Generation o Metaspace desde Java 8. Será el espacio donde se alojan las clases y los métodos. Un ejemplo de configuración de estos parámetros sería como sigue:\n-Xms256m -Xmx1024m -XX:PermSize=512m -XX:MaxPermSize=512m ","id":40,"tag":"Java","title":"Gestión de memoria de la máquina virtual de Java","url":"https://metrosetenta.es/blog/gestion-de-memoria-de-la-maquina-virtual-de-java/"},{"content":"origin es la palabra con la que git nombra por defecto un repositorio remoto cuando lo clonas. Renombrar este repositorio como, por ejemplo, gitlabMetroSetenta es una buena idea, porque origin parece una palabra reservada de Git y puede causar confusiones. Para comprobar con detalle los repositorios remotos que has configurado en un proyecto git, usa el siguiente comando:\ngit remote -v Para renombrar un repositorio remoto origin como miRepositorioRemoto puedes usar este otro:\ngit rename origin miRepositorioRemoto ","id":41,"tag":"Git","title":"Renombrar un repositorio remoto en Git","url":"https://metrosetenta.es/blog/renombrar-un-repositorio-remoto-en-git/"},{"content":"Vamos a mejorar un poco las búsqueda de porciones de código dentro de nuestros proyectos y, para ello, vamos a comenzar por la entrada de datos que le damos a fzf. Te presento a ripgrep, una herramienta de búsquedas que tiene un rendimiento increible en comparación con otras parecidas como ugrep o ack. Ripgrep usa el archivo .gitignore por defecto, lo que le dá un enfoque muy Desarrollo para la búsqueda de código. Aquí tienes los diferentes métodos de instalación. Una vez que hayas aplicado el mejor para tu máquina, prueba a ejecutar, por ejemplo, el comando rg return en la carpeta de uno de tus proyectos, donde rg ejecutará ripgrep y return será el criterio de búsqueda. ¡A mí me pareció impresionante, especialmente, por su velocidad!\nEn Vim, gracias a fzf.vim, puedes darle esta entrada de datos a fzf, con esta capacidad de configuración y velocidad, usando el comando Rg. Seguro que te parece una alternativa mejor a la que te conté en este artículo. Aquí te comenté como instalar fzf.vim.\nEs el turno ahora de mejorar las búsquedas de archivos dentro del proyecto. Si compruebas el listado de comandos disponibles con fzf.vim, verás que Files ejecuta el comando que esté definido en la variable de entorno $FZF_DEFAULT_COMMAND y, en su defecto, ejecutará el comando FZF. Un modo de mejorar nuestra búsqueda de archivos es ajustando dicha variable en la configuración de nuesto Shell para que use ripgrep.\nexport FZF_DEFAULT_COMMAND=\u0026#39;rg --files --follow --hidden\u0026#39; Y ahora sólo resta añadir o modificar nuestro mapa de teclas en la configuración de Vim para usar el comando Files:\nnnoremap \u0026lt;C-p\u0026gt; :Files \u0026lt;CR\u0026gt; ","id":42,"tag":"Vim","title":"Mejorando las búsquedas en Vim","url":"https://metrosetenta.es/blog/mejorando-las-busquedas-en-vim/"},{"content":"Pues nada, que andaba probando programas para crear gifs y, como no me convencieron los que probé del repositorio oficial de Manjaro, pues busqué alguno en AUR, el repositorio de usuarios de Arch. En la interfaz gráfica de pamac pulsé la opción para refrescar las base de datos e instalar las actualizaciones pendientes. Desde ese momento, cualquier intento de instalación de cualquier software, utilizando los repositorios de AUR, me devolvían el siguiente error:\ntarget not found Lo cierto es que nunca he necesitado cacharrear en profundidad con los gestores de paquetes de software y, por este motivo, me costó dar con el problema y solucionarlo. Desde la interfaz gráfica de pamac no tenía ninguna opción para ayudarme a dar con la solución, así que, con ayuda de la documentación, probé a instalar paquetes del AUR usándo pamac desde la consola. Así fué como encontré la siguiente pista.\nERROR: PKGBUILD contains CRLF characters and cannot be sourced. Este mensaje ya me resultaba más familiar. Soy usuario de Linux y, muy a mi pesar, en casi todas las empresas de mi ciudad se utiliza Windows como sistema operativo, que usa una secuencia de control de caracteres, para indicar un final de línea, muy distinto al de los sistemas Unix. Al compartir código usando repositorios de Git es habitual usar la siguiente configuración para que Git no considere, basándose en esta secuencia de control de caracteres, que todas las líneas contienen cambios.\nautocrlf = true Esto se usa para convertir los caracteres LF con los que los sistemas Unix finalizan las líneas de código en los caracteres CRLF propios de Windows. Como AUR podría entenderse como un repositorio de scripts que se descargan habitualmente con Git, estaba claro que, con mi configuración de Git, había cambiado los finales de línea a CRLF al descargar las actualizaciones. Mi equipo Unix estaba intentando ejecutar uno de estos scripts y siempre resultaba en una salida de error. Se trataba, ahora, de localizar los archivos con los finales de línea incorrectos y eliminarlos para descargar de nuevo los scripts correctos. Antes, por supuesto, reajusté la configuración de Git.\nautocrlf = false En mi distribución, los archivos incorrectos estaban descargados en /var/tmp/pamac-build-javier/, así que los borré todos con el comando de abajo. Ten en cuenta que tu dirección será distinta a no ser que tu usuario, en tu sistema operativo, también sea javier.\n$ rm /var/tmp/pamac-build-javier/* -Rf ¡Y eureka, despues de un par de horas de investigación, ya podía volver a instalar software desde el AUR usando pamac!\n","id":43,"tag":"Equipo","title":"Target Not Found en Pamac","url":"https://metrosetenta.es/blog/target-not-found-en-pamac/"},{"content":"En éste artículo ya te conté que es fzf y como usarlo en Vim para buscar archivos dentro de tu proyecto, pero lo cierto es que podemos aplicarlo para muchísimos más usos. Mediante fzf.vim puedes tener configurados varios comandos en Vim, sin estar familiarizado con Vimscript, que ponen a tu disposición las aplicaciones más comunes de fzf en este editor de texto.\nComo siempre, si usas el gestor de conectables vim-plug, que aprendimos a instalar aquí, es muy sencillo conectar fzf.vim. La primera línea instalará el conectable de vim del repositorio básico de fzf. Comprueba si ya la tenías en tu configuración con anterioridad.\nPlug \u0026#39;junegunn/fzf\u0026#39;, { \u0026#39;do\u0026#39;: { -\u0026gt; fzf#install() } } Plug \u0026#39;junegunn/fzf.vim\u0026#39; Y listo, ya tienes un juego de comandos realmente útiles que, además, pueden ser configurados para ajustarlos más a tus gustos y necesidades. Dispones, por ejemplo, del comando Commits, que dará como entrada a fzf un listado de los commits de tu proyecto Git. Con Lines, la entrada que usará fzf serán las líneas de los buffers cargados, y del bufer actual en el caso de que uses BLines. No dejes de echarle un vistazo aquí a los comandos disponibles con fzf.vim, como History, Marks, Tags\u0026hellip;\n","id":44,"tag":"Vim","title":"Sacándole más partido a fzf en Vim","url":"https://metrosetenta.es/blog/sacandole-mas-partido-a-fzf-en-vim/"},{"content":"Seguro que estás acostumbrado a usar criterios de búsquedas y a aplicar filtros a un conjunto de datos. Cada vez que usas un búscador como Google o DuckDuckGo lo haces. Habitualmente los criterios de búsqueda son aplicados usando algoritmos muy rígidos, que devuelven resultados donde las coincidencias de los datos con dichos criterios son exactas. Deja que te hable ahora sobre la búsqueda difusa (Fuzzy search), donde los algoritmos aplicados son más indulgentes, consiguiendo así que podamos lograr resultados a los que no accederíamos con los métodos expuestos anteriormente.\nTe escribo un sencillo ejemplo usando el criterio de búsqueda termino. Si aplicáramos los métodos habituales de búsqueda a un texto, para localizar las líneas que contienen dicho criterio podríamos obtener resultados tal que así:\nSi termino más rápido de lo previsto te aviso para que pases antes a recogerme.\nAplicando una búsqueda difusa, obtendríamos líneas donde el criterio de búsqueda está en plural, con distintos acéntos gráficos, escrito erróneamente, etc.\nSi termino más rápido de lo previsto te aviso para que pases antes a recogerme.\nEn el examen olvidé un término bastante importate, seguro que me ha bajado la nota.\nEl chico que terminó justo antes que yo tropezó cuando iba a entregar el examen.\ny como nadie conocía los términos en los que se había pactado su salida, resultó\n, y aunque te resulte confuso, mirándolo detenidamente seguro que es\nLa búsqueda difusa es útil, por tanto, cuando es importante conseguir resultados donde la relevancia no está determinada por la exactitud con la que estos se ajustan al criterio de búsqueda. En su lugar, el usuario tiende a buscar una idea general, no una etiqueta técnica, espera recibir más resultados y ser él quien decida la relevancia de estos. La búsqueda en archivos académicos, textos traducidos con posibles errores o sistemas de archivos son algunos casos de uso donde aplica perfectamete la búsqueda difusa.\n","id":45,"tag":"Algoritmo","title":"Búsqueda difusa","url":"https://metrosetenta.es/blog/busqueda-difusa/"},{"content":"Prettier es una herramienta de formateo de código de gran popularidad en el ecosistema JavaScript. Mediante unas opciones especificadas en su archivo de configuración podemos conseguir que todos los archivos de código formateados con Prettier luzcan idéntico sangrado (indent), tipo de comillas, uso de espacios, ancho de líneas, y demás aspectos de estilo. Para disponer de Prettier únicamente tienes que añadirlo a tu proyecto.\n$ npm install --save-dev --save-exact prettier # or globally $ npm install --global prettier Puedes ejecutar Prettier mediante su CLI, o puedes engancharlo para que se ejecute justo antes de una confirmación de cógido de Git, un pre-commit hook, pero la intención de este artículo es usarlo en Vim para que dé formato a tu archivo de código cuando lo guardas.\nSi estás usando ESLint del modo que te conté aquí, tienes que saber que algunos métodos de Prettier pueden entrar en conflicto con las capacidades de formateo de ESLint. Para evitar esto es mejor dejar que ESLint cuide de la correción del código mientras que Prettier se encarga únicamente de dar formato al guardar el archivo. Lo puedes conseguir desactivando, mediante eslint-config-prettier, la reglas de ESLint que entra en conflicto con Prettier.\n$ npm install --save-dev eslint-config-prettier También tienes que añadir a ESLint las capacidades de correción de código de Prettier para seguir disponiendo de ellas, y esto lo puedes hacer con eslint-plugin-prettier.\n$ npm install --save-dev eslint-plugin-prettier La forma más sencilla de configurar los dos dispositivos software que acabas de instalar es mediante el archivo de configuración de ESLint.\n{ \u0026#34;extends\u0026#34;: [\u0026#34;plugin:prettier/recommended\u0026#34;] } Eso es todo lo que necesitas para integrar Prettier con ESLint, ahora deja que te muestre como configurarlo con Vim para formatear el código de tu archivo JavaScript en el momento de guardarlo. Además de gestionar correctores de errores, como te mostré aquí, Ale tambén ejecuta correctores de formato de código en el buffer de Vim, todo lo que necesitas hacer es especificar en el archivo de configuración de Vim lo siguiente:\nlet g:ale_fixers[\u0026#39;javascript\u0026#39;] = [\u0026#39;eslint\u0026#39;] \u0026#34; Fix files automatically on save let g:ale_fix_on_save = 1 Con esta configuración será ESLint el encargado de corregir el código y advertir de errores de formato, mientras que Prettier realizará el formateo del código al guardar al archivo.\n","id":46,"tag":"Vim ; JavaScript","title":"Javascript con Vim: Formateando el código","url":"https://metrosetenta.es/blog/javascript-con-vim-formateando-el-codigo/"},{"content":"Los programas de análisis y correción de código son nombrados en inglés como linters. Un corrector de este tipo te ayuda a identificar errores potenciales en tu código sin necesidad de ejecutarlo, evitar patrones problemáticos e, incluso, cumplir con una guía de estilo. En JavaScript, el linter más usado se llama ESLint, y puedes instalarlo siguiendo los pasos aquí indicados. Por defecto, ESLint está configurado mediante un juego de reglas que, comprobando si el código las cumple, gestionan los problemas más comunes. Por supesto, puedes personalizar estas reglas para solucionar problemas específicos; de hecho, existen paquetes de configuración de ESLint, como Airbnb, y es posible que se ajusten a tus necesidades, ahorrándote mucho tiempo en hacer esta tarea.\nESLint es una herramienta de línea de comandos, y al igual que con otras herramientas de líneas de comando, podemos integrarla con Vim para, en este caso, disponer de las características de análisis y correción de código en el mismo instante en el que lo estamos escribiendo. Para conseguir esto Ale es una gran opción. Ale es un motor asíncrono de correción de código que implementa el protocolo LSP (Language Server Protocol). Esto quiere decir que actua entre Vim (cliente) y un proveedor de inteligencia de lenguaje (servidor), para integrar características como autocompletado, ir a definiciones, encontrar referecias y otras utilidades similares útiles para el desarrollo. Aquí tienes los métodos de instalación de Ale. Una vez que lo conectes a Vim y abras un archivo JavaScript de un proyecto configurado con ESLint, este funcionará sin más ajustes. Con el siguiente comando puedes listar los errores y avisos del fichero,\n:lopen y si lo prefieres, puedes preparar unos atajos de teclado para navegar rapidamente entre ellos.\nnmap \u0026lt;silent\u0026gt; [c \u0026lt;Plug\u0026gt;(ale_previous_wrap) nmap \u0026lt;silent\u0026gt; ]c \u0026lt;Plug\u0026gt;(ale_next_wrap) Puedes comprobar que, cuando el cursor se encuentra sobre una línea de código problemática, aparece información a cerca de la advertencia o el error marcado en la parte inferior de Vim.\n","id":47,"tag":"Vim ; JavaScript","title":"JavaScript con Vim: Corrigiendo el código","url":"https://metrosetenta.es/blog/javascript-con-vim-corrigiendo-el-codigo/"},{"content":"","id":48,"tag":"","title":"JavaScript con Vim: Resaltando la sintaxis","url":""},{"content":"Como ya te expliqué en este artículo, al retomar este blog quería mejorar la navegación y, para ello, decidí implementar un buscador que permitiera localizar contenido de la manera más eficaz posible. El tema con el que está generado el blog, Mainroad, incluye la posibilidad de renderizar un buscador en la barra lateral con un sencillo ajuste en el archivo de configuración de Hugo, config.toml.\n[Params.sidebar] # Enable widgets in given order widgets = [\u0026#34;image\u0026#34;, \u0026#34;search\u0026#34;, \u0026#34;categories\u0026#34;] La caja de búsqueda es sencilla, me gusta el estilo, pero cuando se visualiza el blog en una pantalla más pequeña la barra lateral que la contiene se muestra debajo de los primeros posts. Yo prefiero que el buscador esté siempre visible, desde el inicio, para que de este modo se pueda acceder al contenido rápidamente sin tener que navegar a la parte inferior del blog, así que probé a sacar la caja de búsqueda de la barra lateral y colocarla debajo del título, justo antes del primer artículo. Gracias a que el widget de búsqueda esta escrito en su propio partial, es sencillo probar la caja de búsqueda, en esa ubicación, añadiendo una única línea de código en la plantilla base del blog.\n{{partial \u0026#34;widgets/search.html\u0026#34; . }} Al colocar el buscador en esa posición el aspecto del blog en pantallas pequeñas era bueno, pero no en pantallas de tamaño mayor, por esto decidí condicionar su posición al tamaño de la pantalla. Mainroad ya hace esto con la posición de la barra lateral, la gestiona mediante CSS con una Media query, así que usé esa misma condición, la que lleva la barra lateral a la parte inferior del blog, para ocultar la caja de búsqueda en dicha barra lateral y mostrar, en su lugar, la caja de búsqueda que coloqué bajo el título del blog.\n/* Search widget */ @media screen and (min-width: 768px) { .primary .widget-search { display: none; } } @media screen and (max-width: 767px) { .sidebar .widget-search { display: none; } } ","id":49,"tag":"Hugo","title":"Hugo y el buscador: Presentando el widget de búsqueda","url":"https://metrosetenta.es/blog/hugo-y-el-buscador-presentando-el-widget-de-busqueda/"},{"content":"Hugo se presenta como el constructor de sitios web más rápido del mundo. Está escrito con Go y genera sitios de forma estática con la intención de ofrecer mejor rendimiento, seguridad y facilidad de uso. Por cada petición que recibe un sitio generado dinámicamente su servidor HTTP crea un nuevo archivo HTML. Con el tiempo estos generadores de sitios dinámicos comenzaron a almacenar algunas páginas en caché para mejorar el rendimiento. En tanto que Hugo no genera las páginas dinámicamente, sino que renderiza todos los archivos HTML en tu computadora, podemos definirlo como un generador de sitios estáticos. Es como si un generador de sitios dinámicos almacenara todas las páginas en caché. Este método te permite revisar todas las páginas en local tal cual serán enviadas al cliente y, además, el servidor HTTP las servirá usándo únicamente una fracción de cómputo y memoria de la que necesitaría un sitio generado dinámicamente.\n","id":50,"tag":"Hugo","title":"Hugo, generador de sitios estáticos","url":"https://metrosetenta.es/blog/hugo-generador-de-sitios-estaticos/"},{"content":"Cuando gestionaba mis dotfiles era habitual encontrarme, entre los archivos modificados, al misterioso netrwhist. Antes que nada debes de saber que netrw es un script conectable a vim, que está orientado a redes y que permite lectura, escritura y navegación. netrwhist es el archivo donde netrw guarda un histórico de los directorios que fueron modificados. netrw tiene bastantes opciones de configuración:\nlet g:netrw_banner = 0 let g:netrw_liststyle = 3 let g:netrw_browse_split = 4 let g:netrw_altv = 1 let g:netrw_winsize = 25 let g:netrw_dirhistmax = 10 let g:netrw_dirhist_cnt = 6 Un modo de no versionar este histórico es usar el parámetro de configuración netrw_home para guardar su histórico fuera del repositorio dotfiles.\nlet g:netrw_home=$HOME.\u0026#39;/.cache/vim\u0026#39; Otra opción que puedes considerar es añadirlo a tu archivo .gitignore, pero si optas por ella, y eres consistente con el resto de tus archivos históricos, el problema es la gestión de tu .gitignore, que puede crecer demasiado, desactualizarse, etc. El problema de la primera opción es que no exista la carpeta .cache si usas tus dotfiles en una nueva máquina. Yo opté por la primera opción porque este problema me parece más sencillo de solucionar y más eventual. ¿Cuál eliges tú?\n","id":51,"tag":"Git ; Vim","title":"El misterioso netrwhist","url":"https://metrosetenta.es/blog/el-misterioso-netrwhist/"},{"content":"Hace poco un compañero me preguntó cómo podía consultar los archivos que había gestionado en un desarrollo, desde la versión que había partido, hasta la versión actual en la que se encontraba. Consultando un poco dimos con esta opción,\n$ git diff 0819dfcb 20620b5b --name-only Y el resultado era exáctamente el que buscábamos, nombres de los archivos que habian sufrido algún tipo de modificación entre los dos commits que le estábamos pasando al comando git diff.\nbash/aliases bash/rc bash/specific_aliases bash/specific_rc guake/preferences vim/.netrwhist vim/rc Al usar esta información, mi compañero comprobó que los archivos podían haber sido eliminados, además de creados o modificados; esos archivos eliminados no existian en la versión actual, y la nueva necesidad se centró en identificarlos. Para esto usamos git log de este modo,\n$ git log 0819dfcb 20620b5b --diff-filter=D --summary | grep delete pero el resultado no era tan limpio.\ndelete mode 100644 vim/.netrwhist De nuevo, la mejor opción volvia a pasar por git diff,\n$ git diff 0819dfcb 20620b5b --name-status | grep \u0026#39;^D\u0026#39; que daba esta otro resultado más limpio y más sencillo de manipular.\nD\tvim/.netrwhist De hecho, el comamdo que finalmente usó mi compañero para el script que estaba desarrollando, fue\n$ git diff 0819dfcb 20620b5b --name-status con el resultado que sigue, donde además de disponer de los archivos, se aclara el tipo de gestión realizada.\nM bash/aliases M bash/rc M bash/specific_aliases M bash/specific_rc M guake/preferences D vim/.netrwhist M vim/rc Aquí puedes consultar que estado representa cada una de las letras mayúsculas que anteceden a los archivos.\n","id":52,"tag":"Git","title":"Consultar con Git los archivos modificados","url":"https://metrosetenta.es/blog/consultar-con-git-los-archivos-modificados/"},{"content":"Cuando estas desarrollando software y te encuentras por primera vez con un problema que tienes que resolver, puedes dedicar unos 20 minutos a navegar por la red, buscando, probando y valorando la información necesaria para conseguir la solución correcta. En ocasiones debes construir dicha solución con porciones y modificaciones de otras propuestas que no se adaptan correctamente al problema concreto que necesitas solucionar.\nSi después de hacer esto, dedicas un poco de tiempo a escribir sobre ello, puedes disponer de la información en caso de que te haga falta en el futuro y, además, ayudar a otras personas que podrían encontrarse con la misma dificultad que te encontraste tú.\nQue esas personas encuentren tu solución, si es realmente buena, es el trabajo de los motores de búsqueda, por tanto, el hecho de que un artículo ofrezca una solución concisa y comprensible a un problema es, posiblemente, la mejor estrategia SEO para un blog sobre tecnología en tanto que, los motores de búsqueda, intenta facilitar información a sus usuarios para resolver una cuestión.\n","id":53,"tag":"SEO ; Blog","title":"Breve pensamiento sobre SEO","url":"https://metrosetenta.es/blog/breve-pensamiento-sobre-seo/"},{"content":"El modo en el que se suele recomendar el uso de las etiquetas en un blog, que es el que he venido adoptando hasta hace poco, está muy enfocado en obtener buenas clasificaciones en los resultados de buscadores. Al retomar el blog este ha sido un aspecto al que quería darle un enfoque distinto, dejando de lado las recomendaciones para una estrategia SEO convencional.\nCon el nuevo enfoque quería facilitar la navegación, a la par que mejorar la experiencia de usuario, y para conseguirlo me he visto obligado a involucrar también a las categorías de mi blog. El principal usuario de mi blog soy yo mismo, pues lo uso para anotar soluciones a problemas que he encontrado y detalles importantes sobre todo lo que aprendo referente a mi profesión, y esto me hace volver frecuentemente a él para realizar consultas.\nTe cuento sobre las categorías. En el blog no había muchas categorías, que suele ser el error más habitual, pero un artículo podía pertenecer a más de una categoría. El contenido del blog parece mayor usando esta práctica, pero lo cierto es que, la experiencia del usuario que está intentando localizar un contenido concreto, y encuentra un mismo artículo una y otra vez, es realmente mala. Por esto decidí que los artículos han de pertenecer a una única categoría.\nAl usar las categorías para acceder a contenido del blog se te presenta un gran número de páginas por las que navegar y encontrar el artículo deseado, lo cual es realmente tedioso con los artículos más antiguos. Es ahora el turno de las etiquetas. Las etiquetas son un modo de afinar un poco más el tiro cuando ofreces al usuario una nube de etiquetas. También me gusta pensar en ellas como un modo de localizar, al final de un artículo, más artículos que traten el mismo tema. Quiero decir que, cuando terminas de leer un artículo sobre, por ejemplo, programación funcional, y quieres localizar más artículos sobre este tema en el mismo blog, una etiqueta es el modo ideal que el autor puede facilitarte.\n","id":54,"tag":"SEO ; Blog","title":"Sobre categorías y etiquetas","url":"https://metrosetenta.es/blog/sobre-categorias-y-etiquetas/"},{"content":"¿Qué te parece si vemos en acción a Deno, el pequeño diplodocus del que hablamos en el artículo anterior? El primer paso, como no puede ser de otro modo, es instalarlo en tu equipo, y para esto dispones de varias opciones que se adaptan a tu sistema operativo o a tus preferencias. Los usuarios de Linux pueden hacerlo facilmente desde la consola, al igual que los de macOS que, además, pueden usar Homebrew. Para Windos existe la posibilidad de usar PoweShell, Chocolatey o Scoop. En este post nos centraremos en Linux. El comando de instalación es sencillo, y únicamente necesitas tener instalado curl:\n$ curl -fsSL https://deno.land/x/install/install.sh | sh Si necesitas instalar una versión específica puedes indicarlo con el parámetro -s. En el ejemplo que sigue verás que lo uso para instalar la versión v0.38.0:\n$ curl -fsSL https://deno.land/x/install/install.sh | sh -s v0.38.0 Por defecto, la aplicación se instala en tu carpeta de usuario, pero puedes indicar el directorio de instalación si lo necesitas.\n$ curl -fsSL https://deno.land/x/install/install.sh | sudo DENO_INSTALL=/usr/local sh Una vez que el script de instalación haya finalizado, te dará indicaciones para ajustar un par de variables de entorno. Puedes hacerlo en la configuración del procesador de comandos para asegurarte de que están disponibles al usar Deno.\n# Ajustes para Denoland export DENO_INSTALL=\u0026#34;/home/metroSetenta/.deno\u0026#34; export PATH=\u0026#34;$DENO_INSTALL/bin:$PATH\u0026#34; Voy a hablarte ahora de como instalar y gestionar múltiples versiones de Deno, y aquí entra en acción asdf-vm, una herramienta para la Interfaz de línea de comandos que permite gestionar versiones de entornos de ejecución. asdf-vm se puede extender mediante plugins y el plugin para Deno, asdf-deno, ya está disponible. Si no has usado anteriormente asdf-vm, aquí tienes toda la información necesaria para hacerlo. Luego sólo tendrás que añadirle el plugin correspondiente para poder gestinar tus versiones de Deno.\n$ asdf plugin-add deno https://github.com/asdf-community/asdf-deno.git Una vez hecho, es sencillo instalar cualquier versión de Deno,\n$ asdf install deno 0.38.0 activar una versión de manera global,\n$ asdf global deno 0.38.0 o hacerlo simplemente de manera local.\n$ asdf local deno 0.38.0 ","id":55,"tag":"JavaScript ; Deno","title":"Instala y gestiona múltiples versiones de Deno","url":"https://metrosetenta.es/blog/instala-y-gestiona-multiples-versiones-de-deno/"},{"content":"Deno se define a sí mismo como un seguro sistema o entorno de ejecución para JavaScript y Typescript. El creador originar de Node.js, Ryan Dahl, fue quien anunción esta nueva herramienta en 2018, así que es inevitable fijarse en el juego del intercambio de sílabas (No-de / De-no), a pesar del divertido dino que usa como logo, que puede despistar. Pero ¿Por qué querría Dahl crear otra vez Node.js, un software que cuenta con un apoyo y una aceptación suficientes para hacerlo exitoso por décadas? Pues parece que alguna espinita se le quedó clavada, vamos a ver cual puede ser.\nDeno no dispone de gestor oficial de paquetes, lo que puede parecer en un primer momento un inconveniente, pero todo cambia cuando descubres lo siguiente. En lugar de usar el sistema de importación CommonJS, Deno usa ES Modules de un modo realmente interesante. Permíteme explicarme mejor. En lugar de paquetes de código publicados a un repositorio, puedes ejecutar ES Modules publicados en cualquier URL de la red. Esto provoca dos sensaciones contradictorias: La primera de ellas es una sensación de grandes posibilidades, porque puedes crear paquetes sin tener que publicarlos en un repositorio como npm, lo puedes publicar en cualquier URL, pero, posteriormente te invade una sensación de inseguridad al ejecutar contenido en tu máquina de cualquier sitio de la red, sea oficial o no. Bien, para esto último déjame decirte ya que, a diferencia de Node.js, en donde un programa tiene los mismos permisos que el usuario, con Deno esto es diferente. Deno integra una sandbox, es decir, un mecanismo de seguridad para disponer de un entorno aislado del resto del sistema operativo, donde es posible ajustar distintos permisos de acceso para los ejecutables. Y, para finalizar, en todo caso, Deno ya ofrece servicio de repositorio a través de URLs en https://deno.land/x/.\nLas características modernas de ECMAScript que usa Deno en toda su API y biblioteca estandar le permiten prescindir de la biblioteca estandar basada en retrollamadas en la que se basa Node.js, más compleja y comprometida. Deno dispone también de características integradas para realizar pruebas, además de ayudas para gestionar la compatibilidad entre navegadores. Al estar escrito con Rust y TypeScript, también ofrece ventajas si te inclinas por desarrollar en este último lenguaje, pero puedes seguir programando con Javascript si lo prefieres.\nNo dejes de visitar su web para conocerlo porque, aunque no va a destronar a su hermano mayor hoy, sí que puede resultar interesante conocer su evolución.\n","id":56,"tag":"JavaScript ; Deno","title":"¿Qué es Deno?","url":"https://metrosetenta.es/blog/que-es-deno/"},{"content":"Al iniciar el proceso de diseño de una arquitectura software, la primera dificultad que debes acometer es definir con sencillez la necesidad que nuestro desarrollo va a solucionar y cómo va a hacerlo. Este paso es realmente importante, y no imaginas la cantidad de ocasiones que se pasa por alto, lo cual provoca dificultades en fases posteriores del desarrollo e, incluso, el fracaso de algunos proyectos. El mejor modo de comenzar a hacerlo es dividir el desafío propuesto en cuatro aspectos fundamentales:\nFuncional: Describe la necesidad técnica que el cliente o el usuario desea solventar. Formal: Describe la estructura de la solución y el modo en el que se ajusta a distintos entornos, sistemas y tecnologías. Económico: Describe cuanto costará contruir, gestionar y mantener dicha solución. Temporal: Describe la duración del desarrollo, o, en el caso de que existiera, la fecha límite de ejecución. Una vez identificados y descritos estos cuatro aspectos, definir el desafío arquitectónico será mucho más sencillo.\n","id":57,"tag":"Fundamentos arquitectura","title":"1 desafio 4 aspectos","url":"https://metrosetenta.es/blog/1-desafio-4-aspectos/"},{"content":"Lo normal es que tu expectativa de lo que vas a encontrar a continuación dependa de tu experiencia en el desarrollo de software. El concepto de arquitectura de software puede llegar a ser muy distinto dependiendo de la persona que lo use: Lo que comprende un desarrollador como arquitectura puede llegar a ser muy distinto de la idea de un probador o un técnico en devops. Esto hace que publicar una definición básica del concepto de arquitectura de software sea ciertamente arriesgado pero, basándome en lo que me gustaría haber encontrado cuando empecé a estudiarla, y amparado en que existe algún punto común, pues para todos implica la definición de una estructura, me atrevo a darte la que sigue:\n\u0026ldquo;La arquitectura de software trata sobre cómo se ajustan las piezas para construir la solución para un negocio o una necesidad técnica que el cliente o el usuario desea resolver.\u0026rdquo;\n","id":58,"tag":"Fundamentos arquitectura","title":"Definicion de arquitecura software","url":"https://metrosetenta.es/blog/definicion-de-arquitecura-software/"},{"content":"El título de esta entrada suena realmente lógico y no merece profundizar más en él, pero ¿Cómo puedes hacerlo? El problema es que en ocasiones, en la carpeta del proyecto, guardas archivos de configuración que si bien no son propios del proyecto, sí que son necesarios, y resulta cómodo tenerlos en dicha ubicación. Desde mi punto de vista, las configuraciones de los distintos IDEs son el caso más común de esto que te comento.\nRefiriéndonos al gestor de versiones Git, lo habitual es que el proyecto disponga de un archivo específico, nombrado .gitignore, donde se indica qué modificaciones deben ser obviadas en la construción de las distintas versiones. Habitualmente están incluidas en esta declaración las bibliotecas de código de terceros, que se suelen instalar con un gestor de dependencias, y los archivos resultado de una compilación, pero ¿Como puedes a hacer un ejercicio por adelantar que IDEs se usarán, como se nombrarán los documentos que contengan la declaración de sus ajustes y dónde se guardarán? Esta responsabilidad parece que, más bien, debe recaer en cada desarrollador.\nGit te ofrece la posibilidad de configurar un documento del tipo .gitignore con caracter global, llamémosle .gitignore_global, de modo que puedes indicarle a Git que, en todos tus proyectos, ignore tus archivos de configuración personales. Si quieres saber que archivo está usando Git como .gitignore_global ejecuta el siguiente comando:\n$ git config --global core.excludesfile Para configurar otro archivo distinto, sólo tienes usar la sentencia anterior añadiendo al final el archivo que quieres usar.\n$ git config --global core.excludesfile /home/metroSetenta/.gitignore_global El contenido del archivo es sencillo, se trata de patrones. Aquí tienes más información para hacerlos tú mismo, pero puedes tomar de base las propuestas en gitignore.io.\nVamos un poco más allá. Lo cierto es que si trabajas en proyectos muy distintos, en ocasiones no usas las mismas reglas o, incluso, es posible que quieras tener reglas para ignorar archivos que sean contradictorias entre dos o más de tus proyectos. Para esto, también dispones de la opción de configurar dichas reglas en un documento específico para cada proyecto, pero que no será agregado a las siguietes versiones de dicho proyecto . Dentro de la carpeta del proyecto en el que deseas configurar estas reglas específicas navega a .git/info/exclude, e indícalas ahí.\n\u0026ldquo;Se responsable con los proyectos en los que trabajas y no versiones archivos ajenos al proyecto, pídele al gestor de versiones que los ignore.\u0026rdquo;\n","id":59,"tag":"Git","title":"No versiones archivos ajenos al proyecto","url":"https://metrosetenta.es/blog/no-versiones-archivos-ajenos-al-proyecto/"},{"content":"Sin duda, Git es una herramienta potentísima para el control de versiones, y conocer bien su funcionamiento nos facilita muchísimo el trabajo a la hora de escribir, probar y corregir código. Algunas de sus funcionalidades, las más avanzadas, son menos usadas y, por ello, tienden a conocerse menos. Algo que me obliga a ir a la documentación de Git de cuando en cuado es la necesidad de modificar el mensaje en varios commits. Recuerdo que la primera vez que lo necesité fue cuando, en un daily (reunión diaria típica de la metodología Scrum) un compañero nos pidió al resto que los mensajes de todos los commits comenzaran con el número de issue (es algo así como un número de ticket en Jira). En ese momento yo estaba terminando un feature con más de 10 commits, y pensé que hacerlo de uno en uno, usando reword en un rebase interactivo, sería muy tedioso. Ahora, si estoy programando algo y aún no tengo ese número de issue, uso un placeholder para recordar que tengo que poner el número de issue en el lugar que ocupa, y una vez que dispongo de dicho número, uso el siguiente comando de Git, filter-branch:\n$ git filter-branch -f --msg-filter \u0026#39;sed \u0026#34;s/ISSUENUMBERHERE/MP-3242/g\u0026#34;\u0026#39; HEAD~3..HEAD Al ejecutar el comando de aquí arriba, usamos el programa sed para sustituir, en los trés últimos commits, el placeholder ISSUENUMNBERHERE por el identificador de issue MP-3242.\n","id":60,"tag":"Git","title":"Modificando el mensaje de varios commits con un único comando","url":"https://metrosetenta.es/blog/modificando-el-mensaje-de-varios-commits-con-un-unico-comando/"},{"content":"Algo que recuerdo tedioso de mi época de probar distintas distribuciones de Linux es configurar los atajos de teclado. I3 es la primera opción a valorar para quienes tocan el ratón lo mínimo e imprescindible, pero Gnome ha avanzado bastante en este aspecto, y, como es el entorno de escritorio que uso actualmente, te voy a comentar como he configurado en él los espacios de trabajo.\nEn mi distribución de Linux, Manjaro, pulsando la tecla Super (habitualmente ilustrada con una ventana, y conocida como tecla Win en Windows) dispones de un buscador en el sistema operativo. Si comienzas a teclear el nombre de la aplicación Add/Remove Software, la verás sugerida y seleccionable en un instante. En esta aplicación dispones de la posibilidad de buscar paquetes de software haciendo click en el icono de la lupa. Ahí, únicamente teclea Workspaces grid y te aparecerán distintas opciones para construir una parrilla con tus escritorios. Yo personalmente estoy usando Gnome shell vsmatrix.\nMe gusta usar una parrilla de 3x3, disponiendo de este modo de 9 escritorios. No suelo usarlos todos, pero esta configuración me permite disponer de mi editor de código, que es dónde más trabajo, en el escritorio del centro, y desde aquí navegar rápidamente a los demás escritorios y volver con la misma facilidad. Suelo usar uno, del resto de escritorios, para colocar los navegadores, en otro los clientes de gestores de bases de datos, en otro mis canales de comunicación, en otro Amazon music o Spotify\u0026hellip;\nEste software es una extensión que puedes manejar con la aplicación Tweaks, pero la configuración de los atajos de teclado para navegar por los escritorios, o mover las ventanas de uno a otro, no son configurables desde aquí o desde Keyboards shortcuts, pero se pueden configurar desde la consola con el comando gsettings. Puede parecer una desventaja, pero lo cierto es esto nos permite hacer un script reutilizable que podemos guardar y usar cada vez que lo necesitemos, ya sea para realizar esta configuración o restaurarla si la hemos modificado involuntariamente y la queremos recuperar. Aquí abajo está el que yo hice para mí:\n#!/bin/bash # A simple shell script to configure workspaces navigation after install tweak extension workspaces grid # Javier Guerrero - 14/Oct/2019 gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-left \u0026#34;[\\\u0026#34;\u0026lt;Control\u0026gt;\u0026lt;Alt\u0026gt;h\\\u0026#34;]\u0026#34; \u0026amp;\u0026amp; gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-down \u0026#34;[\\\u0026#34;\u0026lt;Control\u0026gt;\u0026lt;Alt\u0026gt;j\\\u0026#34;]\u0026#34; \u0026amp;\u0026amp; gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-up \u0026#34;[\\\u0026#34;\u0026lt;Control\u0026gt;\u0026lt;Alt\u0026gt;k\u0026#34;]\\\u0026#34; \u0026amp;\u0026amp; gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-right \u0026#34;[\\\u0026#34;\u0026lt;Control\u0026gt;\u0026lt;Alt\u0026gt;l\\\u0026#34;]\u0026#34; \u0026amp;\u0026amp; gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-left \u0026#34;[\\\u0026#34;\u0026lt;Control\u0026gt;\u0026lt;Super\u0026gt;\u0026lt;Alt\u0026gt;h\\\u0026#34;]\u0026#34; \u0026amp;\u0026amp; gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-down \u0026#34;[\\\u0026#34;\u0026lt;Control\u0026gt;\u0026lt;Super\u0026gt;\u0026lt;Alt\u0026gt;j\\\u0026#34;]\u0026#34; \u0026amp;\u0026amp; gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-up \u0026#34;[\\\u0026#34;\u0026lt;Control\u0026gt;\u0026lt;Super\u0026gt;\u0026lt;Alt\u0026gt;k\\\u0026#34;]\u0026#34; \u0026amp;\u0026amp; gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-right \u0026#34;[\\\u0026#34;\u0026lt;Control\u0026gt;\u0026lt;Super\u0026gt;\u0026lt;Alt\u0026gt;l\\\u0026#34;]\u0026#34; ","id":61,"tag":"Linux ; Equipo","title":"Navegando por la parrilla de los espacios de trabajo","url":"https://metrosetenta.es/blog/navegando-por-la-parrilla-de-los-espacios-de-trabajo/"},{"content":"Pues no estaba equivocado respecto a la instalación de Manjaro, no. Ha sido mucho más rápido que montar Arch y, tras probarlo, tengo que decir que estoy bastante contento con las opciones de configuración y administración que ofrece. Únicamente he tenido una experiencia negativa hasta el momento de escribir esto, y es que al hacer un escaner de las redes wifi ¡No podía ver ninguna red wifi disponible! Así que tocó investigar un poco. La solución pasaba por desactivar los módulos rtw88 y rtwpci, que fueron instalados junto al kernel, y que no funionaban correctamente con mi tarjeta rtl8822be. Para ello los tenía que añadir a la blacklist del kernel, y atendiendo a la documentación de Arch, lo hice añadiendo estas líneas al archivo /etc/modprobe.d/blacklist.conf que yo mismo tuve que crear:\ninstall rtw88 /bin/false install rtwpci /bin/false Luego, intalé git y el compilador propio de la distro:\n$ sudo pacman -S git dkms Después, instalé el paquete linux-header, para lo cual, primero comprobé la versión de mi kernel:\n$ uname -a $ sudo pacman -S linux-headers Luego cloné en la carpeta tmp el repositorio de código que contenía el controlador actualizado para mi tarjeta de red:\n$ cd /tmp $ git clone -b https://aur.archlinux.org/rtlwifi\\_new-extended-dkms.git Y tras ello, compilé e instalé el módulo correcto para mi tarjeta de red:\n$ sudo dkms add ../rtlwifi\\_new sudo dkms build rtlwifi-new/0.6 sudo dkms install rtlwifi-new/0.6 $ sudo modprobe rtl8822be Como último paso, reinicié el gestor de conexiones y\u0026hellip;\n$ sudo systemctl restart NetworkManger ¡Listo, ya podía visualizar y conectarme a las redes inalámbricas!\n","id":62,"tag":"Linux ; Equipo","title":"Manjaro y la conexión wifi","url":"https://metrosetenta.es/blog/manjaro-y-la-conexion-wifi/"},{"content":"Es habitual que las distribuciones Linux ofrezcan, entre sus opciones de descarga, una imagen de disco. Esta imagen de disco la podemos \u0026ldquo;quemar\u0026rdquo; en un CD/DVD, y también la podemos preparar como ejecutable en un pequeño USB portátil. No obstante, debemos tener en cuenta que si la imagen de disco se descargó incorrectamente, o fué contaminada, en el mejor de los casos no podremos instalar el sistema operativo elegido o, incluso, podríamos instalar un sistema operativo con errores. Para evitar esto, solemos disponer de la posibilidad de comprobar la integridad del archivo con un hash.\nUn hash es una función criptográfica que, mediante un algorítmo matemático, toma un bloque de datos y los procesa para devolver una serie de caracteres que siempre son de la misma longitud. También se suele llamar hash al resultado obtenido tras aplicar dicha función criptofráfica. Si usamos esta potente herramienta, dándole como parámetro de entrada la imagen de disco que hemos descargado, obtendremos un hash relacionado a dicha imagen.\nPor supuesto, en el repositorio de descarga solemos disponer, junto al enlace de descarga de la imagen de disco, de su correspondiente hash. Esto quiere decir que los distrubuidores aplicaron el algoritmo matemático a la imagen de disco, y nos facilitan el resultado. Si la imagen de disco que hemos descargado es exactamente igual a la que ellos facilitan, el hash que publican, y el que nosotros obtenemos al aplicar un hash al archivo descargado, deben ser idénticos. Si nuestro archivo contiene errores, o carece de algún contenido, los hash no serán iguales. Este es el modo de comprobar la integridad del archivo descargado.\nPero, una vez descargado el archivo\u0026hellip; ¿Cómo lo hago? Lo primero es conocer el hash correspondiente al archivo que hemos descargado. En este caso muestro una imagen de la página de descargas de Manjaro.\nAbajo del todo, junto a SHA1, puedes comprobar el código hash de la imagen de disco. SHA1 es un tipo de algorítmo para hacer hash (Secure Hash Algorithm). Otro tipo muy usado es MD5.\nAhora sólo resta hacer hash a nuesta imagen de disco descargada, usando el tipo SHA1, y comprobar que el resultado coincide con el que vemos en la publicación. Para obtener el hash de nuestra imagen de disco descargada podemos hacer ejecutar el siguiente comando en nuestro terminal:\n$ sha1sum -c imagen-de-disco-descargada.iso.sha1 ","id":63,"tag":"Linux","title":"Comprobando una imagen de disco","url":"https://metrosetenta.es/blog/comprobando-una-imagen-de-disco/"},{"content":"Lo cierto es que cuando adquirí el nuevo portátil para trastear en casa con él, mi primera idea fué la de instalar Arch Linux con i3-gaps, pero finalmente me he decantado por instalar Manjaro por los siguiente motivos. Con Arch, se aprende muchisimo sobre instalar, mantener y reparar el sistema operativo, pero lo cierto es que queria dedicar mi tiempo a otros aspectos, y con Arch me quedaba por delante instalar los paquetes básicos, configurar el sistema, zona horaria, configuración de red, localización, sistema de inicio\u0026hellip; Por otro lado, me gusta la filosofía rolling release, pero es cierto que en ocasiones puede crear inestabilidad en el sistema al actualizar un paquete.\nDespués de sopesar varias opciones me decante por Manjaro por varios motivos. Es un sistema operativo basado en Arch, pero tiene un instalador que me permite realizar todas las tareas citadas antes de un modo automático o, en el peor de los casos, bastante más rápido. Añade una capa adicional de pruebas a los lanzamientos de actualizaciones de Arch, así que, de este modo, se reduce la posibilidad de volver al sistema inestable sin renunciar a las últimas versiones de las aplicaciones instaladas. Por último, se puede elegir entre varios escritorios, y aunque existe la opción de i3, que es una versión mantenida por la comunidad, me decanté por Gnome por ser oficial y por el la positiva sensación de uso que tiene para mí. Hay que añadir también que con Manjaro no sólo dispongo de acceso a sus propios repositorios, sino que también puedo usar AUR, el repositorio propio de Arch.\n","id":64,"tag":"Linux ; Equipo","title":"¿Por qué Manjaro?","url":"https://metrosetenta.es/blog/por-que-manjaro/"},{"content":"En mi actual trabajo no puedo usar un sistema operativo distinto de Microsoft Windows 10 enterprise, y el portátil que tengo en casa, por ser compartido, no lo puedo trastear todo lo que quisiera. Por este motivo, y por lo que añoro Linux, he adquirido un nuevo portátil, sin sistema operativo instalado, para mis estudios y proyectos. Estas son algunas especificaciones.\nMarca y modelo Asus Vivobook X510qa-br010 15.6/amd A12-9720p/8gb/ssd256gb/sin So\nProcesador Amd® Quad-core A12-9720p Apu (4 Núcleos, 2mb Caché, 2.7ghz Hasta 3.6ghz, 64-bit)\nPantalla 15.6- (39,62cm) Led Retroiluminado / 60hz / Borde Estrecho / Ultra Slim / 200nits / Hd (1366x768/16:9) / Antirreflejos / Ntsc:45% Memoria Ram 8gb (8gb) Ddr4 2400mhz\nAlmacenamiento 256gb Ssd M.2 Sata3\nGráficos Amd® Radeon- R7 Graphics\nSistema Operativo Sin Sistema Operativo\n","id":65,"tag":"Linux ; Equipo","title":"Portátil para estudios y proyectos","url":"https://metrosetenta.es/blog/portatil-para-estudios-y-proyectos/"},{"content":"Ya te comenté en este pequeñísimo artículo que las funciones tratadas como objetos de primera clase es una de las características que permiten programar con Javascript de un modo funcional. Esto nos permite, por ejemplo, pasar funciones como argumentos de otras funciones, y ahí es donde se produce la inyección de funciones que da título a este artículo.\nLa función sort nos permite mostrar un ejemplo de este concepto. A continuación la función sort en acción:\nlet people = [\u0026#34;Leonardo\u0026#34;, \u0026#34;Rocío\u0026#34;, \u0026#34;Athenea\u0026#34;, \u0026#34;Javier\u0026#34;]; console.log(people.sort()); // [ \u0026#34;Athenea\u0026#34;, \u0026#34;Javier\u0026#34;, \u0026#34;Leonardo\u0026#34;, \u0026#34;Rocío\u0026#34; ] Como puedes ver en el ejemplo, en el momento de ordenar strings, atendiendo a su representación ASCII, funciona genial, pero ¿Y si queremos ordenar el siguiente array, que contiene objetos, basándonos en la edad de cada persona?\nlet people = [ { Nombre: \u0026#34;Leonardo\u0026#34;, Edad: 4 }, { Nombre: \u0026#34;Rocío\u0026#34;, Edad: 38 }, { Nombre: \u0026#34;Athenea\u0026#34;, Edad: 7 }, { Nombre: \u0026#34;Javier\u0026#34;, Edad: 40 } ]; console.log(people.sort()); \\* [ { Nombre: \u0026#34;Leonardo\u0026#34;, Edad: 4 }, { Nombre: \u0026#34;Rocío\u0026#34;, Edad: 38 }, { Nombre: \u0026#34;Athenea\u0026#34;, Edad: 7 }, { Nombre: \u0026#34;Javier\u0026#34;, Edad: 40 } ] */ Ahí nuestra función sort no está tan acertada, pero nos permite que le pasemos como parámetro una función que decidirá, en el momento de comparar dos elementos del array, cual es el orden correcto.\nlet people = [ { Nombre: \u0026#34;Leonardo\u0026#34;, Edad: 4 }, { Nombre: \u0026#34;Rocío\u0026#34;, Edad: 38 }, { Nombre: \u0026#34;Athenea\u0026#34;, Edad: 7 }, { Nombre: \u0026#34;Javier\u0026#34;, Edad: 40 } ]; console.log(people.sort((a, b) =\u0026gt; a.Edad - b.Edad)); \\* [ { Nombre: \u0026#34;Leonardo\u0026#34;, Edad: 4 }, { Nombre: \u0026#34;Athenea\u0026#34;, Edad: 7 }, { Nombre: \u0026#34;Rocío\u0026#34;, Edad: 38 }, { Nombre: \u0026#34;Javier\u0026#34;, Edad: 40 } ] */ ","id":66,"tag":"Programación funcional","title":"Inyección de funciones","url":"https://metrosetenta.es/blog/inyeccion-de-funciones/"},{"content":"Pues no, no se trata de un deporte de disco sobre hielo usando escobas, y tampoco es una práctica consistente en sazonar todas las comidas con la misma especia. En el cálculo lambda se usan funciones con un único parámetro. En posts venideros sobre conexión de funciones conoceremos el motivo, pero ahora centrémonos en aprender como superar esta limitación gracias al currying.\nEl amigo Haskell Curry, además del lenguaje Haskell desarrolló este concepto. ¿Cómo podríamos emular una función que suma tres número recibidos como parámetros, igual a la del ejemplo que sigue, usándo exclusivamente funciones con un único parámetro?\ntriSum = (x, y, z) =\u0026gt; x + y + z; triSum(1, 2, 3); //6 Pues la respuesta no parece muy complicada a simple vista:\ntriSum = x =\u0026gt; y =\u0026gt; z =\u0026gt; x + y + z: triSum(1)(2)(3); //6 Vamos a desglosarlo un poco para verlo más claro:\nlet funcFromStep1 = triSum(1); //y =\u0026gt; z =\u0026gt; 1 + y + z let funcFromStep2 = funcFromStep1(2); //z =\u0026gt; 1 + 2 + z let funcFromStep3 = funcFromStep2(3); //6 No se tú, pero yo me quedé embobado la primera vez que vi esto\u0026hellip;\n","id":67,"tag":"Programación funcional","title":"Currying","url":"https://metrosetenta.es/blog/currying/"},{"content":"Buscando un poco en la red, seguro que encuentras algún lugar donde se otorge al cálculo lambda el título de lenguaje de programación mas pequeño que existe. Básicamente se trata de un esquema para definir funciones y una regla de transformación simple mediante la sustitución de variables.\nUna función tiene un aspecto tal que así en cálculo lambda: λx.1+x. La variable entre el carácter λ y el punto es el parámetro de la función, y la expresión de esta es lo que sigue a dicho punto. Para aplicar esta función le facilitamos un argumento, por ejemplo 14, y lo podemos representar con el uso de paréntesis: (λx.1+x)(14). El resultado en este caso sería 15.\n","id":68,"tag":"Programación funcional","title":"Función lambda","url":"https://metrosetenta.es/blog/funcion-lambda/"},{"content":"Un polyfill es un script de código que sirve para emular una característica que un desarrollador necesita en un navegador cuando este no dispone de ella de forma nativa. En su mayoría, los polyfills están escritos en javascript, y nos permiten desarrollar apliaciones web centrados en nuevos estándares, pues suplen la carencia de los navegadores más antiguos en el momento de implementarlos.\nEl uso de polyfills invita a usar una detección de características del navegador, en lugar de comprobar su versión, puesto que con su uso únicamente habría que comprobar si el navegador, sea cual sea, y sea cual sea su versión, dispone de la cualidad que necesitamos en nuestro desarrollo, y de no ser así, usaríamos un polyfill que la emulara.\nEste pequeño polyfill que sigue comprueba si el motor javascript del navegador implementa la función forEach, y en el caso de no ser así, la define para que pueda ser usada en el resto del código, de modo que podremos usarla en todo nuestro desarrollo.\nif(Array.prototype.forEach === undefined) { Array.prototype.forEach = function(callback, element) { if(typeof(callback) !== \u0026#34;function\u0026#34;) { throw new TypeError(callback + \u0026#34; not function!\u0026#34;); } var len = this.length; for(var i = 0; i \u0026lt; len; i++) { callback.call(element, this[i], i, this) } } } Si necesitas una característica reciente, que no está disponible aún en todos los navegadores, un polyfill es una buena solución, pero antes de escribir uno propio, no dudes en buscar en alguna de las bibliotecas de polifylls que ya existen, como, por ejemplo, modernizr, que se centra en facilitar remedios de este tipo para el estandar HTML5.\n","id":69,"tag":"JavaScript","title":"Polyfills","url":"https://metrosetenta.es/blog/polyfills/"},{"content":"Explicado de un modo muy breve, consiste en que javascript, en el momento de compilar el código, prioriza las declaraciones de variables, siendo lo primero a tratar dentro de cada contexto. Es importante tener en cuenta que esto no aplica para las asignaciones, únicamente para la declaración. Estudia el siguiente script:\n/* * Logamos \u0026#39;undefined\u0026#39; por que la declaración de x ha sido alzada * por encima de esta función, pero no su asignación. Observa, * no obstante, que no recibimos un error de referencia a x a pesar * de que la línea dónde se declara y asigna está justo debajo. */ console.log(\u0026#34;x:\u0026#34;, x) var x = \u0026#34;Primera asignación\u0026#34;; //Ahora sí logamos \u0026#39;Primera asignación\u0026#39; console.log(\u0026#34;x:\u0026#34;, x) (function () { /* No logamos \u0026#39;Primera asignación\u0026#39; sino \u0026#39;undefined\u0026#39;, porque la * segunda declaración ha sido elevada al inicio de su contexto, * sobrescribiendo a la primera declaración y asignación. */ console.log(\u0026#34;x:\u0026#34;, x); var x = \u0026#34;Segunda asignación\u0026#34;; // Ahora sí, logamos \u0026#39;Segunda asignación\u0026#39; console.log(\u0026#34;x:\u0026#34;, x); }()); ","id":70,"tag":"Javascript ; Peculiaridades en lenguajes programación","title":"Hoisting de Javascript","url":"https://metrosetenta.es/blog/hoisting-de-javascript/"},{"content":"Con este post quiero inaugurar una sección donde tratar las peculiaridades de los distintos lenguajes de programación. Me refiero con la palabra peculiaridades a aquellos detalles que, en ocasiones, no se tienen presente cuando se está desarrollando, especialmente cuando se manejan distintos lenguajes en poco espacio de tiempo o cuando se comienza a desarrollar con un nuevo lenguaje que no se conoce en profundidad. Esto puede llevar a comportamientos extraños, o que no alcanzamos a comprender, en nuestra aplicación, y por tanto muy difíciles de resolver. Para comenzar, he elegido el hoisting de javascript.\n","id":71,"tag":"Javascript ; Peculiaridades en lenguajes programación","title":"Peculiaridades en los lenguajes de programación","url":"https://metrosetenta.es/blog/peculiaridades-en-los-lenguajes-de-programacion/"},{"content":"Javascript no fué creado como un lenguaje de programación funcional, pero dispone de ciertas características que nos permiten programar en un estilo funcional al usarlo. Te las listo con la intención de escribir un post sobre cada una de ellas y enlazarlo aquí abajo:\nLas funciones son objetos de primera clase Recursión Funciones flecha (Arrow functions) Clausuras (Closures) El operador spread ","id":72,"tag":"JavaScript ; Programación funcional","title":"Características funcionales de Javascript","url":"https://metrosetenta.es/blog/caracteristicas-funcionales-de-javascript/"},{"content":"La programación funcional es un modo distinto de escribir sofware que puede resultar difícil de aprender. La mayoría del software producido actualmente es una secuencia de órdenes ejecutadas de un modo determinado y, cuyo resultado, se obtiene creando y manipulando objetos. En la programación funcional (FP) el resultado se obtiene evaluando expresiones que están compuestas de funciones. Es habitual pasar funciones como parámetros de otras funciones, o devolverlas como resultados; Se opta por la recursión en lugar de hacer bucles, y en el cuerpo de las funciones se evita modificar objetos o el uso de variables globales, de tal manera que el uso de estas no conlleve efectos secundarios, habitualmente referidos en la literatura técnica como side effects.\nEs habitual asegurar que la programación funcional se enfoca en QUÉ debe ser realizado en lugar de CÓMO debe ser realizado.\n","id":73,"tag":"Programación funcional","title":"Entiende esto antes de estudiar programación funcional","url":"https://metrosetenta.es/blog/entiende-esto-antes-de-estudiar-programacion-funcional/"},{"content":"Indent Rainbow es una extensión de Visual Studio Code, de uso general, que sirve para darle color a las tabulaciones. Es muy útil para mejorar la legibilidad del código, especialmente para conocer en que ámbito te estás moviendo o donde comienzan y finalizan ciertos elementos del código.\nA continuación puedes ver el ajuste por defecto de la extensión, pero, como casi todo en Visual Studio Code, es algo configurable a tu gusto.\n\u0026#34;indentRainbow.colors\u0026#34;: [ \u0026#34;rgba(64,64,16,0.3)\u0026#34;, \u0026#34;rgba(32,64,32,0.3)\u0026#34;, \u0026#34;rgba(64,32,64,0.3)\u0026#34;, \u0026#34;rgba(16,48,48,0.3)\u0026#34;, \u0026#34;rgba(128,32,32,0.3)\u0026#34; ] ","id":74,"tag":"VSCode","title":"Tabulación coloreada en Visual Studio Code","url":"https://metrosetenta.es/blog/tabulaci%C3%B3n-coloreada-en-visual-studio-code/"},{"content":"Si escribes javascript seguro que en tu código hay una gran cantidad de paréntesis, llaves y corchetes. Con Bracket Pair Colorizer 2, una genial extensión de Visual Studio Code, dispondrás de la posibilidad de identificarlos rápidamente mediante colores, y tiene un buen número de elementos configurables, como una línea vertical que delimita el ámbito de estos elementos. Es una extensión muy recomendable, así que, ya estás tardando en probarla.\nMi configuración en Visual Studio Code para esta extensión en el momento de escribir este post:\n\u0026#34;bracket-pair-colorizer-2.scopeLineRelativePosition\u0026#34;: false, \u0026#34;bracket-pair-colorizer-2.showBracketsInGutter\u0026#34;: true, \u0026#34;bracket-pair-colorizer-2.showHorizontalScopeLine\u0026#34;: false, \u0026#34;editor.matchBrackets\u0026#34;: false, \u0026#34;bracket-pair-colorizer-2.highlightActiveScope\u0026#34;: true, \u0026#34;bracket-pair-colorizer-2.activeScopeCSS\u0026#34;: [ \u0026#34;borderStyle : solid\u0026#34;, \u0026#34;borderWidth : 1px\u0026#34;, \u0026#34;borderColor : {color}; opacity: 0.5\u0026#34;, \u0026#34;backgroundColor: {color}\u0026#34; ], \u0026#34;bracket-pair-colorizer-2.scopeLineCSS\u0026#34;: [ \u0026#34;borderStyle : solid\u0026#34;, \u0026#34;borderWidth : 2px\u0026#34;, \u0026#34;borderColor : {color}; opacity: 0.6\u0026#34; ], \u0026#34;bracket-pair-colorizer-2.showBracketsInRuler\u0026#34;: true ","id":75,"tag":"VSCode","title":"¿Dónde diablos cerraba este paréntesis?","url":"https://metrosetenta.es/blog/donde-diablos-cerraba-este-parentesis/"},{"content":"If you think you are worth what you know, you are very wrong. Your knowledge today does not have much value beyond a couple of years. Your value is what you can learn and how can you easily adapt to the changes this profession brings so often.\nSi crees que vales lo que sabes, estás muy equivocado. Tu conocimiento no tiene mucho valor más allá de un par de años. Tu valor es lo que puedes aprender y cómo puedes adaptarte fácilmente a los cambios que esta profesión trae con tanta frecuencia.\n- Jose M. Aguilar\n","id":76,"tag":"","title":"El verdadero valor de un desarrollador","url":"https://metrosetenta.es/blog/el-verdadero-valor-de-un-desarrollador/"},{"content":"Es un fallo habitual, en el manejo de errores dentro de Go, realizar comparasiones de strings contra el mensaje del error que quieres reconocer, pero lo cierto es que, el modo más acertado de discernir que error estas manejando, es hacer una comparación de tipos de error dentro de una sentencia case.\n¿Quiero decir entonces que puedo gestionar distintos tipos de error? Claro que sí. Fíjate que, en Go, todo lo que inplemente la interface error es un error. Además, puedes comprobar por tí mismo que es una interface muy sencilla:\ntype error interface { Error() string } Para darte un ejemplo voy a definir un struct, ErrPizzaIsOver, el va a contener un string llamado message:\ntype ErrPizzaIsOver struct { message string } Para que este struct pueda ser usado como error debe implementar la interface que te mostré más arriba, así que voy a implementarla:\nfunc (e *ErrPizzaIsOver) Error() string { return e.message } Por último, voy a crear una función que, recibiendo como parámetro un string, me devuelva un error del tipo ErrPizzaIsOver, y configurado con dicho string como message.\nfunc NewErrPizzaIsOver(message string) *ErrPizzaIsOver { return \u0026amp;ErrPizzaIsOver{ message: message, } } Todo listo, es el momento de hacer uso de nuestro nuevo tipo de error, así que vamos a ver como quedaría todo. Observa que la magia se hace con el patrón switch err.(type).\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; \u0026#34;strconv\u0026#34; ) type pizza struct { numberOfPieces int } type ErrPizzaIsOver struct { message string } func (e *ErrPizzaIsOver) Error() string { return e.message } func NewErrPizzaIsOver(message string) *ErrPizzaIsOver { return \u0026amp;ErrPizzaIsOver{ message: message, } } func (p *pizza) eatAPiece() error { if !(p.numberOfPieces \u0026gt; 0) { return NewErrPizzaIsOver(\u0026#34;There are no more pieces of pizza\u0026#34;) } else { p.numberOfPieces-- fmt.Println(\u0026#34;Ñam, ñam...\u0026#34;) return nil } } func main() { myPizza := \u0026amp;pizza{8} fmt.Println(\u0026#34;There are \u0026#34; + strconv.FormatUint(uint64(myPizza.numberOfPieces), 10) + \u0026#34; pieces of pizza\u0026#34;) for { err := myPizza.eatAPiece() if err != nil { switch err.(type) { case *ErrPizzaIsOver: fmt.Println(err.Error()) os.Exit(0) default: fmt.Println(\u0026#34;What the f**k just happened?\u0026#34;) } } fmt.Println(\u0026#34;Now there are \u0026#34; + strconv.FormatUint(uint64(myPizza.numberOfPieces), 10) + \u0026#34; pieces of pizza\u0026#34;) } } ","id":77,"tag":"Go ; Bug","title":"Comparación directa de errores en Go","url":"https://metrosetenta.es/blog/comparacion-directa-de-errores-en-go/"},{"content":"Es un fallo habitual, en el manejo de errores dentro de Go, realizar comparasiones de strings contra el mensaje del error que quieres reconocer, pero lo cierto es que, el modo más acertado de discernir que error estas manejando, es hacer una comparación de tipos de error dentro de una sentencia case.\n¿Quiero decir entonces que puedo gestionar distintos tipos de error? Claro que sí. Fíjate que, en Go, todo lo que inplemente la interface error es un error. Además, puedes comprobar por tí mismo que es una interface muy sencilla:\ntype error interface { Error() string } Para darte un ejemplo voy a definir un struct, ErrPizzaIsOver, el va a contener un string llamado message:\ntype ErrPizzaIsOver struct { message string } Para que este struct pueda ser usado como error debe implementar la interface que te mostré más arriba, así que voy a implementarla:\nfunc (e *ErrPizzaIsOver) Error() string { return e.message } Por último, voy a crear una función que, recibiendo como parámetro un string, me devuelva un error del tipo ErrPizzaIsOver, y configurado con dicho string como message.\nfunc NewErrPizzaIsOver(message string) *ErrPizzaIsOver { return \u0026amp;ErrPizzaIsOver{ message: message, } } Todo listo, es el momento de hacer uso de nuestro nuevo tipo de error, así que vamos a ver como quedaría todo. Observa que la magia se hace con el patrón switch err.(type).\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; \u0026#34;strconv\u0026#34; ) type pizza struct { numberOfPieces int } type ErrPizzaIsOver struct { message string } func (e *ErrPizzaIsOver) Error() string { return e.message } func NewErrPizzaIsOver(message string) *ErrPizzaIsOver { return \u0026amp;ErrPizzaIsOver{ message: message, } } func (p *pizza) eatAPiece() error { if !(p.numberOfPieces \u0026gt; 0) { return NewErrPizzaIsOver(\u0026#34;There are no more pieces of pizza\u0026#34;) } else { p.numberOfPieces-- fmt.Println(\u0026#34;Ñam, ñam...\u0026#34;) return nil } } func main() { myPizza := \u0026amp;pizza{8} fmt.Println(\u0026#34;There are \u0026#34; + strconv.FormatUint(uint64(myPizza.numberOfPieces), 10) + \u0026#34; pieces of pizza\u0026#34;) for { err := myPizza.eatAPiece() if err != nil { switch err.(type) { case *ErrPizzaIsOver: fmt.Println(err.Error()) os.Exit(0) default: fmt.Println(\u0026#34;What the * just happened?\u0026#34;) } } fmt.Println(\u0026#34;Now there are \u0026#34; + strconv.FormatUint(uint64(myPizza.numberOfPieces), 10) + \u0026#34; pieces of pizza\u0026#34;) } } ","id":78,"tag":"Go ; Bug","title":"Comprobación de tipo de error en Go","url":"https://metrosetenta.es/blog/comprobacion-de-tipo-de-error-en-go/"},{"content":"Los canales son la característica que Go nos facilita para comunicar entre sí goroutines y poder sincronizar su ejecución.\nvar c chan string = make(chan string) El tipo canal se representa con la palabra chan y, a continuación, el tipo de los datos que viajaran a través de dicho canal. En el ejemplo de arriba he declarado la variable c, que contendrá un canal para transferir datos de tipo string. En la misma línea de código he usado make para crear dicho canal que, como puedes observar, se asigna a la variable c.\nPara enviar o recibir datos por el canal has de usar el signo \u0026lt;- justo delante o detrás del canal de la manera que sigue.\nc \u0026lt;- \u0026#34;Enviando por el canal\u0026#34; var mensaje := \u0026lt;- c Esta operación es síncrona, por tanto, el emisor esperará hasta que el receptor está preparado para recibir el envio. Si usas un canal con buffer, la operación será asíncrona, y por tanto, éste bloqueo sólo tendrá lugar cuando el buffer esté lleno.\nc := make(chan int, 1) ","id":79,"tag":"Go","title":"Canales en Go","url":"https://metrosetenta.es/blog/canales-en-go/"},{"content":"Go incluye un programa llamado go test que te será de gran utilidad para poner a prueba funciones de tu código. Esto es realmente útil para asegurarte de que todo funciona como debería hacerlo después de añadir características o de realizar cambios importantes en tu desarrollo. Para poder fabricar un ejemplo con el uso de go test, supongamos que has escrito un archivo llamado math.go, y que dicho archivo contiene una función que sirve para calcular el promedio de un conjunto de números:\npackage math func Average(numbers []float64) float64 { total := float64(0) for _, x := range numbers { total += x } return total float64(len(numbers)) Para preparar pruebas para esta función, sólo tienes que escribir un archivo con el mismo nombre que el archivo que contiene la función a probar, pero en lugar de finalizar en .go lo hará en _test.go. El compilador de Go ignora los archivos que finalizan de este modo cuando usas go install o go build. Como queremos fabricar pruebas para la función Average, en nuestro nuevo archivo vamos a crear una función llamada TestAverage. La función probadora debe empezar con la palabra Test para que go test la ejecute. Te muestro el código que habría en math_test.go y te sigo explicando\u0026hellip;\npackage math import \u0026#34;testing\u0026#34; func TestAverage(t *testing.T) { result := Average([]float64{2,4}) if result != 3 { t.Error(\u0026#34;Expected 3, got \u0026#34;, result) } } Después de definir el paquete, que es el mismo que el del archivo que contiene la función que estamos probando, has de importa el paquete testing, porque las funciones probadoras usan el tipo *testing.T como parámetro. Dentro de la función probadora, TestAverage, tienes que usar la función que va a ser probada, que en este caso es la función Average. La idea es sencilla, se trata de que le pases a la función Average unos parámetros que generen unos valores de salida que conozcas de antemano, y así podrás comparar los valores devueltos por la función que estás probando con los que tú ya sabes que debería devolver. En el ejemplo se pasan los valores 2 y 4, cuyo resultado previsto, su promedio, es 3. En el caso de que la función Average no devolviera 3 se usa t.Error, que es similar a fmt.Println, con la finalidad de indicarle al programa go test que se ha detectado un error.\nTodo listo, ahora sólo te resta ejecutar go test, el cual te debería mostrar un resultado similar al que sigue.\n$ go test PASS ok goWorkspace/src/Repository/Project/math 0.029s ","id":80,"tag":"Go ; Testing","title":"Go testing","url":"https://metrosetenta.es/blog/go-testing/"},{"content":"Ya has visto como configurar componentes con props en React, pero ¿cómo puedes definir comportamientos o interacciones con dichos componentes? Para ello necesitas que los componentes puedan definir su estado, y con las props es imposible, pues no pueden ser modificadas por el componente, sólo por su componente padre. Para esto existe this.state, donde los componentes pueden disponer de datos privados modificables a traves de la función this.setState().\nYa sabes que función usar para ajustar this.state, pero ¿Cómo podrías definir un estado inicial del componente? Bien, pues para eso disponemos del constructor de clase. En la función render comprobamos el estado del componente para decidir como va a ser mostrado, y ahora viene la magia: cuando modificamos el estado de un componente, automáticamente se volverán a renderizar todos sus componentes hijos y el propio componente. Esto, además, es bastante ágil gracias al DOM virtual de React.\nclass Dashboard extends Component { constructor() { super(...arguments); this.state = { showAdvancedFeatures: false } } render(){ let advancedFeatures; if(this.state.showAdvancedFeatures){ advancedFeatures = ( \u0026lt;AvancedFetaures/\u0026gt; ) } return ( \u0026lt;div className=\u0026#34;dashboard\u0026#34;\u0026gt; \u0026lt;BasicFeatures/\u0026gt; {advancedFeatures} \u0026lt;/div\u0026gt; ); } } ","id":81,"tag":"React","title":"State en React","url":"https://metrosetenta.es/blog/state-en-react/"},{"content":"React es una biblioteca de JavaScript que se basa en la fabricación y combinación de componentes para construir interfaces de usuario. Cada componente se define por separado en un archivo javascript, y tiene un aspecto similar a este que sigue, el cual se encontraría en el archivo listaCompra.js:\nimport React, { Component } from \u0026#39;react\u0026#39;; import Articulo from \u0026#39;./Articulo\u0026#39;; class ListaCompra extends Component { render() { return ( \u0026lt;ul\u0026gt; \u0026lt;Articulo cantidad=\u0026#34;1\u0026#34;\u0026gt;Pan\u0026lt;/Articulo\u0026gt; \u0026lt;Articulo cantidad=\u0026#34;6\u0026#34;\u0026gt;Leche\u0026lt;/Articulo\u0026gt; \u0026lt;Articulo cantidad=\u0026#34;2\u0026#34;\u0026gt;Huevos\u0026lt;/Articulo\u0026gt; \u0026lt;/ul\u0026gt; ); } } Las propiedades, que veremos en el código escritas como props, se usan para configurar componentes, y son pasadas desde los componentes padres a los componentes hijos. Es importante recordar que el componente hijo no puede modificarlas, el propietario es el componente padre.\nEn el ejemplo anterior, el componente ListaCompra usa otro componente que aún no hemos escrito, el componente Articulo. En el JSX que devuelve su método render las props son pasadas al componente hijo como etiquetas de atributos similares a las de HTML. En nuestro caso se está pasando la propiedad cantidad. El contenido entre las etiquetas de apertura y cierre también se puede usar como propiedad del componente hijo. En ListaCompra se trata del nombre del artículo.\nimport React, { Component } from \u0026#39;react\u0026#39;; class Articulo extends Component { render() { return ( \u0026lt;li\u0026gt; {this.props.cantidad} × {this.props.children} \u0026lt;/li\u0026gt; ); } } ","id":82,"tag":"React","title":"Props en React","url":"https://metrosetenta.es/blog/props-en-react/"},{"content":"Otro recurso importante que vas a necesitar en Vim es un buscador de textos. Ante el hecho de que el proyecto que permitía el uso de El surfero de plata en vim, ag.vim, ha quedado obsoleto, te podrías decantar por ack.vim. Con ack.vim puedes hacer uso de ack, que no es ni más ni menos que un grep optimizado para programadores.\nInstalar ack es bastante sencillo en distribuciones Linux deribadas de Debian.\n$ sudo apt-get install ack-grep Si eres usuario de Debian la pega es que, como el paquete ack ya existe, ack es empaquetado como ack-grep. Esto supone teclear más al usar el comando en consola, pero lo puedes solucionar usando las desviaciones de la siguiente manera:\n$ sudo dpkg-divert --local --divert /usr/bin/ack --rename --add /usr/bin/ack-grep Listo, ya dispones de la herramienta ack. Ahora, para poder usarla en tu editor Vim, sólo añádela como un Plugin de un modo similar a como hicíste con fzf aquí.\n\u0026#34; ¡Cualquier URL de git es admitida por vim-plug! Plug \u0026#39;https://github.com/mileszs/ack.vim\u0026#39; Luego en Vim, ejecuata el comando PlugInstall ack.vim. Está bien familiarizarse con los comandos de vim-plug, así que échales un vistazo aquí.\n¿Cómo se usa ack en Vim? Sencillo\u0026hellip;\n:Ack [options] {pattern} [{directories}] Y aquí están los atajos disponibles con esta herramienta.\n","id":83,"tag":"Vim","title":"Buscador de texto en Vim","url":"https://metrosetenta.es/blog/buscador-de-texto-en-vim/"},{"content":"fzf se define a sí mismo como un buscador difuso de línea de comandos para propósitos generales. Más detalladamente, se trata de un filtro para líneas de comandos Unix, que puedes aplicar a listas, archivos, procesos, históricos de comandos, etcétera.\nLo puedes instalar en tu carpeta home clonando su repositorio en GitHub de la siguiente manera:\n$ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf Luego sólo tienes que ejecutar el script de instalación:\n$ ~/.fzf/install Puedes probar este potente filtro en emulador de terminal accediendo a un directorio que contenga varios archivos y ejecutando el comando fzf. Luego prueba a introducir criterios de búsqueda y obseva como fzf los aplica a los nombre de los archivos, de un modo interactivo, para ofrecerte aquellos que contienen coincidencias con el texto que estás facilitando.\nAhora viene lo mejor, y es que fzf puede ser usado como un conectable de Vim. Si usas el gestor de conectables vim-plug, que aprendimos a instalar aquí, es muy sencillo preparar nuestro buscador de archivos, sólo habría que añadir las siguiente línea a los ajustes de vim-plug en el archivo de configuración de Vim:\nPlug \u0026#39;junegunn/fzf\u0026#39;, { \u0026#39;dir\u0026#39;: \u0026#39;~/.fzf\u0026#39;, \u0026#39;do\u0026#39;: \u0026#39;./install --all\u0026#39; } Una vez que abras Vim dispondrás del comando FZF en él, pero para ejecutarlo de un modo mucho más rápido puedes asignarle una combinación de teclas, por ejemplo Ctrl+p, en el archivo mismo archivo de configuración.\nnnoremap \u0026lt;c-p\u0026gt; :FZF \u0026lt;cr\u0026gt; ","id":84,"tag":"Vim","title":"Búsqueda de archivos en vim","url":"https://metrosetenta.es/blog/busqueda-de-archivos-en-vim/"},{"content":"vim-plug es un gestor de paquetes para Vim con características muy interesantes. Lo tienes disponible aquí, donde puedes encontrar toda la información necesaria para usarlo. Su instalación en sistemas Unix se puede realizar con un sencilo comando:\n$ curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \\\\ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim Una vez instalado sólo tienes que ajustarlo en el archivo de configuración de Vim. Aqui abajo tienes un ejemplo con alguna información:\n\u0026#34; Inicia la configuración usando el comando call plug#begin(), \u0026#34; con el cual puedes especificar el directorio donde se colocarán \u0026#34; los conectables que queremos usar en nuestro editor. \u0026#34; En el caso concreto de Vim puedes usarlo así. call plug#begin(\u0026#39;~/.vim/plugged\u0026#39;) \u0026#34; A continuación se solicitarán los conectables necesarios. Hay muchos \u0026#34; modos de hacerlo, por ejemplo, usando una dirección URL de Github. Plug \u0026#39;https://github.com/junegunn/vim-github-dashboard.git\u0026#39; \u0026#34; Finalmente cierra el archivo de configuración con el \u0026#34; comando call plug#end(). call plug#end() Para probarlo abre Vim y ejecuta el comando PlugInstall. Verás un panel lateral donde se iniciará la instalación de los conectables indicados en el archivo de configuración de Vim.\n","id":85,"tag":"Vim","title":"Cómo instalar vim-plug para Vim","url":"https://metrosetenta.es/blog/como-instalar-vim-plug-para-vim/"},{"content":"Estoy convencido de que tienes un estilo personal para organizar tu código, pero cuando trabajas con Go, es conveniente que te adaptes al estilo de su wokspace. El workspace de Go es una carpeta que se compone, a su vez, de otras tres carpetas: bin, pkg y src. Mi workspace, o espacio de trabajo, para los proyectos escritos en Go es ~/Documents/projects/go, por tanto, ahí creé la carpeta src, en la cual guardo los distintos trabajos escritos en este lenguaje.\n... ~/Documents/projects/go/source/project1 ~/Documents/projects/go/source/project2 ... Cuando elijas cual será tu workspace es fundamental que configures Go para que conozca cual es su ubicación. Para esto, únicamente tienes que apuntar la variable de entorno $GOPATH a dicha dirección. En mi caso:\n$ export GOPATH=$HOME/Documents/projects/go En el workspace habrá una carpeta que contenga binarios, así que no olvides añadirla a tu $PATH:\n$ export PATH=$PATH:$GOPATH/bin Por último, es recomendable que las añadas al archivo de configuración de tu terminal para, de este modo, no tener que definirlas repetidamente al iniciarlo.\n","id":86,"tag":"Go","title":"El workspace de Go","url":"https://metrosetenta.es/blog/el-workspace-de-go/"},{"content":"En entradas anteriores te comenté el origen de los errores de software y realicé una pequeña y divertida clasificación. Hoy sigo con la temática bichos para recomendarte una aproximación interesante al modo de gestionar aquellos errores que, en ocasiones, puedan aparecer en tu código. Hay cuatro preguntas fundamentales que deberías hacerte a ti mismo para definir tu estrategia en lo referente a la gestión de cada casuística de error en tu desarrollo:\n1. ¿Voy a mostrar estos errores? Si bien es cierto que en un entorno de desarrollo esto es más que necesario hacerlo, en producción, el hecho de mostrar los errores de nuestra aplicación con toda la información que pueden facilitar es, cuando menos, una invitación a hackearla. Añade a esto que tu desarrollo parecerá mal diseñado y poco profesional a ojos de los usuarios y clientes. Es fundamental, por tanto, que puedas manejar la visibilidad de las notificaciones de error de tu aplicación según el entorno en el que se esté ejecutando, y que nunca se muestren en producción.\n2. ¿Voy a registrar estos errores? Anotar las notificaciones de error en un registro donde poder consultarlas más tarde es un modo excepcional para rastrear posteriormente sus causas. También disponemos de la posibilidad de, por ejemplo mediante syslog, realizar esta gestión a través del sistema operativo.\n3. ¿Voy a ignorar estos errores? Pueden darse errores que no resulten críticos para tu aplicación, como puede ser el intento de lectura de un archivo de configuración inexistente. En este caso, la función de lectura podría lanzar un error crítico que detuviera la ejecución de tu aplicación, sin que el usuario haya recibido ningún tipo de información sobre dicha interrupción. En este caso puede ser interesante ignorar el error para preservar la ejecución.\n4. ¿Voy a actuar frente a estos errores? Seguimos con el caso anterior. Al ignorar el error has evitado un cierre inesperado de la aplicación. Con el fin de facilitar un poco más de información a tu usuario, una vez echo esto, hay que comprobar si disponemos del archivo y podemos hacer uso de él. En el caso que comentamos, no es así, pero ahora podemos informar al usuario de que no ha sido posible disponer del archivo de configuración, informar de posibles causas y soluciones y, a continuación, cerrar nosotros mismos la aplicación de un modo elegante.\nLa combinación de respuestas frente a estas cuatro preguntas te ayudarán a determinar, de un modo sencillo, tu estrategia de gestión para cada casuística de error.\n","id":87,"tag":"Bug","title":"Cuentos de bichos: Estrategia de gestión de errores","url":"https://metrosetenta.es/blog/cuentos-de-bichos-estrategia-de-gestion-de-errores/"},{"content":"Heisenbug Fisonomía Toma su nombre del físico especializado en mecánica cuántica Werner Heisenberg, quien dedujo que el mero hecho de observar un sistema de una manera determinada altera el estado de éste. Cuando se usan herramientas durante el proceso de depuración para localizar y eliminar bichos, se producen pequeñas diferencias respecto al entorno de producción, es decir, con respecto al entorno final en el cual deberá funcionar nuestro dispositivo de software. El Heisenbug es especialista en aprovechar cualquiera de estas leves diferencias para mostrar comportamientos distintos que confundan al programador o para simplemente desaparecer.\nHabilidad principal Su principal fortaleza consiste en la capacidad de mutar o desaparecer cuando se siente amenazado por un depurador.\nCapacidad de destrucción Es especialmente destructivo con respecto a los compromisos de entrega de software, pues puede elevar muchísimo los tiempos de depuración. Se conocen casos de programadores que, víctimas de la desesperación, han propuesto depurar en el entorno de producción para localizar y destruir a este esquivo bicho.\nBohrbug Fisonomía Debe su nombre al modelo atómico que desarrolló el físico Danés Niels Bohr para explicar, entre otros hechos, la estabilidad de la materia. El Bohrbug puede atormentar amargamente al cerebro lógico de un desarrollador, pues tan lógico seso no comprenderá como dicho bug sigue mostrándose una y otra vez, impasible, ante las innumerables correcciones realizadas en el código con el ánimo de destruirlo.\nHabilidad principal Es conocido por su capacidad de permanecer inalterable ante cualquier acción de depuración.\nCapacidad de destrucción Como suele presentarse en etapas tempranas, y se detecta con facilidad en pruebas y depuraciones, a lo que ataca principalmente es al orgullo y a la paciencia de los programadores.\nMandelbug Fisonomía El conjunto fractal descubierto por Benoit Mandelbrot, un monstruo matemático de gran complejidad, inspiró el nombre de este bicho. Su modo de operar consiste en cimentar su existencia en causas tan complejas que sea difícil comprenderlas y atacarlas. Debido a esto, su comportamiento parece caótico, pues tras cada solución aplicada en el código, únicamente se conseguirá que el bicho actúe de un modo distinto, pero no quedará eliminado.\nHabilidad principal Es capaz de formarse a sí mismo a partir de una combinación de pequeños detalles en muchísimas porciones de código aparentemente inconexas. Dar con la combinación que lo sustenta puede llegar a ser una ardua tarea de ingeniería inversa.\nCapacidad de destrucción No es un bicho que se muestre en las últimas fases del desarrollo, así que se detecta a tiempo para depurarlo, pero es ahí donde radica su máximo poder. Destruir un Mandelbug puede ocasionar que se deba corregir muchas porciones de código, creando otros tipos de bugs distintos y alterando la arquitectura y la lógica de la aplicación. Puede, por tanto, herir gravemente e incluso malograr un proyecto a la mitad de su desarrollo.\nSchroedinbug Fisonomía Schrödinger describió una paradoja según la cual, teniendo un gato en una caja, no podemos saber si está vivo o muerto hasta abrir dicha caja, y por lo tanto se considera que se encuentra vivo y muerto a la vez hasta que la abramos. Esta paradoja aplica a la perfección a los Schrodinbugs, pues dichos bichos no se manifiestan hasta que alguien, leyendo el código o usando el programa de un modo poco habitual, descubre que nunca debería haber funcionado. ¡Desde ese momento, la caja queda abierta, y en el programa se muestra una y otra vez el bug que parecía no haber existido nunca!\nHabilidad principal Su capacidad de permanecer invisible el mayor tiempo posible y su omnipresencia una vez determinada la certeza de sus existencia.\nCapacidad de destrucción Depende de la parte que ataque de nuestra aplicación, pero el principal peligro que acarrea este tipo de bichos es la dificultad de una detección prematura, lo cual lo hace muy peligroso.\nStotle Fisonomía Es más que un simple bicho, es la idea de bicho atormentando la mente de un desarrollador que ha sido engañado, confundido. Se trata de introducir, sin ser conscientes, datos incorrectos en la entrada del programa. Al percibir que la salida no es correcta, y estando convencidos de que la entrada si lo era, comenzamos a buscar un bicho que no existe, y que ocupará durante un buen rato nuestro tiempo y nuestra mente. Es como si teclearas en una calculadora la multiplicación de dos por tres, cuando realmente quisiste teclear la suma de estos números. Al ver el resultado de seis en lugar de cinco dudarás del correcto funcionamiento del artilugio, e intentarás arreglar algo que no estaba roto. Su nombre deriva de Aristóteles (Aristotle) porque del mismo modo que Aristóteles no era cuestionado antiguamente, y se asumía que debía estar en lo cierto, el programador no cuestiona la entrada de datos, asumiendo que hay un bicho en el programa.\nHabilidad principal Hacer creer a los programadores que existe.\nCapacidad de destrucción Dado que no es más que una idea de bug, no existe, no es peligroso para el proyecto en sí. No obstante, puede desquiciar a algún desarrollador.\nBugs de fase lunar Fisonomía Son aquellos bichos que se muestran por los motivos más aleatorios o esotéricos que se puedan imaginar. Son fallos en el programa que únicamente suceden cuando la lluvia cae sobre los cristales de las ventanas de la oficina, cuando la chica rubia de recursos humanos pasa por delante de la puerta del despacho donde trabaja el equipo de desarrollo, cuando el aire acondicionado está a veinticuatro grados\u0026hellip;\nHabilidad principal No pertenecen al mundo lógico y racional del desarrollo de software.\nCapacidad de destrucción Mientras no exista dentro del endémico grupo de probadores o depuradores una especialización en exorcismos de dispositivos de software, o algo similar, estos bichos harán estragos allá donde quieran pulular.\nFantasmas en el código Fisonomía Estos bichitos suelen ocultarse en rutinas o subprogramas que se usan en raras ocasiones y, a diferencia del Schroedinbug, no es necesario detectarlos para que se materialicen. Son muy difíciles de descubrir en las fases de desarrollo o pruebas.\nHabilidad principal Se mostrarán motu proprio en el peor momento posible, ya sea cuando estemos presentando el proyecto finalizado a nuestro cliente o cuando lo hayamos puesto en producción.\nCapacidad de destrucción Debido a su habilidad para ocultarse y aparecerse en el momento más inconveniente, pueden herir principalmente la imagen del equipo de desarrollo.\n","id":88,"tag":"Bug","title":"Cuentos de bichos: Bestiario","url":"https://metrosetenta.es/blog/cuentos-de-bichos-bestiario/"},{"content":" La polilla, una de esas pequeñas mariposas nocturnas que se sienten atraídas por la luz y cuya larva destruye la materia donde anida, es un lepidóptero. En fin, que es un bicho. En telegrafía, y en las primeras compañías telefónicas, era habitual usar la expresión \u0026ldquo;bichos en el cable de teléfono\u0026rdquo; para referirse al ruido y las interferencias en la comunicación. Si bien es cierto que la palabra inglesa «bug» ya fue usada para indicar un defecto industrial, y que el mismísimo Thomas Edison dispuso de ella con esta finalidad, es probable que fuera Grace Murray Hopper quien la aplicó por vez primera al ámbito de la informática. Ésta licenciada en física trabajaba como programadora en el Mark II, uno de esos primeros ordenadores que estaba formado por componentes electrónicos y electromecánicos, cuando investigando la causa de un fallo en el mastodóntico computador de IBM, halló una polilla bloqueando el relé 70 del panel F. El hecho se documentó de modo metódico, y el insecto quedó inmortalizado en una foto junto a un texto que rezaba \u0026ldquo;primera vez que se encuentra (en un ordenador) un «bug» de verdad\u0026rdquo;. Así comenzó la leyenda del primer bug informático conocido.\nEn la historia del sofware han sido muchos, y de muy diversa índole, los bichos que han querido alcanzar la fama que ostenta en el mundo de la ingeniería informática la polilla del relé 70. Los bichos han procurado realizar incursiones en los desarrollos de software desde la primera línea escrita de código, no discriminan ninguna posibilidad. Procuran infectar todo tipo de proyectos, tanto aquellos en los cuales apenas tienen el potencial de provocar pequeños inconvenientes, como eso proyectos susceptibles de costar tres billones de dolares a una empresa de automoción; o como aquel caso, en el que uno de esos malévolos bichos pudo haber desencadenado la tercera guerra mundial: En 1983 el sistema de alerta temprana de la Unión Soviética había detectado el lanzamiento de cinco misiles balísticos, el «bug» que se había colado dentro de aquel sistema hizo que éste confundiera el reflejo del sol sobre unas nubes con unos amenazantes proyectiles. El oficial soviético que se encontraba al mando en ese momento dedujo fríamente que, siendo real el ataque, el número de venablos a cohete superaría cinco con creces. De no ser por su acertada decisión de no reaccionar ante la señal de alerta, el ficticio ataque habría recibido una represalia difícil de explicar a la comunidad internacional.\nPara realizar estas correrías los «bugs» aprovechan cualquier resquicio: falta de conocimientos, poca experiencia, descuidos, momentos de baja concentración\u0026hellip; Si bien es cierto que hay quien define que la expresión \u0026ldquo;sofware libre de «bugs»\u0026rdquo; es un oxímoron, y que desarrollar sin cometer ningún error que les abra la puerta a estos pequeños enemigos es utópico, hay métodos para combatirlos y reducir su presencia lo máximo posible. Llegados a este punto, es obligatorio nombrar a aquellos que defienden el uso de la programación funcional como modo de evitar los estados mutables (especialmente cuando el proyecto tiende a una explosión combinatoria), reduciendo drásticamente la presencia de «bugs», pero lo que realmente viene a la cabeza al pensar en un modo de desarrollar evitando bugs son los modelos de desarrollo. Uno de los primeros modelos que se nombra cuando se toca este tema es el desarrollo guiado por pruebas (TDD por sus siglas en inglés), en el cual se describen las pruebas de que el código funciona sin errores antes que el propio código. Las técnicas y principios del TDD fueron combinadas posteriormente con los análisis y diseños propios de la programación orientación a objetos (OOP) y el diseño guiado por dominios (DDD) para dar lugar al desarrollo guiado por comportamientos (BDD), el cual es, con casi toda seguridad, el más extremo y completo método de desarrollo de software en lo que a evitar «bugs» se refiere. Este modelo de desarrollo exige un fuerte apoyo en herramientas como, por ejemplo, Cucumber, lo cual puede conllevar un gasto económico extra que, sumado al inevitable aumento del tiempo de desarrollo, hace pensar que no es apto para todos los proyectos.\nAunque hay proyectos que exigen un aumento del nivel de atención a la gestión de posibles «bugs», éste siempre debe ser, por defecto, lo más elevado posible. A modo de símil, es obvio que no es necesario el mismo nivel de asepsia en el entorno en el se cura una rozadura que en el que se opera a corazón abierto; no obstante, en el caso más laxo de los dos, es indiscutible que también se toman unas precauciones mínimas de higiene. No es necesario usar el mismo modelo de desarrollo para añadir un módulo personalizado a un blog en Worpress y para poner en funcionamiento la transitada tienda web de una empresa de ropa deportiva, la cual conlleva un elevado número de transacciones económicas y no es un proyecto tan inocuo como el primero, pero el nivel mínimo de prevención de «bugs», desde el cual partiremos en ambos casos, ha de ser suficientemente elevado; el error imperdonable, y que nunca se ha de cometer, es rebajar nuestra cota de intolerancia a los «bugs» en pos de la productividad. Una buena conclusión a todo lo expuesto sería la que sigue: En el momento de iniciar un proyecto debemos determinar cuanto es necesario aumentar nuestro estándar de intransigencia a los «bugs» para evitar mariposas en el código.\n","id":89,"tag":"Bug","title":"Cuentos de bichos: Mariposas en el código","url":"https://metrosetenta.es/blog/cuentos-de-bichos-mariposas-en-el-codigo/"},{"content":"En su libro “Advanced PHP programming”, George Schlossnagle hace la siguiente afirmación:\nEn mi empresa, el código producido para los clientes no se considera completo hasta que toda su interfaz externa de programación de la aplicación (API) y cualquier idiosincrasia interna están totalmente documentadas.\nEsta declaración de Schlossnagle es una buena señal de lo importante que puede llegar a ser la gestión de la documentación del código fuente dentro de un proyecto de desarrollo de software.\nAntes de proseguir, habría que discernir entre la documentación del proyecto y la documentación del código.\nLa primera puede contener requerimientos, casos de uso, diagramas y otros tipos de documentos cuya finalidad es definir el producto que se desea obtener y el modo de hacerlo. Sin embargo, la documentación del código, que es la que se trata en este breve texto, forma parte del producto final como se puede deducir de la frase de Schlossnagle.\nAdemás, este mismo autor divide la documentación del código en dos tipos: Los comentarios que los desarrolladores escriben junto a sus líneas de código explicando cómo funciona y los documentos donde se detalla que es lo que hace nuestro código. Haciendo un símil con la documentación de un aparato electrónico, el primer tipo descrito representaría el manual de reparaciones para los empleados del soporte técnico o datasheet, y el segundo tipo coincidiría con las instrucciones de uso para el usuario.\nUn ingeniero en Electrónica, con toda seguridad, es capaz de desarticular un aparato electrónico y, descifrando el modo en el que se ha diseñado que sus componentes interactúen, entender cómo consigue realizar la función para la que dicho aparato fué fabricado. A esta acción de deducir como fue fabricado un producto y qué lo hace funcionar se le llama ingeniería inversa, y es un proceso difícil y muy laborioso. Nuestro ingeniero también sería capaz de realizar modificaciones en el artilugio para mejorarlo o personalizarlo según nuestras necesidades, pero si le facilitamos un datasheet, una documentación técnica, el tiempo que necesitará para hacerlo disminuiría obstensiblemente.\nSi en nuestros desarrollos de software nos aseguramos de que todo el equipo gestiona el primer tipo de documentación de código que mencionábamos, a saber, la documentación técnica sobre cómo funciona, vamos a obtener beneficios de rendimiento, pues las revisiones de código y la depuración de errores se realizarán de un modo ágil. Estos comentarios dentro del mismo código son equiparables al mencionado datasheet de un producto electrónico, y nos ayudarán a recordar rápidamente el modo en el que habíamos ideado un código antiguo si necesitamos ojearlo más tarde para hacer mejoras en él. Definitivamente, facilita la compresión del código sin necesidad de hacer una durísima ingeniería inversa, independientemente de que el código fuera escrito por nosotros o por otro desarrollador.\nPara ejecutar un desarrollo de sofware de calidad, especialmente cuando lo queremos dotar de una API externa, también es indispensable una documentación donde se pueda acceder a la información sobre qué trabajo realiza cada elemento, y sobre qué es lo que necesita para realizarlo; es decir, necesitamos poder comprender el uso y el funcionamiento de los componentes programados sin necesidad de ojear su código fuente. Gestionar la documentación API, el manual de instrucciones de uso, es también una ayuda en el momento del desarrollo: en ocasiones, documentando clases de código recién escritas, he descubierto que la lógica usada no era buena, y he podido rehacer mi trabajo a tiempo para mejorarlo. El hecho de que una tecnología como Java incorpore el sistema de documentación automática JavaDoc, y que dicho sistema haya sido imitado para otros lenguajes de programación, nos muestra la importancia de esta documentación tanto para el desarrollo como para el uso de nuestro producto final.\nImaginemos ahora que adquirimos un robot aspirador automático, uno de esos aparatos que pasean solos por nuestra casa recogiendo la suciedad del suelo mientras nosotros nos podemos dedicar a cualquier otra tarea. Seguro que el pequeño barrendero electrónico dispone de una gran cantidad de parámetros para poder configurar la tarea de limpieza: programar la hora de inicio, la intensidad, la eficiencia, la hora de apagado, etcétera. ¿Cuál sería tu impresión si, al sacarlo de su caja nuevecito y listo para estrenar, no pudieras localizar en el aparato ningún botón o interruptor y comprobaras, además, que no dispone de manual de instrucciones? Recibir un producto software con una API externa sin documentar es algo muy parecido a que te regalen un enorme puck de hockey con la promesa de que limpiará tu hogar.\n","id":90,"tag":"Código profesional","title":"Sin documentos no hay hockey","url":"https://metrosetenta.es/blog/sin-documentos-no-hay-hockey/"},{"content":"Pues eso, que me gusta tabular blandito. Seguro que coincides conmigo en que sangrar el código, es decir, comenzar ciertos renglones más a la derecha que otros, es un modo más que efectivo de organizar y facilitar su lectura. Tanto es así, que esta técnica forma parte de la sintaxis del lenguaje de programación Python.\nLa tecla tabulador de las antiguas máquinas de escribir, al ser pulsada, introducía automáticamente una cantidad determinada de espacios. Actualmente, con el uso de las computadoras, en prácticamente todos los editores de texto podemos elegir entre tabular blando o duro. La primera opción imita el funcionamiento de las antiguas máquinas de escribir: introducir una cantidad determinada de espacios. Esto quiere decir que tabular en cuatro caracteres es lo mismo que pulsar la barra de espacio cuatro veces. En el segundo caso, con la tabulación dura, el espacio sangrado funciona como un bloque indivisible de cuatro espacios. Aunque visualmente nos van a resultar idénticos voy a mostrate a continuación que no lo son y, por este motivo, la elección del tipo de tabulación es importante en los desarrollos de software.\nUn código sangrado con tabulación dura dispone de la ventaja de poder ser leído de un modo más flexible. Aunque originalmente fuera escrito con tabulaciones de hasta cuatro caracteres de longitud, si prefieres que tengan el tamaño de ocho, pues lo ajustas en la configuración de tu editor de texto y listo. Será el propio editor quien se encargue de convertir todas las tabulaciones duras para que se vean con una longitud de ocho caracteres. Las tabulaciones blandas, como realmente son espacios, pues no se verán afectadas.\n¿Qué por qué me gusta tabular blandito? Pues porque en los desarrollos de software se combina habitualmente tabulaciones y espacios, y mediante la tabulación blanda, el look and feel del código permanece inalterado independientemente del editor usado por cada desarrollador y sus preferencias.\n","id":91,"tag":"Código profesional","title":"Tabular blandito","url":"https://metrosetenta.es/blog/tabular-blandito/"},{"content":"Te presento una técnica bastante extendida entre los programadores anglosajones de lenguaje C, y que se podría traducir como código andamio. Consiste en definir, mediante una directiva del preprocesador, una constante durante el código fuente. En el caso que nos ocupa se le suele nombrar DEBUG y el valor puede ser, por ejemplo, 1.\n#define DEBUG 1 A partir de ahora, puedes crear tus propios puntos de testeo del siguiente modo.\n#ifdef DEBUG print(\u0026#34;Valor de variable_testeada = \u0026#34;); print(variable_testeada); #endif Puedes usar la siguiente directiva para desactivar los condicionales anteriores en aquellas partes del código que ya estén testeadas y corregidas.\n#undef DEBUG Y, por último, una vez testeado y corregido todo el código, pues quitamos el andamio. Es decir, eliminamos #define DEBUG 1 y en los archivos objeto no quedará ni rastro.\n","id":92,"tag":"C","title":"Código andamio","url":"https://metrosetenta.es/blog/codigo-andamio/"},{"content":" Piensa en tu placa Arduino ejecutando alegremente y ad libitum el bloque Loop que le ordenaste. En un momento dado necesitas que lo deje todo y realice una acción concreta. Por ejemplo, un láser que al ser interrumpido activa una alarma. Bien, pues para conseguir esto disponemos de un tipo de función avanzada que se llama ISP (Rutina de interrupción de servicio).\nVayamos por partes. Lo primero que necesitamos es adjuntar un interruptor a nuestro código. Para ello usamos la función attachInterrupt(interruptor, función, modo). En arduino Uno podemos usar los pines 2 y 3 para conectar el hardware que ordenará la ejecución de nuestra ISP. En el primer parámetro esta función, el parámetro interruptor, debemos aclarar de que interruptor estamos hablando, por tanto escribe 0 si tu láser está conectado en el pin 2, o escribe 1 si lo está en el pin 3.\nEl parámetro función espera que le indiquemos la función que debe ejecutarse cuando el interruptor hardware (nuestro laser) así lo indique. Para nuestro ejemplo habremos preparado la función activarAlarma(), la cual habremos definido en otro momento.\nVamos con el parámetro modo. Podemos elegir que activarAlarma() se ejecute mientras no recibimos señal en nuestro pin (LOW), cuando la señal cambie de bajo a alto (RISING), cuando cambie en el sentido contrario (FALLING), o simplemente cuando cambie, independientemente de la dirección (CHANGE). Visto todo esto, nos queda así la función: attachInterrupt(0, activarAlarma(), RISING); Si más adelante en tu código necesitas desactivar este interruptor de servicio, puedes usar la función detachInterrupt(interruptor), que sólo necesita como parámetro el pin donde está conectado nuestro interruptor hardware. En nuestro caso quedaría así:\ndetachInterrupt(0); ¡y alarma desactivada!\n","id":93,"tag":"Arduino","title":"Interruptores hardware en Arduino","url":"https://metrosetenta.es/blog/interruptores-hardware-en-arduino/"},{"content":"Cuando recibimos los diferentes niveles de intensidad eléctrica de un sensor en uno de nuestros pines analógicos, el Analogic to digital converter (modificador de analógico a digital) de nuestro microcontrolador transforma esa intensidad en un dato digital almacenado en 10 bits. Por tanto, tenemos una escala de valores posibles desde 0 hasta 1023, donde 0 equivale a 0 voltios y 1023 equivale a 5 voltios (o el valor máximo establecido con la función analogReference()).\nAl igual que tu paleta de pádel, los sensores suelen disponer de un punto dulce. Imagina un sensor de temperatura que nunca baja de una lectura de 20, haga el frio que haga, y que cuando se encuentra en el infierno no pasa casi nunca del valor 900. Ahí es donde nos interesa asegurarnos de que todos los valores obtenidos estarán en el punto dulce de nuestro sensor, y para ello usaremos una línea de código como la que sigue:\nconstrain(valorDelSensor, 20, 900); Con esta función nos aseguraremos de que todos los valores devueltos por el sensor estarán dentro de ese rango, pues una lectura ocasional de 18 quedará en 20 y una lectura ocasional de 901 quedará en 900. Es muy útil para, más tarde, mapear los valores de nuestro sensor a un pin digital y mostrarlos a través de, por ejemplo, una pantalla LCD.\n","id":94,"tag":"Arduino","title":"Función constrain() para arduino","url":"https://metrosetenta.es/blog/funci%C3%B3n-constrain-para-arduino/"},{"content":"Al usar una etiqueta para crear un enlace en HTML y hacerlo funcionar como un botón, serás capaz de observar que el área donde el usuario puede cliquear coincide con la altura y ancho del texto del enlace. Esto se debe a que esta etiqueta se visualiza en línea de forma predeterminada, pero es posible modificarla para darle un diseño más atractivo de cara al usuario cambiando el tipo de visualización, en el código CSS del enlace, al tipo bloque:\na {display: block} A menos que especifiques un ancho específico con la propiedad width, la transformación del enlace en un elemento de tipo bloque hace que éste abarque todo el ancho del elemento en el que se encuentra contenido. Esta modificación lo hace idóneo para los enlaces ubicados en una barra lateral o para los enlaces de un menú desplegable.\nPara terminar, como la conversión que hemos hecho al enlace sólo afecta a su comportamiento y su ancho, termina añadiendo un buen relleno con la propiedad padding. Haciendo esto consigues que la altura del botón sea más adecuada a su contenido y el elemento respirará, es decir, no dará una sensación claustrofóbica respecto al texto visible en su interior.\nRealiza pruebas y combina estas ideas, seguro que te ayudarán a encontrar un estilo propio. Yo te añado aquí abajo una idea para que veas como puedes mejorar fácilmente el aspecto de este elemento.\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;a href=\u0026#34;./\u0026#34;\u0026gt;Para pulsar\u0026lt;/a\u0026gt; \u0026lt;br/\u0026gt;\u0026lt;br/\u0026gt; \u0026lt;a style=\u0026#34;color: white; display: block; padding: 6px; width: 70px; border: 1px solid black; box-shadow: 3px 3px 1px #888888; text-decoration:none; background-color: #088;\u0026#34; href=\u0026#34;./\u0026#34;\u0026gt; Para pulsar \u0026lt;/a\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; ","id":95,"tag":"UI/UX","title":"Usando la etiqueta de enlace como botón","url":"https://metrosetenta.es/blog/usando-la-etiqueta-de-enlace-como-boton/"},{"content":"Vamos a abordar juntos otro par de párrafos sobre SQL injection. En esta ocasión te escribo sobre una variante de esta técnica que se basa en la posibilidad de ejecutar varias consultas, usando el estándar SQL, separadas entre sí con el signo de puntuación punto y coma (;).\nCon una pequeña y rápida prueba, un hacker puede comprobar si nuestro formulario es vulnerable a SQL injection, y si debe usar una comilla sencilla (‘) o comillas dobles (“). Una vez hecho esto únicamente ha de separar el valor legítimo del código malicioso usando dicha comilla, y con el punto y coma finalizar la consulta legítima para iniciar una nueva consulta con la función que el hacker desee realizar sobre la base de datos. Parece difícil, pero cuando veas un ejemplo verás que es realmente sencillo.\nEl valor led zeppelín daría lugar a una consulta del tipo que sigue.\nSELECT * FROM discografía WHERE autor = \u0026#39;led zeppelin\u0026#39; Ahora vamos a ver el resultado introduciendo el valor led zeppelin’; GRANT ALL ON *.* TO ‘hacker’@’%\nSELECT * FROM discografía WHERE autor = \u0026#39;led zeppelin\u0026#39;; GRANT ALL ON *.* TO \u0026#39;hacker\u0026#39;@\u0026#39;%\u0026#39; La extensión de MySQL para PHP no permite realizar múltiples consultas de modo predeterminado, pero la nueva extensión posee la función mysql_multi_query(). La recomendación obvia es que no hagas uso de esta función si deseas preservar tu aplicación de este tipo de ataques. Deberás ser más cuidadoso aún si usas SQLite que, por la facilidad que otorga para el manejo de bases de datos desde PHP, atrajo la atención de muchos desarrolladores en un momento dado. SQLite permite el uso de múltiples consultas, especialmente con comandos INSERT, aunque es más restrictivo con comandos SELECT. No dejes de consultar su documentación en lo relativo a estos aspectos en http://sqlite.org o en http://es.php.net/sqlite.\n","id":96,"tag":"Seguridad ; SQL","title":"SQL injection múltiple","url":"https://metrosetenta.es/blog/sql-injection-multiple/"},{"content":" PEAR se define a sí mismo, al inicio de su página web (http://pear.php.net), como un marco y un sistema de distribución de componentes PHP reutilizables. PEAR son las siglas de PHP Extension and Application Repository, y su logotipo, aprovechando el juego de palabras, es una simpática pera. En alguna ocasión te escribiré acerca de PEAR en este blog (si no lo he hecho ya), y por este motivo hoy te lo presento de un modo liviano.\nCuando programas una aplicación web en PHP puedes tomar la decisión de escribir todas y cada una de las líneas de código que la compondrán, o puedes reutilizar, allí donde te sea útil, líneas de código que otros desarrolladores ya han realizado anteriormente. Te puedes ahorrar un gran número de pulsaciones sobre tu teclado si decides usar el paquete de código para autentificación de usuarios, o el de gestión de correo electrónico, o el de manejo de las características AJAX… pero ¿todo son beneficios?.\nPEAR está muy bien documentado, lo que quiere decir que además de manuales para preparar y usar los paquetes disponibles, puedes ver la cantidad de veces que han sido descargados, los bugs reportados, versiones, etcétera, pero ahora vienen las desventajas. Cuando decides que un paquete te viene al dedillo, resulta que este hace uso de otro paquete. El segundo paquete hace uso de seis paquetes más y, finalmente, todo termina en que debes añadir a tu aplicación una estimablemente elevada cantidad de código ajeno (en la web de PEAR ya se puede leer “Total Lines of Code: 8,306,287” aunque no las necesitarás todas). La comunidad es realmente buena, y dispone de una división denominada The PEAR Quality Assurance Initiative, que asegura la calidad del código, pero aun así deberías estar atento a muchas actualizaciones y enmiendas de bugs para cada paquete de código usado. Resumiendo, te crearías voluntariamente una dependencia de la comunidad de PEAR, que por otro lado parece muy activa, y de su ritmo y estilo de desarrollo. ¿Qué hacer entonces?.\nLa respuesta a esta última pregunta sólo la tienes tú. Si vas a corregir la seguridad de una pequeña y antigua web que hiciste hace unos meses, probablemente lo mejor será añadir un poco de código propio y nada más. Si emprendes una faraónica tarea de desarrollo, no dejes de echar un vistazo a los paquetes, de sopesar si serán útiles e, incluso, valorar si merece la pena adaptarlos a tu estilo o completarlos. Se puede aprender mucho revisando el código de otras personas y, por supuesto, colaborando en una comunidad de desarrolladores como PEAR.\n","id":97,"tag":"PHP","title":"¿Es PEAR la pera?","url":"https://metrosetenta.es/blog/es-pear-la-pera/"},{"content":"Estoy convencido de que si lo piensas detenidamente coincidirás conmigo en que, usualmente, la finalidad de nuestras aplicaciones es la de servir a nuestros usuarios de interfaz para el manejo de extensas bases de datos. Estas bases de datos están diseñadas para facilitar la consulta y el transporte de información hacia el usuario y, en sentido opuesto, para recibir y almacenar información entregada por el mismo. Cada vez que un usuario consulta o envía información a la base de datos de tu aplicación, está participando en la construcción de un código que ejecutará una acción en dicha base de datos. Ahora que hemos dicho esto, pregúntate ¿podría un usuario ilegítimo aprovechar esta pequeña participación para desvirtuar el código y hackear tu base de datos?.\nLa respuesta obviamente es sí, y un método para llegar a conseguirlo es aprovechar esa pequeña participación que le has otorgado a tus usuarios para inyectar código SQL, es decir, a través de una técnica denominada SQL injection. Funciona de un modo sencillo que te paso a ilustrar. Imagina una base de datos que almacena información de animales. Tus usuarios pueden consultarla a través de un formulario web donde deben indicar la especie y, posteriormente, nosotros vamos a recoger esa petición con el código $especie = $_POST[‘especie’]. Una vez que disponemos de la especie que le interesa al usuario, lo usamos para generar la siguiente consulta a la base de datos:\nconsulta = \u0026#34;SELECT * FROM animales WHERE especie = \u0026#34; + especie; Si introducimos la palabra mamífero en el formulario de esta aplicación, se traducirá en la siguiente consulta:\nconsulta = \u0026#34;SELECT * FROM animales WHERE especie = \u0026#34; + mamífero; Hasta aquí el funcionamiento es el esperado, pero ¿y si introducimos la cadena mamífero’ OR 1=1? Volvemos a traducir la consulta resultante:\nconsulta = \u0026#34;SELECT * FROM animales WHERE especie = \u0026#34; + mamífero + \u0026#34; OR 1 = 1\u0026#34;; Ahora, recibiremos la información de todos los animales contenidos en la base de datos, pues usando la comilla sencilla, hemos desvirtuado el código de la consulta, y el condicionante 1=1 que hemos conseguido inyectar se cumple siempre. Este es el fundamento de SQL injection.\n","id":98,"tag":"Seguridad ; SQL","title":"El fundamento de SQL injection","url":"https://metrosetenta.es/blog/el-fundamento-de-sql-injection/"},{"content":" Te cuento más cosas a tener en cuenta cuando procesas información recibida de tus usuarios. En primer lugar quiero que sepas que es conveniente que chequees la longitud de los valores recibidos para prevenir ataques de denegación de servicio o de desbordamiento de buffer. En PHP se puede usar la función strlen() para comprobar la longitud del dato recibido antes de procesarlo.\nIf (strlen($edad) \u0026gt; 3 ) exit ($edad . \u0026#34; es un valor inválido para el campo edad\u0026#34;;) Y ahora hablemos del formato. Cuando comprobamos el tipo de variable y su longitud puede parecer que tenemos todo el trabajo hecho, pero no es del todo cierto. Una fecha de nacimiento y un correo electrónico pueden estar contenidos en una variable del tipo cadena de caracteres, pero tienen aspectos bien distintos, formatos diferentes. La fecha puede estar separada por barras, usar dos caracteres para indicar el día en cifras, tres para indicar el mes en letras y dos para indicar el año en cifras; algo así como 12/jun/78. El correo electrónico contiene dos secciones bien diferencias por el signo @ y finaliza con un punto y una extensión del tipo com, es, tv, org, etc. Otro ejemplo: metroSetenta@gmail.com. En el momento en que nuestro código debe interactuar con el de otras aplicaciones como, por ejemplo, gestores de correos, bases de datos o sistemas subyacentes, el formato usado es esencial para la prevención de errores y brechas de seguridad.\nHay cientos de códigos en la red para validar el tipo, la longitud y el formato de valores recibidos, y es interesante leer algunos y elegir el más adecuado para nuestro estilo de programación, pero, una vez hecho esto, no dejes de modificarlo para completar el código que has elegido y adaptarlo a tu estilo de desarrollo. También puedes escribir desde cero tu propio código de validación una vez estudiado lo que otros ya han hecho anteriormente.\n","id":99,"tag":"Seguridad ; PHP","title":"Longitud y formato también importan","url":"https://metrosetenta.es/blog/longitud-y-formato-tambien-importan/"},{"content":" Este es uno de esos pasos que ya te conté que tienes que dar para conseguir el correcto y seguro funcionamiento de tu aplicación. No es necesario tener un nivel de formación en informática a la altura de poder generar errores del tipo buffer overflow, porque si tu aplicación espera un valor numérico y un usuario maligno introduce texto, lo mejor que puede pasar es que tu programa no funcione correctamente. Se trata, pues, de una gran brecha potencial en la seguridad de tu aplicación. Los tipo de datos, a grandes rasgos se pueden dividir en tres: Cadenas de texto, números y booleanos.\nLas cadenas de texto, en casi todos los lenguajes informáticos, se nombran como strings y, muchos de ellos, valoran como una cadena de texto vacía los campos que el usuario dejó sin completar. Un modo de chequearlas en PHP es con el comando is_string(), que puedes combinar con is_empty() si te interesa filtrar los valores vacíos.\nPara los números, el método is_numeric() es el primero que se le viene a uno a la cabeza, que nos devolverá el TRUE al valorar integers, floats, el número cero, e incluso con números que técnicamente son un string. Podemos ser más precisos y usar métodos como is_integer() o is_long(), o incluso comprobar que nos devuelve el método gettype() y actuar en consecuencia. Valora la posibilidad de hacer casting (convertir al formato que necesitas), porque no crea una buena impresión mostrar un mensaje de error cuando, por ejemplo, esperas un entero del tipo 45 y el usuario ha introducido el valor 45,00.\nFinalmente los booleanos. En PHP es muy importante usar is_bool(), porque el string “false” se evalua como no vacio, y en un contexto en el que lo usemos como booleano su valor será true. ¿Imaginas las posibilidades de obtener true en ciertos contextos, simplemente añadiéndole unas comillas a false para que nuestro equipo valore el dato “false”?\nEso no es todo, amigos, hay que tener una visión muy clara en los lenguajes de programación del modo en el que estos interpretan variables que contienen valores vacíos y aquellas que no contienen valores (Null). Por defecto, ninguna de ellas aportan información a gestionar por nuestra aplicación, por tanto, y como ya te expliqué en la entrada anterior, es mejor no contemplarlas ni gestionarlas.\n","id":100,"tag":"Seguridad","title":"Comprobar el tipo de datos recibidos","url":"https://metrosetenta.es/blog/comprobar-el-tipo-de-datos-recibidos/"},{"content":" En entradas anteriores hice alusión al modo en el que un usuario ilegítimo de tu aplicación web puede hacer tentativas muy básicas de hacking del estilo que sigue:\nhttp://servidor/aplicacion?administrador=TRUE Seguro que esto te hace reflexionar sobre la importancia de no admitir y gestionar variables inesperadas, así que te muestro a continuación un pequeño código para excluir automáticamente valores inapropiados de tus scripts, pudiendo así asumir con certeza que el entorno global está saneado, incluso aunque el atacante remplace el código de tu web para enviar valores no esperados. La idea consiste en listar las variables esperadas por nuestro código en un array, y posteriormente usarlo para chequear las variables recibidas. ¡Es como una lista de invitados a una sala de fiestas pija!. Aunque este ejemplo está en PHP, como casi siempre, te recuerdo que la idea es fácilmente transportable a cualquier lenguaje de programación.\n$grupoVariablesEsperadas = array (\u0026#34;variable1\u0026#34;, \u0026#34;variable2\u0026#34;, \u0026#34;variable3\u0026#34;); foreach ($grupoVariablesEsperadas) { if (!empty($POST[$variable)) { ${variable} = $POST[$variable]; } else { ${variable} = NULL; } } ","id":101,"tag":"Seguridad ; PHP","title":"Tu aplicación contra la gestión de variables inesperadas","url":"https://metrosetenta.es/blog/tu-aplicacion-contra-la-gestion-de-variables-inesperadas/"},{"content":" Son muchos los lenguajes de programación en los cuales no es necesario declarar las variables antes de que estas sean usadas. Si escribes tus códigos en un lenguaje que resulte flexible en este aspecto que te explico, y dispones de la posibilidad de configurarlo de modo que sea obligatorio declarar las variables antes de usarlas, ¡es mejor que no dejes de hacerlo!. Te muestro un código en PHP a modo de ejemplo:\nif ($tipo_usuario_logado == administrador) { $administrador = TRUE; } La variable $administrador se autodeclara con su primer uso, y a partir de ahora nuestro código mostrará condicionales de este tipo:\nif ($administrador) { // Muestra opciones propias del administrador } ¿Que ocurriría si un hacker usara un campo de introducción de datos de nuestra aplicación PHP, que estuviera mal diseñado en lo referente a la seguridad, para introducir una porción de código como el siguiente?:\n$administrador = TRUE; Pues eso es lo que ocurriría exactamente, que la variable sería recibida por nuestra aplicación, y a continuación la declararía y le daría el valor TRUE. A partir de este momento mostraría las características de la aplicación reservadas para los administradores y todas nuestras vergüenzas quedarían expuestas. ¡Observa que incluso podría, en el caso de que estén activas las variables globales, usar maliciosamente la URL de nuestra aplicación con esta finalidad!\nhttp://servidor/aplicación?administrador=TRUE Por todo lo expuesto, es conveniente usar la instrucción error_reporting() en el caso anterior para capturar errores con variables no inicializadas y poder manejar esos errores según nuestra necesidad.\nSi has pensado que sería mucho más fácil manipular la variable $tipo_usuario_logado, la cual damos por hecho que ya está declarada en nuestra aplicación, he de decirte que hay un pequeño hacker en tu interior. Es cierto, y por este motivo es importante que en cada interpretación del código que haga el servidor asignes el valor de esta variable desde, por ejemplo, una consulta a la base de datos de tus usuarios, o que lo realices usando los datos de sección que previamente has almacenado en el servidor. Sólo de esta manera, el gran especialista de seguridad que crece en ti se asegurará de mantener controlado a tu travieso hacker interior.\n","id":102,"tag":"Seguridad","title":"Declaración de variables y seguridad","url":"https://metrosetenta.es/blog/declaracion-de-variables-y-seguridad/"},{"content":" Son muchísimas las ocasiones en las que nuestras aplicaciones solicitan a los usuarios algún tipo de información: Nombre, correo electrónico, texto de opinión, nueva contraseña… Usualmente lo hacemos a través de formularios que contienen distintos campos a rellenar, campos que nuestras aplicaciones van a recibir y posteriormente van a procesar; por tanto, estos campos, que son manipulables por los usuarios, representan una vía a través de la cual se puede amenazar la seguridad de nuestras aplicaciones. Entonces ¿que es lo más básico a tener en cuenta al usar estos campos de entradas de datos?.\nLos meta caracteres son caracteres que pueden tener un significado diferente en función de la parte de tu sistema que lo esté procesando. Si tu aplicación espera recibir caracteres en una codificación que usa un único byte para almacenar cada carácter, pero el usuario introduce datos en una codificación que use dos bytes por carácter, tu aplicación los dividirá y procesará byte por byte, y aquí nos pueden hacer trampas para introducir un código inesperado, un código que no es lo que parece. Algunos meta caracteres peligroso pueden ser ‘ y “ y \\ y | y \\x00 y \\xff y \\x13…\nDeberías chequear los formatos y los tipos de datos recibidos antes de procesarlos. Si allí donde tu aplicación espera recibir un texto pequeño es obsequiada con un archivo de imagen, es muy probable que tu apreciada creación tienda al error en su gestión.\nPermitir sólo la cantidad de datos necesaria es de vital importancia. Si te interesa la seguridad informática empieza a familiarizarte con el término buffer overflow, pues es realmente recurrente en lo referente a ataques de seguridad y está relacionado con esto que te paso a explicar. Un desbordamiento de búfer ocurre cuando la cantidad de datos introducida por el usuario es mayor que la cantidad de memoria asignada para almacenarla (búfer). Esto puede ocasionar que en tu código se sobrescriban variables, que se generen errores incontrolados, que una instrucción sobrescriba a otra, etc.\nPiensa en las interfaces ocultas como en capas de tu aplicación que, aunque no estén visibles para los usuarios ilegítimos, pueden ser intuidas por estos. Un ejemplo muy básico puede ser el de una aplicación web que use la variable $_GET[‘template’] para permitir o denegar la inclusión de scripts en PHP. Seguro que el más novato de los atacantes prueba cosas del estilo http://aplicaionweb.php?template=test o http://aplicacionweb.php?template=debug o http://aplicacionweb.php?template=admin.\nTu aplicación procesa las instrucciones que tu has programado, y procesa también los datos recibidos a través de las entradas de las formularios. Entonces ¿es posible enviar a la aplicación, a través de estas entradas, instrucciones que sean procesadas y con fines maliciosos?. La respuesta a esta pregunta me parece ridículamente obvia después de haberla planteado. Los términos con los que hay que familiarizarse en este aspecto son Cross-site y scripting desde Shell remota, pues son los métodos más conocidos de introducción de instrucciones inesperadas en las entradas.\n","id":103,"tag":"Seguridad","title":"Cinco detalles a cuidar al recibir datos de tus usuarios","url":"https://metrosetenta.es/blog/cinco-detalles-a-cuidar-al-recibir-datos-de-tus-usuarios/"},{"content":" La estética MAYA es un concepto Estudio sobre la belleza. Fue propuesto por Raymond Loewy, y se fundamenta en una sencilla idea: Dentro de los conceptos estéticos ya aceptados, todos tendemos a preferir aquellos que son más modernos. Entre otras cosas, esto implica que los conceptos estéticos no son usualmente bienvenidos en el momento de nacer, sino que requieren un proceso de maduración y aceptación paulatina. Es por este motivo que, al diseñar tu aplicación, el uso de muchas innovaciones estéticas simultáneas ayudará a desconcertar a tus usuarios. Por otro lado, si carece de toda novedad en su diseño, tu aplicación no supondrá ningún estímulo para los usuarios. El concepto MAYA (Most advanced, yet aceptable) te anima como diseñador a buscar el equilibrio entre lo moderno y lo que resulta familiar para tus usuarios, y es sencillo de comprender que resulte usualmente efectivo. Si, eso es, escribí usualmente efectivo, no te extrañes. Es obvio que si vas a realizar la web de una empresa de innovaciones tecnológicas este concepto no aplica por completo. Igualmente, si eres el encargado de ensalzar las cualidades del legendario Commodore 64 en una web de lo más vintage, debes olvidarte de MAYA y aplicar otras ideas, pero permíteme finalizar diciéndote que, salvo casos muy específicos como estos, el uso de conceptos estéticos modernos, con la seguridad de que son comprendidos y aceptados por los usuarios, es una apuesta segura para incrementar las posibilidades de éxito de tus aplicaciones.\n","id":104,"tag":"UI/UX","title":"La estética MAYA","url":"https://metrosetenta.es/blog/la-estetica-maya/"},{"content":" Es posible que después de leer lo que sigue no vuelvas a teclear con tranquilidad el número de tu tarjeta de crédito en ninguna página web, pero hoy te quiero desvelar que la seguridad absoluta no existe en la informática contemporánea. Esto es así hasta el punto de que los expertos en seguridad informática hablan de la gestión del riesgo, y no de su eliminación completa. ¿Por que?.\nNo importa si programas en Java o en PHP, incluso da igual si te has decantado por hacerlo con Ruby on rails o con el Net framework de Microsoft, porque indiferentemente al lenguaje y entorno de programación que uses, finalmente todo se traduce en ceros y unos. Si, así es, todo se reduce a impulsos eléctricos gestionados por un CPU, y el pequeño cerebro de nuestra computadora no sabrá distinguir si realmente todos y cada uno de los binarios que procesa cumplen con las sanas intenciones del legítimo programador de un software en ejecución o, por el contrario, se trata de un añadido malintencionado que aprovecha la más mínima vulnerabilidad de cualquiera de los procesos que corren en el sistema. Entonces ¿es imposible crear un programa perfectamente seguro, sin vulnerabilidades?.\nDesarrollo en un sistema pequeño, donde dispongamos de todo el control, es posible prevenir defectos de seguridad, pero los sistemas operativos modernos gestionan muchos procesos simultáneos que suponen un enorme flujo de megabytes. Además, las aplicaciones en línea tienen el añadido de implicar a un mayor número de equipos y aplicaciones de terceros, por lo tanto complican aún más esta difícil ecuación sobre seguridad. ¿Nos hemos rendido entonces?.\nNo, nunca. El ritmo de detección y reparación de cualquier fisura de seguridad (bug) es tan trepidante que los modos más cotizados de aprovechar una vulnerabilidad son las explotaciones de día cero (0 day exploits) y, en su mayoría, estas no suelen tener una vida muy prolongada. Hay que añadir a esto el compromiso de cualquier desarrollador o administrador de sistemas para mantener libre de bugs sus programas y sistemas, y de este modo evitar comprometer a otros sistemas en la red. ¡Tu reputación será tan buena como la gestión que realizas de la seguridad en tus aplicaciones o sistemas, por lo tanto, no la descuides nunca!.\n","id":105,"tag":"Seguridad","title":"Seguridad informática absoluta y gestión del riesgo","url":"https://metrosetenta.es/blog/seguridad-informatica-absoluta-y-gestion-del-riesgo/"},{"content":" Tan sencillo como reza el título. Cuando ofreces una interfaz gráfica a un usuario, éste se forja una opinión sobre su aspecto y hace una rápida interpretación del modo en el que funciona. El camino que se sigue para desarrollar el diseño de una aplicación informática es muy parecido al usado en sus proyectos por un arquitecto de antes del año 1900: Abordar el problema de la funcionalidad y, desde esta base, solventar posteriormente el asunto de la emotividad.\nSegún esto, primero debes asegurarte de que el programa funciona como lo has concebido, que es fiable y que está libre de errores de programación. En el punto intermedio debes trabajar para que tu aplicación resulte fácil de usar, es decir, para que el usuario esté lo más acertado posible en su interpretación inicial del funcionamiento. Casi terminando, una vez resuelto este trecho, que muchas empresas de desarrollo no consiguen solventar, puedes centrarte en que el uso de la aplicación suponga una experiencia agradable para el usuario. Finalmente, la panacea del diseño, ¿serías capaz de conseguir que tu aplicación resulte significativa para cada persona?. Unas pistas: iGoogle, pestaña diseño en Twitter, temas de hotmail, avatares\u0026hellip;\nAhora te hago dos sugerencias. En primer lugar, prueba a trabajar en el sentido contrario, es decir, comienza desde el enfoque de la experiencia para finalizar trabajando sobre la funcionalidad de tu aplicación. ¡Ya no nos encontramos en el siglo dieciocho!. En segundo lugar, dale una lecturita a algún escrito sobre el arquitecto Frank Lloyd Wright. Él afirmaba que la forma y la función debían ser una unidad, y que es así como se puede observar en la naturaleza. Sea como fuere, y en lo referente al diseño y la funcionalidad, es de extrema utilidad para abordar el inicio de una nueva aplicación, o para trabajar sobre la reestructuración de una aplicación más antigua, tener un criterio establecido sobre el mejor enfoque general y ser capaz de usar distintos y variados enfoques puntuales.\n","id":106,"tag":"UI/UX","title":"El usuario evalua e interpreta los diseños","url":"https://metrosetenta.es/blog/el-usuario-evalua-e-interpreta-los-dise%C3%B1os/"},{"content":" En ocasiones los datos perseguidos por un hacker se encuentran repartidos en más de un servidor. Esto le obligará a rastrear la ubicación de las distintas partes y a obtener la información necesaria de cada servidor para poder acceder a su objetivo. Los programas que usan los hackers para automatizár esas acciones son los llamados web crawlers, y se usan para desentrañar toda la arquitectura de sitios web.\nUna vez que el hacker dispone de toda la información básica necesaria, realiza una búsqueda de información sobre las posibles vulnerabilidades de sus objetivos. Generalmente se tratan de errores de software, y por este motivo se les denominan bugs. Tienes que saber que existen aplicaciones, genéricamente llamadas bugs trackers, que se usan para el seguimiento y gestión de estos bugs. Es facil encontrar en la red bugs trackers enfocados a facilitar el hacking, e igualmente, es fácil encontrar programas diseñados para explotar los bugs de diferentes aplicaciones (exploits), especialmente de servidores.\nEl hacker estudiará los servidores a través de un proxy, de un modo poco agresivo y en intervalos de tiempo controlados para burlar un posible firewall. Por último, un estudio concienzudo de todo el código visible podría facilitarle pistas sobre nombres de directorios, posibles estructuras SQL usadas para las consultas a bases de datos, nombres de campos, variables, etc. Para esto, es de gran ayuda un capturador web, o como también se le denomina, un web snake.\nSeguro que entiendes, después de todo esto, lo importante que es realizar auditorías de seguridad al servidor web, realizar auditorías a las bases de datos que manejan nuestras aplicaciones web, y escribir cuidadosamente el código que pudiera ser visible.\n","id":107,"tag":"Seguridad","title":"Crawlers, Bugs y capturadores web","url":"https://metrosetenta.es/blog/crawlers-bugs-y-capturadores-web/"},{"content":"Ya sabes que tu user’s agent puede usar el protocolo HTTP para hacer una solicitud a un servidor (request), y que éste nos facilitará una respuesta adecuada a dicha solicitud (response). A continuación te presentaré los métodos que usa tu user´s agent para realizar diferentes solicitudes al servidor, pero antes debes conocer un detalle, existen actualmente cuatro versiones del protocolo HTTP, y en las más antiguas no están disponible todos los métodos te paso a presentar.\nGET: Se usa para solicitar datos al servidor, y debido a que usa la URL, no debes tenerlo en cuenta para programar el envío de solicitudes que conlleven la ejecución de acciones, pues un hacker podría manipular con facilidad, y de un modo malicioso, los parámetros enviados a tu aplicación en el servidor. Un ejemplo de este método: GET /iconos/logo.gif HTTP/1.1\nHEAD: Este método funciona de un modo similar a GET, pero con la salvedad de que facilita únicamente meta-información, es decir, datos sobre la cabecera HTML, sobre la respuesta del servidor y sobre el mismo servidor. Muy usado por hackers para obtener información acerca de las propiedades del servidor, y para probarlo puedes teclear lo siguiente es una línea de comandos:\n$ telnet www.cortinasdeducha.com 80 Una vez conectado hacemos una solicitud HTTP como esta que sigue, y pulsamos intro un par de veces:\nHEAD / HTTP/1.0. Finalmente, entre los datos que recibiremos se encuentra una información como la que sigue:\nServer: Apache/2.2.22 (Unix) modsl/2.2.22 OpenSSL/0.9.8e-fips-rhel5 modauthpassthrough/2.1 modbwlimited/1.4 FrontPage/5.0.2.2635 modperl/2.0.5 Perl/v5.8 POST: También es similar a GET, pero con estas salvedades: No usa la URL, estando así menos expuesto a ataques web que el método GET, y normalmente solicita una acción en el servidor mediante CGI, por tanto, deberá aportar los parámetros para realizarla\nPUT: Es el mejor método para solicitar al servidor que almacene datos, puesto que POST utiliza un mensaje que el servidor debe decodificar, pero el método PUT establece directamente una conexión socket con el servidor. Como punto negativo, debes saber que los servidores de hosting compartido no tienen habilitado este método. DELETE: Borra el recurso especificado.\nTRACE: Es usado para realizar comprobaciones y diagnósticos, pues solicita un mensaje de tipo loopback que en la sesión de datos únicamente contiene una réplica de la solicitud realizada.\nOPTIONS: Es muy útil para un hacker, que podrá usarlo para obtener información sobre las funcionalidades de un servidor web, ya que informa sobre los métodos http que este soporta para una URL especifica.\nCONNECT: Se utiliza con proxy para saber si éste nos permite el acceso a un host determinado bajo condiciones especiales como un flujo de datos encriptados, ya que tiene la característica de cambiar de forma dinámica a modo túnel (SSL tunneling).\n","id":108,"tag":"Seguridad","title":"Métodos de los protocolos web y hacking","url":"https://metrosetenta.es/blog/metodos-de-los-protocolos-web-y-hacking/"},{"content":" Hay algo que debes de tener siempre presente si quieres responsabilizarte de la seguridad de tus aplicaciones, o de la seguridad de las aplicaciones de terceros: debes superar, o igualar al menos, los conocimientos de cualquier hacker potencial de dichas aplicaciones. Centrándonos en la web, es básico conocer el protocolo de comunicación que esta usa, pues un hacker lo dominará perfectamente. World wide web es una de la partes más activas de internet, y hace uso de dos protocolos: HTTP y HTTPS.\nEl protocolo de transferencia de hipertexto (HTTP) funciona de un modo sencillo basado en peticiones y respuestas. El agente de usuario encargado de hacer la petición (usualmente un navegador web) solicita un recurso a una computadora que es la encargada de almacenarlo y proveerlo, es decir, al servidor. Después de valorar la petición del cliente, dicho servidor prepara y devuelve la respuesta, que puede consistir en una página web, un documento informático, datos obtenidos al ejecutar un programa o una consulta a una base de datos, etc.\nEstas transacciones constan de un encabezado que estará seguido por una línea en blanco y por los datos principales. En el encabezado se hace referencia a los datos principales, y por este motivo se le conoce como metadatos. El servidor coloca los metadatos en las variables de entorno de CGI, usando el prefijo HTTP_ para distinguirlas del resto.\nEl protocolo seguro de transferencia de hipertexto (HTTPS) es similar a HTTP, pero en esta ocasión crea un canal cifrado mediante SSL/TLS. HTTPS utiliza por defecto el puerto 443, en lugar del puerto 80 propio de HTTP. La finalidad de este protocolo es evitar ataques man-in-the-middle y eavesdropping, mediante los cuales un hacker puede obtener acceso a cuentas de un sitio web o información confidencial.\n","id":109,"tag":"Seguridad","title":"Protocolos web y seguridad","url":"https://metrosetenta.es/blog/protocolos-web-y-seguridad/"},{"content":" Las siglas CGI (Common Gateway Interface) significan Interfaz de entrada común, y definen un estándar usado para que los usuarios web puedan intercambiar datos con una aplicación alojada en un servidor web. A continuación un buen ejemplo de aplicación que usa el estándar CGI:\nEl usuario está buscando en su navegador web una farmacia cercana, y para ello introduce los datos de su domicilio en un formulario y pulsa el botón enviar. La aplicación CGI (que así la llaman por usar este estándar) que se encuentra alojada en el servidor web recibe la solicitud de fulanito, luego hace una consulta a una base de datos basándose en ella y envía a fulanito una web con los resultados perfectamente expuestos y ordenados.\nPuedes escribir aplicaciones CGI si desarrollas en C, C++, Java, Visualbasic, C#, Perl\u0026hellip; ¡casi en cualquier lenguaje de programación!. Y ahora viene lo interesante para la seguridad informática: las variables de entorno de una aplicación CGI. A continuación las puedes ver expuestas tal y cual las numera Wikipedia. Es posible conocer el puerto de acceso o el protocolo usado gracias a ellas, y las tres últimas contienen información del servidor, que es lo primero que un hacker buscará al realizar un ataque web.\nVariables de entorno que se intercambian de cliente a CGI:\nQUERY_STRING: Es la cadena de entrada del CGI cuando se utiliza el método GET sustituyendo algunos símbolos especiales por otros. Cada elemento se envía como una pareja Variable=Valor. Si se utiliza el método POST esta variable de entorno está vacía.\nCONTENT_TYPE: Tipo MIME de los datos enviados al CGI mediante POST. Con GET está vacía. Un valor típico para esta variable es: Application/X-www-form-urlencoded.\nCONTENT_LENGTH: Longitud en bytes de los datos enviados al CGI utilizando el método POST. Con GET está vacía.\nPATH_INFO: Información adicional de la ruta (el \u0026ldquo;path\u0026rdquo;) tal y como llega al servidor en el URL.\nREQUEST_METHOD: Nombre del método (GET o POST) utilizado para invocar al CGI.\nSCRIPT_NAME: Nombre del CGI invocado.\nSERVER_PORT: Puerto por el que el servidor recibe la conexión.\nSERVER_PROTOCOL: Nombre y versión del protocolo en uso. (Ej.: HTTP/1.0 o 1.1)\nVariables de entorno que se intercambian de servidor a CGI:\nSERVER_SOFTWARE: Nombre y versión del software servidor de www.\nSERVER_NAME: Nombre del servidor.\nGATEWAY_INTERFACE: Nombre y versión de la interfaz de comunicación entre servidor y aplicaciones CGI/1.12.\n","id":110,"tag":"Seguridad","title":"Variables de entorno y CGI","url":"https://metrosetenta.es/blog/variables-de-entorno-y-cgi/"},{"content":"Son muchas las herramientas a disposición de un hacker para explotar vulnerabilidades a tu aplicación web, por esto, son muchas las posibilidades que tiene de realizar un ataque exitoso con un mínimo de conocimientos. Toma notas, porque esto es lo más básico que debes considerar para ir empezando a trabajar la seguridad de tus aplicaciones web:\nFiltra los caracteres peligrosos de cualquier dato ingresado por los usuarios de tu aplicación, y no olvides hacer lo mismo con los campos ocultos de formularios si los sueles usar.\nRealiza una validación de los datos mencionados anteriormente, comprueba que es correcta la longitud, o el tipo de datos, o que son valores correctos\u0026hellip; Por ejemplo, un modo de filtrar un texto en PHP, para comprobar que sólo se trata de números y letras, es así:\ntextoLimpio = preg_replace(\u0026#39;(\\[^A-Za-z0-9\\])\u0026#39;, \u0026#39;\u0026#39;, textoALimpiar) Si usas parámetros en las direcciones URL de tu aplicación revisa a conciencia que no sea posible ingresar parámetros peligrosos. Igualmente comprueba que sea imposible ver los listados de directorios.\nActualiza el programa servidor constantemente e instala los parches necesarios para corregir los bugs descubiertos que puedan presentar oportunidades para uso malicioso.\n","id":111,"tag":"Seguridad ; PHP","title":"Lo más básico contra ataques web","url":"https://metrosetenta.es/blog/lo-mas-basico-contra-ataques-web/"},{"content":"Hay un modo sencillo y rápido para detectar posibles errores en el diseño de tus aplicaciones: ”si no puedes reproducir tu diseño en la realidad palpable, algo debe estar mal”. Observa como el uso de las sombras, en las ventanas de la imagen más a la izquierda, resulta desconcertante. Prueba a tomar dos folios e intenta reproducir con ellos la posición de las ventanas indicadas.\nComprueba cómo la imagen que se encuentra más a la derecha reproduce con mayor acierto las sombras proyectadas, evitando el desconcierto del modelo anterior y clarificando cual es la ventana más cercana y cual es la que se encuentra al fondo.\nEsto es especialmente útil para valorar el uso de sombras, brillos y luces de tu diseño, y para incluirlos correctamente.\n","id":112,"tag":"UI/UX","title":"Diseños reproducibles","url":"https://metrosetenta.es/blog/disenos-reproducibles/"},{"content":" Conocer la forma en la que el cerebro percibe e interpreta sombras, colores, formas, etc. es una poderosa herramienta de diseño. En el momento de tomar decisiones relacionadas con el diseño, debes ser capaz de incluir y valorar compresiones intuitivas (percived afforance). Te aclaro de que se trata con un ejemplo: una señal octogonal de color rojo practicamente no necesita la palabra STOP en el centro, pues su forma y color característico son suficiente para que el usuario pueda intuir el significado. Observa, por otro lado, lo desconcertante que puede ser una señal de stop de color azul y cuadrada.\nAl ver esto, entenderás rápidamente que en el momento de incluir un botón en tu aplicación, es preferible optar por formas rectangulares o redondas y desechar alardes de creatividad mal interpretada que puedan dar lugar, por ejemplo, a un botón con forma de pentágono. La manera en la que nuestro cerebro interpreta una interfaz gráfica es más bien del modo: ”Si parece un botón, debe funcionar como un botón”.\n","id":113,"tag":"UI/UX","title":"Compresiones intuitivas","url":"https://metrosetenta.es/blog/compresiones-intuitivas/"},{"content":" Antes de abarcar otras cuestiones relacionadas con el diseño, necesitas planificar la experiencia que va a tener el usuario a través de tu aplicación. Esto es algo que raramente se planifica y, sin embargo, es esencial para el éxito de cualquier web o aplicación de escritorio. Hasta tal punto esto es así, que los usuarios casi siempre optan por las webs o las aplicaciones que les seducen frente a otras más rápidas, robustas, seguras o con mayores posibilidades. Una cuidada plantificación de la experiencia, por parte de los desarrolladores, es el primer paso para conseguir seducir al usuario.\nPara conseguir la experiencia positiva que arriba te comento, hay que pensar en factores como la usabilidad y la motivación. Cuando evitas frustraciones a tu usuario, eliminando posibles retenes en el uso de tu aplicacción, estas potenciando la usabilidad de esta. Pero recuerda algo muy importante: \u0026ldquo;usabilidad\u0026rdquo; no es sinónimo a \u0026ldquo;provoca deseo de uso\u0026rdquo;. Para esto segundo tienes que trabajar con la motivación del usuario, que es un elemento realmente importante. Piensa que puedes diseñar el formulario de obtención de nombre y correo electrónico más horrible y engorroso de la historia de la informática, pero si el usuario obtiene 1000€ por completarlo, la motivación cubrirá con toda seguridad las deficiencias de usabilidad.\nPermíteme darte una idea más antes de finalizar. El mayor éxito que puedes obtener en el diseño de la experiencia de tu aplicación es conseguir que esta resulte significativa y emotiva al usuario. Pero, si cada usuario es distinto ¿como vas a adivinar que le emotiva o tiene significado para él?. Piensa ahora en como puedes personalizar la barra superior de navegadores web para poner, por ejemplo, la foto de tu hijo recién nacido; o piensa en como puedes establecer tu color favorito como el color predominante de tu correo electrónico. ¿Sueles dejar como fondo de tu teléfono móvil el que tiene como predeterminado?. Estos son anzuelos lanzados directa e intencionadamente para captar al usuario a través de su sensibilidad.\n","id":114,"tag":"UI/UX","title":"Planificar la experiencia","url":"https://metrosetenta.es/blog/planificar-la-experiencia/"},{"content":" Por fin has terminado de programar, pero notas que aún falta algo. La aplicación está funcionando correctamente, las soluciones en el código son elegantes y los posibles errores están controlados. La interfaz que interactua con el usuario es agradable de usar, intuitiva y manejable; pero algo no va bien. El número de interesados es bajo, no se registran nuevos usuarios o no descargan tu aplicación. Los usuarios que se han registrado no la usan con frecuencia o migran rápidamente a otras aplicaciones similares, y por supuesto no obtienes de ellos sus recomendaciones para otros usuarios potenciales y, no obtienes un buen feedback. Entonces ¿que es lo que está fallando?.\nHay una faceta del desarrollo de aplicaciones informáticas que está realmente abandonada: conseguir que tus aplicaciones sean atractivas. Los desarrolladores informáticos obviamos muchos de los componentes necesarios para causar en el usuario una experiencia positiva que, posteriormente, se traduzca en el éxito de nuestras aplicaciones. Debemos procurar, a través de un diseño claro y sugerente, que el usuario se sienta cómodo.\n","id":115,"tag":"UI/UX","title":"Aplicaciones atractivas","url":"https://metrosetenta.es/blog/aplicaciones-atractivas/"}]