Skip to content

Add a lazy composition root to application services#10

Open
koder95 wants to merge 6 commits into
mainfrom
codex/propose-lazy-initialization-and-di-system
Open

Add a lazy composition root to application services#10
koder95 wants to merge 6 commits into
mainfrom
codex/propose-lazy-initialization-and-di-system

Conversation

@koder95

@koder95 koder95 commented May 7, 2026

Copy link
Copy Markdown
Contributor

Motivation

  • Wprowadzić leniwą inicjalizację i prosty composition root aby odroczyć koszt tworzenia zależności do momentu faktycznego użycia usług.
  • Utrzymać DI przez konstruktor w implementacjach serwisów, dzięki czemu zależności pozostają jawne i testowalne.
  • Zakomunikować aktualny stan architektury i kroki potrzebne, aby zbliżyć projekt do zasad Clean Architecture (w szczególności wyodrębnienie adapterów infrastrukturalnych spoza domeny).

Description

  • Dodano nowy pakiet pl.vtt.wpi.core.application.context z interfejsem ApplicationServices będącym fasadą dla przypadków użycia biblioteki. (src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java).
  • Dodano thread-safe memoizujący Lazy<T> i implementację LazyApplicationServices z builderem, które rejestrują dostawców portów i tworzą konkretne serwisy dopiero przy pierwszym wywołaniu; zależności przekazywane są przez konstruktory istniejących implementacji usług. (src/main/java/pl/vtt/wpi/core/application/context/Lazy.java, .../LazyApplicationServices.java).
  • Zaktualizowano module-info.java, aby eksportować pakiet application.context i udostępnić composition root klientom modułu. (src/main/java/module-info.java).
  • Dodano testy jednostkowe pokrywające zachowanie leniwej inicjalizacji, nierozwiązywanie niepowiązanych zależności oraz sprawdzanie wymagalności supplierów, oraz dokument z analizą architektury i wskazówkami migracji ku Clean Architecture. (src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java, docs/architecture-proposal.md).

Testing

  • Skompilowano źródła poleceniem javac --release 25 -d /tmp/wpi-core-classes $(find src/main/java -name '*.java') i kompilacja zakończyła się pomyślnie.
  • git diff --check nie zgłosiło problemów statycznych w zmianach (brak konfliktów/whitespace errors).
  • Próba uruchomienia testów z mvn test zakończyła się niepowodzeniem z powodu niemożności pobrania maven-resources-plugin:3.3.1 (HTTP 403), a mvn -o test również nie mogło uruchomić testów ponieważ wymagany plugin nie był dostępny w lokalnym cache.

Codex Task

Summary by CodeRabbit

  • Refactor

    • Reorganized application service architecture to support lazy initialization of core services, enabling more efficient dependency resolution.
    • Implemented standardized service accessor patterns across the application services context.
  • Tests

    • Added comprehensive tests validating lazy service creation, caching behavior, and selective dependency resolution.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces an application-level composition root (ApplicationServices interface) backed by a Lazy memoization utility and LazyApplicationServices implementation with a fluent Builder for dependency injection. The context package is exported via module-info.java, and comprehensive tests validate lazy instantiation, caching, dependency isolation, and fail-fast validation.

Changes

Application Context Composition Root

Layer / File(s) Summary
ApplicationServices interface contract
src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java
ApplicationServices public interface declares accessor methods for nine core use-case services (LoginService, AdminPasswordService, UserManagementService, RuntimeDataService, PixelProgramService, NetworkConfigurationService, DeviceInfoService, DebugService, RebootService) that form the application-level composition root contract.
Lazy memoization utility
src/main/java/pl/vtt/wpi/core/application/context/Lazy.java
Lazy<T> generic Supplier implementation uses volatile fields and double-checked locking to cache the first computed value on first get() call, enforce non-null initialization and result, and clear the initializer reference after first use to reduce memory overhead.
LazyApplicationServices implementation and Builder
src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java
LazyApplicationServices implements ApplicationServices with Lazy-wrapped port suppliers and services; provides static builder() factory returning a nested Builder class with fluent setter methods for each dependency port (authOutputPort, adminPasswordResetInputPort, usersOutputPort, etc.), non-null validation via lazy() helper, and build() method that constructs the configured composition root from accumulated dependencies.
Module export
src/main/java/module-info.java
module-info.java exports pl.vtt.wpi.core.application.context package to make ApplicationServices, Lazy, and LazyApplicationServices publicly available for module consumers.
Lazy instantiation and caching tests
src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java
LazyApplicationServicesTest validates lazy behavior with three test cases: verifies dependency suppliers are invoked only on first service access and results are cached, confirms unrelated port suppliers are not resolved when unneeded, asserts fail-fast NullPointerException when required dependencies are missing, and includes baseBuilder() helper and output/input port factory methods for test setup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • VerbaTechTeam/wpi-core#1: Introduces LoginService and LoginServiceImpl that are wired and exposed through the LazyApplicationServices composition root created in this PR.

Poem

🐰 A lazy rabbit builds with care,
Wiring services through the air,
Cache at first call, then relax—
No work twice when memoized stacks!
bzzzzzzt 🔌

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add a lazy composition root to application services' directly and clearly describes the main change: introducing a lazy initialization composition root (LazyApplicationServices) with supporting infrastructure (Lazy, ApplicationServices interface) to the application context package.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/propose-lazy-initialization-and-di-system

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/pl/vtt/wpi/core/application/context/Lazy.java`:
- Around line 30-38: The branch in Lazy.get() that handles currentInitializer ==
null can never legitimately run and currently assigns a null to current,
violating the non-null contract; replace this dead guard with an explicit
failure (e.g., throw an AssertionError or IllegalStateException) to make the
invariant explicit and fail fast. Locate the synchronized block in class Lazy
(the method using variables initializer, value, current and Supplier<? extends
T>) and swap the "if (currentInitializer == null) { current = value; }" branch
for an assertion/exception indicating invariant violation (include a clear
message referencing initializer/value) so any unexpected state is loudly
reported instead of returning null.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 750444fd-1003-43d7-8e35-2115084f3896

📥 Commits

Reviewing files that changed from the base of the PR and between 386e4a5 and 8c0ae3e.

📒 Files selected for processing (6)
  • docs/architecture-proposal.md
  • src/main/java/module-info.java
  • src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java
  • src/main/java/pl/vtt/wpi/core/application/context/Lazy.java
  • src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java
  • src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java

Comment thread src/main/java/pl/vtt/wpi/core/application/context/Lazy.java Outdated
@koder95 koder95 changed the title Dodaj leniwy composition root dla serwisów aplikacyjnych Add a lazy composition root to application services May 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java (1)

25-27: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Restore interrupt status on InterruptedException — inconsistent with peer OutputPort adapters.

Both DeviceInfoOutputPort and UsersOutputPort in this same package check for InterruptedException and call Thread.currentThread().interrupt() before re-throwing. RuntimeDataOutputPort silently clears the interrupt flag.

🐛 Proposed fix
     } catch (Exception e) {
+        if (e instanceof InterruptedException) {
+            Thread.currentThread().interrupt();
+        }
         throw new OutputPortException("Cannot load runtime data", e);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java`
around lines 25 - 27, In RuntimeDataOutputPort's exception handling (the
catch(Exception e) that throws new OutputPortException("Cannot load runtime
data", e)), detect if the caught exception is an InterruptedException and if so
call Thread.currentThread().interrupt() before rethrowing the
OutputPortException; mirror the behavior in DeviceInfoOutputPort and
UsersOutputPort so the thread's interrupt status is preserved when wrapping the
exception into OutputPortException.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java`:
- Around line 25-27: In RuntimeDataOutputPort's exception handling (the
catch(Exception e) that throws new OutputPortException("Cannot load runtime
data", e)), detect if the caught exception is an InterruptedException and if so
call Thread.currentThread().interrupt() before rethrowing the
OutputPortException; mirror the behavior in DeviceInfoOutputPort and
UsersOutputPort so the thread's interrupt status is preserved when wrapping the
exception into OutputPortException.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 879dcedb-0268-433d-893f-06e7fa031413

📥 Commits

Reviewing files that changed from the base of the PR and between 8c0ae3e and 2025b10.

📒 Files selected for processing (40)
  • README.md
  • docs/architecture-proposal.md
  • src/main/java/module-info.java
  • src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java
  • src/main/java/pl/vtt/wpi/core/application/context/Lazy.java
  • src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java
  • src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java
  • src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/AuthOutputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/CurrentStateOutputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/DeviceInfoOutputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/LogsDeleteInputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsInputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsOutputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RestartInputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataInputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UserCreateInputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UsersOutputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/WifiConfigInputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/dto/AdminPasswordResetRequest.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/dto/PasswordDto.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/dto/UserCreateRequest.java
  • src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java
✅ Files skipped from review due to trivial changes (14)
  • src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/dto/UserCreateRequest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/AuthOutputPort.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/CurrentStateOutputPort.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java
  • src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java
  • src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsOutputPort.java
  • src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java
  • src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java
  • src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java
  • src/main/java/pl/vtt/wpi/core/application/context/Lazy.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/architecture-proposal.md`:
- Line 20: W zdaniu opisującym rolę LazyApplicationServices zamień kolokację
„pełni rolę composition root” na poprawną formę, np. „LazyApplicationServices
odgrywa rolę composition root” lub „LazyApplicationServices pełni funkcję
composition root”, aby użyć naturalnej polskiej frazeologii; zaktualizuj tylko
ten fragment w opisie odnoszącym się do LazyApplicationServices.
- Line 18: Zmień nagłówek sekcji który obecnie brzmi "Leniwa inicjalizacja i DI
przez konstruktor" na poprawną formę gramatyczną, np. "Leniwa inicjalizacja i DI
przez konstruktory" (alternatywnie "Leniwa inicjalizacja i DI konstruktorowe"),
aktualizując dokładnie tekst nagłówka "Leniwa inicjalizacja i DI przez
konstruktor" w dokumencie.
- Line 59: W zdaniu zawierającym porównanie dodaj brakujący przecinek: zmień
fragment "tak dobrze jak dedykowane gatewaye" na "tak dobrze, jak dedykowane
gatewaye" w miejscu opisującym generyczne InputPort<T> i OutputPort<T> oraz
przykłady UserGateway, RuntimeDataGateway i PixelProgramGateway.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd3267dd-cf6c-49e6-8632-1562c1b02187

📥 Commits

Reviewing files that changed from the base of the PR and between 8e5e98e and 8f7f4a8.

📒 Files selected for processing (1)
  • docs/architecture-proposal.md

Comment thread docs/architecture-proposal.md Outdated
Comment thread docs/architecture-proposal.md Outdated
Comment thread docs/architecture-proposal.md Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant