eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
Docspt-BR
Arquitetura de pacotes
Edit this page
6 min read
🌐 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:
1
2
3
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
1
2
3
4
5
6
7
8
9
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:
1
2
3
4
5
<!-- 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:
1
2
3
4
5
6
7
<!-- 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:
1
2
3
4
5
6
<!-- 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:
1
2
3
4
5
6
7
8
<!-- 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:
1
2
3
4
5
6
7
<!-- 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:
1
2
3
4
5
6
7
<!-- 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:
1
2
3
4
<!-- 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:
1
2
3
4
5
6
7
8
9
10
11
<!-- 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:
1
2
3
<PropertyGroup>
<EQuanticUIVersion>0.1.5</EQuanticUIVersion>
</PropertyGroup>
Ou especificar versões manualmente:
1
2
3
<ItemGroup>
<PackageReference Include="eQuantic.UI.Runtime" Version="0.1.6" />
</ItemGroup>
Cenários de desenvolvimento vs. de consumo
Cenário do consumidor (pacotes NuGet)
1
2
3
4
5
6
7
8
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)
1
2
3
4
5
6
7
8
9
10
Á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
1
Error: eQuantic.UI: Runtime not found. Ensure eQuantic.UI.Runtime package is installed.
Causa: $(PkgeQuantic_UI_Runtime) está vazio
Correção:
1
2
3
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
1
Error: eQuantic.UI: Standard components not found. Ensure eQuantic.UI.Components package is installed.
Causa: $(PkgeQuantic_UI_Components) está vazio
Correção:
1
2
3
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:
1
2
3
4
5
# 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