<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.dataxbi.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.dataxbi.com/" rel="alternate" type="text/html" hreflang="es" /><updated>2026-10-24T10:18:29+02:00</updated><id>https://www.dataxbi.com/feed.xml</id><title type="html">dataXbi</title><subtitle>Te ayudamos a transformar tus datos en soluciones</subtitle><entry><title type="html">Agent Skills en Microsoft Fabric (vídeo)</title><link href="https://www.dataxbi.com/blog/2026/10/09/fabric-skills-video/" rel="alternate" type="text/html" title="Agent Skills en Microsoft Fabric (vídeo)" /><published>2026-10-09T00:00:00+02:00</published><updated>2026-10-09T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/10/09/fabric-skills-video</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/10/09/fabric-skills-video/"><![CDATA[<p>Vídeo de la charla “Agent Skills en Microsoft Fabric” impartida en el evento Tenerife Summer Sessions organizado por GuarandingaTECH.</p>

<!--more-->

<iframe src="https://www.youtube.com/embed/jz_NIDDTz90?si=cTYtZqAfPHCXDV0R" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="fabric-skills" /><category term="fabric" /><category term="video" /><summary type="html"><![CDATA[Vídeo de la charla “Agent Skills en Microsoft Fabric” impartida en el evento Tenerife Summer Sessions organizado por GuarandingaTECH.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-10-09-fabric-skills-video/agent-skills-en-microsoft-fabric-haciendo-mas-det_lopez_1243010_banner.jpeg" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-10-09-fabric-skills-video/agent-skills-en-microsoft-fabric-haciendo-mas-det_lopez_1243010_banner.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Mi primera App en Fabric (vídeo)</title><link href="https://www.dataxbi.com/blog/2026/10/06/mi-primera-fabric-app-video/" rel="alternate" type="text/html" title="Mi primera App en Fabric (vídeo)" /><published>2026-10-06T00:00:00+02:00</published><updated>2026-10-06T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/10/06/mi-primera-fabric-app-video</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/10/06/mi-primera-fabric-app-video/"><![CDATA[<p>Vídeo de la charla “Mi primera App en Fabric” impartida en el evento Global Fabric Day 2026 Online, organizado por el Fabric User Group ESP (FUGE)</p>

<!--more-->

<iframe src="https://www.youtube.com/embed/fnxRg9uiwrQ?si=-PBnBXrvDb31IfVA" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="fabric-apps" /><category term="fabric" /><category term="video" /><summary type="html"><![CDATA[Vídeo de la charla “Mi primera App en Fabric” impartida en el evento Global Fabric Day 2026 Online, organizado por el Fabric User Group ESP (FUGE)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-10-06-mi-primera-fabric-app-video/Global_Fabric_Day_2026_Online_Fabric_Apps.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-10-06-mi-primera-fabric-app-video/Global_Fabric_Day_2026_Online_Fabric_Apps.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fabric Apps - Consumo</title><link href="https://www.dataxbi.com/blog/2026/10/04/fabric-apps-consumo/" rel="alternate" type="text/html" title="Fabric Apps - Consumo" /><published>2026-10-04T00:00:00+02:00</published><updated>2026-10-04T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/10/04/fabric-apps-consumo</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/10/04/fabric-apps-consumo/"><![CDATA[<p>En la sexta parte de la serie sobre <em>Fabric Apps</em> te muestro el consumo de la capacidad (CUs) de las demos que hemos implementado.</p>

<!--more-->

<p>Para dar una idea del consumo he hecho algo muy sencillo y manual:</p>
<ul>
  <li>Me he puesto a explorar varias de las <em>Fabric Apps</em> de los posts anteriores, que están todas en la misma área de trabajo “Fabric App”</li>
  <li>Luego he utilizado <a href="https://learn.microsoft.com/es-es/fabric/enterprise/metrics-app?wt.mc_id=MVP_367391">Fabric Capacity Metrics</a> para mirar el consumo de cómputo y de almacenamiento de dicha área de trabajo durante el día en que hice las pruebas.</li>
</ul>

<blockquote>
  <p>Si aún no te has leído <a href="https://www.dataxbi.com/blog/tag/fabric-apps/?orden=asc">las entradas anteriores de la serie</a>, te animo a que lo hagas antes de continuar.</p>
</blockquote>

<p>Este post será muy breve porque consiste básicamente en mostrarte los gráficos de Capacity Metrics. Pero primero vamos a repasar lo que dice la documentación.</p>

<h2 id="qué-dice-la-documentación-sobre-el-consumo">¿Qué dice la documentación sobre el consumo?</h2>

<p>Aquí puedes leer la documentación oficial de precios y uso de capacidad: <a href="https://learn.microsoft.com/es-es/fabric/apps/pricing?wt.mc_id=MVP_367391">https://learn.microsoft.com/es-es/fabric/apps/pricing</a>.</p>

<p>Ten en cuenta que las <em>Fabric Apps</em> aún están en <strong>versión preliminar</strong>.</p>

<p>Los elementos externos a la Fabric App <strong>si se cobran</strong> por el consumo de la capacidad:</p>
<ul>
  <li>Base de datos SQL de Fabric</li>
  <li>API de Graph QL</li>
  <li>Funciones de Datos de Usuario (UDF) de Fabric</li>
  <li>La utilización que hace de OneLake el contenido estático de la Fabric App</li>
</ul>

<p>Aquí yo añadiría:</p>
<ul>
  <li>Consultas DAX a los modelos semánticos que utilice la Fabric App</li>
</ul>

<p>Mientras que los componentes de la propia Fabric <strong>no se cobran</strong> por el consumo consumen de la capacidad:</p>
<ul>
  <li>El servicio de hospedaje de la aplicación web</li>
  <li>La autenticación</li>
  <li>Las operaciones de despliegue (rayfin up)</li>
</ul>

<p>Esto no significa que estas operaciones no puedan provocar consumo en otros elementos. Por ejemplo, las operaciones de escritura en OneLake que se realizan durante un despliegue sí consumen capacidad.</p>

<p>A continuación voy a mostrar como reflejan estos consumos Fabric Capacity Metrics.</p>

<h2 id="cómputo">Cómputo</h2>

<p>En la tabla de la imagen muestro los consumos por cómputo de los elementos de algunas de las Fabric Apps. He filtrado por el área de trabajo y por el día en que hice las pruebas. La tabla está ordenada por el nombre de la aplicaciones (columna <em>Item name</em>).</p>

<p><img src="/assets/images/posts/2026-10-04-fabric-apps-consumo/dataXbi-fabric-apps-consumo-computo.png" alt="Tabla de Fabric Capacity Metrics con los detalles del consumo de cómputo de algunas Fabric App" /></p>

<p>Si te fijas en la segunda columna (<em>Item kind</em>), que indica el tipo0 de elemento que generó el consumo, verás 3 valores diferentes: <em>AppBackend</em>, <em>SQLDbNative</em>, <em>Dataset</em>. Algunas aplicaciones tienen solo <em>AppBackend</em>, mientras que otras tienen <em>AppBackend</em> y <em>SQLDbNative</em>. En la lista también aparecen los modelos semánticos Finanzas y Ventas, que son los únicos del tipo <em>Dataset</em>.</p>

<p>Vamos a tomar de ejemplo las Fabric Apps <strong>fabapp-dax-finance</strong> y <strong>fabapp-etl-config</strong>.</p>

<h3 id="consumo-de-cómputo-de-fabapp-dax-finance">Consumo de cómputo de fabapp-dax-finance</h3>

<p>En la imagen se muestran los detalles de <strong>fabapp-dax-finance</strong>, que aparece una sola vez en el listado con el tipo <em>AppBackend</em>. Te recuerdo que esta <em>Fabric App</em> se conecta al modelo semántico de Finanzas y que no utiliza una base de datos SQL.</p>

<p><img src="/assets/images/posts/2026-10-04-fabric-apps-consumo/dataXbi-fabric-apps-consumo-computo-dax-finance.png" alt="Detalles del consumo de cómputo de la Fabric App fabapp-dax-finance" /></p>

<p>En los detalles se observan solo 2 operaciones <em>billables</em> relacionadas con OneLake: <strong>OneLake Other Operations</strong> y <strong>OneLake Read via Proxy</strong> y que están relacionadas con el acceso al contenido estático de la Fabric App que se utiliza en el frontend y que está almacenado en OneLake pero no tenemos acceso directo a él.</p>

<p>Hay otro consumo de cómputo en el que ha incurrido esta aplicación y que no se ve en la imagen con los detalles: las consultas al modelo semántico Finanzas.</p>

<p>En esta imagen muestro los detalles del consumo para Finanzas, donde se aprecia que la única operación es <strong>XMLA Read Operation</strong>, debido a las consultas DAX que se hacen desde la <em>Fabric App</em>.</p>

<p><img src="/assets/images/posts/2026-10-04-fabric-apps-consumo/dataXbi-fabric-apps-consumo-computo-dataset-finance.png" alt="Detalles del consumo de cómputo del modelo semántico Finanzas" /></p>

<h3 id="consumo-de-cómputo-de-fabapp-etl-config">Consumo de cómputo de fabapp-etl-config</h3>

<p>Ahora vamos a ver los detalles del consumo de cómputo de la <em>Fabric App</em> <strong>fabapp-etl-config</strong> que si utiliza una base de datos SQL y no utiliza un modelo semántico. Esta aplicación aparece dos veces en el listado, una vez con <strong>AppBackend</strong> y la otra con <strong>SQLDbNative</strong>.</p>

<p>En la siguiente imagen he expandido los detalles de <strong>AppBackend</strong>, donde se observan cuatro operaciones. <strong>OneLake Other Operations</strong> y <strong>OneLake Read via Proxy</strong> ya la vimos en la aplicación anterior. <strong>OneLake Write via Proxy</strong> es nuevo y se produce por un despliegue que hice de esta aplicación mediante rayfin up. La última operación es <strong>GraphQL Query</strong> es la que más me interesa porque representa el consumo de las consultas GraphQL sobre la base de datos SQL.</p>

<p><img src="/assets/images/posts/2026-10-04-fabric-apps-consumo/dataXbi-fabric-apps-consumo-computo-etl-config.png" alt="Detalles del consumo de cómputo de la Fabric App fabapp-etl-config donde se observa la operación GraphQL Query" /></p>

<p>También he abierto los detalles de la otra línea de esta <em>Fabric App</em>, correspondiente a <strong>SQLDbNative</strong> y que nos muestra las operaciones de la base de datos SQL vinculada a la aplicación.</p>

<p>La operación más importante es <strong>Sql Usage</strong>, que se refiere al consumo de las consultas T-SQL. También se ven varias operaciones de OneLake que son propias de la operación de la base de datos SQL.</p>

<p><img src="/assets/images/posts/2026-10-04-fabric-apps-consumo/dataXbi-fabric-apps-consumo-computo-etl-config-sql.png" alt="Detalles del consumo de cómputo de la base de datos SQL asociada con la Fabric App fabapp-etl-config" /></p>

<h3 id="consumo-de-cómputo-de-otras-fabric-apps">Consumo de cómputo de otras <em>Fabric Apps</em></h3>

<p>He mostrado las operaciones que consumen capacidad de dos aplicaciones, una que se conecta a un modelo semántico y otra que se conecta a una base de datos SQL. Pero recuerda que entre los ejemplos de esta serie de blogs hay una aplicación que combina dos modelos semánticos y otra que combina un modelo semántico con una base de datos SQL. En este último caso aparecerán tanto operaciones de <strong>Dataset</strong> como de <strong>SQLDbNative</strong> dentro de la misma <em>Fabric App</em>.</p>

<h2 id="almacenamiento">Almacenamiento</h2>

<p>En cuanto al consumo de almacenamiento, adjunto una imagen con la página de almacenamiento de Capacity Metrics, filtrada por el área de trabajo donde están las <em>Fabric Apps</em> que he implementado en esta serie de blogs.</p>

<p>Puedes observar que hay operaciones de almacenamiento tanto en OneLake como en la base de datos SQL. El consumo de OneLake corresponde principalmente al contenido estático de las aplicaciones.</p>

<p>En el gráfico inferior izquierdo se aprecia que el almacenamiento se ha mantenido estable en los últimos días. Esto se debe a que todas las aplicaciones habían sido desplegadas a Fabric con anterioridad al primer día mostrado en el gráfico, y aunque he usado algunas de las aplicaciones, no he insertado muchos datos en ninguna de las bases de datos SQL.</p>

<p>El gráfico inferior derecho muestra el almacenamiento facturable acumulado durante el periodo, que se calcula mediante un prorrateo de los datos almacenados durante el intervalo de tiempo. Que el gráfico crezca de forma lineal indica que el volumen de datos se ha mantenido estable.</p>

<p><img src="/assets/images/posts/2026-10-04-fabric-apps-consumo/dataXbi-fabric-apps-cconsumo-almacenamiento.png" alt="Reporte del consumo de almacenamiento de las Fabric Apps reportado por Fabric Capacity Metric" /></p>

<h2 id="continuará">Continuará…</h2>

<p>Gracias por llegar hasta aquí. 😊</p>

<p>La serie continuará, en la próxima entrega hablaré sobre las funciones.</p>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="fabric-apps" /><category term="fabric" /><summary type="html"><![CDATA[En la sexta parte de la serie sobre Fabric Apps te muestro el consumo de la capacidad (CUs) de las demos que hemos implementado.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-10-04-fabric-apps-consumo/dataXbi-fabric-apps-consumo-computo.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-10-04-fabric-apps-consumo/dataXbi-fabric-apps-consumo-computo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fabric Apps - Plantillas</title><link href="https://www.dataxbi.com/blog/2026/08/21/fabric-apps-plantillas/" rel="alternate" type="text/html" title="Fabric Apps - Plantillas" /><published>2026-08-21T00:00:00+02:00</published><updated>2026-08-21T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/08/21/fabric-apps-plantillas</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/08/21/fabric-apps-plantillas/"><![CDATA[<p>La quinta entrega de esta serie se la dedico a las plantillas de Rayfin. Te explico dónde encontrar otras plantillas y cómo crear las tuyas propias. Además, te comparto una que he creado para ayudarte a estandarizar la creación de Fabric Apps en tu empresa.</p>

<!--more-->

<p>Cuando creamos una <em>Fabric App</em>, el backend se gestiona a través de <em>Rayfin</em>, pero en el frontend tenemos libertad total. Las plantillas nos ofrecen un punto de partida a través de la instalación de componentes, para tener disponible un <em>framework</em> con el que podemos acelerar el desarrollo.</p>

<blockquote>
  <p>Si aún no te has leído <a href="https://www.dataxbi.com/blog/tag/fabric-apps/?orden=asc">las entradas anteriores de la serie</a>, te animo a que lo hagas antes de continuar.</p>
</blockquote>

<h2 id="las-plantillas-incluidas-con-rayfin">Las plantillas incluidas con Rayfin</h2>

<p>En entradas anteriores hemos visto que la herramienta de línea de comandos (CLI) de <em>Rayfin</em> incorpora varias plantillas:</p>

<ol>
  <li><strong>Blank App</strong>: Una aplicación web muy sencilla de una sola página.
    <ul>
      <li>Sin base de datos.</li>
      <li>Biblioteca <a href="https://es.react.dev/">React</a> para los componentes del frontend.</li>
      <li>Herramienta <a href="https://vite.dev/">Vite</a> para gestionar el desarrollo de la aplicación web.</li>
      <li>La única <em>skill</em> incluida es la de Rayfin.</li>
    </ul>
  </li>
  <li><strong>Data App</strong>: Aplicación web analítica preparada para conectarse a modelos semánticos de Fabric.
    <ul>
      <li>Sin base de datos.</li>
      <li>También utiliza React y Vite.</li>
      <li>Además de la <em>skill</em> de <em>Rayfin</em>, incluye varias <em>skills</em> para la planificación, desarrollo y validación de la aplicación analítica, como explico en un <a href="https://www.dataxbi.com/blog/2026/06/20/fabric-apps-hola-dax/">post</a> de esta serie.</li>
    </ul>
  </li>
  <li><strong>Todo App</strong>: Aplicación web de ejemplo que gestiona una lista de tareas.
    <ul>
      <li>Utiliza una base de datos SQL.</li>
      <li>También utiliza React y Vite.</li>
      <li>La única <em>skill</em> es la de <em>Rayfin</em>, que incluye instrucciones para gestionar la base de datos desde del <em>frontend</em> con GraphQL, como comento en <a href="https://www.dataxbi.com/blog/2026/07/21/fabric-apps-hola-graphql/">otro post</a> de esta serie.</li>
      <li>Hay dos plantillas de este tipo y la principal diferencia entre ellas es que una incorpora la biblioteca <a href="https://www.radix-ui.com/">Radiux UI</a>.</li>
    </ul>
  </li>
</ol>

<p>Si utilizamos <a href="https://www.dataxbi.com/blog/2026/08/05/fabric-apps-fabricator/">Fabricator</a> para desarrollar nuestra <em>Fabric App</em>, tenemos disponible una plantilla propia llamada <strong>Universal App</strong>.</p>

<blockquote>
  <p>Todas estas plantillas incorporan autenticación con Fabric, que hasta el momento es un requisito para utilizar las <em>Fabric Apps</em>.</p>
</blockquote>

<h2 id="awesome-rayfin">Awesome Rayfin</h2>

<p>Además de las plantillas incluidas con Rayfin, Microsoft mantiene un repositorio en GitHub con más plantillas, algunas desarrolladas por la comunidad.</p>

<p>Este es el repositorio: <a href="https://github.com/microsoft/awesome-rayfin">https://github.com/microsoft/awesome-rayfin</a>.</p>

<p>Para crear una <em>Fabric App</em> con <em>Rayfin CLI</em> a partir de alguna de las plantillas, utiliza este comando:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm create @microsoft/rayfin@latest "Mi app" -- --template https://github.com/microsoft/awesome-rayfin
</code></pre></div></div>

<blockquote>
  <p>Los guiones dobles (–) hay que repetirlos, no es un error.</p>
</blockquote>

<p>Al ejecutar el comando anterior, se presentará el listado de las plantillas disponibles, como se aprecia en la siguiente imagen. Ten en cuenta que solo se muestran 7 plantillas, pero hay más, que puedes ver desplazándote con la flecha hacia abajo.</p>

<p><img src="/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-rayfin-cli.png" alt="Utilizando las plantillas de la comunidad con Rayfin CLI" /></p>

<p>Si utilizas Fabricator, puedes acceder a estas plantillas cuando creas un proyecto con la opción Template/Community.</p>

<p><img src="/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-fabricator.png" alt="Utilizando las plantillas de la comunidad con Fabricator" /></p>

<p>Si entras al <a href="https://github.com/microsoft/awesome-rayfin">repositorio</a> puedes ver los detalles y el código de las plantillas que contiene. En este momento hay 9 plantillas, y a continuación te comento las que me parecen más interesantes.</p>

<h3 id="angular-blank-app">Angular Blank App</h3>

<p><a href="https://github.com/microsoft/awesome-rayfin/tree/main/templates/angular-blankapp">https://github.com/microsoft/awesome-rayfin/tree/main/templates/angular-blankapp</a></p>

<p>Es una plantilla mínima, sin base de datos, que utiliza en el <em>frontend</em> <a href="https://angular.dev/">Angular</a> en lugar de React, y la biblioteca de componentes de UI <a href="https://material.angular.dev/">Angular Material</a>.</p>

<p>No incluye <em>skills</em> adicionales, solo la de <em>Rayfin</em>.</p>

<h3 id="angular-dashboard-app">Angular Dashboard App</h3>

<p><a href="https://github.com/microsoft/awesome-rayfin/tree/main/templates/angular-dashboard">https://github.com/microsoft/awesome-rayfin/tree/main/templates/angular-dashboard</a></p>

<p>Esta plantilla crea un cuadro de mandos para manejar proyectos. Piensa en una aplicación <em>Todo</em> pero más avanzada. O en <a href="https://docs.github.com/es/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects">GitHub Projects</a>. De hecho puede conectarse a un repositorio público de GitHub y mostrar los <em>issues</em> y las <em>pull requests</em>.</p>

<p>Utiliza una base de datos SQL y se basa en la plantilla Angular Blank App.</p>

<p><img src="/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-angular-dashboard-app.png" alt="Pantallazo de una Fabric App creada con la plantilla Angular Dashboard App" /></p>

<h3 id="slide-deck">Slide Deck</h3>

<p><a href="https://github.com/microsoft/awesome-rayfin/tree/main/templates/slide-deck">https://github.com/microsoft/awesome-rayfin/tree/main/templates/slide-deck</a></p>

<p>Con esta plantilla puedes implementar una <em>Fabric App</em> para crear diapositivas con Markdown o HTML y presentarlas a tu audiencia, con registro de participantes y chat en vivo.</p>

<p>Utiliza una base de datos SQL.</p>

<p><img src="/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-slide-deck.png" alt="Pantallazo de una Fabric App creada con la plantilla Slide Deck" /></p>

<h3 id="power-bi-fixer">Power BI Fixer</h3>

<p><a href="https://github.com/microsoft/awesome-rayfin/tree/main/templates/pbi-fixer">https://github.com/microsoft/awesome-rayfin/tree/main/templates/pbi-fixer</a></p>

<p>Esta plantilla es maravillosa porque crea una <em>Fabric App</em> llena de funcionalidades:</p>
<ul>
  <li>Edición de modelos semánticos</li>
  <li>Optimización de modelos semánticos asistida por IA</li>
  <li>Edición de reportes</li>
  <li>Tareas de administración de Fabric</li>
  <li>Creación de Fabric Apps</li>
  <li>Y mucho más…</li>
</ul>

<p>El autor es también el creador del proyecto <a href="https://kornalexander.github.io/pbi_fixer/index.html">PBI Fixer</a>, un <em>notebook</em> para Fabric que permite optimizar modelos semánticos e informes de una manera interactiva y que utiliza <a href="https://github.com/microsoft/semantic-link-labs">Semantic Link Labs</a>.</p>

<p>En esta plantilla ha incorporado todo lo que está en el <em>notebook</em> y ha añadido nuevas opciones. Vale mucho la pena revisar el código, porque incluso ha utilizado User Defined Functions (UDF) de Fabric que aún no están soportadas oficialmente en <em>Rayfin</em>.</p>

<p><img src="/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-pbi-fixer.png" alt="Fragmento del sitio web de PBI Fixer con las dos ediciones: Notebook y Fabric App" /></p>

<h2 id="crear-tu-propia-plantilla">Crear tu propia plantilla</h2>

<p>Es muy fácil crear una plantilla para <em>Rayfin</em> porque no es más que una <em>Fabric App</em> pero con alguna configuración extra.</p>

<p>En el repositorio <em>Awesome Rayfin</em> hay un documento con las directrices para crear una plantilla: <a href="https://github.com/microsoft/awesome-rayfin/blob/main/docs/template-guidelines.md">https://github.com/microsoft/awesome-rayfin/blob/main/docs/template-guidelines.md</a>.</p>

<p>También hay un fichero <a href="https://github.com/microsoft/awesome-rayfin/blob/main/AGENTS.md">AGENTS.md</a> con instrucciones para los agentes de IA.</p>

<p>Y los requisitos para publicar una plantilla en el repositorio: <a href="https://github.com/microsoft/awesome-rayfin/blob/main/CONTRIBUTING.md">https://github.com/microsoft/awesome-rayfin/blob/main/CONTRIBUTING.md</a>.</p>

<p>Si revisas este último documento verás que hay una serie de pasos para publicar nuestra plantilla en <em>Awesome Rayfin</em> y por supuesto hay que hacer un Pull Request (PR) y esperar la aprobación.</p>

<p>Para utilizar una plantilla, no es obligatorio que esté en <em>Awesome Rayfin</em>, sino que podemos hacerlo desde un repositorio propio o incluso desde una carpeta.</p>

<p>A continuación te comento dos ficheros que si son imprescindibles en las plantillas.</p>

<h3 id="fichero-rayfin-templateyml">Fichero rayfin-template.yml</h3>

<p>Toda plantilla tiene que tener un fichero <code class="language-plaintext highlighter-rouge">rayfin-template.yml</code> con esta estructura:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apiVersion: v1
metadata:
  name: angular-blankapp
  displayName: Angular Blank App
  description: Bare-bones Fabric-authenticated Angular + Material app — sign-in, routing, and a placeholder home page, with no data layer to remove
entries:
  - path: .
    name: Angular Blank App
</code></pre></div></div>

<blockquote>
  <p>El código anterior es de la plantilla Angular Blank App <a href="https://github.com/microsoft/awesome-rayfin/blob/main/templates/angular-blankapp/rayfin-template.yml">https://github.com/microsoft/awesome-rayfin/blob/main/templates/angular-blankapp/rayfin-template.yml</a></p>
</blockquote>

<p><code class="language-plaintext highlighter-rouge">name</code> tiene que coincidir con el nombre de la carpeta donde está la plantilla y el formato tiene que ser <a href="https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case">kebab case</a>.</p>

<p><code class="language-plaintext highlighter-rouge">entries</code> contiene la ruta al código de la plantilla y el nombre de la plantilla.</p>

<p>Si en una carpeta hay varias planillas, se crea un elemento de <code class="language-plaintext highlighter-rouge">entries</code> para cada una, por ejemplo:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apiVersion: v1
metadata:
  name: awesome-rayfin
  displayName: Awesome Rayfin Templates
  description: Community-curated template gallery for Project Rayfin
entries:
  - path: templates/angular-blankapp
    name: Angular Blank App
    description: Bare-bones Fabric-authenticated Angular + Material app — sign-in, routing, and a placeholder home page, with no data layer to remove
  - path: templates/angular-dashboard
    name: Angular Dashboard App
    description: Responsive Angular Material dashboard — top navbar + collapsible side menu, Project/Task data model, and optional one-click GitHub Issues/PRs sync for a public repo.
  - path: templates/field-technician
    name: Field Technician App
    description: Field service management app with role-based dashboards for dispatchers and technicians, job tracking, customer lookup, and dual-mode auth (local password + Fabric)
</code></pre></div></div>

<blockquote>
  <p>El código anterior es un fragmento del fichero <code class="language-plaintext highlighter-rouge">rayfin-template.yml</code> del repositorio Awesome Rayfin <a href="https://github.com/microsoft/awesome-rayfin/blob/main/rayfin-template.yml">https://github.com/microsoft/awesome-rayfin/blob/main/rayfin-template.yml</a></p>
</blockquote>

<p>La carpeta de cada plantilla tendrá que contener a su vez un fichero <code class="language-plaintext highlighter-rouge">rayfin-template.yml</code> como el del primero ejemplo.</p>

<h3 id="fichero-manifestjson">Fichero manifest.json</h3>

<p>El fichero <code class="language-plaintext highlighter-rouge">manifest.json</code> está presente en las Fabric Apps aunque no sean plantillas, pero en el caso de estas la propiedad <code class="language-plaintext highlighter-rouge">templatedId</code> tiene que coincidir con el nombre de la carpeta.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
  "templateId": "angular-blankapp",
  "icon": "angular-blankapp",
  "services": {
    "auth": true,
    "data": true,
    "storage": false,
    "staticHosting": true
  },
  "hasDabSchema": false,
  "tokens": [
    "__RAYFIN_API_URL__",
    "__RAYFIN_PK__",
    "__FABRIC_ITEM_ID__",
    "__FABRIC_WORKSPACE_ID__",
    "__FABRIC_PORTAL_URL__"
  ]
}
</code></pre></div></div>

<blockquote>
  <p>El código anterior es de la plantilla Angular Blank App <a href="https://github.com/microsoft/awesome-rayfin/blob/main/templates/angular-blankapp/manifest.json">https://github.com/microsoft/awesome-rayfin/blob/main/templates/angular-blankapp/manifest.json</a></p>
</blockquote>

<h3 id="hola-plantillas">Hola plantillas</h3>

<p>Para demostrar como puedes crear tu propia plantilla, voy a preparar una muy básica que he llamado <code class="language-plaintext highlighter-rouge">hola-plantillas</code>, y que consiste en una única página estática.</p>

<p>Para hacer una plantilla te recomiendo que primero implementes una <em>Fabric App</em> y compruebes que funcione y luego la conviertes en plantilla añadiendo el fichero <code class="language-plaintext highlighter-rouge">rayfin-template.yml</code> y el resto de los metadatos. Y que te asegures de eliminar cualquier referencia a áreas de trabajo u otros elementos que puedan aparecer en la <em>Fabric App</em> al hacer pruebas o despliegue.</p>

<p>Estos son los pasos que he seguido para crear la plantilla <code class="language-plaintext highlighter-rouge">hola-plantillas</code>:</p>

<ol>
  <li>He creado una <em>Fabric App</em> con el nombre “Hola plantillas” utilizando la plantilla “Blank App”. Lo que crea la carpeta <code class="language-plaintext highlighter-rouge">hola-plantillas</code>.</li>
  <li>He utilizado un agente de IA para que modifique la aplicación y la simplifica aún más dejando solo una página web y la autenticación, y que deshabilite la base de datos.</li>
  <li>He probado la aplicación localmente y también la he desplegado a Fabric.</li>
  <li>He clonado el repositorio Awesome Rayfin en mi ordenador con el comando: <code class="language-plaintext highlighter-rouge">git clone https://github.com/microsoft/awesome-rayfin.gi</code></li>
  <li>He recurrido de nuevo el agente de IA para que me ayude a convertir la Fabric App en una plantilla, indicándole que utilice la SKILL de la copia local de Awesome Rayfin.
    <ul>
      <li>He comprobado que exista el fichero <code class="language-plaintext highlighter-rouge">rayfin-template.yml</code> y que esté correcto.</li>
      <li>He comprobado que el fichero <code class="language-plaintext highlighter-rouge">manifest.json</code> tenga el valor correcto (hola-plantilla) en <code class="language-plaintext highlighter-rouge">templateId</code></li>
    </ul>
  </li>
  <li>He limpiado las referencias a datos privados que pudieran quedar de la Fabric App:
    <ul>
      <li>En el fichero <code class="language-plaintext highlighter-rouge">rayfin/rayfin.yml</code> quitar los elementos de <code class="language-plaintext highlighter-rouge">allowedRedirectUris</code> que no incluyan <code class="language-plaintext highlighter-rouge">localhost</code>.</li>
      <li>Eliminar el fichero fichero <code class="language-plaintext highlighter-rouge">rayfin/.deployments.json</code></li>
      <li>Eliminar el fichero fichero <code class="language-plaintext highlighter-rouge">rayfin/.env</code></li>
      <li>Eliminar el fichero fichero <code class="language-plaintext highlighter-rouge">.env.local</code></li>
    </ul>
  </li>
</ol>

<p>El resultado lo he publicado en GitHub: <a href="https://github.com/dataxbi/fabric-app-templates/tree/main/hola-plantillas">https://github.com/dataxbi/fabric-app-templates/tree/main/hola-plantillas</a></p>

<p>Para crear una <em>Fabric App</em> con <em>Rayfin CLI</em> a partir de esta plantilla, utiliza este comando:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm create @microsoft/rayfin@latest "Hola Plantillas" -- --template https://github.com/dataxbi/fabric-app-templates --template-name hola-plantillas
</code></pre></div></div>

<p>Si utilizas Fabricator, al crear el proyecto ve a la opción Template/Community y has <em>scroll</em> hasta el final de la lista y busca el <em>link</em> “Use a custom URL” que está abajo a la derecha, y llena el URL del repositorio y el nombre de la plantilla como muestra la imagen.</p>

<p><img src="/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-hola-plantillas-fabricator.png" alt="Utilizando la plantilla hola-plantillas con Fabricator" /></p>

<p>En realidad el repositorio donde está <code class="language-plaintext highlighter-rouge">hola-plantillas</code> contiene otra plantilla que veremos a continuación, y también tiene un fichero <a href="https://github.com/dataxbi/fabric-app-templates/blob/main/rayfin-template.yml"><code class="language-plaintext highlighter-rouge">rayfin-template.yml</code></a> general para que se pueda usar directamente el URL del repositorio y aparezca el listado de las plantillas disponibles.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm create @microsoft/rayfin@latest "Otra Plantillas" -- --template https://github.com/dataxbi/fabric-app-templates
</code></pre></div></div>

<h2 id="una-plantilla-corporativa">Una plantilla corporativa</h2>

<p>La <strong>gobernanza</strong> ha sido una de las principales preocupaciones desde que fueron anunciadas las <em>Fabric Apps</em>, y con razón, porque sin ningún control podemos tener una explosión de aplicaciones web y bases de datos en las áreas de trabajo de Fabric.</p>

<p>Creo que las plantillas pueden servirnos para dar un pequeño paso en la buena dirección, porque las veo como el lugar donde podemos recoger las reglas que deben cumplir las Fabric Apps que se desplieguen en la empresa. Es verdad que por ahora Fabric no provee <em>out of the box</em> un mecanismo para forzar el uso de determinadas plantillas, ni de validar antes de desplegar, pero recuerda que las Fabric Aps aún están en versión preliminar, y además creo que con las herramientas que existen para CI/CD y las APÎs se podría implementar algún mecanismo a la medida.</p>

<p>A modo de ejemplo, he creado una plantilla para crear cuadros de mandos a partir de modelos semánticos publicados en Fabric, que ya incorpora un diseño corporativo. Lo puedes ver como el equivalente a un tema de Power BI, pero que además de indicar los colores, también tiene la disposición de los elementos generales (logo de la empresa, el encabezado, el menú), y también una biblioteca de componentes específicos para las visualizaciones</p>

<p>El nombre de la plantilla es <code class="language-plaintext highlighter-rouge">brand-dashboard</code> y la he publicado en el mismo repositorio de GitHub que he mencionado antes. Puedes acceder directamente con este URL: <a href="https://github.com/dataxbi/fabric-app-templates/tree/main/brand-dashboard">https://github.com/dataxbi/fabric-app-templates/tree/main/brand-dashboard</a></p>

<p>Para crearla me he basado en la plantilla <em>Data App</em> de <em>Rayfin</em> con las siguientes adaptaciones:</p>
<ul>
  <li><strong>Shell corporativo</strong>: Sustituye el layout simple por una barra lateral expandible/contraíble y cabecera compartida.</li>
  <li><strong>Componentes visuales</strong>: Reemplaza VegaVisual por Recharts + componentes shadcn/ui para consistencia visual.</li>
</ul>

<p>La idea con esta planilla no es que la uses directamente para crear una Fabric App, sino que la descargues, clonando el repositorio, y la personalices con el logo y los colores corporativos.</p>

<p>Luego creas las <em>Fabric Apps</em> a partir de la plantilla con tus modificaciones. O que compartas la plantilla modificada en algún repositorio para que la puedan utilizar otros colegas.</p>

<p>Esto se explica en la <a href="https://github.com/dataxbi/fabric-app-templates/blob/main/brand-dashboard/docs/ADMINISTRADORES.md">guía para administradores</a> que está en el repositorio.</p>

<p>También hay <a href="https://github.com/dataxbi/fabric-app-templates/blob/main/brand-dashboard/docs/EMPLEADOS.md">otra guía</a> para los colegas que utilicen la plantilla modificada.</p>

<p>Así luce la plantilla:</p>

<p><img src="/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-brand-dashboard.png" alt="Pantallazo de la plantilla brand-dashboard" /></p>

<p>Así luce una aplicación implementada con esta planilla:</p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-brand-dashboard-app.mp4" type="video/mp4" />
</video>

<h2 id="continuará">Continuará…</h2>

<p>Gracias por llegar hasta aquí. 😊</p>

<p>La serie continuará, en la próxima entrega hablaré del consumo de CUs de la capacidad Fabric.</p>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="fabric-apps" /><category term="fabric" /><summary type="html"><![CDATA[La quinta entrega de esta serie se la dedico a las plantillas de Rayfin. Te explico dónde encontrar otras plantillas y cómo crear las tuyas propias. Además, te comparto una que he creado para ayudarte a estandarizar la creación de Fabric Apps en tu empresa.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-brand-dashboard.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-08-21-fabric-apps-plantillas/dataXbi-fabric-apps-plantillas-brand-dashboard.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fabric Apps - Fabricator</title><link href="https://www.dataxbi.com/blog/2026/08/05/fabric-apps-fabricator/" rel="alternate" type="text/html" title="Fabric Apps - Fabricator" /><published>2026-08-05T00:00:00+02:00</published><updated>2026-08-05T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/08/05/fabric-apps-fabricator</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/08/05/fabric-apps-fabricator/"><![CDATA[<p>En la cuarta entrega de esta serie quiero presentarte Fabricator, una aplicación de escritorio gratuita y de código abierto desarrollada por Sachin Patney con la que puedes crear tus <em>Fabric Apps</em>.</p>

<!--more-->

<p>Todos los ejemplos que te he mostrado en esta serie los he construido en la línea de comandos (Terminal de Windows), por requerimientos de <a href="https://github.com/microsoft/rayfin">Rayfin</a> y también por preferencias personales. Pero este post va a ser diferente porque mostraré una aplicación de escritorio desde la que puedes controlar todo el ciclo de vida de una <em>Fabric App</em>, desde la instalación de los requerimientos, la conexión al modelo semántico, el desarrollo de la aplicación, el control de versiones con Git y el despliegue en Fabric.</p>

<blockquote>
  <p>Si aún no te has leído <a href="https://www.dataxbi.com/blog/tag/fabric-apps/?orden=asc">las entradas anteriores de la serie</a>, te animo a que lo hagas antes de continuar.</p>
</blockquote>

<h2 id="instalando-fabricator">Instalando Fabricator</h2>

<p>Todo el código de Fabricator está disponible en GitHub: <a href="https://github.com/spatney/rayfin-fabricator">https://github.com/spatney/rayfin-fabricator</a>.</p>

<p>Su autor, <a href="https://www.linkedin.com/in/spatney/">Sachin Patney</a>, es empleado de Microsoft donde lidera el desarrollo de <em>Rayfin</em>, pero <strong>Fabricator es un proyecto personal</strong> que realiza en su tiempo libre por lo que no está soportado por Microsoft.</p>

<p>Desde el repositorio de GitHub se puede descargar el instalador para Windows o para Mac: <a href="https://github.com/spatney/rayfin-fabricator/releases/latest">https://github.com/spatney/rayfin-fabricator/releases/latest</a></p>
<ul>
  <li>El nombre del instalador para Windows tiene este formato: <code class="language-plaintext highlighter-rouge">Rayfin Fabricator_&lt;version&gt;_x64-setup.exe</code></li>
  <li>El nombre del instalador para Mac tiene este formato: <code class="language-plaintext highlighter-rouge">Rayfin Fabricator_&lt;version&gt;_aarch64.dmg</code></li>
</ul>

<p>Una vez instalado, lo ejecutamos, y lo primero que hace es comprobar si en el ordenador están las herramientas necesarias:</p>
<ul>
  <li>Node.js</li>
  <li>Git</li>
  <li>Azure CLI</li>
  <li>GitHub CLI (opcional)</li>
</ul>

<p>Si alguna no está, podemos instalarla desde el propio Fabricator.</p>

<p>GitHub CLI solo es necesario si tenemos una cuenta en GitHub y queremos clonar repositorios donde tengamos <em>Fabric Apps</em>.</p>

<p>Además de estas herramientas, se requiere:</p>
<ul>
  <li>Tener acceso a un área de trabajo de Fabric en un inquilino donde se hayan habilitado las <em>Fabric Apps</em></li>
  <li>Tener una licencia de <a href="https://github.com/features/copilot/plans?locale=es-419">GitHub Copilot</a></li>
</ul>

<p>Fabricator te permite entrar tus credenciales para conectarte a GitHub Copilot y a Fabric a través de Azure CLI.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-instalacion.png" alt="Pantalla inicial de Fabricator donde se comprueba que estén instaladas las herramientas y el acceso a Fabric y GitHub Copilot" /></p>

<h2 id="creando-la-fabric-app">Creando la <em>Fabric App</em></h2>

<p>Después de instalar las herramientas y conectarnos a GitHub Copilot y a Fabric, estamos listos para comenzar a crear nuestra <em>Fabric App</em>. Para ello utilizamos el botón “Enter Fabricator →” que encontrarás en la parte inferior derecha de la pantalla.</p>

<p>Se nos presentan tres opciones:</p>
<ul>
  <li>New project</li>
  <li>Open folder</li>
  <li>Clone from GitHub (esta opción necesita instalar GitHub CLI)</li>
</ul>

<p>También te recomiendo prestar atención al recuadro que aparece abajo, con la ruta a la carpeta donde Fabricator creará los proyectos, que puedes cambiar si lo deseas.</p>

<p>Vamos a escoger la opción “New project” que nos llevará a una pantalla como la que te muestro abajo, donde debemos escribir el nombre del proyecto y seleccionar una plantilla.</p>

<p>Yo le he puesto el nombre “Hola Fabricator”, por lo que el proyecto se creará en la carpeta <code class="language-plaintext highlighter-rouge">hola-fabricator</code>.</p>

<p>En cuanto a la plantilla, he utilizado “Universal App”, que es la predeterminada y es propia de Fabricator, pero también se puede escoger la plantilla “Todo App” (que comenté en el post anterior) u otras plantillas creadas por la comunidad.</p>

<blockquote>
  <p>Te adelanto que la próxima entrega de esta serie estará dedicada a las plantillas.</p>
</blockquote>

<p>Le damos al botón “Create project” y esperamos unos minutos a que se descarguen los componentes de la plantilla en la carpeta del proyecto.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-crear-app-1.png" alt="Creando una Fabric App con el nombre Hola Fabricator utilizando la plantilla Universal App de Fabricator" /></p>

<p>Antes de comenzar a trabajar en la <em>Fabric App</em> tenemos que desplegarla en un área de trabajo con capacidad Fabric, y para ello Fabricator nos mostrará una pantalla donde podemos buscar entre las áreas de trabajo, o crear una.</p>

<p>Yo he escogido el área de trabajo con el nombre “Fabric_App”.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-crear-app-2.png" alt="Antes de comenzar a personalizar la Fabric App hay que publicarla en un áre de trabajo con capacidad Fabric" /></p>

<p>Cuando haya terminado el despliegue, aparecerá una pantalla como la que te muestro continuación, dividida en dos paneles: a la izquierda tenemos un chat para darle las instrucciones a GitHub Copilot, y a la derecha podemos interactuar con la <em>Fabric App</em> que por ahora es poco más que un lienzo en blanco.</p>

<p>Encima de los paneles al centro, tenemos cuatro botones:</p>
<ul>
  <li>Build</li>
  <li>Code</li>
  <li>Model</li>
  <li>Advisor</li>
</ul>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-crear-app-3.png" alt="Pantallazo del entorno de trabajo de Fabricator, con el chat a la izquierda y la Fabric App a la derecha" /></p>

<p>En la pantalla de la imagen anterior estamos en el modo “Build”, pero si cambiamos al modo “Code” podemos ver el código de la aplicación con un editor que incorpora Fabricator, o podemos abrir Visual Studio Code mediante el botón que está arriba a la derecha.</p>

<p>También podemos revisar el historial de cambios, lo que es posible porque Fabricator se encarga de utilizar Git para hacer <em>commit</em> cada vez que se hace una modificación.</p>

<p>Y en el mismo modo “Code” también podemos activar y desactivar <em>skills</em> con instrucciones y herramientas para los agentes de IA.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-skills.png" alt="Gestión de las skills disponibles en Fabricator" /></p>

<h2 id="construyendo-un-cuadro-de-mando">Construyendo un cuadro de mando</h2>

<p>Vamos a regresar al modo “Build” para construir un cuadro de mando de ventas utilizando el mismo modelo semántico del segundo <em>post</em> de esta serie.</p>

<p>Recomiendo habilitar el <strong>modo plan</strong> de GitHub Copilot, que en Fabricator está inactivo de inicio por ser una funcionalidad que áun está en pruebas (<em>beta</em>). Hay que buscar el botón “Settings” en la esquina superior derecha de la pantalla y dentro de la ventana de configuración abrir la opción EXPERIMENTS, que está al final, y activar la opción “Chat mode selector”.</p>

<p>Después de activarlo, hay que seleccionarlo en el panel de chat.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-chat-modo-plan.png" alt="Seleccionando el modo plan en el panel de chat" /></p>

<p>De esta manera podemos trabajar siguiendo el mismo proceso de toda la serie: <strong>plan</strong> - <strong>especificación</strong> - <strong>implementación</strong> - <strong>testing</strong> - <strong>despliegue</strong>, en varias iteraciones.</p>

<p>Las primeras instrucciones que le daremos al agente de IA serán para indicarle el modelo semántico que debe utilizar. Fabricator nos ofrece una manera muy practica mediante un botón situado en la esquina inferior derecha del chat, como se muestra en la imagen. Al pulsarlo, aparece una lista con los modelos semánticos disponibles en el área de trabajo donde hemos desplegado la <em>Fabric App</em>. Cuando seleccionamos uno, se añaden al chat las instrucciones con los identificadores del modelo semántico y del área de trabajo.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-chat-modelo-semantico.png" alt="Seleccionando un modelo semántico en el panel de chat" /></p>

<p>También es posible adjuntar imágenes en el chat, por ejemplo, un boceto del cuadro de mando, o un logotipo, pero en este caso no he utilizado ninguna imagen.</p>

<p>Por cierto, otra de las maravillas de Fabricator es que podemos revisar la estructura del modelo semántico seleccionado, con el botón “Model” de la parte superior de la pantalla.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-modelo-semantico.png" alt="Explorando el modelo semántico seleccionado" /></p>

<p>Para continuar, escribimos en el chat algunas instrucciones generales sobre el cuadro de mando, para que el agente de IA nos proponga un plan.</p>

<p>Yo comencé con estas instrucciones:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Crea un cuadro de mandos que ofrezca una visión general de las ventas utilizando las medidas que ya existen en el modelo. Que los indicadores se comparen con el año anterior.
</code></pre></div></div>

<p>El agente me hizo algunas preguntas y yo le hice algunas sugerencias adicionales, y este fue el plan acordado:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- Primera entrega: crear únicamente docs/especificaciones-dashboard.md con alcance, KPIs, comparación interanual, visuales, filtros, estados y criterios de aceptación.
- Detener todo trabajo después de crear el documento para que puedas revisarlo.
- Mantener bloqueados el pack analytics, la conexión al modelo, el DAX y el dashboard hasta recibir tu aprobación explícita de las especificaciones.
- Tras aprobarlas, continuar con la conexión ventas, descubrimiento del esquema e implementación validada contra datos reales.
</code></pre></div></div>

<p>Revisé el documento con las especificaciones dentro del propio fabricator utilizando el botón “Code”, y le di el OK al agente de IA para que implementara el plan.</p>

<p>Después de trabajar durante un rato, el agente de IA tuvo lista y desplegada una primera versión del cuadro de mando, que te muestro en el vídeo.</p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-cuadro-mando-version-inicial.mp4" type="video/mp4" />
</video>

<blockquote>
  <p>Como esta <em>Fabric App</em> utiliza un modelo semántico, he tenido que utilizar el botón “Fabric”, de la esquina superior derecha, para que se abra dentro del portal de Fabric.</p>
</blockquote>

<h2 id="modo-diseño">Modo diseño</h2>

<p>El cuadro de mando ha quedado bastante bien, pero quiero dos mejoras:</p>
<ul>
  <li>Cambiar el gráfico de <em>donut</em> por uno de columnas</li>
  <li>Quitar los títulos de los ejes de los gráficos</li>
</ul>

<p>Para el primer cambio, Fabricator nos ofrece otra herramienta maravillosa: el modo diseño, que se activa con el botón “Design” ubicado en la esquina superior derecha, junto al botón “Fabric”.</p>

<p>En dicho modo podemos modificar el aspecto de la <em>Fabric App</em> de manera visual, por ejemplo, seleccionar un elemento para cambiar sus dimensiones, el font o el color. O seleccionar un gráfico para cambiarle el tipo. O incluso, dibujar o escribir comentarios. Es un poco como Power Point o Canvas.</p>

<p>Pero esto no modifica directamente la <em>Fabric App</em>, sino que hay que pulsar el botón “Send tu chat” para que se genere una imagen y unas instrucciones que se envían al chat para que el agente de IA haga las modificaciones en el código.</p>

<p>En este caso, seleccioné el gráfico de donut y cambié el tipo a gráfico de barras, como se aprecia en la imagen.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-modo-diseno.png" alt="Utilizando el modo diseño para cambiar el tipo de un gráfico" /></p>

<p>Para el segundo cambio no utilicé el modo diseño, sino que se lo pedí directamente en el chat.</p>

<h2 id="despliegue">Despliegue</h2>

<p>Cada vez que el agente de IA realiza cambios en la <em>Fabric App</em>, automáticamente se hace un <em>commit</em> con Git, y se vuelve a desplegar en el área de trabajo de Fabric que indicamos al crear el proyecto.</p>

<p>Si necesitamos hacer un despliegue puntual, podemos utilizar el botón “Redeploy” ubicado en la esquina superior derecha.</p>

<p>Y también podemos añadir otras áreas de trabajo para desplegar la misma <em>Fabric App</em>, por ejemplo, para tener un entorno de desarrollo y otro de pruebas, como se muestra en la imagen.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-despliegue.png" alt="Se han configurado dos áreas de trabajo para desplegar la misma Fabric App, uno para desarrollo y otro para pruebas." /></p>

<h2 id="advisor">Advisor</h2>

<p>Para finalizar este recorrido por Fabricator quiero mencionar otra maravilla: la pantalla “Advisor”, que utiliza a GitHub Copilot para revisar el código de la aplicación y detectar problemas de seguridad, de rendimiento o de accesibilidad.</p>

<p>Yo lo ejecuté con la aplicación y encontró dos problemas, uno de rendimiento y otro de accesibilidad, como se muestra en la imagen.</p>

<p>Si se pulsa en el botón “Fix with Copilot” de un problema, se copia el texto en el chat para que el agente de IA trate de resolverlo.</p>

<p><img src="/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator-advisor.png" alt="La pantalla &quot;Advisor&quot; con dos problemas que ha encontrado, uno de rendimiento y otro de accesibilidad." /></p>

<h2 id="continuará">Continuará…</h2>

<p>Gracias por llegar hasta aquí. 😊</p>

<p>En la próxima entrega de la serie hablaré sobre las plantillas de Rayfin.</p>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="fabric-apps" /><category term="fabric" /><summary type="html"><![CDATA[En la cuarta entrega de esta serie quiero presentarte Fabricator, una aplicación de escritorio gratuita y de código abierto desarrollada por Sachin Patney con la que puedes crear tus Fabric Apps.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-08-05-fabric-apps-fabricator/dataXbi-fabric-apps-fabricator.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fabric Apps - Hola GraphQL</title><link href="https://www.dataxbi.com/blog/2026/07/21/fabric-apps-hola-graphql/" rel="alternate" type="text/html" title="Fabric Apps - Hola GraphQL" /><published>2026-07-21T00:00:00+02:00</published><updated>2026-07-21T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/07/21/fabric-apps-hola-graphql</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/07/21/fabric-apps-hola-graphql/"><![CDATA[<p>En esta tercera parte de la serie sobre Fabric Apps explico la integración con una base de datos SQL de Fabric. Cómo se gestionan la estructura y los permisos de la base de datos con Rayfin (<em>backend</em>), y cómo se utiliza GraphQL para acceder y modificar los datos desde el <em>frontend</em>. Desarrollo tres ejemplos, el primero con la plantilla <em>Basic Todo App</em> de Rayfin, el segundo parte de la misma plantilla para implementar la edición de una tabla de configuración de metadatos de una ETL, y el tercero combina la base de datos SQL con consultas DAX a un modelo semántico de Finanzas para hacer un análisis <em>what-if</em> de pérdidas y ganancias (PyG).</p>

<!--more-->

<p>Si aún no te has leído <a href="https://www.dataxbi.com/blog/tag/fabric-apps/?orden=asc">las entradas anteriores de la serie</a>, te animo a que lo hagas antes de continuar.</p>

<h2 id="hola-graphql">Hola GraphQL</h2>

<p>Vamos a comenzar creando una <em>Fabric App</em> a partir de la plantilla <em>Basic Todo App</em> de <em>Rayfin</em>.  Para ello, abro una terminal y ejecuto el comando:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm create @microsoft/rayfin@latest "Hola GraphQL"
</code></pre></div></div>

<p>Una aplicación <em>Todo</em> nos permite gestionar una lista de tareas por hacer, y es un ejemplo clásico en los tutoriales de lenguajes de programación, porque es bastante sencilla de implementar.</p>

<blockquote>
  <p>Recuerda que debes cumplir con los requisitos indicados en la primera entrada de la serie.</p>
</blockquote>

<p><img src="/assets/images/posts/2026-07-21-fabric-apps-hola-graphql/dataXbi-fabric-apps-hola-graphql-rayfin-basic-todo-app.png" alt="Creando la Fabric App en la línea de comando con la plantilla Basic Todo App de Rayfin" /></p>

<p>Si te fijas, verás que he escogido la opción 4, aunque la opción 3 también es una plantilla <em>Todo App</em>. La principal diferencia es que en la opción 3 el <em>frontend</em> está más elaborado que en la opción básica que he elegido, y utiliza la biblioteca de componentes <a href="https://www.radix-ui.com/">Radix UI</a>.</p>

<p>La ejecución de la plantilla crea la carpeta <code class="language-plaintext highlighter-rouge">hola-graphql</code> con el código de la <em>Fabric App</em>.</p>

<p>En la terminal, entra a la carpeta: <code class="language-plaintext highlighter-rouge">cd hola-graphql</code>.</p>

<p>Despliega la <em>Fabric App</em> con el comando  <code class="language-plaintext highlighter-rouge">npx rayfin up</code>.</p>

<p>Ve al área de trabajo de Fabric y prueba la aplicación creando algunas tareas. También entra a la base de datos y mira las tablas que se han creado.</p>

<p>Detalles a tener en cuenta en este tipo de <em>Fabric App</em>, que accede a una base de datos SQL:</p>
<ul>
  <li><strong>Si se puede abrir de manera independiente, sin estar dentro del portal de Fabric</strong>, a diferencia de las <em>Fabric Apps</em> que acceden a modelos semánticos que, por ahora, sólo se pueden abrir dentro del portal de Fabric.</li>
  <li>Cada <em>Fabric App</em> tiene su propia base de datos SQL y, por ahora, no se puede conectar a otra.</li>
</ul>

<p>El código lo he publicado en GitHub: <a href="https://github.com/dataxbi/fabapp-hello-graphql">https://github.com/dataxbi/fabapp-hello-graphql</a></p>

<p>Hice unos cambios mínimos en el frontend para agregarle el ícono y el logo de dataXbi. Los hice yo mismo, sin ningún agente de IA 😊.</p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-07-21-fabric-apps-hola-graphql/dataXbi-fabric-app-hola-graphql.mp4" type="video/mp4" />
</video>

<p>¿Te has preguntado por qué le he llamado “Hola GraphQL” a esta <em>Fabric App</em>? La respuesta te la doy al final de la siguiente sección.</p>

<h2 id="revisando-el-código-generado-por-la-plantilla-basic-todo-app">Revisando el código generado por la plantilla <em>Basic Todo App</em></h2>

<p>Aunque las <em>Fabric Apps</em> han sido concebidas para que el código no lo piquemos nosotros, sino que contemos con la ayuda de los agentes de IA, creo que vale la pena tener una idea general del funcionamiento interno.</p>

<p>Por lo que vamos a revisar algunos aspectos del código generado por la plantilla.</p>

<blockquote>
  <p>Puedes abrir Visual Studio Code desde la terminal con el comando <code class="language-plaintext highlighter-rouge">code .</code>.</p>
</blockquote>

<p>Lo primero que te quiero mostrar del código son las instrucciones para los agentes de IA, que están en los ficheros <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/AGENTS.md">/AGENTS.md</a> y /<a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/.agents/skills/rayfin/SKILL.md">.agents/skills/rayfin/SKILL.md</a> y que son prácticamente los mismos que los de la plantilla <em>Blank App</em> que mostré en el post inicial de la serie. De estas instrucciones, las secciones importantes para el trabajo con una base de datos son: <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/.agents/skills/rayfin/SKILL.md#security">Security</a>, <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/.agents/skills/rayfin/SKILL.md#data-modeling">Data Modeling</a>, <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/.agents/skills/rayfin/SKILL.md#querying">Querying</a> y <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/.agents/skills/rayfin/SKILL.md#schema">Schema</a>.</p>

<p>Estas son las únicas instrucciones para agentes de IA incluidas con esta plantilla. No viene el conjunto de skills de la plantilla <em>Data App</em> que mostré en el segundo post de la serie.</p>

<h3 id="backend"><em>Backend</em></h3>

<p>Otra parte interesante del código está en la carpeta <a href="https://github.com/dataxbi/fabapp-hello-graphql/tree/main/rayfin">/rayfin</a> donde se configura el <em>backend</em>. Dentro de esta carpeta encontramos el fichero <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/rayfin/rayfin.yml">rayfin.yml</a> que ya conocemos, y donde está habilitada la base de datos SQL de Fabric:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  data:
    enabled: true
    dialect: mssql
</code></pre></div></div>

<p>La estructura de esta base de datos se establece en la carpeta <a href="https://github.com/dataxbi/fabapp-hello-graphql/tree/main/rayfin/data">/rayfin/data</a> a través del fichero <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/rayfin/data/schema.ts">schema.ts</a> y de un fichero por tabla.</p>

<p>Fichero <code class="language-plaintext highlighter-rouge">schema.ts</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import { Todo } from './Todo.js';

export type TodoAppSchema = {
  Todo: Todo;
};

export const schema = [Todo];
</code></pre></div></div>

<p>En este caso solo hay una tabla con el nombre Todo cuya definición está en el fichero <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/rayfin/data/Todo.ts">Todo.ts</a>.</p>

<p>Fichero <code class="language-plaintext highlighter-rouge">Todo.ts</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import {
  entity,
  role,
  text,
  boolean,
  date,
  uuid,
} from '@microsoft/rayfin-core';

@entity()
@role('authenticated', '*', {
  policy: (claims, item) =&gt; claims.sub.eq(item.user_id),
})
export class Todo {
  @uuid() id!: string;
  @text({ min: 1, max: 100 }) title!: string;
  @boolean() isCompleted!: boolean;
  @date() createdAt!: Date;
  @text() user_id!: string;
}
</code></pre></div></div>

<p>El nombre que tendrá la tabla en la base de datos se define en la instrucción <code class="language-plaintext highlighter-rouge">export class Todo</code>, y a dicho nombre se le agregará una <strong>s</strong>.</p>

<p>A partir de esta configuración, Rayfin crea la base de datos cuando desplegamos la <em>Fabric App</em> con el comando <code class="language-plaintext highlighter-rouge">npx rayfin up</code>.</p>

<p>Aunque la estructura de la base de datos se puede modificar directamente en Fabric, <strong>es muy recomendable que todos los cambios se hagan aquí en esta configuración</strong>. De esta manera se actualizarán cada vez que hagamos un despliegue. Podemos utilizar el comando <code class="language-plaintext highlighter-rouge">npx rayfin up db apply</code> si sólo queremos cambiar la base de datos sin actualizar el resto de la <em>Fabric App</em>.</p>

<p>Te invito a leer en la <a href="https://learn.microsoft.com/es-es/fabric/apps/data-models?wt.mc_id=MVP_367391">documentación oficial</a> los detalles de la configuración de la base de datos (modelo de datos).</p>

<p>Si luego de desplegar la Fabric App, vas al área de trabajo y revisas la base de datos, podrás comprobar que se ha creado una tabla con el nombre Todo<strong>s</strong>.</p>

<h3 id="permisos-de-datos">Permisos de datos</h3>

<p>Si revisas con atención <code class="language-plaintext highlighter-rouge">Todo.ts</code> encontrarás que, además de la estructura de la tabla, se incluye este fragmento de código:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@role('authenticated', '*', {
  policy: (claims, item) =&gt; claims.sub.eq(item.user_id),
})
</code></pre></div></div>

<p>Aquí se está indicando que solo pueden acceder a esta tabla los usuarios con el rol <code class="language-plaintext highlighter-rouge">authenticated</code> (que son todos porque recuerda que para acceder a cualquier <em>Fabric App</em> hay que autenticarse en Fabric), y que pueden realizar cualquier operación de creación, lectura, modificación o borrado. Si se crea o modifica una fila, el ID del usuario se guardará en la columna user_id. Y si se lee la tabla se filtrarán las filas donde el ID del usuario coincida con el valor en la columna user_id.</p>

<p>El ID del usuario proviene de <em>Microsoft Entra ID</em> que es el servicio de autenticación que utiliza Fabric.</p>

<p>Cuando revisaste la base de datos creada en Fabric habrás notado que además de la tabla Todos, también se había creado una tabla <strong>Users</strong>. Esta es una tabla de sistema, creada automáticamente por Rayfin.</p>

<p>Puedes profundizar sobre los permisos de datos en la <a href="https://learn.microsoft.com/es-es/fabric/apps/data-permissions?wt.mc_id=MVP_367391">documentación oficial</a>.</p>

<h3 id="frontend"><em>Frontend</em></h3>

<p>Para finalizar este recorrido por el código, te voy a llevar a la carpeta <a href="https://github.com/dataxbi/fabapp-hello-graphql/tree/main/src">/src</a> que forma parte del <em>frontend</em> de la <em>Fabric App</em>. Y dentro de esta carpeta voy a abrir el fichero <a href="https://github.com/dataxbi/fabapp-hello-graphql/blob/main/src/services/todos.ts">/src/services/todos.ts</a> que contiene las operaciones de lectura, inserción, modificación y borrado que se hacen en la base de datos. Estas operaciones utilizan internamente el lenguaje de consultas para APIs <a href="https://graphql.org/"><strong>GraphQL</strong></a> y es la razón de que el nombre de esta <em>Fabric App</em> sea “Hola GraphQL”.</p>

<p>A continuación muestro algunos fragmentos del código de <code class="language-plaintext highlighter-rouge">todo.ts</code>.</p>

<p>Listar las filas de la tabla Todos:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  const client = getRayfinClient();
  const results = await client.data.Todo.select([
    'id',
    'title',
    'isCompleted',
    'createdAt',
  ])
    .orderBy({ createdAt: 'desc' })
    .execute();
</code></pre></div></div>

<p>Crear una fila en la tabla Todos:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  const client = getRayfinClient();
    const session = client.auth.getSession();
    if (!session.isAuthenticated || !session.user) {
      throw new Error('Cannot create todo: user is not authenticated.');
    }
    const todo = await client.data.Todo.create({
      title,
      isCompleted: false,
      createdAt: new Date(),
      user_id: session.user.id,
    });
</code></pre></div></div>

<p>Modificar una fila de la tabla Todos:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  const client = getRayfinClient();
  await client.data.Todo.update({ id }, updates);
  const todo = await client.data.Todo.findById(id);

</code></pre></div></div>

<p>Borrar una fila de la tabla Todos:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  const client = getRayfinClient();
  await client.data.Todo.delete({ id });
</code></pre></div></div>

<h2 id="etl-config">ETL Config</h2>

<p>En el segundo ejemplo he hecho una <em>Fabric App</em> para gestionar la configuración de la ingesta de datos hacia la capa bronce de una arquitectura Medallion. Cuando implemento una ETL me gusta que sea <em>metadata driven</em>, y una base de datos SQL es una buena opción para guardar los metadatos de configuración. Algunos de esos metadatos se pueden llenar de manera automática, pero es útil poder hacer modificaciones puntuales, y con una <em>Fabric App</em> se logra una solución muy elegante.</p>

<p>He partido de la misma plantilla <em>Basic Todo App</em>, y he utilizado agentes de IA siguiendo el mismo proceso que ya he explicado en otros posts de esta serie: <strong>plan</strong> - <strong>especificación</strong> - <strong>implementación</strong> - <strong>testing</strong> - <strong>despliegue</strong>, en varias iteraciones.</p>

<blockquote>
  <p>Recomiendo instalar el <a href="https://learn.microsoft.com/es-es/training/support/mcp?wt.mc_id=MVP_367391">MCP de Microsoft Learn</a> para que los agentes tengan acceso a la documentación de la configuración del modelo de la base de datos SQL.</p>
</blockquote>

<p>Lo primero que definí fue la estructura de la tabla de metadatos:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">EtlConfigIngestions</span> <span class="p">(</span>
    <span class="n">ConfigId</span> <span class="n">UNIQUEIDENTIFIER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="k">DEFAULT</span> <span class="n">NEWID</span><span class="p">(),</span>

    <span class="n">IsEnabled</span> <span class="nb">BIT</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">DEFAULT</span> <span class="mi">1</span><span class="p">,</span>

    <span class="n">SourceConnectionName</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">SourceSchemaName</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">SourceTableName</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>

    <span class="n">LoadMode</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">IncrementalColumnName</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span> <span class="k">NULL</span><span class="p">,</span>

    <span class="n">TargetWorkspaceName</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">TargetLakehouseName</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">TargetSchemaName</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">TargetTableName</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>

    <span class="n">CreatedAt</span> <span class="n">DATETIME2</span> <span class="k">NOT</span> <span class="k">NULL</span>
        <span class="k">DEFAULT</span> <span class="n">SYSDATETIME</span><span class="p">(),</span>

    <span class="n">UpdatedAt</span> <span class="n">DATETIME2</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">UpdatedByUserId</span> <span class="n">NVARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span> <span class="k">NULL</span><span class="p">,</span>

    <span class="k">CONSTRAINT</span> <span class="n">CK_EtlConfigIngestions_LoadMode</span>
        <span class="k">CHECK</span> <span class="p">(</span><span class="n">LoadMode</span> <span class="k">IN</span> <span class="p">(</span><span class="s1">'Full'</span><span class="p">,</span> <span class="s1">'Incremental'</span><span class="p">))</span>
<span class="p">);</span>
</code></pre></div></div>

<p>En cuanto a la parte visual de la aplicación, le indiqué que se basara en el diseño de nuestro sitio web y que utilizara la biblioteca <a href="https://tanstack.com/table/latest">TanStack Table</a>.</p>

<p>La especificación completa la puedes leer aquí: <a href="https://github.com/dataxbi/fabapp-etl-config/blob/main/specs/00-especificacion-general.md">https://github.com/dataxbi/fabapp-etl-config/blob/main/specs/00-especificacion-general.md</a></p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-07-21-fabric-apps-hola-graphql/dataXbi-fabric-app-etl-config.mp4" type="video/mp4" />
</video>

<p>Todo el código está disponible en GitHub: <a href="https://github.com/dataxbi/fabapp-etl-config">https://github.com/dataxbi/fabapp-etl-config</a></p>

<p>Si revisas <a href="https://github.com/dataxbi/fabapp-etl-config/commits/main/">el historial de los commits</a> podrás ver cómo fui iterando hasta llegar a la versión final.</p>

<h2 id="análisis-what-if-pyg">Análisis What-If PyG</h2>

<p>Para el último ejemplo de hoy voy a combinar en una <em>Fabric App</em> un modelo semántico de finanzas y una base de datos SQL, para implementar un análisis <em>what-if</em> de pérdidas y ganancias (PyG).</p>

<p>El modelo semántico es el mismo que utilicé en el segundo post de esta serie para implementar un informe de PyG. La base de datos SQL almacena posibles escenarios de aumento o disminución de los ingresos y los gastos, expresados en valores porcentuales. La <em>Fabric App</em> permite gestionar los escenarios (crear, modificar, borrar), y comprobar cómo influyen en el informe de PyG, calculando la variación con el estado actual. Esto ocurre de forma interactiva y con un tiempo de respuesta bajo, por lo que la experiencia del usuario es muy buena.</p>

<blockquote>
  <p>Esta <em>Fabric App</em> solo se puede abrir dentro del portal de Fabric porque accede a un modelo semántico.</p>
</blockquote>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-07-21-fabric-apps-hola-graphql/dataXbi-fabric-app-what-if.mp4" type="video/mp4" />
</video>

<p>Lo que ocurre internamente es lo siguiente:</p>
<ul>
  <li>La gestión de los escenarios se hace con GraphQL. Excepto el listado de cuentas al insertar un ajuste, que se obtiene con una consulta DAX al modelo semántico.</li>
  <li>Para construir la tabla PyG What-if, primero se hace una consulta GraphQL para obtener los valores del escenario. Estos datos se incluyen en una consulta DAX que se envía al modelo semántico, para obtener los valores actuales de ingresos, gastos y total, y calcular la variación con el escenario.</li>
</ul>

<p>En el modelo semántico no hay medidas para calcular la variación, porque los datos del escenario no están en el modelo. Hay expresiones DAX que solo están en la <em>Fabric App</em>.</p>

<p>En lugar de crear tablas desconectadas dentro del modelo, las estamos externalizamos a una base de datos SQL, y también externalizamos, en el <em>frontend</em> de la <em>Fabric App</em>, el código DAX que hace la conexión y los cálculos.</p>

<p><img src="/assets/images/posts/2026-07-21-fabric-apps-hola-graphql/dataXbi-fabric-app-what-if-diagrama.png" alt="Diagrama explicando como se combinan las consultas GraphQL y DAX en esta Fabric App" /></p>

<p>Al implementar este ejemplo, en lugar de crear una nueva aplicación con Rayfin, cloné el <a href="https://github.com/dataxbi/fabapp-dax-finance">repositorio con el informe de PyG</a>. Si hubiera empezado de cero, utilizaría la plantilla <em>Data App</em> porque  incluye todo lo necesario para hacer y probar las consultas DAX, que es lo más complicado.</p>

<p>A partir de aquí, fui iterando en mi proceso habitual de trabajo con agentes de IA: <strong>plan</strong> - <strong>especificación</strong> - <strong>implementación</strong> - <strong>testing</strong> - <strong>despliegue</strong>.</p>

<p>Todo el código está disponible en GitHub: <a href="https://github.com/dataxbi/fabapp-what-if">https://github.com/dataxbi/fabapp-what-if</a></p>

<p>Te invito a revisar:</p>
<ul>
  <li>Las especificaciones: <a href="https://github.com/dataxbi/fabapp-what-if/tree/main/docs">https://github.com/dataxbi/fabapp-what-if/tree/main/docs</a></li>
  <li>El esquema de la base de datos: <a href="https://github.com/dataxbi/fabapp-what-if/tree/main/rayfin/data">https://github.com/dataxbi/fabapp-what-if/tree/main/rayfin/data</a></li>
  <li>Las consultas DAX: <a href="https://github.com/dataxbi/fabapp-what-if/tree/main/src/queries/pl">https://github.com/dataxbi/fabapp-what-if/tree/main/src/queries/pl</a></li>
</ul>

<h2 id="continuará">Continuará…</h2>

<p>Gracias por llegar hasta aquí. 😊</p>

<p>En la próxima entrega de la serie te mostraré una herramienta gratuita que te permite crear las <em>Fabric Apps</em> desde una aplicación de escritorio, nada de línea de comando.</p>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="fabric-apps" /><category term="fabric" /><summary type="html"><![CDATA[En esta tercera parte de la serie sobre Fabric Apps explico la integración con una base de datos SQL de Fabric. Cómo se gestionan la estructura y los permisos de la base de datos con Rayfin (backend), y cómo se utiliza GraphQL para acceder y modificar los datos desde el frontend. Desarrollo tres ejemplos, el primero con la plantilla Basic Todo App de Rayfin, el segundo parte de la misma plantilla para implementar la edición de una tabla de configuración de metadatos de una ETL, y el tercero combina la base de datos SQL con consultas DAX a un modelo semántico de Finanzas para hacer un análisis what-if de pérdidas y ganancias (PyG).]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-07-21-fabric-apps-hola-graphql/dataXbi-fabric-app-what-if.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-07-21-fabric-apps-hola-graphql/dataXbi-fabric-app-what-if.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fabric Apps - Hola DAX</title><link href="https://www.dataxbi.com/blog/2026/06/20/fabric-apps-hola-dax/" rel="alternate" type="text/html" title="Fabric Apps - Hola DAX" /><published>2026-06-20T00:00:00+02:00</published><updated>2026-06-20T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/06/20/fabric-apps-hola-dax</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/06/20/fabric-apps-hola-dax/"><![CDATA[<p>Esta es la segunda entrega de la serie sobre Fabric Apps en la que mostraré cómo construir cuadros de mando a partir de modelos semánticos. Tomaré como base la plantilla <em>Data App</em> que trae Rayfin para hacer tres ejemplos: un cuadro de mando de ventas, un informe de PyG y un cuadro de mando para la dirección general que combine consultas de los dos modelos semánticos anteriores.</p>

<p>Si en el post anterior me di el capricho de regresar por unos momentos a mi niñez para jugar 😉, en este me enfoco más en mostrar el <em>business value</em> que pueden tener las Fabric Apps.</p>

<!--more-->

<h3 id="la-plantilla-data-app-de-rayfin">La plantilla <em>Data App</em> de Rayfin</h3>

<p>Para comenzar, voy a implementar un cuadro de mando de ventas, a partir de un modelo semántico que ya esta publicado en un área de trabajo de Fabric.</p>

<p>Más adelante hablaré del modelo, porque ahora utilizaré Rayfin para sentar los cimientos de esta Fabric App.</p>

<p>Si quieres seguir cada paso, te recomiendo revisar los requisitos y la forma de trabajar con Rayfin en el <a href="https://www.dataxbi.com/blog/2026/06/11/fabric-apps-hola-mundo/">post anterior</a>.</p>

<p>Pues si ya tienes todos los requisitos, manos a la obra con la nueva Fabric App.</p>
<ol>
  <li>Abre la <strong>Terminal</strong> y ejecuta el comando <code class="language-plaintext highlighter-rouge">npm create @microsoft/rayfin@latest "Hola DAX"</code>
    <ol>
      <li>En el menú que aparece, escoge la primera opción: 📦 <strong>Use a template (built-in)</strong></li>
      <li>Selecciona la segunda <em>template</em>:  2) <strong>Data App - Build data analytics app based on your data in Fabric</strong></li>
      <li>Espera a que se instalen todos los componentes en la carpeta <strong>hola-dax</strong>.</li>
    </ol>
  </li>
  <li>Cámbiate a la nueva carpeta <strong>hola-dax</strong>.</li>
  <li>Ya dentro de la carpeta, abre VS Code con el comando: <code class="language-plaintext highlighter-rouge">code .</code></li>
</ol>

<p>Vamos a detenernos en este punto para echarle un vistazo a lo que ha instalado esta plantilla. ¡Yo flipé con lo que me encontré!</p>

<p>Por cierto, en esta plantilla <strong>si viene deshabilitada la base de datos</strong>, por lo que no hay que recurrir al truco que vimos en el primer post para evitar desplegar en Fabric una base de datos SQL que no vamos a utilizar.</p>

<p>A primera vista la estructura de carpetas es similar a la creada por la plantilla que utilizamos en el primer post (<em>Blank App</em>,).</p>

<p>Pero, hay un tesoro frente a nosotros, y para descubrirlo basta con abrir el archivo <a href="https://github.com/dataxbi/fabapp-hello-dax/blob/main/AGENTS.md">AGENTS.md</a>.</p>

<p>En <em>Blank App</em> este archivo solo contenía las instrucciones de Rayfin, pero ahora tiene una gran cantidad de instrucciones, dirigidas a implementar una aplicación de datos en el <em>front-end</em> que es capaz de ejecutar consultas DAX en modelos semánticos de Power BI / Fabric.</p>

<p>Aquí tenemos un verdadero <em>framework</em> dividido en varias capas y totalmente preparado para los agentes de IA generativa.</p>

<p>AGENTS.md es solo el inicio, porque luego cada capa tiene su propia SKILL.md. Las puedes encontrar en la carpeta <a href="https://github.com/dataxbi/fabapp-hello-dax/tree/main/.agents/skills">.agents/skills</a>. Muchas de estas SKILLS hacen referencia a módulos que se han instalado en la carpeta <strong>node_modules</strong>.</p>

<p>Creo que vale la pena revisar los archivos AGENT y SKILLS para comprender el funcionamiento del <em>framework</em>.</p>

<p><img src="/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-vscode-data-app.png" alt="Estructura de carpetas de la plantilla Data App de Rayfin" /></p>

<p>Como punto de partida utiliza la documentación oficial: <a href="https://learn.microsoft.com/es-es/fabric/apps/data-apps-template?wt.mc_id=MVP_367391">https://learn.microsoft.com/es-es/fabric/apps/data-apps-template</a></p>

<p>Aquí te dejo un resumen de cada capa:</p>

<ul>
  <li>
    <p><strong>Experiencia de aplicación</strong>: 
Responsable de estructurar la aplicación en páginas y de la navegación.</p>
  </li>
  <li>
    <p><strong>Presentación visual</strong>:
Responsable de dibujar gráficos, tablas, paneles y otros controles de UI, además de capturar las interacciones del usuario. Utiliza <a href="https://vega.github.io/vega-lite/">Vega-Lite</a> para los gráficos y el componente <a href="https://www.npmjs.com/package/@microsoft/fabric-datagrid">@microsoft/fabric-datagrid</a> para las tablas.</p>
  </li>
  <li>
    <p><strong>Orquestación de página</strong>:
Responsable de conectar la UI con la lógica de filtrado, selección y parámetros que alimentan las consultas DAX.</p>
  </li>
  <li>
    <p><strong>Arquitectura de consultas</strong>:
Responsable de definir qué se consulta, cómo se describe cada visual y qué metadatos acompañan al resultado.</p>
  </li>
  <li>
    <p><strong>Acceso a datos runtime</strong>:
Responsable de ejecutar consultas DAX en el modelo semántico y de su caché. Utiliza el componente <a href="https://www.npmjs.com/package/@microsoft/fabric-app-data">@microsoft/fabric-app-data</a></p>
  </li>
  <li>
    <p><strong>Modelo semántico</strong>:
Responsable de aportar medidas, dimensiones, relaciones y metadatos del modelo publicado en Fabric/Power BI.</p>
  </li>
  <li>
    <p><strong>Transformación de datos</strong>:
Responsable de convertir la respuesta cruda de las consultas DAX en un formato visual consumible y enriquecido.</p>
  </li>
  <li>
    <p><strong>Diseño y temas</strong>:
Responsable de los colores, la tipografía, el <em>branding</em> y la consistencia visual entre componentes.</p>
  </li>
  <li>
    <p><strong>Validación y calidad</strong>:
Responsable de comprobar el comportamiento de la aplicación. Realiza tests y validación real utilizando <a href="https://playwright.dev/agent-cli/introduction">Playwright CLI</a>.</p>
  </li>
  <li>
    <p><strong>Plataforma · Rayfin / Fabric embed</strong>:
Responsable de desplegar la aplicación en Fabric, de la autenticación y la configuración del entorno.</p>
  </li>
</ul>

<p>Con estas capas no solo podemos implementar la aplicación, sino que además nos permiten descubrir las estructura del modelo semántico y probar las consultas DAX antes de utilizarlas, hacer <em>testing</em> una vez implementada la aplicación, y desplegarla en Fabric.</p>

<h3 id="hola-dax">Hola DAX</h3>

<p>Como hemos visto, este <em>framework</em> está muy bien preparado para los agentes de IA. Así que vamos a continuar implementando el cuadro de mando de ventas con la ayuda de nuestra herramienta de IA preferida. En mi caso he utilizado indistintamente <a href="https://github.com/features/copilot/cli">GitHub Copilot CLI</a> y <a href="https://developers.openai.com/codex/cli">Codex CLI</a>.</p>

<p>Antes de continuar, te recomiendo que le pidas al agente que <strong>compruebe si se puede conectar a Fabric</strong> y con cuál usuario lo está haciendo, para evitar que esté utilizando un usuario incorrecto.</p>

<p>O también lo puedes hacer directamente en la terminal con el comando <code class="language-plaintext highlighter-rouge">npx rayfin login status</code> y así te ahorras un par de tokens.</p>

<p>Cuando trabajo con agentes de IA en un proyecto lo primero que hago es configurar GIT, para mantener la historia de todos los cambios. Y he hecho un primer commit justo después de instalar la plantilla.</p>

<p>Lo siguiente que hago es elaborar un plan inicial, con la ayuda de la herramienta de IA, que guardo en un archivo de especificaciones y en archivos con las diferentes tareas. Una vez que las especificaciones están revisadas y corregidas por mí, hago un commit, y le pido al agente que lo implemente.</p>

<p>Para elaborar el plan para una <em>Data App</em> (esta plantilla) hay que tener en cuenta el <strong>flujo de trabajo que seguirán los agentes</strong>, como se describe en el archivo AGENTS, y que tiene tres fases:</p>

<ol>
  <li><strong>Fase de diseño y creación de consultas</strong>: Exploración de los datos y validar las consultas. En esta fase todavía no se escribe código de la aplicación.</li>
  <li><strong>Fase de diseño de la aplicación</strong>: Diseño de la experiencia de usuario (UX) de la aplicación web antes de escribir cualquier código de ejecución.</li>
  <li><strong>Fase de desarrollo de la aplicación</strong>: Implementación de los componentes  que obtienen los datos en tiempo de ejecución utilizando el SDK de Fabric.</li>
</ol>

<p>Como parte del plan hay que indicar la URL del modelo semántico en Fabric, que tiene un formato como este:
https://app.fabric.microsoft.com/onelake/details/id-area-trabajo/dataset/id-modelo-semantico</p>

<p>Esta información le servirá a los agentes para para configurar la aplicación.</p>

<p>El modelo de ventas con el que trabajé tiene la estructura mostrada en la siguiente imagen, y las medidas <code class="language-plaintext highlighter-rouge">[Facturacion], [Margen] y [% Margen]</code>.</p>

<p><img src="/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-modelo-semantico-ventas.png" alt="Estructura del modelo de ventas" /></p>

<p>Como punto de partida del plan utilicé un <em>prompt</em> bastante abierto:</p>

<blockquote>
  <p>Construir una Fabric App ejecutiva sobre el modelo semántico Ventas (https://app.fabric.microsoft.com/onelake/details/id-area-trabajo/dataset/id-modelo-semantico) para ofrecer una visión resumida del negocio y páginas de detalle que permitan profundizar en los principales ejes comerciales.</p>
</blockquote>

<p>Las únicas indicaciones de diseño que incluí fueron que utilizara el logo y el tono visual de nuestro sitio web, y que los formatos de fechas y números fueran los de España.</p>

<p>El agente se encargó de conectarse al modelo para descubrir las tablas y medidas y me propuso un informe con cuatro páginas:</p>
<ul>
  <li>Overview</li>
  <li>Regiones</li>
  <li>Portafolio comercial</li>
  <li>Clientes</li>
</ul>

<p>Continué iterando hasta tener un plan completo que puedes ver en GitHub: <a href="https://github.com/dataxbi/fabapp-hello-dax/blob/main/docs/especificacion-cuadro-mando-ventas.md">https://github.com/dataxbi/fabapp-hello-dax/blob/main/docs/especificacion-cuadro-mando-ventas.md</a></p>

<p>Durante la ejecución del plan por parte del agente se puede observar como utiliza las diferentes <em>skills</em> y herramientas para crear las consultas DAX que utilizará en cada visual y para probarlas antes de incluirlas en la aplicación.</p>

<p>Y una vez que termina de implementar la aplicación ejecuta comandos como <code class="language-plaintext highlighter-rouge">npm run test</code>, <code class="language-plaintext highlighter-rouge">npm run build</code> y <code class="language-plaintext highlighter-rouge">npm run lint</code> que son propios del flujo de desarrollo de aplicaciones front-end.</p>

<p>Al final, el agente ejecuta la Fabric App localmente y hace validaciones utilizando Playwright CLI. Aquí me falló algunas veces porque el back-end de la aplicación no se publicaba en Fabric la primera vez.</p>

<p>Para desplegar la aplicación completa en Fabric, se lo puedes pedir al agente o, si te quires ahorrar unos tokens, utilizar directamente el comando <code class="language-plaintext highlighter-rouge">npx rayfin up</code>.</p>

<p>Ten en cuenta que, por ahora, <strong>las Fabric Apps que se conectan a un modelo semántico solo se pueden abrir dentro del portal Fabric</strong>, no lo pueden hacer de manera independiente como vimos en el primer post. Esto se debe a que la autenticación está implementada de una manera que depende de funcionalidad presente en el portal de Fabric. Pero esto debe cambiar en el futuro.</p>

<p>La primera versión de la aplicación salió bastante bien, pero no cargaba datos en las tablas, así que le pedí a mi agente que revisara y corrigiera.</p>

<p>También tuve que pedirle mejoras en la parte visual, sobre todo en la coherencia, porque utilizó colores diferentes en los gráficos.</p>

<p>Pienso que estos resultados iniciales podrían haber sido mejores si hubiera incluido más detalles de diseño en la especificación y hubiera utilizado otro modelo de LLM. Aquí he utilizado GPT 5.4.</p>

<p>Así luce la Fabric App:</p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-ventas.mp4" type="video/mp4" />
</video>

<p>Todo el código esta en GitHub: <a href="https://github.com/dataxbi/fabapp-hello-dax">https://github.com/dataxbi/fabapp-hello-dax</a></p>

<h4 id="cross-filter">Cross-filter</h4>

<p>Aún no hemos terminado con este primer ejemplo, porque esta versión inicial <strong>no tiene <em>cross-filter</em></strong> entre los visuales, a pasar de que el <em>framework</em> de <em>Data App</em> viene preparado para implementarlo. Aquí también influyen dos factores: 1) la especificación, donde no mencioné que se implementara <em>cross-filter</em>, y 2) el modelo de LLM que estoy utilizando.</p>

<p>Bueno, para implementarlo seguí el mismo proceso de planificar primero y que el agente implemente después, enriqueciendo mi propuesta con las sugerencia del modelo LLM.</p>

<p>El resultado puedes verlo a continuación. Además de aplicar el filtrado entre visuales, hay un panel en la página que muestra los filtros aplicados y botones para limpiarlos.</p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-cross-filter.mp4" type="video/mp4" />
</video>

<h4 id="panel-de-filtros-dinámicos">Panel de filtros dinámicos</h4>

<p>Llegados a este punto, me vine un poco arriba, y quise ir un poco más allá, para poner a prueba los límites de lo que se puede hacer.</p>

<p>Con la ayuda de los agentes de IA he implementado un panel de filtros dinámicos, que trabaja de la siguiente manera:</p>
<ul>
  <li>Al cargar la página utiliza las funciones INFO de DAX para encontrar las dimensiones del modelo semántico, descartando las tablas y columnas ocultas.</li>
  <li>Agrupa cada dimensión por su tipo de dato: texto, numérico o fecha.</li>
  <li>Crea en la página web un panel de filtros con un control específico para cada tipo de dato.
    <ul>
      <li>Para los números utiliza un rango con mínimo y máximo.</li>
      <li>Para las fechas utiliza un rango con calendarios.</li>
      <li>Para los textos utiliza una lista desplegable, pero los datos solo se cargan en el momento en que se abre el filtro.</li>
    </ul>
  </li>
</ul>

<p>Para implementar estos controles le pedí en la especificación que utilizara la librería de código abierto <a href="https://ui.shadcn.com/docs">shadcn/ui</a>.</p>

<p>Con esto he extendido la funcionalidad que trae el <em>framework</em> de la plantilla Data App, pero solo para esta aplicación.</p>

<p>Para reutilizar este panel de filtros dinámico en otros proyectos, debería haber creado una SKILL. Algo que no descarto hacer en el futuro.</p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-filtros-dinamicos.mp4" type="video/mp4" />
</video>

<h3 id="informe-pyg">Informe PyG</h3>

<p>Para el segundo ejemplo he utilizado otro modelo semántico, esta vez de finanzas, que tiene la estructura que muestro en la imagen y con medidas como <code class="language-plaintext highlighter-rouge">[Gastos], [Ingresos], [Presupuesto], [Variacion]</code></p>

<p><img src="/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-modelo-semantico-finanzas.png" alt="Estructura del modelo de finanzas" /></p>

<p>Creé un nuevo proyecto con la plantilla Data App y seguí el mismo proceso que he explicado antes.</p>

<p>Esta vez fuí más explicito al crear la especificación:</p>

<blockquote>
  <p>Construir una Fabric App ejecutiva sobre el modelo semántico de P&amp;L para ofrecer una visión del resultado económico del negocio con una tabla detallada y tarjetas resumen, usando componentes propios en React.</p>
</blockquote>

<p>Puedes leer el texto completo aquí: <a href="https://github.com/dataxbi/fabapp-dax-finance/blob/main/docs/especificacion-informe-pl.md">https://github.com/dataxbi/fabapp-dax-finance/blob/main/docs/especificacion-informe-pl.md</a>, que es el resultado de varias iteraciones.</p>

<p>El fragmento de la especificación termina diciendo “… usando componentes propios…” y si abres la especificación completa verás que en el stack de visuales se indica que para la tabla utilice un componente propio basado en <a href="https://tanstack.com/table/latest">@tanstack/react-table</a> que es una librería de código abierto.</p>

<p>Además, he modificado la <a href="https://github.com/dataxbi/fabapp-dax-finance/blob/main/.agents/skills/visuals/SKILL.md">SKILL de la capa visual</a> para que al dibujar tablas se utilice este componente en lugar del original de la plantilla Data App.</p>

<p>Y también he cambiado las <a href="https://github.com/dataxbi/fabapp-dax-finance/blob/main/.agents/skills/visuals/references/data-grid-visual.md">instrucciones para construir las tablas</a>.</p>

<p>Todo el código está en GitHub: <a href="https://github.com/dataxbi/fabapp-dax-finance">https://github.com/dataxbi/fabapp-dax-finance</a></p>

<p>Y el resultado lo puedes ver en este clip:</p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-finanzas.mp4" type="video/mp4" />
</video>

<h3 id="cuadro-de-mando-multi-modelo">Cuadro de mando multi modelo</h3>

<p>El tercer ejemplo lo que tiene de novedoso es que combina datos y métricas de los dos modelos de ventas y de finanzas, para hacer un cuadro de mando ejecutivo para la dirección general.</p>

<p>De nuevo creé un proyecto con la plantilla Data App, y seguí el mismo proceso: <strong>plan - especificación - implementación - testing - despliegue</strong>, a través de varias iteraciones.</p>

<p>La especificación final comienza de esta manera:</p>

<blockquote>
  <p>Construir un dashboard ejecutivo para Dirección General sobre dos modelos semánticos de Microsoft Fabric, ventas y finanzas.</p>

  <p>El dashboard debe presentar una vista clara del negocio con KPIs, comparativa inter anual y al menos un gráfico temporal que combine medidas de ambos modelos mediante su dimension común de fecha.</p>
</blockquote>

<p>Puedes ver el texto completo aquí: <a href="https://github.com/dataxbi/fabapp-dax-multi-model/blob/main/docs/especificacion-general.md">https://github.com/dataxbi/fabapp-dax-multi-model/blob/main/docs/especificacion-general.md</a></p>

<p>Los modelos semánticos siguen estando separados en Fabric, <strong>la mezcla de los datos ocurre en el <em>front-end</em></strong>, luego de hacer consultas DAX independientes para cada modelo.</p>

<p>Todo el código está en GitHub: <a href="https://github.com/dataxbi/fabapp-dax-multi-model">https://github.com/dataxbi/fabapp-dax-multi-model</a></p>

<p>Y el resultado lo puedes ver en este clip:</p>

<video width="100%" autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-multi-modelo.mp4" type="video/mp4" />
</video>

<h3 id="continuará">Continuará…</h3>

<p>Este post me ha salido un poco largo, así que te agradezco si has llegado hasta aquí. 👏</p>

<p>Mi objetivo ha sido mostrar las posibilidades de la plantilla Data App y cómo se puede personalizar y extender.</p>

<p>He creado algunos ejemplos que me han parecido interesantes con la esperanza de dejarte con ganas de probar y de innovar.</p>

<p>Recuerda que todo lo que hemos visto hoy ocurre en el <strong>front-end</strong> de la aplicación web.</p>

<p>La próxima entrega de la serie estará dedicada como hacer Fabric Apps para gestionar una base de datos SQL en Fabric.</p>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="fabric-apps" /><category term="fabric" /><summary type="html"><![CDATA[Esta es la segunda entrega de la serie sobre Fabric Apps en la que mostraré cómo construir cuadros de mando a partir de modelos semánticos. Tomaré como base la plantilla Data App que trae Rayfin para hacer tres ejemplos: un cuadro de mando de ventas, un informe de PyG y un cuadro de mando para la dirección general que combine consultas de los dos modelos semánticos anteriores.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-multi-modelo.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-06-20-fabric-apps-hola-dax/dataXbi-fabric-apps-hola-dax-multi-modelo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fabric Apps - Hola mundo</title><link href="https://www.dataxbi.com/blog/2026/06/11/fabric-apps-hola-mundo/" rel="alternate" type="text/html" title="Fabric Apps - Hola mundo" /><published>2026-06-11T00:00:00+02:00</published><updated>2026-06-11T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/06/11/fabric-apps-hola-mundo</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/06/11/fabric-apps-hola-mundo/"><![CDATA[<p>En Microsoft Build 2026 fueron presentadas las Fabric Apps, y ya están disponibles en versión preliminar. Con este post inicio una serie donde iré explicando qué son, cómo funcionan y qué podemos hacer con ellas.</p>

<!--more-->

<h3 id="qué-es-una-fabric-app">¿Qué es una Fabric App?</h3>

<p>En esencia, una <a href="https://learn.microsoft.com/es-es/fabric/apps/overview?wt.mc_id=MVP_367391">Fabric App</a> es una aplicación web que se hospeda en un área de trabajo de Fabric.</p>

<p>Como toda aplicación web, está formada por un <em>front-end</em> que se ejecuta en un navegador en tu ordenador, y un <em>back-end</em> que en este caso se ejecuta en Fabric.</p>

<p>En el <em>front-end</em> tenemos casi total libertad para implementar lo que se nos ocurra.</p>

<p>Mientras que en el <em>back-end</em> estamos limitados a utilizar los servicios que se nos permitan, que por ahora son dos: autenticación y base de datos Fabric SQL.</p>

<p>Para gestionar los servicios del <em>back-end</em> y la publicación de la aplicación en Fabric debemos utilizar <a href="https://www.microsoft.com/en-us/microsoft-fabric/features/rayfin">Rayfin</a>, un proyecto de <a href="https://github.com/microsoft/rayfin">código abierto</a> desarrollado por Microsoft.</p>

<p>Con estos elementos tenemos todo lo necesario para construir, con la ayuda de agentes de IA generativa, un cuadro de mando que se conecte a un modelo semántico de Power BI, o incluso a varios.</p>

<p>También podemos implementar una aplicación para gestionar una base de datos Fabric SQL.</p>

<p>O una aplicación que combine las dos anteriores.</p>

<p>Las posibilidades son infinitas. Pero vayamos paso a paso.</p>

<p>En este post me centraré en lo más básico y mostraré <strong>tres ejemplos que utilizan únicamente el front-end</strong>.</p>

<p>En realidad, lo único que tienen de Fabric Apps es que están publicados en Fabric.</p>

<h3 id="hola-mundo">Hola mundo</h3>

<p>Voy a comenzar mostrando paso a paso como crear una Fabric App minimalista, formada con una sola página y <strong>sin ninguna base de datos</strong>.</p>

<ol>
  <li>Requisitos antes de comenzar:
    <ol>
      <li>Que un administrador de Fabric haya activado la opción: <strong>Habilitar elementos de aplicación de Fabric (versión preliminar)</strong></li>
      <li>Tener acceso a un área de trabajo con capacidad Fabric.</li>
      <li>Instalar <strong>Node.js</strong> en tu ordenador, que se puede descargar desde <a href="https://nodejs.org/">https://nodejs.org/</a></li>
      <li>También te recomiendo instalar <strong><a href="https://code.visualstudio.com/">Visual Studio Code</a></strong>, si es que aún no lo tienes.</li>
    </ol>
  </li>
  <li>
    <p>Rayfin trabaja en la línea de comandos, por lo que debes abrir la aplicación <strong>Terminal de Windows</strong>. Una manera de hacerlo es con la combinación de teclas <strong>Windows</strong> + <strong>R</strong> y luego escribe <strong>wt</strong> y pulsa <strong>Enter</strong>.</p>
  </li>
  <li>Para crear la Fabric App, dentro de la terminal ejecuta este comando: <strong>npm create @microsoft/rayfin@latest “Hola Mundo”</strong>, que creará la carpeta <strong>hola-mundo</strong> con la estructura de un proyecto Rayfin e instalará varias librerías.
    <ol>
      <li>En el menú que aparece, escoge la primera opción: 📦 <strong>Use a template (built-in)</strong></li>
      <li>Selecciona la primera <em>template</em>: 1) <strong>Blank App - Bare-bones Fabric-authenticated React + Vite app — sign-in, routing, and a placeholder home page, with
no data layer to remove</strong></li>
      <li>Espera a que se instalen todos los componentes en la carpeta <strong>hola-mundo</strong>.</li>
    </ol>
  </li>
  <li>
    <p>Cámbiate a dicha carpeta: <strong>hola-mundo</strong></p>
  </li>
  <li>Ya dentro de la carpeta, ejecuta el comando: <strong>npm run dev</strong>
    <ol>
      <li>Este comando te permite probar la Fabric App en tu ordenador antes de la publicación definitiva en Fabric. Aunque actualmente necesita publicar una parte de la aplicación en Fabric para que funcione la autenticación.</li>
      <li>Escribe el <strong>nombre del área de trabajo</strong> donde quieres publicar la Fabric App.</li>
      <li>Espera hasta que termine de prepararse todo.</li>
      <li>Al finalizar la preparación, en la terminal debe aparecer un texto similar a este: <strong>Local: http://localhost:5173</strong>, que significa que hay un pequeño servidor web en tu ordenador listo para probar la Fabric App.</li>
      <li>Para abrir el link (http://localhost:5173) puedes pasar el cursor sobre él y utilizar la combinación <strong>Ctrl</strong> + <strong>clic</strong>.</li>
      <li>Primero te pedirá autenticarte con sus credenciales de Fabric, si no estás autenticado ya.</li>
      <li>A continuación te aparecerá una página con el texto “Hello, World”.</li>
    </ol>
  </li>
  <li>Una vez superada la prueba local estamos listos para desplegar la Fabric App para que se pueda utilizar por otros usuarios.
    <ol>
      <li>Regresa a la terminal y, situado en la carpeta hola-mundo, ejecuta el comando: <strong>npx rayfin up</strong></li>
      <li>Esto despliega en Fabric la versión definitiva de la Fabric App.</li>
      <li>Si vas al área de trabajo verás que hay un elemento con el nombre <strong>hola-mundo</strong>.</li>
      <li>Hay dos maneras de acceder a la Fabric App:
        <ul>
          <li>
            <p>Utilizando un URL como este: https://app.fabric.microsoft.com/groups/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/appbackends/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</p>

            <p>Para navegar a este URL basta con hacer clic en el elemento dentro del área de trabajo.</p>

            <p>La Fabric App se ve dentro del portal de Fabric.</p>
          </li>
          <li>
            <p>Utilizando un URL como este: https://xxxxxxxxxxxxxxxxxxxxx-westeurope.webapp.fabricapps.net/</p>

            <p>Este URL se puede ver en los mensajes que imprimió en la pantalla el comando que ejecutamos para publicar: npx rayfin up</p>

            <p>La Fabric App se ve como una aplicación web independiente, sin los elementos de Fabric.</p>

            <p>En <strong>ambos casos</strong> es necesario <strong>autenticarse</strong> con un usuario de Fabric.</p>
          </li>
        </ul>
      </li>
    </ol>
  </li>
</ol>

<p>¡Felicidades! Has creado tu primera Fabric App.</p>

<p><img src="/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-hello-world.png" alt="Pantallazo con una Fabric App generada con la plantilla &quot;Blank App&quot; y que solo dice &quot;Hello, World.&quot;" /></p>

<p>Pero, <strong>aún no hemos terminado este primer ejemplo</strong>. Porque te había prometido que no utilizaríamos una base de datos, y de hecho no la estamos usando, pero si se está creando una en Fabric.</p>

<p>Si entras al área de trabajo de Fabric verás que bajo el nuevo elemento hola-mundo hay una base de datos SQL y su punto de conexión de SQL analítico. 😯 Y eso que escogimos una <em>template with no data layer</em>. Pero parece que ya crean la base de datos aunque esté vacía por si más tarde cambiamos de idea.</p>

<p><img src="/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-hello-world-workspace.png" alt="En el área de trabajo se observa que al crear la Fabric App también se crea una base de datos Fabric SQL" /></p>

<p>Pero esto tiene una solución muy fácil, que consiste en modificar un archivo de configuración de Rayfin.</p>

<p>Para demostrarlo, vamos a <strong>crear otra Fabric App</strong> con el nombre “<strong>Hola Mundo 2</strong>”.</p>

<p>Repite los pasos <strong>2 y 3</strong> anteriores, pero usando en el paso 3 el comando <strong>npm create @microsoft/rayfin@latest “Hola Mundo 2”</strong> (lo único que cambia es el 2 al final).</p>

<p>Todavía no pruebes la nueva Fabric App, ni mucho menos la despliegues.</p>

<p>Antes has lo siguiente:</p>

<ul>
  <li>Cámbiate a la carpeta: <strong>hola-mundo-2</strong></li>
  <li>Ejecuta el comando <strong>code .</strong> para entrar a VS Code con la carpta abierta.
    <ul>
      <li>Ya en VS Code abre la <strong>subcarpeta rayfin</strong>
        <ul>
          <li>Dentro de la subcarpeta, abre el archivo <strong>rayfin.yml</strong></li>
          <li>En el archivo busca la <strong>línea 14</strong> y cambia true por false.</li>
          <li>Guarda los cambios (Ctrl + s)</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p>Ahora sí, prueba la Fabric App y luego despliégala siguiendo los pasos 5 y 6. Utiliza la misma área de trabajo.</p>

<p>Una vez desplegada, revisa el área de trabajo y debes ver el nuevo elemento <strong>hola-mundo-2</strong>, pero en solitario, <strong>sin la base de datos SQL</strong> 😊.</p>

<p><img src="/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-hello-world-2-workspace.png" alt="La nueva Fabric App no tiene ninguna base de datos asociada" /></p>

<p>Yo he hecho una versión del “Hola Mundo” en la que además de deshabilitar la base de datos, he modificado algunos detallitos del sitio web generado por la plantilla para poner el texto en español y añadir una imagen y un ícono.</p>

<p>Todo el código lo he compartido en un repositorio de GitHub: <a href="https://github.com/dataxbi/fabapp-hello-world">https://github.com/dataxbi/fabapp-hello-world</a></p>

<p><img src="/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-hola-mundo.png" alt="Pantallazo de mi Fabric App Hola Mundo" /></p>

<h3 id="rayfin-y-los-agentes-de-ia">Rayfin y los agentes de IA</h3>

<p>Rayfin viene preparado para los agentes de IA generativa, lo que puedes comprobar revisando el código de las Fabric Apps en tu ordenador, o en el repositorio que te compartí con mi “Hola Mundo”.</p>
<ul>
  <li>Archivo <a href="https://github.com/dataxbi/fabapp-hello-world/blob/main/AGENTS.md">AGENTS.md</a></li>
  <li>
    <p>Archivo <a href="https://github.com/dataxbi/fabapp-hello-world/blob/main/.agents/skills/rayfin/SKILL.md">SKILL.md</a></p>

    <p>Esta skill puede acceder a la documentación de dos maneras:</p>
    <ul>
      <li>Con un <strong>MCP Server</strong>, si se ha configurado: <a href="https://github.com/microsoft/rayfin/tree/main/packages/tools/mcp">https://github.com/microsoft/rayfin/tree/main/packages/tools/mcp</a></li>
      <li>Utilizando <strong>Rayfin CLI</strong> en la línea de comandos, que se instala cuando generamos el sitio web utilizando la <em>template</em>.</li>
    </ul>
  </li>
</ul>

<p>Para mostrar cómo trabaja, he utilizado <a href="https://github.com/features/copilot/cli">GitHub Copilot CLI</a> en la carpeta de mi “Hola Mundo” y le he pedido lo siguiente:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>confirma si la última versión de la fabric app está publicada en fabric en qué área de trabajo y con cual urls puedo acceder
</code></pre></div></div>

<p>El resultado lo puedes ver en la imagen, donde se aprecia como:</p>
<ul>
  <li>Ha utilizado la <em>skill</em> de Rayfin.</li>
  <li>Ha comparado la fecha del último despliegue en Fabric con el último commit en GIT.</li>
  <li>Muestra el nombre y el ID del área de trabajo y si la aplicación está disponible.</li>
  <li>Muestra la URL para acceder a través del portal de Fabric y la URL pública.</li>
</ul>

<p><img src="/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-rayfin-skill-github-copilot-cli-1.png" alt="Pantallazo de GitHub Copilot CLI utilizando la skill de Rayfin" /></p>

<p><img src="/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-rayfin-skill-github-copilot-cli-2.png" alt="Pantallazo de GitHub Copilot CLI con la respuesta a la pregunta que le hice sobre sobre la Fabric App" /></p>

<h3 id="fabric-invaders">Fabric Invaders</h3>

<p>En el ejemplo “Hola mundo” los cambios fueron muy sencillos y los hice yo mismo, sin la ayuda de la IA.😉</p>

<p>El segundo ejemplo que te quiero compartir lo he hecho con agentes de IA utilizando GitHub Copilot CLI, aunque no se han utilizado las instrucciones 
ni la <em>skill</em> de Rayfin porque los cambios solo afectan al <em>front-end</em>.</p>

<p>He implementado un juego del tipo Space Invaders pero utilizando los magníficos <a href="https://libraries.excalidraw.com/?theme=light&amp;sort=default#mwc360-microsoft-fabric-architecture-icons">íconos de Microsoft Fabric para Excalidraw</a> creados y mantenidos por <a href="https://www.linkedin.com/in/mileswcole/">Miles Cole</a>.</p>

<p>A esta nueva Fabric App le he llamado <strong>Fabric Invaders</strong> y para crearla he seguido los mismos pasos que en mi “Hola Mundo” y luego:</p>
<ul>
  <li>He descargado el archivo con los íconos de Fabric desde el sitio de Excalidraw hacia la carpeta src/assets de la Fabric App.</li>
  <li>He utilizado el modo plan de GitHub Copilot CLI para crear una especificación para el juego, indicándole el stack técnico, donde encontrar los íconos de Fabric y pidiéndole explícitamente que no toque los archivos de Rayfin porque las modificaciones deben ser solo en el <em>front-end</em>.</li>
  <li>Una vez que he considerado que la especificación está lista, le he pedido a GitHub Copilot CLI que cree un plan a partir de ella y que proceda a implementar.</li>
</ul>

<p>El primer resultado me dejó impresionado porque el juego funcionó perfectamente.</p>

<p><img src="/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-fabric-invaders.gif" alt="Clip con una partida de Fabric Invaders" /></p>

<p>Hice algunas iteraciones más para añadirle sonido, incluir un logo y optimizar la carga de los archivos del <em>front-end</em>.</p>

<p>Si quieres revisar el código o probarlo por ti mismo, está en GitHub: <a href="https://github.com/dataxbi/fabapp-fabric-invaders">https://github.com/dataxbi/fabapp-fabric-invaders</a></p>
<ul>
  <li>Este es el archivo AGENTS.md modificado para hacer referencias a la especificación del juego: <a href="https://github.com/dataxbi/fabapp-fabric-invaders/blob/main/AGENTS.md">https://github.com/dataxbi/fabapp-fabric-invaders/blob/main/AGENTS.md</a></li>
  <li>Esta es la especificación: <a href="https://github.com/dataxbi/fabapp-fabric-invaders/blob/main/docs/SPEC.md">https://github.com/dataxbi/fabapp-fabric-invaders/blob/main/docs/SPEC.md</a></li>
  <li>Y en el historial de <em>commits</em> puedes ver las iteraciones: <a href="https://github.com/dataxbi/fabapp-fabric-invaders/commits/main/">https://github.com/dataxbi/fabapp-fabric-invaders/commits/main/</a></li>
</ul>

<h3 id="pokémon-hunter">Pokémon Hunter</h3>

<p>Mi intención con el ejemplo anterior ha sido mostrar que tenemos libertad para ser creativos en el <em>front-end</em>.</p>

<p>En este último ejemplo quiero poner a prueba esa libertad, llamando a una API REST que no tenga nada que ver con Fabric.</p>

<p>La última Fabric App de este post se llama <strong>Pokémon Hunter</strong> y es una versión del juego anterior, pero sustituyendo los íconos de Fabric por imágenes de pokémon y la nave espacial por la <a href="https://www.wikidex.net/wiki/Cazadora_Pok%C3%A9mon_J">cazadora Pokémon J</a>.</p>

<p>La imagen de la cazadora es estática y la he descargado a la carpeta src/assets de la Fabric App.</p>

<p>Pero las imágenes de los pokémon no están guardadas en la aplicación, sino que se obtienen dinámicamente a través de la <a href="https://pokeapi.co/">PokéAPI</a>.</p>

<p>Cada vez que comienza una partida se llama a la PokéAPI para obtener los URLs con las imágenes de 20 pokémons de manera aleatoria.</p>

<p><img src="/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-pokemon-hunter.gif" alt="Clip con una partida de Pokémon Hunter" /></p>

<p>Este es el repositorio con el código: <a href="https://github.com/dataxbi/fabapp-pokemon-hunter">https://github.com/dataxbi/fabapp-pokemon-hunter</a></p>
<ul>
  <li>AGENTS.md: <a href="https://github.com/dataxbi/fabapp-pokemon-hunter/blob/main/AGENTS.md">https://github.com/dataxbi/fabapp-pokemon-hunter/blob/main/AGENTS.md</a></li>
  <li>Especificaciones: <a href="https://github.com/dataxbi/fabapp-pokemon-hunter/blob/main/docs/SPEC.md">https://github.com/dataxbi/fabapp-pokemon-hunter/blob/main/docs/SPEC.md</a></li>
  <li>En la <a href="https://github.com/dataxbi/fabapp-pokemon-hunter/commits/main/">historia de commits</a> se puede apreciar que para este último ejemplo partí de clonar el repo de <strong>Fabric Invaders</strong>.</li>
</ul>

<p>Esta API es sencilla y no requiere autenticación, por lo que no hay ninguna dificultad al llamarla desde el <em>front-end</em>.</p>

<p>Pero, para utilizar una API que requiera autenticación hay que ser cuidadoso de no exponer credenciales.</p>

<h3 id="continuará">Continuará…</h3>

<p>En los tres ejemplos que te he mostrado, Fabric no pinta casi nada. De hecho, estas aplicaciones web se podrían haber implementado sin Rayfin y desplegarlas en cualquier sitio web.</p>

<p>Lo único que hemos ganado con Rayfin es poderlas desplegar en un área de trabajo de Fabric y controlar el acceso.</p>

<p>En próximas entregas de esta serie sí utilizaremos elementos de Fabric.</p>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="fabric-apps" /><category term="fabric" /><summary type="html"><![CDATA[En Microsoft Build 2026 fueron presentadas las Fabric Apps, y ya están disponibles en versión preliminar. Con este post inicio una serie donde iré explicando qué son, cómo funcionan y qué podemos hacer con ellas.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-fabric-invaders-inicio.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-06-11-fabric-apps-hola-mundo/dataXbi-fabric-apps-fabric-invaders-inicio.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Comparación de rendimiento de Direct Lake en OneLake entre el lakehouse y el warehouse</title><link href="https://www.dataxbi.com/blog/2026/05/12/comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse/" rel="alternate" type="text/html" title="Comparación de rendimiento de Direct Lake en OneLake entre el lakehouse y el warehouse" /><published>2026-05-12T00:00:00+02:00</published><updated>2026-05-12T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/05/12/comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/05/12/comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse/"><![CDATA[<p>En este post comparo el rendimiento en Direct Lake de varias configuraciones de tablas Delta en un <em>lakehouse</em> y en un <em>warehouse</em>.</p>

<!--more-->

<p>Es una continuación de los dos post anteriores (<a href="https://www.dataxbi.com/blog/2026/04/21/comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse/">post 1</a>, <a href="https://www.dataxbi.com/blog/2026/05/05/comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/">post 2</a>), pero esta vez no se han ejecutado consultas T-SQL sino consultas DAX sobre modelos semánticos Direct Lake en OneLake.</p>

<p>Para realizar estas pruebas se ha creado una nueva área de trabajo con capacidad Fabric de prueba F64.</p>

<p>Al igual que en los posts anteriores he contado con la valiosa ayuda de GitHub Copilot CLI, con el mismo flujo de trabajo que he explicado en el primer post de la serie.</p>

<p>El código y las instrucciones las puedes consultar en el repositorio en GitHub <a href="https://github.com/dataxbi/fablab-direct-lake">https://github.com/dataxbi/fablab-direct-lake</a>.</p>

<h3 id="preparación-de-los-datos">Preparación de los datos</h3>

<p>Se crearon 6 modelos semánticos en modo de almacenamiento Direct Lake en OneLake, a partir de los datos y escenarios (esquemas) utilizados en los posts anteriores.</p>

<table>
  <thead>
    <tr>
      <th>Nombre del modelo semántico</th>
      <th>Tipo de fuente</th>
      <th>Esquema</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SM_DL_LH_Default</code></td>
      <td>Direct Lake → Lakehouse</td>
      <td><code class="language-plaintext highlighter-rouge">benchmark_default</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SM_DL_LH_Partitioned</code></td>
      <td>Direct Lake → Lakehouse</td>
      <td><code class="language-plaintext highlighter-rouge">benchmark_partitioned</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SM_DL_LH_VOrder</code></td>
      <td>Direct Lake → Lakehouse</td>
      <td><code class="language-plaintext highlighter-rouge">benchmark_vorder</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SM_DL_WH</code></td>
      <td>Direct Lake → Warehouse</td>
      <td><code class="language-plaintext highlighter-rouge">benchmark</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SM_DL_WH_Frag</code></td>
      <td>Direct Lake → Warehouse</td>
      <td><code class="language-plaintext highlighter-rouge">benchmark_frag</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SM_DL_LH_Frag</code></td>
      <td>Direct Lake → Lakehouse</td>
      <td><code class="language-plaintext highlighter-rouge">benchmark_frag</code> ⚠️ excluido (refresco falla)</td>
    </tr>
  </tbody>
</table>

<p>En el listado anterior hay una nota indicando que para el último modelo semántico falló el refresco de los datos, que en el caso de Direct Lake es en realidad el enmarcado (<em>framing</em>) y que consiste en actualizar solo los metadatos de las tablas Delta. 
La causa del fallo es que en el escenario del <em>lakehouse</em> fragmentado la tabla de hechos tiene muchos archivos Parquet pequeños (29.000 archivos 😢), por lo que está muy por encima del <a href="https://learn.microsoft.com/es-es/fabric/fundamentals/direct-lake-overview?wt.mc_id=MVP_367391#fabric-capacity-requirements">límite para una capacidad F64</a> que son 5.000 archivos Parquet por tabla Delta. Por tanto, este modelo se excluyó de las pruebas.</p>

<p>Sin embargo, esto no sucede en el modelo semántico que utiliza las tablas fragmentadas del <em>warehouse</em> y la razón es que en este caso Fabric se encarga de ejecutar OPTIMIZE automáticamente, por lo que se agruparon varios archivos Parquet en uno solo. En una investigación que hice con la ayuda de Copilot encontré que en el <em>warehouse</em> los archivos Parquet se redujeron de casi 29.000 a 149 😎.</p>

<p>Todos los modelos semánticos tienen la misma estructura, que se muestra en la imagen. En el repositorio de GitHub está la <a href="https://github.com/dataxbi/fablab-direct-lake/tree/main/tmdl">definición en TMDL</a>.</p>

<p><img src="/assets/images/posts/2026-05-12-comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse/dataxbi-fablab-direct-lake-modelo-semantico.png" alt="Estructura del modelo semántico creado a partir de las tablas Delta. Los 6 modelos tienen la misma estructura." /></p>

<h3 id="consultas-dax">Consultas DAX</h3>

<p>Las consultas DAX que se utilizaron para medir el rendimiento se crearon a partir de las consultas T-SQL de las pruebas anteriores, que a su vez están inspiradas en consultas de TPC-DS.</p>

<ul>
  <li>
    <p>Q1 — Agregación simple: <a href="https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q01_simple_agg.dax">https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q01_simple_agg.dax</a></p>
  </li>
  <li>
    <p>Q2 — Join grande (star schema): <a href="https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q02_large_join.dax">https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q02_large_join.dax</a></p>
  </li>
  <li>
    <p>Q3 — Top N con filtros selectivos:  <a href="https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q03_top_n_selective.dax">https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q03_top_n_selective.dax</a></p>
  </li>
  <li>
    <p>Q4 — Query compleja tipo TPC-DS real: <a href="https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q04_complex_tpcds.dax">https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q04_complex_tpcds.dax</a></p>
  </li>
  <li>
    <p>Q5 — Función ventana analítica: <a href="https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q05_window_function.dax">https://github.com/dataxbi/fablab-direct-lake/blob/main/dax/q05_window_function.dax</a></p>
  </li>
</ul>

<p>Para crear las consultas DAX, utilicé primero el modelo Claude Opus 4.7 para traducir las consultas T-SQL. Luego cambié al modelo GPT-5.5 y le pedí que las revisara. Y finalmente las revisé yo.</p>

<p>Ambos modelos LLM se conectaron a uno de los modelos semánticos para validar que las consultas se ejecutaban bien. Esta ejecución sirvió para detectar que en una de las consultas había un filtro con un valor que no existía en la dimensión, y fue corregido por Claude Opus. En la revisión hecha por GPT sugirió cambiar un FILTER dentro de un SUMMARIZECOLUMNS por un TREATAS, lo que me pareció muy bien.</p>

<p>En mi revisión lo único que encontré fue un par de SUMMARIZE donde se añadían directamente columnas con expresiones y lo cambié por ADDCOLUMNS / SUMMARIZE.</p>

<h3 id="pruebas-realizadas">Pruebas realizadas</h3>

<p>Para las pruebas se creó un <a href="https://github.com/dataxbi/fablab-direct-lake/tree/main/benchmark">script</a> que utiliza la API REST de Power BI para hacer lo siguiente:</p>
<ul>
  <li>Refrescar los 5 modelos semánticos para asegurar que no haya datos cargados en memoria.</li>
  <li>Ejecutar una vez las 5 consultas DAX en cada uno de los 5 modelos semánticos. (Ejecución en frío.)</li>
  <li>Ejecutar 3 veces las 5 consultas DAX en cada uno de los 5 modelos semánticos. (Ejecución en caliente.)</li>
</ul>

<p>En total se hicieron <strong>100 ejecuciones</strong> de las consultas DAX.</p>

<p>Los resultados de estas pruebas se guardaron en archivos CSV.</p>

<p>También se creó un <a href="https://github.com/dataxbi/fablab-direct-lake/blob/main/analysis/analyze_results.ipynb">notebook Python</a> para analizar los resultados de estas ejecuciones.</p>

<h3 id="resultados">Resultados</h3>

<p>👇 El primer resultado es la comparación del tiempo de ejecución de las consultas en frío (barras izquierdas, azules) vs. la mediana del tiempo de ejecución de las consultas en caliente (barras derechas, naranjas) para cada uno de los 5 modelos semánticos.</p>

<p>Aquí resalta lo obvio: <strong>Las consultas en frío son más lentas en todos los casos.</strong></p>

<p><img src="/assets/images/posts/2026-05-12-comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse/dataxbi-fablab-direct-lake-results-1.png" alt="Gráficos de columnas donde se comparan los tiempos de las consultas en frío vs. la mediana de las consultas en caliente para cada uno de los 5 modelos semánticos." /></p>

<p>👇 En el segundo gráfico se analizan solo las ejecuciones en caliente, mostrándose para cada consulta DAX la mediana de las 3 ejecuciones en cada uno de los 5 modelos semánticos.</p>

<p>Dentro de cada consulta los tiempos son bastante parecidos en 4 de los modelos y sólo hay una diferencia notable en el <strong>modelo que utiliza las tablas del lakehouse con V-Order, que es el más lento</strong>. Este resultado me ha sorprendido porque esperaba que el V-Order ayudara en la <strong>transcodificación</strong> del formato Parquet a VertiPaq, pero al parecer no ha ocurrido así. Es algo que hay que analizar más a fondo y puede que tenga que ver con el tamaño de los archivos Parquet y de los grupos de filas dentro de estos.</p>

<p>El comportamiento del modelo semántico que utiliza los datos del warehouse fragmentado es similar al resto y esto se debe a la optimización automática que expliqué antes.</p>

<p><img src="/assets/images/posts/2026-05-12-comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse/dataxbi-fablab-direct-lake-results-2.png" alt="Gráfico de columnas donde se compara la mediana de las 3 repeticiones en caliente de las 5 consultas DAX para cada uno de los cinco modelos semánticos." /></p>

<p>👇 Para finalizar muestro el tiempo de las 3 ejecuciones en caliente para cada una de las 5 consultas DAX en los 5 modelos semánticos.</p>

<p>Me llama un poco la atención que en los tres escenarios del <em>lakehouse</em> (gráficos de arriba) se aprecia que para cada consulta, con cada repetición mejora el rendimiento. Sin embargo, en los dos escenarios del warehouse (gráficos de abajo) casi que ocurre lo contrario.</p>

<p>En el caso del lakehouse con V-Order (tercer gráfico de arriba) hay una mejora apreciable en la tercera repetición.</p>

<p><img src="/assets/images/posts/2026-05-12-comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse/dataxbi-fablab-direct-lake-results-3.png" alt="Gráficos de columnas donde se comparan los tiempos de cada repetición en caliente de cada una de las 5 consultas en los 5 modelos semánticos." /></p>

<p>Como ves, este tema todavía da para mucho. Ojalá te anime a hacer tus propias pruebas y a contribuir al repositorio.</p>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="direct-lake" /><category term="rendimiento" /><category term="fabric" /><summary type="html"><![CDATA[En este post comparo el rendimiento en Direct Lake de varias configuraciones de tablas Delta en un lakehouse y en un warehouse.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-05-12-comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse/dataxbi-fablab-direct-lake-results-2.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-05-12-comparacion-rendimiento-direct-lake-lakehouse-vs-warehouse/dataxbi-fablab-direct-lake-results-2.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Comparación de rendimiento entre el punto de conexión SQL del lakehouse y el warehouse - Fragmentación</title><link href="https://www.dataxbi.com/blog/2026/05/05/comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/" rel="alternate" type="text/html" title="Comparación de rendimiento entre el punto de conexión SQL del lakehouse y el warehouse - Fragmentación" /><published>2026-05-05T00:00:00+02:00</published><updated>2026-05-05T00:00:00+02:00</updated><id>https://www.dataxbi.com/blog/2026/05/05/comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion</id><content type="html" xml:base="https://www.dataxbi.com/blog/2026/05/05/comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/"><![CDATA[<p>En este post comparo la influencia de la fragmentación de las tablas Delta en el rendimiento de las consultas T-SQL en un <em>lakehouse</em> vs. un <em>warehouse</em>.</p>

<!--more-->

<p>Se han utilizado los mismos datos que expliqué en el <a href="https://www.dataxbi.com/blog/2026/04/21/comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse/">post anterior</a> y que son un subconjunto de las tablas de TPC-DS, con el factor de escala SF100.</p>

<table>
  <thead>
    <tr>
      <th>Tabla</th>
      <th>Tipo</th>
      <th>Descripción</th>
      <th style="text-align: right">Filas (SF100)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">store_sales</code></td>
      <td>Hecho</td>
      <td>Transacciones de ventas en tienda</td>
      <td style="text-align: right">287,997,099</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">customer</code></td>
      <td>Dimensión</td>
      <td>Clientes</td>
      <td style="text-align: right">2,000,000</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">customer_demographics</code></td>
      <td>Dimensión</td>
      <td>Segmentación demográfica de clientes</td>
      <td style="text-align: right">1,920,800</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">item</code></td>
      <td>Dimensión</td>
      <td>Catálogo de productos</td>
      <td style="text-align: right">204,000</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">date_dim</code></td>
      <td>Dimensión</td>
      <td>Calendario (dimensión temporal)</td>
      <td style="text-align: right">73,049</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">household_demographics</code></td>
      <td>Dimensión</td>
      <td>Demografía del hogar</td>
      <td style="text-align: right">7,200</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">promotion</code></td>
      <td>Dimensión</td>
      <td>Campañas y promociones</td>
      <td style="text-align: right">1,000</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">store</code></td>
      <td>Dimensión</td>
      <td>Tiendas físicas</td>
      <td style="text-align: right">402</td>
    </tr>
  </tbody>
</table>

<p>También he contado con la valiosa ayuda de GitHub Copilot CLI, y en el post anterior explico el flujo de trabajo que he seguido.</p>

<p>Todo el código, los datos y las instrucciones las puedes consultar en el repositorio en GitHub <a href="https://github.com/dataxbi/fablab-sql-endpoint">https://github.com/dataxbi/fablab-sql-endpoint</a>.</p>

<h3 id="preparación-de-los-datos">Preparación de los datos</h3>

<p>Esta vez se ha creado un nuevo esquema en el <em>lakehouse</em> y otro en el <em>warehouse</em>, ambos con el mismo nombre: <strong>benchmark_frag</strong>.</p>

<p>Las tablas de dimensiones se han copiado desde el esquema <strong>benchmark_default</strong>, explicado en el post anterior, usando consultas CREATE TABLE AS SELECT (CTAS). En el <em>lakehouse</em> se ha utilizado Spark SQL y en el <em>warehouse</em> T-SQL.</p>

<p>La tabla de hechos <strong>store_sales</strong> es la que se ha fragmentado en archivos Parquet muy pequeños, de 10.000 filas cada uno. Por lo que para alcanzar las casi 288 millones de filas se han creado unos 29.000 archivos Parquet.</p>

<p>⚠️ Este proceso de fragmentación lo estoy haciendo para estas pruebas, pero en un proyecto real por lo general se hace todo lo contrario. 😉</p>

<p>El proceso de fragmentación fue diferente en cada elemento de almacenamiento.</p>

<p>En el <strong><em>lakehouse</em></strong> se partió de la tabla store_sales del esquema benchmark_default y se copiaron sus datos en una tabla con el mismo nombre pero en el nuevo esquema benchmark_frag, con sobrescritura y lo más importante, con la opción <strong>maxRecordsPerFile</strong>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">MAX_RECORDS_PER_FILE</span> <span class="o">=</span> <span class="mi">10000</span>

<span class="n">spark</span><span class="p">.</span><span class="n">table</span><span class="p">(</span><span class="s">"benchmark_default.store_sales"</span><span class="p">)</span>
    <span class="p">.</span><span class="n">write</span>
    <span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">"delta"</span><span class="p">)</span>
    <span class="p">.</span><span class="n">mode</span><span class="p">(</span><span class="s">"overwrite"</span><span class="p">)</span>
    <span class="p">.</span><span class="n">option</span><span class="p">(</span><span class="s">"overwriteSchema"</span><span class="p">,</span> <span class="s">"true"</span><span class="p">)</span>
    <span class="p">.</span><span class="n">option</span><span class="p">(</span><span class="s">"maxRecordsPerFile"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">MAX_RECORDS_PER_FILE</span><span class="p">))</span>
    <span class="p">.</span><span class="n">saveAsTable</span><span class="p">(</span><span class="s">"benchmark_frag.store_sales"</span><span class="p">)</span>

</code></pre></div></div>

<p>La imagen muestra un fragmento del <em>delta_log</em> de la tabla Delta creada en el <em>lakehouse</em>, donde se puede apreciar que en una misma transacción se han creado todos los archivos Parquet y que cada uno contiene 10.000 filas.</p>

<p><img src="/assets/images/posts/2026-05-05-comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/dataxbi-fablab-sql-endpoint-frag-lakehouse-delta-log.png" alt="Fragmento del delta log de la tabla Delta fragmentada del lakehouse" /></p>

<p>En el <strong><em>warehouse</em></strong> se utilizó una estrategia diferente porque en T-SQL no hay una opción equivalente a maxRecordsPerFile.</p>

<p>Se partió del archivo CSV con los datos de la tabla store_sale, cuya generación se explicó en el post anterior. Este se dividió en archivos CSV con 10.000 filas cada uno, obteniendo casi 29.000 archivos CSV. Los archivos se compactaron, se subieron a una carpeta del <em>lakehouse</em> y se descompactaron.</p>

<p>Se creó la tabla <strong>store_sale</strong> en el esquema benchmark_frag del <em>warehouse</em>.</p>

<p>Se utilizó el comando T-SQL COPY INTO para insertar los datos desde los archivos CSV en el <em>lakehouse</em> hacia la tabla en el <em>warehouse</em>.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">COPY</span> <span class="k">INTO</span> <span class="n">benchmark_frag</span><span class="p">.</span><span class="n">store_sales</span>
<span class="k">FROM</span> <span class="o">&lt;</span><span class="n">FILE_URL</span><span class="o">&gt;</span>
<span class="k">WITH</span> <span class="p">(</span><span class="n">FILE_TYPE</span> <span class="o">=</span> <span class="s1">'CSV'</span><span class="p">,</span> <span class="n">FIELDTERMINATOR</span> <span class="o">=</span> <span class="s1">'|'</span><span class="p">)</span>
</code></pre></div></div>

<p>Para optimizar la copia, se creó un <a href="https://github.com/dataxbi/fablab-sql-endpoint/blob/main/fragmentation/02_copy_into_wh.py">script</a> que utiliza el driver ODBC de SQL Server para conectarse al <em>warehouse</em> desde mi ordenador, y que implementa un mecanismo para ejecutar varias consultas COPY INTO en paralelo.</p>

<p>Las inserciones no se han hecho dentro de una misma transacción por lo que en la tabla Delta se ha creado un <em>delta_log</em> por cada archivo Parquet que se ha creado. En la figura se ve el  contenido de uno de estos <em>delta_log</em>.</p>

<p><img src="/assets/images/posts/2026-05-05-comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/dataxbi-fablab-sql-endpoint-frag-warehouse-delta-log.png" alt="Fragmento del delta log de la tabla Delta fragmentada del warehouse" /></p>

<h3 id="pruebas-realizadas">Pruebas realizadas</h3>

<p>Para medir el rendimiento se han utilizado las mismas 5 consultas T-SQL que en el post anterior, inspiradas en consultas del TCP-DS:</p>

<ul>
  <li>
    <p>Q1 — Agregación simple: <a href="https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q01_simple_agg.sql">https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q01_simple_agg.sql</a></p>
  </li>
  <li>
    <p>Q2 — Join grande (star schema): <a href="https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q02_large_join.sql">https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q02_large_join.sql</a></p>
  </li>
  <li>
    <p>Q3 — Top N con filtros selectivos:  <a href="https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q03_top_n_selective.sql">https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q03_top_n_selective.sql</a></p>
  </li>
  <li>
    <p>Q4 — Query compleja tipo TPC-DS real: <a href="https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q04_complex_tpcds.sql">https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q04_complex_tpcds.sql</a></p>
  </li>
  <li>
    <p>Q5 — Función ventana analítica: <a href="https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q05_window_function.sql">https://github.com/dataxbi/fablab-sql-endpoint/blob/main/sql/q05_window_function.sql</a></p>
  </li>
</ul>

<p>Se extendió el script explicado en el post anterior, para poder ejecutar estas consultas sobre las tablas de los nuevos esquemas benchmark_frag.</p>

<p>En este caso todas las consultas se hicieron <em>en caliente</em>, sin pausar la capacidad Fabric. Y se utilizó una capacidad Fabric F64 de pruebas.</p>

<p>Se ejecutaron 3 repeticiones de cada consulta en los 6 esquemas: los cuatro que se explicaron en el post anterior, y los los dos nuevos <strong><em>benchmark_frag</em></strong>.</p>

<p>En total se hicieron <strong>90 ejecuciones</strong>.</p>

<p>Los resultados de estas pruebas se guardaron en archivos CSV para poder analizarlos.</p>

<h3 id="resultados">Resultados</h3>

<p>A continuación muestro tres imágenes con los resultados de las pruebas.</p>

<p><img src="/assets/images/posts/2026-05-05-comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/dataxbi-fablab-sql-endpoint-frag-results-1.png" alt="Gráfico de columnas donde se muestra el tiempo de cada una de las 3 repeticiones de las 5 consultas en las tablas fragmentadas del warehouse y del lakehouse." /></p>

<p>👆La primera imagen contiene dos gráficos de columnas con el tiempo de las 3 ejecuciones de cada consulta sobre las tablas fragmentadas (esquema benchmark_frag) del <em>warehouse</em> (gráfico de la izquierda) y el <em>lakehouse</em> (gráfico de la derecha).</p>

<p>De primeras se aprecia que las consultas del <em>lakehouse</em> tardaron más que las del <em>warehouse</em>. Y si prestamos más atención al eje Y, nos daremos cuenta que la <strong>escala es logarítmica</strong>, por lo que la diferencia es aún mayor que lo que muestran las columnas. Como referencia, la consulta que más tardó en el <em>warehouse</em> fue de 14 segundos, mientras que la que más tardó en el <em>lakehouse</em> fue de 147 segundos.</p>

<p>También hay que destacar cómo la consulta Q04 va mejorando el rendimiento en cada repetición, y esto sucede en ambos elementos de almacenamiento.</p>

<p><img src="/assets/images/posts/2026-05-05-comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/dataxbi-fablab-sql-endpoint-frag-results-2.png" alt="Gráfico de columnas donde se compara la mediana de las 3 repeticiones de las 5 consultas en las tablas fragmentadas entre warehouse y del lakehouse." /></p>

<p>👆 La segunda imagen es un gráfico de columnas, también con escala logarítmica, donde se compara la mediana de las 3 repeticiones de cada consulta en las tablas fragmentadas (esquema benchmark_frag) entre el <em>warehouse</em> (izquierda, azul) y el <em>lakehouse</em> (derecha, naranja). Se aprecia claramente como el <em>warehouse</em> tiene un rendimiento que es varios órdenes de magnitud superior al del <em>lakehouse</em>, llegando a ser hasta 29 veces más rápido (consulta Q03).</p>

<p>La excepción es la consulta Q04 que ya vimos en el primer gráfico que mejora mucho el rendimiento luego de la primera repetición, por lo que al considerar la mediana en este gráfico no hay casi ninguna diferencia entre el <em>lakehouse</em> y el <em>warehouse</em>.</p>

<p><img src="/assets/images/posts/2026-05-05-comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/dataxbi-fablab-sql-endpoint-frag-results-3.png" alt="Gráfico de columnas donde se compara la mediana las 3 repeticiones de cada consulta entre los 6 escenarios que se han visto en este blog y el anterior." /></p>

<p>👆 La última imagen es otro gráfico de columnas con escala logarítmica que compara la mediana de las ejecuciones de las 5 consultas en todos los escenarios de este post y del anterior.</p>

<p>Aquí se aprecia claramente que el <em>lakehouse</em> fragmentado (naranja, derecha) es el de peor rendimiento, excepto en la consulta Q04.</p>

<h3 id="conclusión">Conclusión</h3>

<p>La principal conclusión que podemos extraer de estas pruebas es algo bastante conocido: las tablas Delta no rinden bien cuando contienen muchos archivos de datos de pequeño tamaño. Es por esa razón que en un <em>lakehouse</em> debemos encargarnos de su mantenimiento utilizando los comandos OPTIMIZE y VACUUM, junto a otras técnicas.</p>

<p>Por otra parte, aunque un <em>warehouse</em> también utiliza tablas Delta, hay procesos automáticos que se encargan del mantenimiento.</p>

<p>Si quieres profundizar sobre el mantenimiento y optimización de las tablas Delta, te dejo un artículo de la documentación oficial: 
<a href="https://learn.microsoft.com/es-es/fabric/fundamentals/table-maintenance-optimization?wt.mc_id=MVP_367391">https://learn.microsoft.com/es-es/fabric/fundamentals/table-maintenance-optimization</a></p>]]></content><author><name>Nelson López Centeno</name></author><category term="sin-categoria" /><category term="rendimiento" /><category term="fabric" /><summary type="html"><![CDATA[En este post comparo la influencia de la fragmentación de las tablas Delta en el rendimiento de las consultas T-SQL en un lakehouse vs. un warehouse.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.dataxbi.com/assets/images/posts/2026-05-05-comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/dataxbi-fablab-sql-endpoint-frag-results-1.png" /><media:content medium="image" url="https://www.dataxbi.com/assets/images/posts/2026-05-05-comparacion-rendimiento-lakehouse-sql-endpoint-vs-warehouse-fragmentacion/dataxbi-fablab-sql-endpoint-frag-results-1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>