Architecture & Internals
Quarkus Forge follows a layered architecture that separates HTTP transport, JSON parsing, domain logic, UI state management, and rendering into focused modules.
High-Level Architecture
graph TD
subgraph Entry["Entry Points"]
CLI[QuarkusForgeCli]
HCLI[HeadlessCli]
HGS[HeadlessGenerationService]
end
subgraph TUI["TUI Layer"]
TBS[TuiBootstrapService]
CTC[CoreTuiController]
end
subgraph Headless["Headless Layer"]
PRF[ProjectRequestFactory]
HCC[HeadlessCatalogClient]
HOP[HeadlessOutputPrinter]
HT[HeadlessTimeouts]
end
subgraph API["API Layer"]
QAC[QuarkusApiClient]
APP[ApiPayloadParser]
JFR[JsonFieldReader]
CSC[CatalogSnapshotCache]
CDS[CatalogDataService]
end
subgraph Archive["Archive Layer"]
PAS[ProjectArchiveService]
SZE[SafeZipExtractor]
end
subgraph PostGen["Post-Generation"]
PTA[PostTuiActionExecutor]
SE[ShellExecutor]
ID[IdeDetector]
end
CLI --> TBS
CLI --> HGS
HCLI --> HGS
HGS --> PRF
HGS --> HCC
HGS --> HOP
HGS --> HT
TBS --> CTC
CTC --> QAC
HCC --> QAC
QAC --> APP
APP --> JFR
QAC --> CDS
CDS --> CSC
QAC --> PAS
PAS --> SZE
PTA --> SE
PTA --> ID
UI Layer Design
The TUI layer uses an immediate-mode rendering architecture built on the TamboUI framework.
Component Decomposition
CoreTuiController (Orchestration Shell)
│
├── UiEventRouter Preserves key-priority contract
│ │
│ ├── Overlay handlers (command palette, help, post-gen menu)
│ ├── Cancel flow (filter unwind priority chain)
│ ├── Global shortcuts
│ ├── Focus navigation
│ └── Input handling
│
├── UiStateSnapshotMapper Builds immutable UiState read model from controller internals
│
├── CoreUiReducer Pure transition logic for migrated slices
│ ├── post-generation intent handling
│ ├── generation callback intent handling
│ └── focus/input intent handling
│
├── UiEffectsRunner Executes side effects emitted by reducer
│ ├── generation start/cancel
│ ├── post-generation menu updates
│ └── repaint + async coordination hooks
│
├── UiRenderer State-driven render orchestration
│ ├── OverlayRenderer Stateless overlay rendering
│ ├── BodyPanelRenderer Layout: metadata + extensions panels
│ └── FooterLinesComposer Status bar composition
│
├── State Managers
│ ├── PostGenerationMenuState Post-gen menu state machine
│ ├── MetadataSelectorManager Platform/build/java selectors
│ ├── ExtensionCatalogState Search, filter, favorites, presets
│ ├── CatalogLoadState Sealed type: Loading → Loaded | Failed
│ ├── StartupOverlayTracker Startup overlay timing
│ └── GenerationStateTracker Generation progress tracking
│
└── Extracted Helpers
├── ExtensionInteractionHandler Bridge to catalog interactions
├── CatalogRowBuilder Pure function: row building
├── UiTextConstants UI text content
├── AsyncFailureHandler Reusable async exception mapping
├── GenerationFlowCoordinator Async generation lifecycle
└── UiTextInputKeys Text-input edit-key normalization
State Machine: Generation Flow
stateDiagram-v2
[*] --> IDLE
IDLE --> VALIDATING: Enter / Alt+G
VALIDATING --> ERROR: validation fails
VALIDATING --> LOADING: validation passes
LOADING --> CANCELLED: Esc / Ctrl+C
LOADING --> SUCCESS: download complete
ERROR --> IDLE: dismissed
CANCELLED --> IDLE: dismissed
SUCCESS --> PostGenMenu: show menu
PostGenMenu --> IDLE: Generate Again
state PostGenMenu {
[*] --> ActionList
ActionList --> ExportForgefile
ActionList --> PublishGitHub
ActionList --> OpenIDE
ActionList --> OpenTerminal
ActionList --> Quit
PublishGitHub --> VisibilitySubMenu
}
State Machine: Catalog Loading
stateDiagram-v2
[*] --> Initial: CatalogLoadState.initial()
Initial --> Loading1: loadExtensionCatalogAsync()
state Loading1 <>
Loading1 --> Loaded_live: success
Loading1 --> Failed: failure
Loaded_live --> Loading2: reload requested
state Loading2 <>
Loading2 --> Loaded_new: success
Loading2 --> Loaded_live: failure (preserves catalog)
note right of Initial: Loaded("snapshot", false, false)
note right of Loading1: Loading(previous=Initial)
note right of Loading2: Loading(previous=Loaded_live)
UI State-Machine Refactor Invariants (ADR-UI-001)
The UI refactor to explicit UiState/UiIntent/UiReducer/UiEffect must preserve these behavior contracts:
-
Routing precedence remains fixed in
UiEventRouter:-
help overlay keys / toggle
-
command palette toggle + keys
-
post-generation menu
-
extension cancel flow
-
quit flow
-
loading lock handling
-
global shortcuts
-
focus navigation
-
submit flow
-
extension flow
-
metadata selectors
-
text input
-
-
Generation lifecycle semantics from
GenerationFlowCoordinatorremain unchanged:-
token-based stale completion protection
-
cancellation semantics (
CancellationExceptionand cancel flag) -
dropped-callback reconciliation via
reconcileCompletionIfDone
-
-
Post-generation flow semantics from
PostGenerationMenuStateremain unchanged:-
action selection and visibility sub-menu behavior
-
stable exit-plan generation
-
Generate againreturns to active TUI session without quitting
-
-
Non-key event behavior remains unchanged:
-
TickEventdrives repaint/progress updates -
ResizeEventupdates status text while preserving state -
unknown events are ignored
-
-
Overlay rendering order remains fixed:
-
header/body/footer
-
generation overlay
-
command palette
-
help overlay
-
post-generation menu
-
startup overlay
-
-
State updates follow single-writer discipline: reducer transitions execute on the render thread.
-
TUI refactor cannot change headless behavior or CLI semantics.
Extension Cancel Flow (Esc Priority Chain)
When the user presses Esc in the extension search or list, the TUI unwinds filters in priority order:
Esc pressed in extension search/list
│
├──[1] Search text not empty? ──► Clear search
│
├──[2] Favorites filter on? ──► Disable favorites filter
│
├──[3] Selected-only filter on? ──► Disable selected-only filter
│
├──[4] Preset filter active? ──► Clear preset filter
│
├──[5] Category filter active? ──► Clear category filter
│
├──[6] In search field? ──► Focus extension list
│
└──[7] None of the above ──► Fall through to quit flow
API Layer Design
Architectural Guardrails
Headless/TUI boundaries are protected by ArchUnit tests to prevent dependency drift:
-
Headless orchestration classes must not depend on
dev.ayagmar.quarkusforge.ui.*. -
Headless orchestration classes must not depend on
dev.tamboui.*. -
Core non-UI layers (
api,archive,domain,diagnostics,util) must not depend onui.
Transport vs Parsing Separation
graph TD
REQ[HTTP Request] --> QAC
subgraph QAC["QuarkusApiClient (Transport)"]
direction TB
R[Retry with backoff]
T[Timeout management]
H[HTTP status handling]
C[Cancellation support]
end
QAC -->|raw JSON| APP
subgraph APP["ApiPayloadParser (Pure Parsing)"]
direction TB
E[Extension deserialization]
M[Metadata extraction]
S[Stream/preset parsing]
end
APP --> JFR
subgraph JFR["JsonFieldReader (Shared)"]
direction TB
RS[readString / readInt]
RL[readStringList]
RO[readNestedObject]
end
Catalog Freshness Strategy
graph TD
START[Application Start] --> LM[Load metadata sync]
LM -->|success| MCC[MetadataCompatibilityContext]
LM -->|failure| BSF[Bundled snapshot fallback]
MCC -->|constrains build/java| LE[Load extensions async]
LE --> LIVE[Live API]
LE --> CACHE[Cache fallback]
LIVE --> CSC[CatalogSnapshotCache]
CACHE --> CSC
BSF --> RENDER[TUI renders immediately
with default extensions]
CSC --> REPLACE[Catalog replaces defaults
when ready]
Domain Layer
graph LR
subgraph Domain["Domain Model"]
PR["ProjectRequest
(immutable)"]
PRF["ProjectRequestFactory"]
PRV["ProjectRequestValidator"]
MCC["MetadataCompatibilityContext"]
CPM["CliPrefillMapper"]
FF["Forgefile + ForgefileLock"]
FFS["ForgefileStore"]
end
PRF -->|creates| PR
PRV -->|validates| PR
MCC -->|constrains| PR
CPM -->|maps CLI options| PR
FFS -->|read/write| FF
Archive Layer
graph TD
ZIP[ZIP Download] --> PAS[ProjectArchiveService]
PAS -->|progress reporting| SZE[SafeZipExtractor]
PAS -->|cancellation check| SZE
SZE --> ZS["Zip-Slip protection
(path traversal guard)"]
SZE --> ZB["Zip-Bomb protection
(size + ratio + entry count)"]
SZE --> ROOT["Root directory normalization
(strips single root folder)"]
Design Principles
| Principle | Application |
|---|---|
Single Responsibility |
Each class has one reason to change. |
Sealed Types |
|
Immutable Snapshots |
|
DRY |
|
Derived State Elimination |
|
Encapsulation |
|
Resource Safety |
|
Atomic Persistence |
|
Testing Strategy
-
Extensive unit tests with deterministic async harnesses for TUI state machine testing
-
Integration tests using WireMock for API contract verification
-
Dedicated unit tests for extracted state managers:
PostGenerationMenuState,MetadataSelectorManager,CatalogRowBuilder,StartupOverlayTracker -
Async TUI tests use
UiScheduler.immediate()andDuration.ZEROdebounce for deterministic behavior -
Property-based tests for ZIP extraction edge cases
See Testing Strategy for details.