Architecture overview

BrainDeer is organized in layers under src/brain_deer/. Later layers depend on earlier ones; nothing reaches up into presentation or plugins from infrastructure.

The layers

app / presentation / plugins
              │
              ▼
     application  (API, events, ports, commands)
              │
              ▼
     domain · warp · surface
              │
              ▼
     infrastructure  (io, bids)
              │
              ▼
     rendering (VTK) · plugin_api / sdk

Layer

Package

Responsibility

App

app/

Entry point (launch.py), CLI, startup

Application

application/

Services, event bus, ports, brain_deer_api

Domain

domain/

Domain models and logic

Domain modules

warp/, surface/

Warp engine; surfaces, tracts, projection

Infrastructure

infrastructure/

NIfTI / volume proxy, BIDS mapping

Presentation

presentation/

Qt UI (qt/), colormaps, typography

Rendering

rendering/

VTK 3D scene, surface LOD, scene rig, tracts

Plugins

plugins/, plugins_host/, plugin_api/, sdk/

Extension host and stable author API

Commands

commands/

Named actions shared by UI, plugins, and scripts

Design rationale

These principles shaped the 2.0 restructure and should be preserved:

  • Host + plugins, not a monolith. Domain and I/O stay callable without the full Qt shell where practical. Plugins talk to the running app through plugin_api.v1 / the app facade — not by reaching into private widget internals.

  • No caller-name coupling. Behavior comes from explicit parameters and injected dependencies (the facade, event bus, command registry), never from who called a function.

  • Commands are the shared action surface. UI buttons, plugin widgets, and scripting should trigger the same command definitions under commands/ whenever the action is user-facing.

  • Events decouple producers from consumers. Load finished, subject changed, interaction capture — subscribe on the bus instead of hard-wiring panel A to panel B.

  • Wrap mature tools. Nibabel, VTK, PyQt, and friends stay behind thin adapters (infrastructure.io, rendering/); we do not reimplement numerics.

Core mechanisms

Event bus

application/events/bus.py provides a lightweight pub/sub with priorities. Plugins register handlers via PluginBase.register_event_handler; the host and core emit through the same bus so UI and plugins stay loosely coupled.

Command registry

Actions under commands/ are registered once and can be invoked from the command palette, plugins, or scripts. Prefer adding a command over inventing a one-off callback chain between widgets.

Plugin host

plugins_host/plugin_manager.py discovers plugins/<id>/plugin.py, restricted to _BUNDLED_PLUGIN_ALLOWLIST. Convention: plugin.py (lifecycle + logic) and widget.py (Qt tab). See Plugin development.

App facade

plugin_api.v1.BrainDeerAppFacade (and the concrete brain_deer_api) is what plugins should use for viewer access, interaction capture, and high-level operations — the stable door into the host.

Data flow (load → view → render)

Presentation          Application API         infrastructure.io
     │                      │                        │
     │  open volume/BIDS    │                        │
     │─────────────────────►│  load NIfTI / proxy    │
     │                      │───────────────────────►│
     │                      │◄───────────────────────│ image + affine
     │                      │                        │
     │                      │  register layer ──► Viewer / layers
     │                      │                           │
     │◄──────── paint ──────┼──────── VTK planes/actors◄┘

See also