Module Architecture & Reactive Flow
Brian S. Yandell
03 August 2026
architecture.RmdThis document details the high-level software architecture and interactive reactivity design patterns of the ewing Shiny application dashboard. It explains how independent component modules communicate state and route simulation data dynamically.
1. Modular Communication Structure
The dashboard layout and server linkages are defined in R/ewingApp.R.
Rather than a monolithic codebase, the interface is split into dedicated
Shiny modules using moduleServer().
Below is the state communication and data flow diagram:
graph TD
%% Node definitions
IP[initParApp: Parameters Selector]
SS[simApp: Simulation Engine]
DP[distPlotApp: Age Distributions]
SP[substrateApp: Spatial Grid Map]
EP[envPlotApp: Multi-Run Envelopes]
DL[downloadApp: File Downloader]
%% Communications
IP -->|upload paths & species parameters| SS
SS -->|sim_data$simres reactive| IP
SS -->|sim_data$simres reactive| DP
SS -->|sim_data$simres reactive| SP
SS -->|sim_data$simres reactive & nsim| EP
SS -->|sim_data| DL
DP -->|distplot plot reactive| DL
SP -->|sppplot plot reactive| DL
EP -->|envplot plot reactive| DL
style IP fill:#D5F5E3,stroke:#27AE60,stroke-width:2px;
style SS fill:#FCF3CF,stroke:#F1C40F,stroke-width:2px;
style DP fill:#E8F8F5,stroke:#17A589,stroke-width:2px;
style SP fill:#E8F8F5,stroke:#17A589,stroke-width:2px;
style EP fill:#E8F8F5,stroke:#17A589,stroke-width:2px;
style DL fill:#EBDEF0,stroke:#8E44AD,stroke-width:2px;
Server Coordination (ewingServer)
The coordinating server loop in ewingServer() maps these interactions explicitly:
ewingServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
# 1. Initialize Parameters Table & Species Models
init_par <- initParServer("init_par", simres = shiny::reactive({ tryCatch(sim_data$simres(), error=function(e) NULL) }))
# 2. Main Run Simulation
sim_data <- simServer("sim", init_par)
# 3. Dynamic Visualizations (Evaluating on active state)
dist_plot <- distPlotServer("dist_plot", sim_data$simres)
spp_plot <- substrateServer("substrate", sim_data$simres)
env_plot <- envPlotServer("env_plot", sim_data$simres, sim_data$nsim)
# 4. Bind the File Extractor
downloadServer("download", sim_data = sim_data, distplot = dist_plot, sppplot = spp_plot, envplot = env_plot)
...
})
}2. UI Layout & Dynamic Tabs
The user interface uses bslib::page_sidebar()
to build a split-frame responsive layout: - Global
Sidebar: Contains panels from initParInput(),
simInput(),
distPlotInput(),
envPlotInput(),
and downloadInput().
- Dynamic Main Panel: The active tabs change
dynamically based on the value of nsim chosen by the
user.
# Dynamic tab builder in R/ewingApp.R
output$dynamic_tabs <- shiny::renderUI({
sims <- shiny::req(sim_data$nsim())
if (sims == 1) {
bslib::navset_tab(
bslib::nav_panel("Dist Plots", distPlotOutput(ns("dist_plot"))),
bslib::nav_panel("Substrate Plots", bslib::card(substrateOutput(ns("substrate")))),
bslib::nav_panel("Input Data", initParOutput(ns("init_par")))
)
} else {
bslib::navset_tab(
bslib::nav_panel("Envelope Plots", bslib::card(envPlotOutput(ns("env_plot")))),
bslib::nav_panel("Input Data", initParOutput(ns("init_par")))
)
}
})3. Data Flow and Module API Bindings
| Module | Exposed Reactive Outputs | Subscribed Inputs |
|---|---|---|
Parameters (initPar) |
- host: Initial host count- parasite:
Initial parasite count- datafile: Selected Excel
filepath |
- simres: Active simulation result for DT
visualization |
Simulation (sim) |
- simres: The ewing or
ewing_discrete run output- nsim: Selected
number of simulation runs |
- init_par: Config state list |
Age Distributions (distPlot) |
- distplot: The active ggplot age overlay object |
- simres: Main single-run results |
Spatial Substrate (substrate) |
- sppplot: The active ggplot individual positions
grid |
- simres: Main single-run results |
Envelope Bounds (envPlot) |
- envplot: The active ggplot confidence boundary |
- simres: Multi-run discrete data |
Downloads (download) |
- (None, output is written directly to disk via user click triggers) | - sim_data: Contains run metrics- distplot, sppplot, envplot
reactives |
For deep dives into each section, see: - Simulation Panels - Visualization Panels - Utilities & Download Controls
4. Peer Application Wrappers & Shared Sub-Modules
IsleRoyaleApp.R and sysetholApp.R are built
as peer application wrappers sharing identical underlying Shiny
sub-modules: - step_controls
(step_size_slider / parse_step_size /
axisUnitInput /
ageClassControlInput): Centralized logarithmic
step click slider, dynamic axis units selector (Steps
vs. Days for Isle Royale; Steps
vs. Time for general apps), and Age Classes display options
sub-modules in R/step_controls.R.
- distPlotApp (distPlotOutput /
distPlotServer): Renders age-class population
dynamics over simulation steps or days. - inputApp
(inputAppInput / inputAppOutput /
inputAppServer /
discover_dataset_tables): Dynamically discovers
and displays input configuration data tables from any simulation site
directory or Excel workbook. - substrateApp
(substrateInput / substrateOutput /
substrateServer): Renders substrate grid overlays
and organism positions across tridiagonal plant networks or real-world
GIS spatial meshes. - Tab-Aware Sidebar Decluttering:
Both applications utilize conditionalPanel logic bound to
the active tab ID (input.tabset). Display options (e.g.,
spatial map feature toggles, age-class normalization options, envelope
confidence controls) are contextually shown strictly when their target
tab is active, keeping the sidebar uncluttered.