Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions docs/decisions/0055-dependency-injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: Use of Dependency Injection in the Application
nav_order: 100
parent: Decision Records
---

# Dependency Injection Strategy for JavaFX and Core Components

Check failure on line 7 in docs/decisions/0055-dependency-injection.md

View workflow job for this annotation

GitHub Actions / Markdown

Multiple top-level headings in the same document

docs/decisions/0055-dependency-injection.md:7 MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: "Dependency Injection Strategy ..."] https://github.com/DavidAnson/markdownlint/blob/v0.39.0/doc/md025.md

## Context and Problem Statement

JabRef uses JavaFX for its graphical interface and plain Java for its core logic. We need a consistent and maintainable strategy for dependency injection (DI).

Check failure on line 11 in docs/decisions/0055-dependency-injection.md

View workflow job for this annotation

GitHub Actions / Markdown

Trailing spaces

docs/decisions/0055-dependency-injection.md:11:160 MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1] https://github.com/DavidAnson/markdownlint/blob/v0.39.0/doc/md009.md

JavaFX imposes certain constraints on controllers and view models, especially regarding how FXML loads classes. At the same time, the rest of the app benefits from clear, explicit, and testable construction patterns. However, sometimes there are too much arguments in a constructor. The question is how to balance JavaFX requirements with the architectural clarity of constructor-based DI.

## Decision Drivers

* JavaFX requires empty constructors for classes instantiated through FXML (or very simple arguments like integers or text).
* JabRef prefers clarity, type safety, and ease of reasoning over magic.
* Testing core logic should be simple and isolated.
* DI should not add unnecessary framework complexity.

## Considered Options

* Use DI framework for everything.
* Use constructor-based DI everywhere.
* Use a mix of a DI framework and constructor-based DI.

## Decision Outcome

Chosen option: "Use a mix of a DI framework and constructor-based DI.", because this approach works with the constraints of JavaFX while preserving explicitness and testability in the rest of the system.

### Consequences

* Good, because the GUI stays compatible with FXML, empty constructors, and custom views.
* Good, because core logic remains clean, explicit, and easy to unit-test.
* Good, because the amount of DI configuration stays minimal.
* Bad, because there are two DI styles in the codebase, increasing conceptual overhead.

### Confirmation

Compliance can be verified by code review:
JavaFX view models should not expose required constructor parameters and should be wired through the DI mechanism used during FXML loading.
Core classes should expose dependencies through constructors, with no field injection or framework-specific annotations.
Static analysis or ArchUnit rules can ensure constructors in non-UI components receive all dependencies explicitly.

## Pros and Cons of the Options

### Use DI framework for everything

* Good, because dependency creation is centralized.
* Good, because configuration can be standardized across all components.
* Bad, because the whole application becomes more dependent on a framework and harder to test without it.

### Use constructor-based DI everywhere

* Good, because constructors clearly express dependencies.
* Good, because testing is straightforward.
* Bad, because JavaFX does not allow passing complex constructor arguments (like `Services` or `Preferences`) to controllers or view models loaded via FXML.
* Bad, because nesting views in FXML becomes harder without a DI mechanism.

### Use DI for JavaFX view models; constructor-based DI for core

* Good, because it respects JavaFX requirements.
* Good, because core components remain simple and testable.
* Good, because the DI surface area stays small.
* Bad, because the team must remember the rule boundary.
Loading