01
Visão em camadas
Clean Architecture em três projetos. A interface conhece apenas contratos; o domínio não conhece a interface; a persistência implementa os contratos do domínio.
WingetEasy.App
WinUI 3WingetEasy.Core
DomínioWingetEasy.Data
EF CoreInversão de controle
DI02
Apresentação MVVM
Views finas, ViewModels testáveis. O estado vive em propriedades observáveis e cada ação é um comando.
comCommunityToolkit.Mvvm ,ObservableObject ,[ObservableProperty] eRelayCommand .AsyncRelayCommand- Atualizações de tela sempre marshalled para a UI thread via
; trabalho de I/O roda emDispatcherQueue .Task.Run é Singleton: a página principal e o painel da bandeja (UpdatesViewModel ) compartilham o mesmo estado de fila.UpdateFlyout- Listas longas usam virtualização XAML com
; a busca tem debounce para não recalcular a cada tecla.ItemsStackPanel
03
Ciclo de vida completo
Do primeiro milissegundo ao descarte na bandeja: Serilog, hooks do Velopack, instância única por Mutex e decisão do modo de execução pelos argumentos.
04
Modos de execução
O mesmo executável assume quatro papéis, decididos por CliArgumentsParser antes de qualquer janela existir.
| Argumento | Modo | O que acontece |
|---|---|---|
| (nenhum) | Desktop interativo | DI completo, migrations, MainWindow com Mica e coordenadas restauradas. |
| --minimized | Bandeja | Sem janela DirectX: só o ícone H.NotifyIcon, verificação silenciosa e 7–15 MB de RAM. |
| --check · --update-all · --backup · --restore | Headless | HeadlessRunner executa sem UI e devolve um código de saída padronizado. |
| --elevated-update <job> | Worker elevado | Processo filho com UAC lê o job JSON, executa o lote como administrador e grava o resultado. |
05
Gestão crítica de memória
Aplicações WinUI 3 self-contained são pesadas por natureza. O WingetEasy trata RAM como recurso de primeira classe.
Contexto de dados efêmero
Árvore XAML descartada
Compactação do LOH
Imagens decodificadas no tamanho certo
06
Concorrência e resiliência
Separação rígida entre UI thread e workers, cancelamento cooperativo de ponta a ponta e um circuit breaker para verificações em segundo plano.
- Paralelismo limitado com
; cancelamento propagado porSemaphoreSlim até o processo filho do winget (CancellationTokenSource ).process.Kill() - Streams
eStandardOutput lidos de forma assíncrona para evitar deadlock quando a saída excede o pipe anônimo (~4 KB).StandardError usaSchedulerService e abre o circuito após 3 falhas seguidas, com backoff exponencial.PeriodicTimer
07
Segurança, privilégios e UAC
Princípio do menor privilégio: o app nunca roda elevado. Quando um pacote exige administrador, apenas um processo filho isolado é elevado — uma única vez por lote.
- 01
Sanitização
Cada PackageId é validado contra
(Source Generator Regex). Aspas, ponto-e-vírgula ou caracteres de controle são rejeitados.^[A-Za-z0-9._-+]+$ - 02
Job temporário
A lista de pacotes é serializada (AOT,
) emCoreJsonContext .%TEMP%WingetEasyjob_<guid>.json - 03
Elevação sob demanda
O próprio executável é iniciado com
e--elevated-update : um único prompt do UAC.Verb = "runas" - 04
Captura e limpeza
O pai lê
, desserializa os resultados e apaga os dois arquivos.result_<guid>.json
08
Persistência SQLite
EF Core sobre SQLite em modo WAL: leituras da interface nunca esperam por escritas do modo headless ou de serviços em segundo plano.
PRAGMA journal_mode=WAL;
PRAGMA busy_timeout=5000;
await using var db = await _factory.CreateDbContextAsync(ct);| Tabela | Repositório | Conteúdo |
|---|---|---|
| Settings | SettingsRepository | Chaves tipadas (GetAsync<T>/SetAsync<T>), estado da janela e flags. |
| UpdateHistories | UpdateHistoryRepository | Auditoria FromVersion → ToVersion, status e mensagem de erro. |
| Packages | PackageRepository | Regras de supressão, pin, versão ignorada e prazo de adiamento. |
| BackupSnapshots | BackupSnapshotRepository | Snapshots JSON completos do winget export com metadados da máquina. |
| Profiles | ProfileRepository | Perfis temáticos e lista serializada de pacotes. |
| CheckLogs | CheckLogRepository | Telemetria de cada verificação: duração, pacotes encontrados, sucesso. |
Migrações versionadas pelo EF Core: InitialSchemaAddPackageSuppressionFields
09
Contratos de serviço
Os “endpoints” internos do WingetEasy: cada capacidade é uma interface em WingetEasy.Core.Interfaces.
| Interface | Responsabilidade |
|---|---|
| IWingetService | Interoperação direta com winget.exe: verificar, listar instalados, instalar e reparar fontes. |
| IUpdateService | Orquestrador da fila: pré-voo de processos, lote padrão, fallback UAC e auditoria. |
| IBackupService | Snapshots via winget export, restauração granular e geração de scripts .ps1. |
| IProfilesService | CRUD de perfis e instalação apenas dos pacotes ausentes (diff engine). |
| IRunningProcessService | Detecção de apps abertos e fechamento gracioso (WM_CLOSE) ou forçado. |
| ISchedulerService | Verificações periódicas internas com circuit breaker. |
| ITaskSchedulerService | Tarefa WingetEasy_Background_Check no Agendador de Tarefas do Windows. |
| IToastNotificationService | Notificações interativas, respeitando horário silencioso e som. |
| INotificationService | Contrato genérico de notificação, implementado por ToastNotificationService. |
| IAdminElevationHelper | Execução isolada de lotes elevados via processo filho runas. |
| IAppUpdateService | Atualização do próprio WingetEasy via Velopack e GitHub Releases. |
| ILocalizationService | Textos da interface traduzidos, registrado como instância única (LocalizationService.Instance). |
10
Stack e dependências
Windows 10 build 19041+ ou Windows 11, .NET 10, C# 14 e Windows App SDK.
| Pacote | Uso |
|---|---|
| CommunityToolkit.Mvvm | Propriedades observáveis e comandos gerados por source generators. |
| Microsoft.EntityFrameworkCore.Sqlite | Persistência local com migrations e modo WAL. |
| H.NotifyIcon | Ícone, tooltip dinâmico e menu de contexto na bandeja. |
| Microsoft.Windows.AppNotifications | Toasts nativos interativos. |
| Microsoft.Win32.TaskScheduler | Criação da tarefa agendada de verificação. |
| System.CommandLine | Parsing dos argumentos do modo headless. |
| Serilog | Logs estruturados diários em %LocalAppData%. |
| Velopack | Auto-update delta do próprio aplicativo. |
