Skip to content

PackageArchitecture pt BR

Edgar Mesquita edited this page Aug 13, 2026 · 2 revisions

Arquitetura de pacotes

🌐 Esta página em: English · Português

Este documento explica a arquitetura de pacotes do eQuantic.UI e os princípios por trás do design autocontido dela.

Princípio central: pacotes autocontidos

Cada pacote do eQuantic.UI é autocontido e gerencia os próprios artefatos. O SDK age como um orquestrador que referencia os outros pacotes pelas propriedades autogeradas do NuGet.

A razão do design

O antipadrão que o design rejeita, o SDK embarcando artefatos de outros pacotes:

eQuantic.UI.Sdk.nupkg ❌
├─ tools/runtime/runtime.js (copiado do Runtime)
└─ tools/StandardComponents/*.cs (copiado do Components)

Por que esse formato falha:

  • Acoplamento forte: o SDK conhece diretamente as entranhas do Runtime e do Components
  • Conflitos de versão: o consumidor instala o Runtime 0.1.3 mas o SDK contém artefatos embarcados da 0.1.2
  • Duplicação de artefatos: os mesmos arquivos existem em vários pacotes
  • Inflexível: não dá para atualizar o Runtime ou o Components de forma independente sem republicar o SDK

A arquitetura: pacotes autocontidos

eQuantic.UI.Runtime.nupkg ✅
└─ tools/runtime/runtime.js (autogerido)

eQuantic.UI.Components.nupkg ✅
└─ tools/source/*.cs (autogerido)

eQuantic.UI.Sdk.nupkg ✅
├─ Sdk/Sdk.props (inclui Core, Components, Server e Runtime automaticamente)
└─ Sdk/Sdk.targets (referencia os pacotes pelas propriedades $(Pkg*))

Benefícios:

  • Desacoplamento: cada pacote possui e gerencia os artefatos dele
  • Versionamento correto: a versão instalada pelo consumidor é sempre a usada
  • Sem duplicação: uma única fonte da verdade por artefato
  • Evolução independente: os pacotes podem ser atualizados separadamente
  • Interface clara: o SDK usa propriedades bem definidas do NuGet

Responsabilidades dos pacotes

eQuantic.UI.Core

Propósito: abstrações e tipos centrais

Contém:

  • A interface IComponent
  • Os tipos base HtmlNode, HtmlElement
  • Abstrações de ciclo de vida de componente
  • Contexto de renderização

Empacota: apenas a DLL compilada (sem artefatos adicionais)

eQuantic.UI.Components

Propósito: a biblioteca de componentes padrão (Button, Input, Container, etc.)

Contém:

  • A DLL compilada para uso em tempo de execução
  • Os arquivos de código (tools/source/*.cs) para a resolução de tipos do compilador

Empacotamento:

<!-- eQuantic.UI.Components.csproj -->
<ItemGroup>
  <Content Include="**\*.cs" Exclude="obj\**;bin\**"
           PackagePath="tools\source\" />
</ItemGroup>

Por que arquivos de código?

O compilador (eqc.dll) precisa de acesso ao código dos componentes para resolver tipos externos durante a compilação. Quando um consumidor usa <Button>, o compilador procura a definição da classe Button no pacote Components.

Uso pelo SDK:

<!-- Resolvido automaticamente pelo NuGet -->
<PropertyGroup>
  <_ComponentsSource>$(PkgeQuantic_UI_Components)/tools/source</_ComponentsSource>
</PropertyGroup>

<!-- Passado ao compilador -->
<Exec Command="dotnet eqc.dll &quot;$(ProjectDir);$(_ComponentsSource)&quot; ..." />

eQuantic.UI.Runtime

Propósito: o runtime do browser (DOM virtual, reconciliador, gestão de estado)

Contém:

  • O runtime TypeScript/JavaScript compilado com o Vite
  • runtime.js (tools/runtime/runtime.js) - arquivo único empacotado (~49KB)

Empacotamento:

<!-- eQuantic.UI.Runtime.csproj -->
<ItemGroup>
  <Content Include="dist\index.js"
           PackagePath="tools\runtime\runtime.js"
           Condition="Exists('dist\index.js')" />
</ItemGroup>

Processo de build:

  1. Código TypeScript → npm run build (Vite + tsc)
  2. Saída: dist/index.js (bundle único via inlineDynamicImports)
  3. Empacotado: eQuantic.UI.Runtime.nupkg/tools/runtime/runtime.js

Uso pelo SDK:

<!-- Resolvido automaticamente pelo NuGet -->
<PropertyGroup>
  <_RuntimeSource>$(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js</_RuntimeSource>
</PropertyGroup>

<!-- Copiado para o wwwroot do consumidor -->
<Copy SourceFiles="$(_RuntimeSource)"
      DestinationFiles="wwwroot/_equantic/runtime.js" />

eQuantic.UI.Runtime.{Plataforma}

Propósito: executáveis do Bun específicos de plataforma (Osx64, Win64, Linux64)

Contém:

  • O executável do Bun (compactado) para o empacotamento
  • Binários específicos de plataforma (~60MB cada)

Por que pacotes separados?

  • Os consumidores só baixam o executável da plataforma deles
  • Reduz o tamanho do pacote (não é preciso ter as 3 plataformas)
  • Separação limpa entre a lógica de runtime e as ferramentas de build

eQuantic.UI.Server

Propósito: integração com o ASP.NET Core

Contém:

  • Renderização no servidor (SSR)
  • O sistema de RPC dos Server Actions
  • Middleware e roteamento
  • O runtime.js embarcado, servido em /_equantic/runtime.js

Nota: o Server embarca a própria cópia do runtime.js como EmbeddedResource para servir por HTTP. Isso é separado da cópia de tempo de build do consumidor.

eQuantic.UI.Sdk

Propósito: o orquestrador do SDK MSBuild

Contém:

  • Sdk.props - inclui automaticamente os pacotes Core, Components, Server e Runtime
  • Sdk.targets - os targets MSBuild de compilação, empacotamento e geração de CSS
  • tools/net10.0/eqc.dll - o executável do compilador

NÃO contém:

  • ❌ Artefatos do runtime (referencia o pacote Runtime)
  • ❌ Código dos componentes (referencia o pacote Components)

Responsabilidades:

  1. Gestão de pacotes: inclui automaticamente os pacotes necessários pelo Sdk.props
  2. Orquestração do build: coordena compilação, empacotamento e geração de CSS
  3. Resolução de artefatos: resolve o runtime.js e as fontes dos componentes a partir dos pacotes deles
  4. Ferramental: fornece o compilador (eqc.dll) e a infraestrutura de build

eQuantic.UI.Lucide / Heroicons / ...

Propósito: provedores de conjuntos de ícones

Contêm:

  • Lógica de resolução de SVG
  • Componentes de ícone especializados
  • Implementação de IIconProvider
  • API fluente: registra a si mesmo pela extensão Use{Name}Icons() sobre o UIOptions.

eQuantic.UI.Charts.ChartJs / ApexCharts

Propósito: componentes de gráfico especializados

Contêm:

  • Wrappers de gráfico
  • Declarações de asset via IRequireAssets
  • API fluente: registra a si mesmo pelas extensões UseChartJs() / UseApexCharts() sobre o UIOptions.

Propriedades de pacote do NuGet

O NuGet gera automaticamente propriedades $(Pkg*) para os pacotes instalados:

<!-- Autogerado em obj/*.nuget.g.props -->
<PropertyGroup>
  <PkgeQuantic_UI_Core>/Users/name/.nuget/packages/equantic.ui.core/0.1.2</PkgeQuantic_UI_Core>
  <PkgeQuantic_UI_Components>/Users/name/.nuget/packages/equantic.ui.components/0.1.2</PkgeQuantic_UI_Components>
  <PkgeQuantic_UI_Runtime>/Users/name/.nuget/packages/equantic.ui.runtime/0.1.2</PkgeQuantic_UI_Runtime>
  <PkgeQuantic_UI_Runtime_Osx64>/Users/name/.nuget/packages/equantic.ui.runtime.osx64/0.1.2</PkgeQuantic_UI_Runtime_Osx64>
</PropertyGroup>

O SDK usa essas para resolver artefatos:

<!-- Sdk/Sdk.targets -->
<Target Name="CopyEQuanticRuntime">
  <PropertyGroup>
    <_RuntimeSource>$(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js</_RuntimeSource>
  </PropertyGroup>
  <Copy SourceFiles="$(_RuntimeSource)" DestinationFiles="..." />
</Target>

Benefícios:

  • ✅ O SDK não precisa conhecer os detalhes de estrutura dos pacotes
  • ✅ Funciona com qualquer versão de pacote que o consumidor instale
  • ✅ Resolução de cache automática pelo NuGet
  • ✅ Nenhum caminho fixo

Gestão de versões

Versionamento independente

Os pacotes podem evoluir de forma independente:

<!-- O consumidor pode misturar versões -->
<PackageReference Include="eQuantic.UI.Core" Version="0.1.3" />
<PackageReference Include="eQuantic.UI.Runtime" Version="0.1.4" />
<PackageReference Include="eQuantic.UI.Sdk" Version="0.1.2" />

O SDK vai usar:

  • O runtime.js do Runtime 0.1.4 (não a cópia embarcada da 0.1.2)
  • Os arquivos de código do Components 0.1.3 (não a cópia embarcada da 0.1.2)

Versionamento coordenado

Por simplicidade, o Sdk.props do SDK define uma versão padrão:

<!-- Sdk/Sdk.props -->
<PropertyGroup>
  <EQuanticUIVersion>0.1.2</EQuanticUIVersion>
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="eQuantic.UI.Core" Version="$(EQuanticUIVersion)" />
  <PackageReference Include="eQuantic.UI.Components" Version="$(EQuanticUIVersion)" />
  <PackageReference Include="eQuantic.UI.Runtime" Version="$(EQuanticUIVersion)" />
  <PackageReference Include="eQuantic.UI.Server" Version="$(EQuanticUIVersion)" />
</ItemGroup>

Os usuários podem sobrepor:

<PropertyGroup>
  <EQuanticUIVersion>0.1.5</EQuanticUIVersion>
</PropertyGroup>

Ou especificar versões manualmente:

<ItemGroup>
  <PackageReference Include="eQuantic.UI.Runtime" Version="0.1.6" />
</ItemGroup>

Cenários de desenvolvimento vs. de consumo

Cenário do consumidor (pacotes NuGet)

dotnet restore
  ↓ O NuGet instala os pacotes no cache
~/.nuget/packages/equantic.ui.runtime/0.1.2/
~/.nuget/packages/equantic.ui.components/0.1.2/
  ↓ O NuGet gera as propriedades $(Pkg*)
obj/project.nuget.g.props
  ↓ O SDK resolve os artefatos
O Sdk.targets usa $(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js

Cenário de desenvolvimento (árvore de código)

Árvore de código em: /Users/name/equantic-ui/
  ↓ Pacotes locais em: artifacts/packages/
  ↓ O NuGet.config prioriza o local
<packageSources>
  <add key="local" value="../../artifacts/packages" />
</packageSources>
  ↓ Caminhos alternativos no Sdk.targets
<_RuntimeSource Condition="'$(PkgeQuantic_UI_Runtime)' == ''">
  $(MSBuildThisFileDirectory)../../eQuantic.UI.Runtime/dist/index.js
</_RuntimeSource>

Benefícios:

  • Quem desenvolve o framework pode trabalhar direto no código
  • Não é preciso empacotar/restaurar a cada mudança
  • Os mesmos targets funcionam nos dois cenários

Boas práticas

✅ Faça

  1. Deixe cada pacote gerenciar os próprios artefatos

    • O Runtime empacota o runtime.js
    • O Components empacota os arquivos de código
  2. Referencie pelas propriedades do NuGet

    • Use $(PkgeQuantic_UI_*) em vez de caminhos fixos
  3. Forneça alternativas de desenvolvimento

    • Permita que o SDK encontre os artefatos na árvore de código ao desenvolver o framework
  4. Inclua mensagens de erro claras

    • Diga aos usuários qual pacote está faltando quando os artefatos não forem encontrados

❌ Não faça

  1. Não embarque artefatos de outros pacotes

    • O SDK NÃO deve copiar o runtime.js do Runtime para dentro dele
  2. Não use caminhos relativos entre pacotes

    • Ruim: $(MSBuildThisFileDirectory)../../../Runtime/dist/
    • Bom: $(PkgeQuantic_UI_Runtime)/tools/runtime/
  3. Não assuma que as versões dos pacotes batem

    • O consumidor pode usar Runtime 0.1.3 + SDK 0.1.2
  4. Não crie dependências circulares

    • Os pacotes devem ter um grafo de dependências claro

Solução de problemas

runtime.js não encontrado

Error: eQuantic.UI: Runtime not found. Ensure eQuantic.UI.Runtime package is installed.

Causa: $(PkgeQuantic_UI_Runtime) está vazio

Correção:

dotnet restore --force
# Confira que o pacote Runtime está instalado
ls ~/.nuget/packages/equantic.ui.runtime/0.1.2/

Código do Components não encontrado

Error: eQuantic.UI: Standard components not found. Ensure eQuantic.UI.Components package is installed.

Causa: $(PkgeQuantic_UI_Components) está vazio

Correção:

dotnet restore --force
# Verifique se o pacote Components tem os arquivos de código
ls ~/.nuget/packages/equantic.ui.components/0.1.2/tools/source/

Usando a versão errada

Sintoma: o build usa um runtime.js velho apesar de o pacote Runtime ter sido atualizado

Causa: o cache do NuGet não foi limpo

Correção:

# Limpe o pacote específico do cache
rm -rf ~/.nuget/packages/equantic.ui.runtime/0.1.2

# Force a restauração
dotnet restore --force --no-cache

Documentação relacionada

  • Fluxo de build - o pipeline de build completo
  • Runtime - a arquitetura e a distribuição do runtime
  • Compilador - como o compilador de C# para JS funciona

Clone this wiki locally