From 8c0ae3ed7b8e2720302107b4ea454acb88f833bb Mon Sep 17 00:00:00 2001 From: Kamil Jan Mularski Date: Thu, 7 May 2026 21:17:26 +0200 Subject: [PATCH 1/6] Add lazy application service composition root --- docs/architecture-proposal.md | 61 ++++ src/main/java/module-info.java | 1 + .../context/ApplicationServices.java | 34 +++ .../wpi/core/application/context/Lazy.java | 44 +++ .../context/LazyApplicationServices.java | 265 ++++++++++++++++++ .../context/LazyApplicationServicesTest.java | 95 +++++++ 6 files changed, 500 insertions(+) create mode 100644 docs/architecture-proposal.md create mode 100644 src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java create mode 100644 src/main/java/pl/vtt/wpi/core/application/context/Lazy.java create mode 100644 src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java create mode 100644 src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java diff --git a/docs/architecture-proposal.md b/docs/architecture-proposal.md new file mode 100644 index 0000000..5432163 --- /dev/null +++ b/docs/architecture-proposal.md @@ -0,0 +1,61 @@ +# Propozycja: leniwa inicjalizacja, DI przez konstruktor i kierunek Clean Architecture + +## Aktualna architektura + +Projekt jest biblioteką warstwową z elementami architektury portów i adapterów: + +- `domain.model` przechowuje modele domenowe, np. użytkownika, poświadczenia i dane urządzenia. +- `domain.port` definiuje kontrakty `InputPort` i `OutputPort`, a podpakiety `input` oraz `output` zawierają porty specyficzne dla endpointów. +- `application.service` definiuje przypadki użycia widoczne dla klienta biblioteki. +- `application.service.impl` orkiestruje przypadki użycia i komunikuje się z portami przez konstruktory. +- `infrastructure` definiuje model żądania/odpowiedzi oraz abstrakcje wysyłania i obsługi żądań. + +To nie jest jeszcze ścisła Clean Architecture. Najważniejszy problem polega na tym, że pakiet `domain.port.input`/`domain.port.output` zawiera klasy portów, które znają infrastrukturę HTTP (`RequestFactory`, `RequestHandler`, `RequestSender`) i konkretne endpointy. W Clean Architecture domena powinna definiować abstrakcje wejścia/wyjścia, a adaptery infrastrukturalne powinny je implementować na zewnątrz warstwy domenowej. Obecnie część logiki adaptera znajduje się w `domain`. + +## Implementacja leniwej inicjalizacji i DI przez konstruktor + +Dodany został prosty composition root w `application.context`: + +1. `ApplicationServices` jest fasadą udostępniającą interfejsy przypadków użycia. +2. `LazyApplicationServices` przyjmuje dostawców portów w builderze. +3. Każdy dostawca portu jest opakowany w memoizujący `Lazy`. +4. Konkretne serwisy są tworzone dopiero przy pierwszym wywołaniu odpowiedniej metody fasady. +5. Gdy serwis jest tworzony, zależności są przekazywane przez jego konstruktor, więc same implementacje usług pozostają niezależne od kontenera. + +Przykład użycia po stronie aplikacji-klienta: + +```java +ApplicationServices services = LazyApplicationServices.builder() + .authOutputPort(() -> new AuthOutputPort(requestFactory, authHandler)) + .adminPasswordResetInputPort(() -> adminPasswordResetPort) + .usersOutputPort(() -> usersOutputPort) + .userCreateRequestInputPort(() -> userCreateRequestInputPort) + .changePasswordInputPort(() -> changePasswordInputPort) + .removeUserInputPort(() -> removeUserInputPort) + .runtimeDataOutputPort(() -> runtimeDataOutputPort) + .pixelProgramsOutputPort(() -> pixelProgramsOutputPort) + .runtimeDataInputPort(() -> runtimeDataInputPort) + .pixelProgramsInputPort(() -> pixelProgramsInputPort) + .wifiConfigInputPort(() -> wifiConfigInputPort) + .deviceInfoOutputPort(() -> deviceInfoOutputPort) + .logsOutputPort(() -> logsOutputPort) + .logsDeleteInputPort(() -> logsDeleteInputPort) + .currentStateOutputPort(() -> currentStateOutputPort) + .rebootInputPort(() -> rebootInputPort) + .build(); + +LoginService loginService = services.loginService(); +``` + +## Co zrobić, aby architekturę można było uznać za Clean Architecture + +1. Przenieść klasy implementujące komunikację z endpointami z `domain.port.input` i `domain.port.output` do zewnętrznej warstwy adapterów, np. `infrastructure.adapter.http`. +2. Pozostawić w domenie wyłącznie stabilne modele i abstrakcyjne porty, bez zależności od `RequestFactory`, `RequestHandler`, `RequestSender`, URL-i ani metod HTTP. +3. Rozdzielić porty use-case od portów gateway. Interfejsy przypadków użycia mogą pozostać w `application.service`, natomiast gatewaye powinny wyrażać język domeny, np. `UserGateway`, `RuntimeDataGateway`, `PixelProgramGateway`. +4. Przenieść DTO transportowe zależne od API urządzenia poza domenę, jeśli reprezentują format komunikacji, a nie pojęcia domenowe. +5. Utrzymywać composition root na brzegu systemu. `LazyApplicationServices` może być wygodnym, lekkim composition rootem biblioteki, ale pełna aplikacja powinna konfigurować konkretne adaptery infrastrukturalne poza domeną i aplikacją. +6. Zachować regułę zależności: domena nie importuje aplikacji ani infrastruktury, aplikacja importuje domenę i porty abstrakcyjne, infrastruktura implementuje porty zdefiniowane wewnątrz. + +## Dlaczego DI przez konstruktor jest dobrym kierunkiem + +DI przez konstruktor sprawia, że zależności są jawne, obiekt nie może istnieć bez wymaganych portów, testy mogą przekazywać fałszywe implementacje, a implementacje usług nie muszą znać żadnego kontenera. Leniwy composition root uzupełnia ten model o opóźnienie kosztu inicjalizacji do momentu faktycznego użycia danej usługi lub portu. diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index d8c38e1..36c5118 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,4 +1,5 @@ module wpi.core { + exports pl.vtt.wpi.core.application.context; exports pl.vtt.wpi.core.application.exception; exports pl.vtt.wpi.core.application.service; exports pl.vtt.wpi.core.domain.dto; diff --git a/src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java b/src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java new file mode 100644 index 0000000..8118033 --- /dev/null +++ b/src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java @@ -0,0 +1,34 @@ +package pl.vtt.wpi.core.application.context; + +import pl.vtt.wpi.core.application.service.AdminPasswordService; +import pl.vtt.wpi.core.application.service.DebugService; +import pl.vtt.wpi.core.application.service.DeviceInfoService; +import pl.vtt.wpi.core.application.service.LoginService; +import pl.vtt.wpi.core.application.service.NetworkConfigurationService; +import pl.vtt.wpi.core.application.service.PixelProgramService; +import pl.vtt.wpi.core.application.service.RebootService; +import pl.vtt.wpi.core.application.service.RuntimeDataService; +import pl.vtt.wpi.core.application.service.UserManagementService; + +/** + * Composition-root facade for application use cases exposed by wpi-core. + */ +public interface ApplicationServices { + LoginService loginService(); + + AdminPasswordService adminPasswordService(); + + UserManagementService userManagementService(); + + RuntimeDataService runtimeDataService(); + + PixelProgramService pixelProgramService(); + + NetworkConfigurationService networkConfigurationService(); + + DeviceInfoService deviceInfoService(); + + DebugService debugService(); + + RebootService rebootService(); +} diff --git a/src/main/java/pl/vtt/wpi/core/application/context/Lazy.java b/src/main/java/pl/vtt/wpi/core/application/context/Lazy.java new file mode 100644 index 0000000..0977ebe --- /dev/null +++ b/src/main/java/pl/vtt/wpi/core/application/context/Lazy.java @@ -0,0 +1,44 @@ +package pl.vtt.wpi.core.application.context; + +import java.util.Objects; +import java.util.function.Supplier; + +/** + * Thread-safe, memoizing supplier used by the application composition root. + * + * @param lazily initialized dependency type + */ +public final class Lazy implements Supplier { + private volatile Supplier initializer; + private volatile T value; + + private Lazy(Supplier initializer) { + this.initializer = Objects.requireNonNull(initializer, "initializer cannot be null"); + } + + public static Lazy of(Supplier initializer) { + return new Lazy<>(initializer); + } + + @Override + public T get() { + T current = value; + if (current == null) { + synchronized (this) { + current = value; + if (current == null) { + Supplier currentInitializer = initializer; + if (currentInitializer == null) { + current = value; + } else { + current = Objects.requireNonNull(currentInitializer.get(), + "initializer cannot return null"); + value = current; + initializer = null; + } + } + } + } + return current; + } +} diff --git a/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java b/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java new file mode 100644 index 0000000..6c863db --- /dev/null +++ b/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java @@ -0,0 +1,265 @@ +package pl.vtt.wpi.core.application.context; + +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; +import pl.vtt.wpi.core.application.service.AdminPasswordService; +import pl.vtt.wpi.core.application.service.DebugService; +import pl.vtt.wpi.core.application.service.DeviceInfoService; +import pl.vtt.wpi.core.application.service.LoginService; +import pl.vtt.wpi.core.application.service.NetworkConfigurationService; +import pl.vtt.wpi.core.application.service.PixelProgramService; +import pl.vtt.wpi.core.application.service.RebootService; +import pl.vtt.wpi.core.application.service.RuntimeDataService; +import pl.vtt.wpi.core.application.service.UserManagementService; +import pl.vtt.wpi.core.application.service.impl.AdminPasswordServiceImpl; +import pl.vtt.wpi.core.application.service.impl.DebugServiceImpl; +import pl.vtt.wpi.core.application.service.impl.DeviceInfoServiceImpl; +import pl.vtt.wpi.core.application.service.impl.LoginServiceImpl; +import pl.vtt.wpi.core.application.service.impl.NetworkConfigurationServiceImpl; +import pl.vtt.wpi.core.application.service.impl.PixelProgramServiceImpl; +import pl.vtt.wpi.core.application.service.impl.RebootServiceImpl; +import pl.vtt.wpi.core.application.service.impl.RuntimeDataServiceImpl; +import pl.vtt.wpi.core.application.service.impl.UserManagementServiceImpl; +import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.UserCreateRequest; +import pl.vtt.wpi.core.domain.model.Credentials; +import pl.vtt.wpi.core.domain.model.User; +import pl.vtt.wpi.core.domain.model.device.CurrentState; +import pl.vtt.wpi.core.domain.model.device.DeviceInfo; +import pl.vtt.wpi.core.domain.model.device.PixelProgram; +import pl.vtt.wpi.core.domain.model.device.RuntimeData; +import pl.vtt.wpi.core.domain.model.device.WifiConfig; +import pl.vtt.wpi.core.domain.port.InputPort; +import pl.vtt.wpi.core.domain.port.OutputPort; + +/** + * Lazy composition root for the default application services. + * + *

Dependencies are registered as suppliers, resolved once on first use, and + * then passed to service implementations through their constructors.

+ */ +public final class LazyApplicationServices implements ApplicationServices { + private final Lazy> authOutputPort; + private final Lazy> adminPasswordResetInputPort; + private final Lazy>> usersOutputPort; + private final Lazy> userCreateRequestInputPort; + private final Lazy> changePasswordInputPort; + private final Lazy> removeUserInputPort; + private final Lazy> runtimeDataOutputPort; + private final Lazy>> pixelProgramsOutputPort; + private final Lazy> runtimeDataInputPort; + private final Lazy>> pixelProgramsInputPort; + private final Lazy> wifiConfigInputPort; + private final Lazy> deviceInfoOutputPort; + private final Lazy>> logsOutputPort; + private final Lazy> logsDeleteInputPort; + private final Lazy> currentStateOutputPort; + private final Lazy> rebootInputPort; + + private final Lazy loginService; + private final Lazy adminPasswordService; + private final Lazy userManagementService; + private final Lazy runtimeDataService; + private final Lazy pixelProgramService; + private final Lazy networkConfigurationService; + private final Lazy deviceInfoService; + private final Lazy debugService; + private final Lazy rebootService; + + private LazyApplicationServices(Builder builder) { + this.authOutputPort = lazy(builder.authOutputPort, "authOutputPort"); + this.adminPasswordResetInputPort = lazy(builder.adminPasswordResetInputPort, + "adminPasswordResetInputPort"); + this.usersOutputPort = lazy(builder.usersOutputPort, "usersOutputPort"); + this.userCreateRequestInputPort = lazy(builder.userCreateRequestInputPort, + "userCreateRequestInputPort"); + this.changePasswordInputPort = lazy(builder.changePasswordInputPort, "changePasswordInputPort"); + this.removeUserInputPort = lazy(builder.removeUserInputPort, "removeUserInputPort"); + this.runtimeDataOutputPort = lazy(builder.runtimeDataOutputPort, "runtimeDataOutputPort"); + this.pixelProgramsOutputPort = lazy(builder.pixelProgramsOutputPort, "pixelProgramsOutputPort"); + this.runtimeDataInputPort = lazy(builder.runtimeDataInputPort, "runtimeDataInputPort"); + this.pixelProgramsInputPort = lazy(builder.pixelProgramsInputPort, "pixelProgramsInputPort"); + this.wifiConfigInputPort = lazy(builder.wifiConfigInputPort, "wifiConfigInputPort"); + this.deviceInfoOutputPort = lazy(builder.deviceInfoOutputPort, "deviceInfoOutputPort"); + this.logsOutputPort = lazy(builder.logsOutputPort, "logsOutputPort"); + this.logsDeleteInputPort = lazy(builder.logsDeleteInputPort, "logsDeleteInputPort"); + this.currentStateOutputPort = lazy(builder.currentStateOutputPort, "currentStateOutputPort"); + this.rebootInputPort = lazy(builder.rebootInputPort, "rebootInputPort"); + + this.loginService = Lazy.of(() -> new LoginServiceImpl(authOutputPort.get())); + this.adminPasswordService = Lazy.of(() -> new AdminPasswordServiceImpl(adminPasswordResetInputPort.get())); + this.userManagementService = Lazy.of(() -> new UserManagementServiceImpl(usersOutputPort.get(), + userCreateRequestInputPort.get(), changePasswordInputPort.get(), removeUserInputPort.get())); + this.runtimeDataService = Lazy.of(() -> new RuntimeDataServiceImpl(runtimeDataOutputPort.get(), + pixelProgramsOutputPort.get(), runtimeDataInputPort.get())); + this.pixelProgramService = Lazy.of(() -> new PixelProgramServiceImpl(pixelProgramsOutputPort.get(), + pixelProgramsInputPort.get())); + this.networkConfigurationService = Lazy.of(() -> new NetworkConfigurationServiceImpl(wifiConfigInputPort.get())); + this.deviceInfoService = Lazy.of(() -> new DeviceInfoServiceImpl(deviceInfoOutputPort.get())); + this.debugService = Lazy.of(() -> new DebugServiceImpl(logsOutputPort.get(), logsDeleteInputPort.get(), + currentStateOutputPort.get())); + this.rebootService = Lazy.of(() -> new RebootServiceImpl(rebootInputPort.get())); + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public LoginService loginService() { + return loginService.get(); + } + + @Override + public AdminPasswordService adminPasswordService() { + return adminPasswordService.get(); + } + + @Override + public UserManagementService userManagementService() { + return userManagementService.get(); + } + + @Override + public RuntimeDataService runtimeDataService() { + return runtimeDataService.get(); + } + + @Override + public PixelProgramService pixelProgramService() { + return pixelProgramService.get(); + } + + @Override + public NetworkConfigurationService networkConfigurationService() { + return networkConfigurationService.get(); + } + + @Override + public DeviceInfoService deviceInfoService() { + return deviceInfoService.get(); + } + + @Override + public DebugService debugService() { + return debugService.get(); + } + + @Override + public RebootService rebootService() { + return rebootService.get(); + } + + private static Lazy lazy(Supplier supplier, String name) { + return Lazy.of(Objects.requireNonNull(supplier, name + " supplier cannot be null")); + } + + public static final class Builder { + private Supplier> authOutputPort; + private Supplier> adminPasswordResetInputPort; + private Supplier>> usersOutputPort; + private Supplier> userCreateRequestInputPort; + private Supplier> changePasswordInputPort; + private Supplier> removeUserInputPort; + private Supplier> runtimeDataOutputPort; + private Supplier>> pixelProgramsOutputPort; + private Supplier> runtimeDataInputPort; + private Supplier>> pixelProgramsInputPort; + private Supplier> wifiConfigInputPort; + private Supplier> deviceInfoOutputPort; + private Supplier>> logsOutputPort; + private Supplier> logsDeleteInputPort; + private Supplier> currentStateOutputPort; + private Supplier> rebootInputPort; + + public Builder authOutputPort(Supplier> authOutputPort) { + this.authOutputPort = authOutputPort; + return this; + } + + public Builder adminPasswordResetInputPort( + Supplier> adminPasswordResetInputPort) { + this.adminPasswordResetInputPort = adminPasswordResetInputPort; + return this; + } + + public Builder usersOutputPort(Supplier>> usersOutputPort) { + this.usersOutputPort = usersOutputPort; + return this; + } + + public Builder userCreateRequestInputPort( + Supplier> userCreateRequestInputPort) { + this.userCreateRequestInputPort = userCreateRequestInputPort; + return this; + } + + public Builder changePasswordInputPort(Supplier> changePasswordInputPort) { + this.changePasswordInputPort = changePasswordInputPort; + return this; + } + + public Builder removeUserInputPort(Supplier> removeUserInputPort) { + this.removeUserInputPort = removeUserInputPort; + return this; + } + + public Builder runtimeDataOutputPort(Supplier> runtimeDataOutputPort) { + this.runtimeDataOutputPort = runtimeDataOutputPort; + return this; + } + + public Builder pixelProgramsOutputPort( + Supplier>> pixelProgramsOutputPort) { + this.pixelProgramsOutputPort = pixelProgramsOutputPort; + return this; + } + + public Builder runtimeDataInputPort(Supplier> runtimeDataInputPort) { + this.runtimeDataInputPort = runtimeDataInputPort; + return this; + } + + public Builder pixelProgramsInputPort( + Supplier>> pixelProgramsInputPort) { + this.pixelProgramsInputPort = pixelProgramsInputPort; + return this; + } + + public Builder wifiConfigInputPort(Supplier> wifiConfigInputPort) { + this.wifiConfigInputPort = wifiConfigInputPort; + return this; + } + + public Builder deviceInfoOutputPort(Supplier> deviceInfoOutputPort) { + this.deviceInfoOutputPort = deviceInfoOutputPort; + return this; + } + + public Builder logsOutputPort(Supplier>> logsOutputPort) { + this.logsOutputPort = logsOutputPort; + return this; + } + + public Builder logsDeleteInputPort(Supplier> logsDeleteInputPort) { + this.logsDeleteInputPort = logsDeleteInputPort; + return this; + } + + public Builder currentStateOutputPort(Supplier> currentStateOutputPort) { + this.currentStateOutputPort = currentStateOutputPort; + return this; + } + + public Builder rebootInputPort(Supplier> rebootInputPort) { + this.rebootInputPort = rebootInputPort; + return this; + } + + public ApplicationServices build() { + return new LazyApplicationServices(this); + } + } +} diff --git a/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java b/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java new file mode 100644 index 0000000..c0813c6 --- /dev/null +++ b/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java @@ -0,0 +1,95 @@ +package pl.vtt.wpi.core.application.context; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.UserCreateRequest; +import pl.vtt.wpi.core.domain.model.Credentials; +import pl.vtt.wpi.core.domain.model.User; +import pl.vtt.wpi.core.domain.model.device.CurrentState; +import pl.vtt.wpi.core.domain.model.device.DeviceInfo; +import pl.vtt.wpi.core.domain.model.device.PixelProgram; +import pl.vtt.wpi.core.domain.model.device.RuntimeData; +import pl.vtt.wpi.core.domain.model.device.WifiConfig; +import pl.vtt.wpi.core.domain.port.InputPort; +import pl.vtt.wpi.core.domain.port.OutputPort; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class LazyApplicationServicesTest { + + @Test + @DisplayName("Creates services and dependencies lazily") + void creates_services_and_dependencies_lazily() { + AtomicInteger authPortCreations = new AtomicInteger(); + ApplicationServices services = baseBuilder() + .authOutputPort(() -> { + authPortCreations.incrementAndGet(); + return outputPort(new Credentials("admin", "token")); + }) + .build(); + + assertEquals(0, authPortCreations.get()); + + assertSame(services.loginService(), services.loginService()); + assertEquals(1, authPortCreations.get()); + } + + @Test + @DisplayName("Does not resolve unrelated dependencies") + void does_not_resolve_unrelated_dependencies() { + AtomicInteger runtimeDataPortCreations = new AtomicInteger(); + ApplicationServices services = baseBuilder() + .runtimeDataOutputPort(() -> { + runtimeDataPortCreations.incrementAndGet(); + return outputPort(null); + }) + .build(); + + assertNotNull(services.loginService()); + + assertEquals(0, runtimeDataPortCreations.get()); + } + + @Test + @DisplayName("Fails fast when a required supplier is missing") + void fails_fast_when_required_supplier_is_missing() { + LazyApplicationServices.Builder builder = baseBuilder().authOutputPort(null); + + assertThrows(NullPointerException.class, builder::build); + } + + private static LazyApplicationServices.Builder baseBuilder() { + return LazyApplicationServices.builder() + .authOutputPort(() -> outputPort(new Credentials("admin", "token"))) + .adminPasswordResetInputPort(() -> inputPort()) + .usersOutputPort(() -> outputPort(List.of())) + .userCreateRequestInputPort(() -> inputPort()) + .changePasswordInputPort(() -> inputPort()) + .removeUserInputPort(() -> inputPort()) + .runtimeDataOutputPort(() -> outputPort(null)) + .pixelProgramsOutputPort(() -> outputPort(List.of())) + .runtimeDataInputPort(() -> inputPort()) + .pixelProgramsInputPort(() -> inputPort()) + .wifiConfigInputPort(() -> inputPort()) + .deviceInfoOutputPort(() -> outputPort(null)) + .logsOutputPort(() -> outputPort(List.of())) + .logsDeleteInputPort(() -> inputPort()) + .currentStateOutputPort(() -> outputPort(null)) + .rebootInputPort(() -> inputPort()); + } + + private static OutputPort outputPort(T value) { + return () -> value; + } + + private static InputPort inputPort() { + return ignored -> { }; + } +} From 2025b10b1b6eed022c9fa124551f9f2dd1515930 Mon Sep 17 00:00:00 2001 From: Kamil Jan Mularski Date: Fri, 8 May 2026 13:48:37 +0200 Subject: [PATCH 2/6] Move HTTP adapters out of domain --- README.md | 18 ++++++---- docs/architecture-proposal.md | 13 +++---- src/main/java/module-info.java | 5 ++- .../context/ApplicationServices.java | 3 -- .../wpi/core/application/context/Lazy.java | 34 +++++++------------ .../context/LazyApplicationServices.java | 12 ++----- .../service/AdminPasswordService.java | 2 +- .../service/UserManagementService.java | 2 +- .../impl/AdminPasswordServiceImpl.java | 4 +-- .../impl/UserManagementServiceImpl.java | 4 +-- .../adapter/http}/AuthOutputPort.java | 2 +- .../adapter/http}/CurrentStateOutputPort.java | 2 +- .../adapter/http}/DeviceInfoOutputPort.java | 2 +- .../adapter/http}/LogsDeleteInputPort.java | 2 +- .../adapter/http}/PixelProgramsInputPort.java | 2 +- .../http}/PixelProgramsOutputPort.java | 2 +- .../adapter/http}/RestartInputPort.java | 2 +- .../adapter/http}/RuntimeDataInputPort.java | 2 +- .../adapter/http}/RuntimeDataOutputPort.java | 2 +- .../adapter/http}/UserCreateInputPort.java | 4 +-- .../adapter/http}/UsersOutputPort.java | 2 +- .../adapter/http}/WifiConfigInputPort.java | 2 +- .../dto/AdminPasswordResetRequest.java | 2 +- .../dto/PasswordDto.java | 2 +- .../dto/UserCreateRequest.java | 2 +- .../context/LazyApplicationServicesTest.java | 6 ++-- .../impl/AdminPasswordServiceImplTest.java | 4 +-- .../impl/UserManagementServiceImplTest.java | 4 +-- .../core/domain/port/AuthOutputPortTest.java | 2 +- .../port/CurrentStateOutputPortTest.java | 2 +- .../domain/port/DeviceInfoOutputPortTest.java | 2 +- .../domain/port/LogsDeleteInputPortTest.java | 2 +- .../port/PixelProgramsInputPortTest.java | 2 +- .../port/PixelProgramsOutputPortTest.java | 2 +- .../domain/port/RestartInputPortTest.java | 2 +- .../domain/port/RuntimeDataInputPortTest.java | 2 +- .../port/RuntimeDataOutputPortTest.java | 2 +- .../domain/port/UserCreateInputPortTest.java | 6 ++-- .../core/domain/port/UsersOutputPortTest.java | 2 +- .../domain/port/WifiConfigInputPortTest.java | 2 +- 40 files changed, 79 insertions(+), 92 deletions(-) rename src/main/java/pl/vtt/wpi/core/{domain/port/output => infrastructure/adapter/http}/AuthOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{domain/port/output => infrastructure/adapter/http}/CurrentStateOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{domain/port/output => infrastructure/adapter/http}/DeviceInfoOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{domain/port/input => infrastructure/adapter/http}/LogsDeleteInputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{domain/port/input => infrastructure/adapter/http}/PixelProgramsInputPort.java (97%) rename src/main/java/pl/vtt/wpi/core/{domain/port/output => infrastructure/adapter/http}/PixelProgramsOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{domain/port/input => infrastructure/adapter/http}/RestartInputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{domain/port/input => infrastructure/adapter/http}/RuntimeDataInputPort.java (97%) rename src/main/java/pl/vtt/wpi/core/{domain/port/output => infrastructure/adapter/http}/RuntimeDataOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{domain/port/input => infrastructure/adapter/http}/UserCreateInputPort.java (92%) rename src/main/java/pl/vtt/wpi/core/{domain/port/output => infrastructure/adapter/http}/UsersOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{domain/port/input => infrastructure/adapter/http}/WifiConfigInputPort.java (96%) rename src/main/java/pl/vtt/wpi/core/{domain => infrastructure}/dto/AdminPasswordResetRequest.java (66%) rename src/main/java/pl/vtt/wpi/core/{domain => infrastructure}/dto/PasswordDto.java (81%) rename src/main/java/pl/vtt/wpi/core/{domain => infrastructure}/dto/UserCreateRequest.java (72%) diff --git a/README.md b/README.md index 69bcc23..c1da8d0 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,11 @@ pl.vtt.wpi.core │ ├── ... # Service interfaces (LoginService, RuntimeDataService, etc.) │ └── impl # Internal service implementations ├── domain -│ ├── dto │ ├── model -│ └── port # Input/Output port contracts + endpoint-specific port implementations +│ └── port # InputPort / OutputPort contracts └── infrastructure + ├── dto # Transport request DTOs + ├── adapter.http # HTTP implementations of input/output ports ├── Request / Response ├── RequestFactory / RequestHandler / RequestSender └── factory # SynchronizedRequestFactory @@ -132,14 +133,17 @@ The package `pl.vtt.wpi.core.application.service` currently exposes interfaces f Concrete implementations are provided in `pl.vtt.wpi.core.application.service.impl`. -### Domain Ports +### Ports and HTTP Adapters -The package `pl.vtt.wpi.core.domain.port` contains port contracts and endpoint-specific implementations: +The package `pl.vtt.wpi.core.domain.port` contains generic port contracts: -- **Output ports**: `AuthOutputPort`, `DeviceInfoOutputPort`, `RuntimeDataOutputPort`, `CurrentStateOutputPort`, `PixelProgramsOutputPort`, `UsersOutputPort` -- **Input ports**: `RuntimeDataInputPort`, `WifiConfigInputPort`, `PixelProgramsInputPort`, `UserCreateInputPort`, `RestartInputPort`, `LogsDeleteInputPort` +- **Port contracts**: `InputPort`, `OutputPort` -These ports encapsulate endpoint/method selection and exception mapping (`InputPortException` / `OutputPortException`), making application services thinner and easier to test. +Endpoint-specific HTTP implementations are provided by `pl.vtt.wpi.core.infrastructure.adapter.http`: + +- **HTTP port adapters**: `AuthOutputPort`, `DeviceInfoOutputPort`, `RuntimeDataOutputPort`, `CurrentStateOutputPort`, `PixelProgramsOutputPort`, `UsersOutputPort`, `RuntimeDataInputPort`, `WifiConfigInputPort`, `PixelProgramsInputPort`, `UserCreateInputPort`, `RestartInputPort`, `LogsDeleteInputPort` + +These adapters encapsulate endpoint/method selection and exception mapping (`InputPortException` / `OutputPortException`), making application services thinner and easier to test. ## Building & Testing diff --git a/docs/architecture-proposal.md b/docs/architecture-proposal.md index 5432163..21b678a 100644 --- a/docs/architecture-proposal.md +++ b/docs/architecture-proposal.md @@ -5,12 +5,12 @@ Projekt jest biblioteką warstwową z elementami architektury portów i adapterów: - `domain.model` przechowuje modele domenowe, np. użytkownika, poświadczenia i dane urządzenia. -- `domain.port` definiuje kontrakty `InputPort` i `OutputPort`, a podpakiety `input` oraz `output` zawierają porty specyficzne dla endpointów. +- `domain.port` definiuje kontrakty `InputPort` i `OutputPort`, a implementacje portów specyficzne dla endpointów znajdują się w `infrastructure.adapter.http`. - `application.service` definiuje przypadki użycia widoczne dla klienta biblioteki. - `application.service.impl` orkiestruje przypadki użycia i komunikuje się z portami przez konstruktory. - `infrastructure` definiuje model żądania/odpowiedzi oraz abstrakcje wysyłania i obsługi żądań. -To nie jest jeszcze ścisła Clean Architecture. Najważniejszy problem polega na tym, że pakiet `domain.port.input`/`domain.port.output` zawiera klasy portów, które znają infrastrukturę HTTP (`RequestFactory`, `RequestHandler`, `RequestSender`) i konkretne endpointy. W Clean Architecture domena powinna definiować abstrakcje wejścia/wyjścia, a adaptery infrastrukturalne powinny je implementować na zewnątrz warstwy domenowej. Obecnie część logiki adaptera znajduje się w `domain`. +To nadal nie jest jeszcze ścisła Clean Architecture. Implementacje portów HTTP znajdują się już poza domeną, ale warstwa aplikacyjna importuje transportowe DTO z `infrastructure.dto`, a `domain.model.endpoint` nadal przechowuje szczegóły endpointów i metod komunikacji. W Clean Architecture kierunek zależności powinien prowadzić do środka: aplikacja nie powinna importować infrastruktury, a szczegóły protokołu HTTP powinny pozostać w adapterach zewnętrznych. ## Implementacja leniwej inicjalizacji i DI przez konstruktor @@ -49,12 +49,13 @@ LoginService loginService = services.loginService(); ## Co zrobić, aby architekturę można było uznać za Clean Architecture -1. Przenieść klasy implementujące komunikację z endpointami z `domain.port.input` i `domain.port.output` do zewnętrznej warstwy adapterów, np. `infrastructure.adapter.http`. +1. Pozostawić implementacje komunikacji z endpointami w zewnętrznym pakiecie adapterów, np. `infrastructure.adapter.http`, i nie cofać ich do domeny. 2. Pozostawić w domenie wyłącznie stabilne modele i abstrakcyjne porty, bez zależności od `RequestFactory`, `RequestHandler`, `RequestSender`, URL-i ani metod HTTP. 3. Rozdzielić porty use-case od portów gateway. Interfejsy przypadków użycia mogą pozostać w `application.service`, natomiast gatewaye powinny wyrażać język domeny, np. `UserGateway`, `RuntimeDataGateway`, `PixelProgramGateway`. -4. Przenieść DTO transportowe zależne od API urządzenia poza domenę, jeśli reprezentują format komunikacji, a nie pojęcia domenowe. -5. Utrzymywać composition root na brzegu systemu. `LazyApplicationServices` może być wygodnym, lekkim composition rootem biblioteki, ale pełna aplikacja powinna konfigurować konkretne adaptery infrastrukturalne poza domeną i aplikacją. -6. Zachować regułę zależności: domena nie importuje aplikacji ani infrastruktury, aplikacja importuje domenę i porty abstrakcyjne, infrastruktura implementuje porty zdefiniowane wewnątrz. +4. Nie przeciekać transportowych DTO z `infrastructure.dto` do warstwy aplikacyjnej. Jeśli `PasswordDto`, `UserCreateRequest` albo `AdminPasswordResetRequest` są częścią kontraktu przypadków użycia, powinny zostać zastąpione modelami wejściowymi aplikacji, a adapter HTTP powinien mapować je na własne DTO transportowe. +5. Przenieść szczegóły endpointów, metod HTTP i URL-i z domeny do adapterów infrastrukturalnych albo do konfiguracji adaptera. +6. Utrzymywać composition root na brzegu systemu. `LazyApplicationServices` może być wygodnym, lekkim composition rootem biblioteki, ale pełna aplikacja powinna konfigurować konkretne adaptery infrastrukturalne poza domeną i aplikacją. +7. Zachować regułę zależności: domena nie importuje aplikacji ani infrastruktury, aplikacja importuje domenę i porty abstrakcyjne, infrastruktura implementuje porty zdefiniowane wewnątrz. ## Dlaczego DI przez konstruktor jest dobrym kierunkiem diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 36c5118..502bc0f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -2,15 +2,14 @@ exports pl.vtt.wpi.core.application.context; exports pl.vtt.wpi.core.application.exception; exports pl.vtt.wpi.core.application.service; - exports pl.vtt.wpi.core.domain.dto; exports pl.vtt.wpi.core.domain.port.exception; exports pl.vtt.wpi.core.domain.model; exports pl.vtt.wpi.core.domain.model.color; exports pl.vtt.wpi.core.domain.model.device; exports pl.vtt.wpi.core.domain.model.endpoint; exports pl.vtt.wpi.core.domain.port; - exports pl.vtt.wpi.core.domain.port.input; - exports pl.vtt.wpi.core.domain.port.output; exports pl.vtt.wpi.core.infrastructure; + exports pl.vtt.wpi.core.infrastructure.adapter.http; + exports pl.vtt.wpi.core.infrastructure.dto; exports pl.vtt.wpi.core.infrastructure.exception; } diff --git a/src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java b/src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java index 8118033..7a1162f 100644 --- a/src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java +++ b/src/main/java/pl/vtt/wpi/core/application/context/ApplicationServices.java @@ -10,9 +10,6 @@ import pl.vtt.wpi.core.application.service.RuntimeDataService; import pl.vtt.wpi.core.application.service.UserManagementService; -/** - * Composition-root facade for application use cases exposed by wpi-core. - */ public interface ApplicationServices { LoginService loginService(); diff --git a/src/main/java/pl/vtt/wpi/core/application/context/Lazy.java b/src/main/java/pl/vtt/wpi/core/application/context/Lazy.java index 0977ebe..66375a5 100644 --- a/src/main/java/pl/vtt/wpi/core/application/context/Lazy.java +++ b/src/main/java/pl/vtt/wpi/core/application/context/Lazy.java @@ -3,11 +3,6 @@ import java.util.Objects; import java.util.function.Supplier; -/** - * Thread-safe, memoizing supplier used by the application composition root. - * - * @param lazily initialized dependency type - */ public final class Lazy implements Supplier { private volatile Supplier initializer; private volatile T value; @@ -22,23 +17,20 @@ public static Lazy of(Supplier initializer) { @Override public T get() { - T current = value; - if (current == null) { - synchronized (this) { - current = value; - if (current == null) { - Supplier currentInitializer = initializer; - if (currentInitializer == null) { - current = value; - } else { - current = Objects.requireNonNull(currentInitializer.get(), - "initializer cannot return null"); - value = current; - initializer = null; - } - } + Supplier currentInitializer = initializer; + if (currentInitializer == null) { + return value; + } + synchronized (this) { + currentInitializer = initializer; + if (currentInitializer == null) { + return value; } + T current = Objects.requireNonNull(currentInitializer.get(), + "initializer cannot return null"); + value = current; + initializer = null; + return current; } - return current; } } diff --git a/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java b/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java index 6c863db..5ef62dc 100644 --- a/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java +++ b/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java @@ -21,9 +21,9 @@ import pl.vtt.wpi.core.application.service.impl.RebootServiceImpl; import pl.vtt.wpi.core.application.service.impl.RuntimeDataServiceImpl; import pl.vtt.wpi.core.application.service.impl.UserManagementServiceImpl; -import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; -import pl.vtt.wpi.core.domain.dto.PasswordDto; -import pl.vtt.wpi.core.domain.dto.UserCreateRequest; +import pl.vtt.wpi.core.infrastructure.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.model.Credentials; import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.model.device.CurrentState; @@ -34,12 +34,6 @@ import pl.vtt.wpi.core.domain.port.InputPort; import pl.vtt.wpi.core.domain.port.OutputPort; -/** - * Lazy composition root for the default application services. - * - *

Dependencies are registered as suppliers, resolved once on first use, and - * then passed to service implementations through their constructors.

- */ public final class LazyApplicationServices implements ApplicationServices { private final Lazy> authOutputPort; private final Lazy> adminPasswordResetInputPort; diff --git a/src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java b/src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java index 596e320..d7085bf 100644 --- a/src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java +++ b/src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java @@ -1,6 +1,6 @@ package pl.vtt.wpi.core.application.service; -import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; import pl.vtt.wpi.core.application.exception.AdminPasswordResetException; public interface AdminPasswordService { diff --git a/src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java b/src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java index 10c3d6e..830c683 100644 --- a/src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java +++ b/src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java @@ -4,7 +4,7 @@ import pl.vtt.wpi.core.application.exception.UserAlreadyExistsException; import pl.vtt.wpi.core.application.exception.UserNotExistsException; import pl.vtt.wpi.core.application.exception.UserManagementOperationException; -import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; import pl.vtt.wpi.core.domain.model.User; public interface UserManagementService { diff --git a/src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java b/src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java index 2c742c3..a3afb89 100644 --- a/src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java +++ b/src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java @@ -3,8 +3,8 @@ import java.util.Objects; import pl.vtt.wpi.core.application.exception.AdminPasswordResetException; import pl.vtt.wpi.core.application.service.AdminPasswordService; -import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; -import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; import pl.vtt.wpi.core.domain.port.InputPort; import pl.vtt.wpi.core.domain.port.exception.InputPortException; diff --git a/src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java b/src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java index 5ffc1de..10e84c6 100644 --- a/src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java +++ b/src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java @@ -9,8 +9,8 @@ import pl.vtt.wpi.core.application.exception.UserManagementOperationException; import pl.vtt.wpi.core.application.exception.UserNotExistsException; import pl.vtt.wpi.core.application.service.UserManagementService; -import pl.vtt.wpi.core.domain.dto.PasswordDto; -import pl.vtt.wpi.core.domain.dto.UserCreateRequest; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.port.InputPort; import pl.vtt.wpi.core.domain.port.OutputPort; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/output/AuthOutputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/AuthOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/domain/port/output/AuthOutputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/AuthOutputPort.java index fbf7db3..b15ccff 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/output/AuthOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/AuthOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.output; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestHandler; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/output/CurrentStateOutputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/CurrentStateOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/domain/port/output/CurrentStateOutputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/CurrentStateOutputPort.java index 10a4251..8d92886 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/output/CurrentStateOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/CurrentStateOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.output; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestHandler; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/output/DeviceInfoOutputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/DeviceInfoOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/domain/port/output/DeviceInfoOutputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/DeviceInfoOutputPort.java index 144482a..140a7e6 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/output/DeviceInfoOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/DeviceInfoOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.output; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestHandler; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/input/LogsDeleteInputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/LogsDeleteInputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/domain/port/input/LogsDeleteInputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/LogsDeleteInputPort.java index 60239da..c586755 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/input/LogsDeleteInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/LogsDeleteInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.input; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.domain.port.InputPort; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/input/PixelProgramsInputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsInputPort.java similarity index 97% rename from src/main/java/pl/vtt/wpi/core/domain/port/input/PixelProgramsInputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsInputPort.java index 458b564..18efc00 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/input/PixelProgramsInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.input; +package pl.vtt.wpi.core.infrastructure.adapter.http; import java.util.List; import pl.vtt.wpi.core.infrastructure.RequestFactory; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/output/PixelProgramsOutputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/domain/port/output/PixelProgramsOutputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsOutputPort.java index bd6d072..b714d37 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/output/PixelProgramsOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.output; +package pl.vtt.wpi.core.infrastructure.adapter.http; import java.util.List; import pl.vtt.wpi.core.infrastructure.RequestFactory; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/input/RestartInputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RestartInputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/domain/port/input/RestartInputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RestartInputPort.java index 6da2b22..ae7560b 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/input/RestartInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RestartInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.input; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.domain.port.InputPort; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/input/RuntimeDataInputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataInputPort.java similarity index 97% rename from src/main/java/pl/vtt/wpi/core/domain/port/input/RuntimeDataInputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataInputPort.java index 1d66150..e170d34 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/input/RuntimeDataInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.input; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.domain.port.InputPort; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/output/RuntimeDataOutputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/domain/port/output/RuntimeDataOutputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java index aa81a4e..7762b06 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/output/RuntimeDataOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.output; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestHandler; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UserCreateInputPort.java similarity index 92% rename from src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UserCreateInputPort.java index e0c185e..c967de2 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UserCreateInputPort.java @@ -1,8 +1,8 @@ -package pl.vtt.wpi.core.domain.port.input; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestSender; -import pl.vtt.wpi.core.domain.dto.UserCreateRequest; +import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.port.exception.InputPortException; import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.endpoint.Method; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/output/UsersOutputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UsersOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/domain/port/output/UsersOutputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UsersOutputPort.java index 263842d..6a941de 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/output/UsersOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UsersOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.output; +package pl.vtt.wpi.core.infrastructure.adapter.http; import java.util.List; import pl.vtt.wpi.core.infrastructure.RequestFactory; diff --git a/src/main/java/pl/vtt/wpi/core/domain/port/input/WifiConfigInputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/WifiConfigInputPort.java similarity index 96% rename from src/main/java/pl/vtt/wpi/core/domain/port/input/WifiConfigInputPort.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/WifiConfigInputPort.java index ae1266a..aa6bb93 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/port/input/WifiConfigInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/WifiConfigInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.port.input; +package pl.vtt.wpi.core.infrastructure.adapter.http; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.domain.port.InputPort; diff --git a/src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java b/src/main/java/pl/vtt/wpi/core/infrastructure/dto/AdminPasswordResetRequest.java similarity index 66% rename from src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/dto/AdminPasswordResetRequest.java index a4aef71..ca4c4f6 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/dto/AdminPasswordResetRequest.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.dto; +package pl.vtt.wpi.core.infrastructure.dto; public record AdminPasswordResetRequest(String secureKey, PasswordDto passwordDto) { } diff --git a/src/main/java/pl/vtt/wpi/core/domain/dto/PasswordDto.java b/src/main/java/pl/vtt/wpi/core/infrastructure/dto/PasswordDto.java similarity index 81% rename from src/main/java/pl/vtt/wpi/core/domain/dto/PasswordDto.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/dto/PasswordDto.java index 5ebeee1..6ffd593 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/dto/PasswordDto.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/dto/PasswordDto.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.dto; +package pl.vtt.wpi.core.infrastructure.dto; public record PasswordDto(String password, String passwordConfirmation) { @Override diff --git a/src/main/java/pl/vtt/wpi/core/domain/dto/UserCreateRequest.java b/src/main/java/pl/vtt/wpi/core/infrastructure/dto/UserCreateRequest.java similarity index 72% rename from src/main/java/pl/vtt/wpi/core/domain/dto/UserCreateRequest.java rename to src/main/java/pl/vtt/wpi/core/infrastructure/dto/UserCreateRequest.java index a2830da..2ad3480 100644 --- a/src/main/java/pl/vtt/wpi/core/domain/dto/UserCreateRequest.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/dto/UserCreateRequest.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.domain.dto; +package pl.vtt.wpi.core.infrastructure.dto; import pl.vtt.wpi.core.domain.model.User; diff --git a/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java b/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java index c0813c6..7d7a6cc 100644 --- a/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java +++ b/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java @@ -4,9 +4,9 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; -import pl.vtt.wpi.core.domain.dto.PasswordDto; -import pl.vtt.wpi.core.domain.dto.UserCreateRequest; +import pl.vtt.wpi.core.infrastructure.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.model.Credentials; import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.model.device.CurrentState; diff --git a/src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java b/src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java index d35dd8e..5a487f1 100644 --- a/src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java +++ b/src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java @@ -3,8 +3,8 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; import pl.vtt.wpi.core.application.exception.AdminPasswordResetException; -import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; -import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; import pl.vtt.wpi.core.domain.port.InputPort; import pl.vtt.wpi.core.domain.port.exception.InputPortException; diff --git a/src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java b/src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java index f7cd50e..dd4abb7 100644 --- a/src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java +++ b/src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java @@ -9,8 +9,8 @@ import pl.vtt.wpi.core.application.exception.InvalidPasswordException; import pl.vtt.wpi.core.application.exception.UserAlreadyExistsException; import pl.vtt.wpi.core.application.exception.UserManagementOperationException; -import pl.vtt.wpi.core.domain.dto.PasswordDto; -import pl.vtt.wpi.core.domain.dto.UserCreateRequest; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.model.endpoint.UserGroup; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java index 8e84b5c..e84a0fe 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java @@ -7,7 +7,7 @@ import pl.vtt.wpi.core.domain.model.Credentials; import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.output.AuthOutputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.AuthOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java index 93288d0..7115795 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java @@ -6,7 +6,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.CurrentState; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.output.CurrentStateOutputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.CurrentStateOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java index 3d7b405..0f8e224 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java @@ -8,7 +8,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.DeviceInfo; import pl.vtt.wpi.core.domain.model.endpoint.RequestTarget; -import pl.vtt.wpi.core.domain.port.output.DeviceInfoOutputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.DeviceInfoOutputPort; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java index 7efe5d3..41a9587 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java @@ -6,7 +6,7 @@ import pl.vtt.wpi.core.infrastructure.RequestSender; import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.input.LogsDeleteInputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.LogsDeleteInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java index 550f9f4..f317115 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java @@ -10,7 +10,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.PixelProgram; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.input.PixelProgramsInputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.PixelProgramsInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java index 3115bbf..7b376e2 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java @@ -7,7 +7,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.PixelProgram; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.output.PixelProgramsOutputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.PixelProgramsOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java index ed0afa3..68bb431 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java @@ -8,7 +8,7 @@ import pl.vtt.wpi.core.domain.port.exception.InputPortException; import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.input.RestartInputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.RestartInputPort; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java index a5ea6ca..8baf266 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java @@ -9,7 +9,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.RuntimeData; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.input.RuntimeDataInputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.RuntimeDataInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java index e26774d..5cca3f1 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java @@ -6,7 +6,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.RuntimeData; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.output.RuntimeDataOutputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.RuntimeDataOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java index f87f27c..8632257 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java @@ -3,8 +3,8 @@ import java.util.EnumSet; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; -import pl.vtt.wpi.core.domain.dto.PasswordDto; -import pl.vtt.wpi.core.domain.dto.UserCreateRequest; +import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestSender; import pl.vtt.wpi.core.domain.port.exception.InputPortException; @@ -13,7 +13,7 @@ import pl.vtt.wpi.core.domain.model.endpoint.Method; import pl.vtt.wpi.core.domain.model.endpoint.RequestTarget; import pl.vtt.wpi.core.domain.model.endpoint.UserGroup; -import pl.vtt.wpi.core.domain.port.input.UserCreateInputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.UserCreateInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java index 1d89acb..18cf1c8 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java @@ -9,7 +9,7 @@ import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.model.endpoint.Method; import pl.vtt.wpi.core.domain.model.endpoint.UserGroup; -import pl.vtt.wpi.core.domain.port.output.UsersOutputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.UsersOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java index d8293a7..3dd01ed 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java @@ -8,7 +8,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.WifiConfig; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.input.WifiConfigInputPort; +import pl.vtt.wpi.core.infrastructure.adapter.http.WifiConfigInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; From 8e5e98eed79b434d526fbc10a868b962c9df43f4 Mon Sep 17 00:00:00 2001 From: Kamil Jan Mularski Date: Fri, 8 May 2026 17:43:32 +0200 Subject: [PATCH 3/6] Preserve runtime data interrupt status --- .../adapter/http/RuntimeDataOutputPort.java | 3 +++ .../port/RuntimeDataOutputPortTest.java | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java index 7762b06..ff76a36 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java @@ -23,6 +23,9 @@ public RuntimeData load() throws OutputPortException { try { return requestHandler.handle(requestFactory.create(null, Method.GET, RequestTarget.DATA_READ)); } catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } throw new OutputPortException("Cannot load runtime data", e); } } diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java index 5cca3f1..a846eb0 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java @@ -6,9 +6,12 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.RuntimeData; import pl.vtt.wpi.core.domain.model.endpoint.Method; +import pl.vtt.wpi.core.domain.port.exception.OutputPortException; import pl.vtt.wpi.core.infrastructure.adapter.http.RuntimeDataOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class RuntimeDataOutputPortTest { @Test @@ -23,4 +26,23 @@ void load_usesGetMethod() throws Exception { assertEquals(10, port.load().nol()); } + + @Test + void load_preservesInterruptStatusWhenInterrupted() { + Thread.interrupted(); + RequestFactory requestFactory = (payload, method, target, _) -> + new Request<>(null, method, target.url("http://localhost"), null, payload); + RequestHandler requestHandler = _ -> { + throw new InterruptedException("interrupted"); + }; + RuntimeDataOutputPort port = new RuntimeDataOutputPort(requestFactory, requestHandler); + + try { + OutputPortException exception = assertThrows(OutputPortException.class, port::load); + assertEquals("interrupted", exception.getCause().getMessage()); + assertTrue(Thread.currentThread().isInterrupted()); + } finally { + Thread.interrupted(); + } + } } From 8f7f4a889d21233ae15826450209016e344fcf01 Mon Sep 17 00:00:00 2001 From: Kamil Jan Mularski Date: Wed, 13 May 2026 16:15:30 +0200 Subject: [PATCH 4/6] Update architecture proposal --- docs/architecture-proposal.md | 82 +++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/docs/architecture-proposal.md b/docs/architecture-proposal.md index 21b678a..0b3628f 100644 --- a/docs/architecture-proposal.md +++ b/docs/architecture-proposal.md @@ -1,28 +1,31 @@ -# Propozycja: leniwa inicjalizacja, DI przez konstruktor i kierunek Clean Architecture +# Architektura: stan obecny i kierunek Clean Architecture -## Aktualna architektura +## Stan obecny -Projekt jest biblioteką warstwową z elementami architektury portów i adapterów: +Projekt jest biblioteką warstwową z elementami Ports & Adapters: -- `domain.model` przechowuje modele domenowe, np. użytkownika, poświadczenia i dane urządzenia. -- `domain.port` definiuje kontrakty `InputPort` i `OutputPort`, a implementacje portów specyficzne dla endpointów znajdują się w `infrastructure.adapter.http`. -- `application.service` definiuje przypadki użycia widoczne dla klienta biblioteki. -- `application.service.impl` orkiestruje przypadki użycia i komunikuje się z portami przez konstruktory. -- `infrastructure` definiuje model żądania/odpowiedzi oraz abstrakcje wysyłania i obsługi żądań. +- `domain.model` zawiera modele domenowe, np. użytkownika, poświadczenia, kolory i dane urządzenia. +- `domain.port` zawiera abstrakcyjne kontrakty `InputPort` i `OutputPort` oraz wyjątki portów. +- `application.service` zawiera publiczne interfejsy przypadków użycia. +- `application.service.impl` zawiera implementacje przypadków użycia, które orkiestrują logikę aplikacyjną i przyjmują zależności przez konstruktory. +- `application.context` zawiera lekki composition root: `ApplicationServices`, `Lazy` i `LazyApplicationServices`. +- `infrastructure` zawiera model żądania/odpowiedzi, fabrykę żądań, abstrakcje wysyłania/obsługi żądań, DTO transportowe oraz adaptery HTTP. +- `infrastructure.adapter.http` zawiera endpoint-specific implementacje portów wejściowych i wyjściowych. +- `infrastructure.dto` zawiera transportowe rekordy żądań, np. `PasswordDto`, `UserCreateRequest` i `AdminPasswordResetRequest`. -To nadal nie jest jeszcze ścisła Clean Architecture. Implementacje portów HTTP znajdują się już poza domeną, ale warstwa aplikacyjna importuje transportowe DTO z `infrastructure.dto`, a `domain.model.endpoint` nadal przechowuje szczegóły endpointów i metod komunikacji. W Clean Architecture kierunek zależności powinien prowadzić do środka: aplikacja nie powinna importować infrastruktury, a szczegóły protokołu HTTP powinny pozostać w adapterach zewnętrznych. +Aktualny układ jest bliższy Clean Architecture niż wcześniejszy wariant, ponieważ implementacje portów HTTP i DTO transportowe zostały przeniesione poza domenę. Nie jest to jednak jeszcze ścisła Clean Architecture, ponieważ warstwa aplikacyjna nadal importuje DTO z `infrastructure.dto`, a `domain.model.endpoint` nadal zawiera szczegóły komunikacji, takie jak zasoby, metody HTTP i cele żądań. -## Implementacja leniwej inicjalizacji i DI przez konstruktor +## Leniwa inicjalizacja i DI przez konstruktor -Dodany został prosty composition root w `application.context`: +`LazyApplicationServices` pełni rolę composition root biblioteki: -1. `ApplicationServices` jest fasadą udostępniającą interfejsy przypadków użycia. -2. `LazyApplicationServices` przyjmuje dostawców portów w builderze. -3. Każdy dostawca portu jest opakowany w memoizujący `Lazy`. -4. Konkretne serwisy są tworzone dopiero przy pierwszym wywołaniu odpowiedniej metody fasady. -5. Gdy serwis jest tworzony, zależności są przekazywane przez jego konstruktor, więc same implementacje usług pozostają niezależne od kontenera. +1. Builder przyjmuje dostawców portów (`Supplier>` / `Supplier>`). +2. Dostawcy są opakowywani w memoizujący `Lazy`. +3. Konkretne serwisy aplikacyjne są tworzone dopiero przy pierwszym wywołaniu odpowiedniej metody z `ApplicationServices`. +4. Po utworzeniu instancja serwisu jest reużywana. +5. Zależności nadal są przekazywane do implementacji serwisów przez konstruktory, więc same serwisy nie zależą od kontenera ani od mechanizmu lazy loading. -Przykład użycia po stronie aplikacji-klienta: +Przykład użycia: ```java ApplicationServices services = LazyApplicationServices.builder() @@ -47,16 +50,41 @@ ApplicationServices services = LazyApplicationServices.builder() LoginService loginService = services.loginService(); ``` -## Co zrobić, aby architekturę można było uznać za Clean Architecture +## Dlaczego to nadal nie jest pełna Clean Architecture -1. Pozostawić implementacje komunikacji z endpointami w zewnętrznym pakiecie adapterów, np. `infrastructure.adapter.http`, i nie cofać ich do domeny. -2. Pozostawić w domenie wyłącznie stabilne modele i abstrakcyjne porty, bez zależności od `RequestFactory`, `RequestHandler`, `RequestSender`, URL-i ani metod HTTP. -3. Rozdzielić porty use-case od portów gateway. Interfejsy przypadków użycia mogą pozostać w `application.service`, natomiast gatewaye powinny wyrażać język domeny, np. `UserGateway`, `RuntimeDataGateway`, `PixelProgramGateway`. -4. Nie przeciekać transportowych DTO z `infrastructure.dto` do warstwy aplikacyjnej. Jeśli `PasswordDto`, `UserCreateRequest` albo `AdminPasswordResetRequest` są częścią kontraktu przypadków użycia, powinny zostać zastąpione modelami wejściowymi aplikacji, a adapter HTTP powinien mapować je na własne DTO transportowe. -5. Przenieść szczegóły endpointów, metod HTTP i URL-i z domeny do adapterów infrastrukturalnych albo do konfiguracji adaptera. -6. Utrzymywać composition root na brzegu systemu. `LazyApplicationServices` może być wygodnym, lekkim composition rootem biblioteki, ale pełna aplikacja powinna konfigurować konkretne adaptery infrastrukturalne poza domeną i aplikacją. -7. Zachować regułę zależności: domena nie importuje aplikacji ani infrastruktury, aplikacja importuje domenę i porty abstrakcyjne, infrastruktura implementuje porty zdefiniowane wewnątrz. +Najważniejsze pozostałe naruszenia kierunku zależności: -## Dlaczego DI przez konstruktor jest dobrym kierunkiem +1. `application.service` i `application.service.impl` używają typów z `infrastructure.dto`. Warstwa aplikacyjna nie powinna importować infrastruktury. +2. `domain.model.endpoint` opisuje endpointy HTTP, zasoby, metody i reguły dostępu do request targetów. To jest szczegół komunikacji zewnętrznej, a nie czysty model domenowy. +3. Generyczne `InputPort` i `OutputPort` są poprawnym krokiem separacji, ale nie opisują języka biznesowego tak dobrze jak dedykowane gatewaye, np. `UserGateway`, `RuntimeDataGateway` albo `PixelProgramGateway`. +4. `application.context` tworzy konkretne implementacje usług, więc powinien pozostać composition rootem na brzegu modułu, a nie zależnością używaną wewnątrz domeny lub logiki aplikacyjnej. -DI przez konstruktor sprawia, że zależności są jawne, obiekt nie może istnieć bez wymaganych portów, testy mogą przekazywać fałszywe implementacje, a implementacje usług nie muszą znać żadnego kontenera. Leniwy composition root uzupełnia ten model o opóźnienie kosztu inicjalizacji do momentu faktycznego użycia danej usługi lub portu. +## Kroki do uzyskania Clean Architecture + +1. Wprowadzić modele wejściowe przypadków użycia w warstwie aplikacyjnej albo domenowej i usunąć importy `infrastructure.dto` z `application.service` oraz `application.service.impl`. +2. Zostawić DTO transportowe wyłącznie w `infrastructure.dto`; adaptery HTTP powinny mapować modele aplikacyjne/domenowe na DTO transportowe i odwrotnie. +3. Przenieść szczegóły endpointów (`RequestTarget`, `Resource`, HTTP `Method` używane jako protokół transportowy) z domeny do infrastruktury lub do konfiguracji adapterów HTTP. +4. Zastąpić część generycznych portów dedykowanymi gatewayami opisującymi intencje domenowe, np. odczyt użytkowników, zapis danych runtime albo aktualizację programów pikseli. +5. Utrzymywać regułę zależności: domena nie importuje aplikacji ani infrastruktury; aplikacja importuje domenę i abstrakcje; infrastruktura implementuje porty i zależy od warstw wewnętrznych. +6. Traktować `LazyApplicationServices` jako wygodny composition root biblioteki. Aplikacja wyższego poziomu może użyć własnego kontenera DI, o ile nadal wstrzykuje zależności przez konstruktory. + +## Rekomendowany docelowy podział pakietów + +```text +pl.vtt.wpi.core +├── domain +│ ├── model +│ └── gateway # dedykowane porty/gatewaye w języku domeny +├── application +│ ├── command/query # modele wejściowe przypadków użycia, jeśli nie są domenowe +│ ├── service # interfejsy przypadków użycia +│ └── service.impl # implementacje przypadków użycia +└── infrastructure + ├── adapter.http # implementacje gatewayów/portów przez HTTP + ├── dto # DTO transportowe + └── factory # fabryki requestów i konfiguracja adapterów +``` + +## Podsumowanie + +Obecna architektura jest warstwowa i korzysta z elementów Ports & Adapters. Po przeniesieniu adapterów HTTP i DTO do infrastruktury domena jest czystsza, ale pełna Clean Architecture wymaga jeszcze usunięcia zależności aplikacji od infrastruktury oraz wyniesienia szczegółów HTTP z domeny. From fb412c1700977a307811d26d99ce59402f003abc Mon Sep 17 00:00:00 2001 From: Kamil Jan Mularski Date: Fri, 15 May 2026 11:31:05 +0200 Subject: [PATCH 5/6] Keep only lazy DI implementation changes --- README.md | 18 ++-- docs/architecture-proposal.md | 90 ------------------- src/main/java/module-info.java | 5 +- .../context/LazyApplicationServices.java | 6 +- .../service/AdminPasswordService.java | 2 +- .../service/UserManagementService.java | 2 +- .../impl/AdminPasswordServiceImpl.java | 4 +- .../impl/UserManagementServiceImpl.java | 4 +- .../dto/AdminPasswordResetRequest.java | 2 +- .../dto/PasswordDto.java | 2 +- .../dto/UserCreateRequest.java | 2 +- .../port/input}/LogsDeleteInputPort.java | 2 +- .../port/input}/PixelProgramsInputPort.java | 2 +- .../port/input}/RestartInputPort.java | 2 +- .../port/input}/RuntimeDataInputPort.java | 2 +- .../port/input}/UserCreateInputPort.java | 4 +- .../port/input}/WifiConfigInputPort.java | 2 +- .../port/output}/AuthOutputPort.java | 2 +- .../port/output}/CurrentStateOutputPort.java | 2 +- .../port/output}/DeviceInfoOutputPort.java | 2 +- .../port/output}/PixelProgramsOutputPort.java | 2 +- .../port/output}/RuntimeDataOutputPort.java | 5 +- .../port/output}/UsersOutputPort.java | 2 +- .../context/LazyApplicationServicesTest.java | 6 +- .../impl/AdminPasswordServiceImplTest.java | 4 +- .../impl/UserManagementServiceImplTest.java | 4 +- .../core/domain/port/AuthOutputPortTest.java | 2 +- .../port/CurrentStateOutputPortTest.java | 2 +- .../domain/port/DeviceInfoOutputPortTest.java | 2 +- .../domain/port/LogsDeleteInputPortTest.java | 2 +- .../port/PixelProgramsInputPortTest.java | 2 +- .../port/PixelProgramsOutputPortTest.java | 2 +- .../domain/port/RestartInputPortTest.java | 2 +- .../domain/port/RuntimeDataInputPortTest.java | 2 +- .../port/RuntimeDataOutputPortTest.java | 24 +---- .../domain/port/UserCreateInputPortTest.java | 6 +- .../core/domain/port/UsersOutputPortTest.java | 2 +- .../domain/port/WifiConfigInputPortTest.java | 2 +- 38 files changed, 56 insertions(+), 174 deletions(-) delete mode 100644 docs/architecture-proposal.md rename src/main/java/pl/vtt/wpi/core/{infrastructure => domain}/dto/AdminPasswordResetRequest.java (66%) rename src/main/java/pl/vtt/wpi/core/{infrastructure => domain}/dto/PasswordDto.java (81%) rename src/main/java/pl/vtt/wpi/core/{infrastructure => domain}/dto/UserCreateRequest.java (72%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/input}/LogsDeleteInputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/input}/PixelProgramsInputPort.java (97%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/input}/RestartInputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/input}/RuntimeDataInputPort.java (97%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/input}/UserCreateInputPort.java (92%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/input}/WifiConfigInputPort.java (96%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/output}/AuthOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/output}/CurrentStateOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/output}/DeviceInfoOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/output}/PixelProgramsOutputPort.java (95%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/output}/RuntimeDataOutputPort.java (87%) rename src/main/java/pl/vtt/wpi/core/{infrastructure/adapter/http => domain/port/output}/UsersOutputPort.java (95%) diff --git a/README.md b/README.md index c1da8d0..69bcc23 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,10 @@ pl.vtt.wpi.core │ ├── ... # Service interfaces (LoginService, RuntimeDataService, etc.) │ └── impl # Internal service implementations ├── domain +│ ├── dto │ ├── model -│ └── port # InputPort / OutputPort contracts +│ └── port # Input/Output port contracts + endpoint-specific port implementations └── infrastructure - ├── dto # Transport request DTOs - ├── adapter.http # HTTP implementations of input/output ports ├── Request / Response ├── RequestFactory / RequestHandler / RequestSender └── factory # SynchronizedRequestFactory @@ -133,17 +132,14 @@ The package `pl.vtt.wpi.core.application.service` currently exposes interfaces f Concrete implementations are provided in `pl.vtt.wpi.core.application.service.impl`. -### Ports and HTTP Adapters +### Domain Ports -The package `pl.vtt.wpi.core.domain.port` contains generic port contracts: +The package `pl.vtt.wpi.core.domain.port` contains port contracts and endpoint-specific implementations: -- **Port contracts**: `InputPort`, `OutputPort` +- **Output ports**: `AuthOutputPort`, `DeviceInfoOutputPort`, `RuntimeDataOutputPort`, `CurrentStateOutputPort`, `PixelProgramsOutputPort`, `UsersOutputPort` +- **Input ports**: `RuntimeDataInputPort`, `WifiConfigInputPort`, `PixelProgramsInputPort`, `UserCreateInputPort`, `RestartInputPort`, `LogsDeleteInputPort` -Endpoint-specific HTTP implementations are provided by `pl.vtt.wpi.core.infrastructure.adapter.http`: - -- **HTTP port adapters**: `AuthOutputPort`, `DeviceInfoOutputPort`, `RuntimeDataOutputPort`, `CurrentStateOutputPort`, `PixelProgramsOutputPort`, `UsersOutputPort`, `RuntimeDataInputPort`, `WifiConfigInputPort`, `PixelProgramsInputPort`, `UserCreateInputPort`, `RestartInputPort`, `LogsDeleteInputPort` - -These adapters encapsulate endpoint/method selection and exception mapping (`InputPortException` / `OutputPortException`), making application services thinner and easier to test. +These ports encapsulate endpoint/method selection and exception mapping (`InputPortException` / `OutputPortException`), making application services thinner and easier to test. ## Building & Testing diff --git a/docs/architecture-proposal.md b/docs/architecture-proposal.md deleted file mode 100644 index 0b3628f..0000000 --- a/docs/architecture-proposal.md +++ /dev/null @@ -1,90 +0,0 @@ -# Architektura: stan obecny i kierunek Clean Architecture - -## Stan obecny - -Projekt jest biblioteką warstwową z elementami Ports & Adapters: - -- `domain.model` zawiera modele domenowe, np. użytkownika, poświadczenia, kolory i dane urządzenia. -- `domain.port` zawiera abstrakcyjne kontrakty `InputPort` i `OutputPort` oraz wyjątki portów. -- `application.service` zawiera publiczne interfejsy przypadków użycia. -- `application.service.impl` zawiera implementacje przypadków użycia, które orkiestrują logikę aplikacyjną i przyjmują zależności przez konstruktory. -- `application.context` zawiera lekki composition root: `ApplicationServices`, `Lazy` i `LazyApplicationServices`. -- `infrastructure` zawiera model żądania/odpowiedzi, fabrykę żądań, abstrakcje wysyłania/obsługi żądań, DTO transportowe oraz adaptery HTTP. -- `infrastructure.adapter.http` zawiera endpoint-specific implementacje portów wejściowych i wyjściowych. -- `infrastructure.dto` zawiera transportowe rekordy żądań, np. `PasswordDto`, `UserCreateRequest` i `AdminPasswordResetRequest`. - -Aktualny układ jest bliższy Clean Architecture niż wcześniejszy wariant, ponieważ implementacje portów HTTP i DTO transportowe zostały przeniesione poza domenę. Nie jest to jednak jeszcze ścisła Clean Architecture, ponieważ warstwa aplikacyjna nadal importuje DTO z `infrastructure.dto`, a `domain.model.endpoint` nadal zawiera szczegóły komunikacji, takie jak zasoby, metody HTTP i cele żądań. - -## Leniwa inicjalizacja i DI przez konstruktor - -`LazyApplicationServices` pełni rolę composition root biblioteki: - -1. Builder przyjmuje dostawców portów (`Supplier>` / `Supplier>`). -2. Dostawcy są opakowywani w memoizujący `Lazy`. -3. Konkretne serwisy aplikacyjne są tworzone dopiero przy pierwszym wywołaniu odpowiedniej metody z `ApplicationServices`. -4. Po utworzeniu instancja serwisu jest reużywana. -5. Zależności nadal są przekazywane do implementacji serwisów przez konstruktory, więc same serwisy nie zależą od kontenera ani od mechanizmu lazy loading. - -Przykład użycia: - -```java -ApplicationServices services = LazyApplicationServices.builder() - .authOutputPort(() -> new AuthOutputPort(requestFactory, authHandler)) - .adminPasswordResetInputPort(() -> adminPasswordResetPort) - .usersOutputPort(() -> usersOutputPort) - .userCreateRequestInputPort(() -> userCreateRequestInputPort) - .changePasswordInputPort(() -> changePasswordInputPort) - .removeUserInputPort(() -> removeUserInputPort) - .runtimeDataOutputPort(() -> runtimeDataOutputPort) - .pixelProgramsOutputPort(() -> pixelProgramsOutputPort) - .runtimeDataInputPort(() -> runtimeDataInputPort) - .pixelProgramsInputPort(() -> pixelProgramsInputPort) - .wifiConfigInputPort(() -> wifiConfigInputPort) - .deviceInfoOutputPort(() -> deviceInfoOutputPort) - .logsOutputPort(() -> logsOutputPort) - .logsDeleteInputPort(() -> logsDeleteInputPort) - .currentStateOutputPort(() -> currentStateOutputPort) - .rebootInputPort(() -> rebootInputPort) - .build(); - -LoginService loginService = services.loginService(); -``` - -## Dlaczego to nadal nie jest pełna Clean Architecture - -Najważniejsze pozostałe naruszenia kierunku zależności: - -1. `application.service` i `application.service.impl` używają typów z `infrastructure.dto`. Warstwa aplikacyjna nie powinna importować infrastruktury. -2. `domain.model.endpoint` opisuje endpointy HTTP, zasoby, metody i reguły dostępu do request targetów. To jest szczegół komunikacji zewnętrznej, a nie czysty model domenowy. -3. Generyczne `InputPort` i `OutputPort` są poprawnym krokiem separacji, ale nie opisują języka biznesowego tak dobrze jak dedykowane gatewaye, np. `UserGateway`, `RuntimeDataGateway` albo `PixelProgramGateway`. -4. `application.context` tworzy konkretne implementacje usług, więc powinien pozostać composition rootem na brzegu modułu, a nie zależnością używaną wewnątrz domeny lub logiki aplikacyjnej. - -## Kroki do uzyskania Clean Architecture - -1. Wprowadzić modele wejściowe przypadków użycia w warstwie aplikacyjnej albo domenowej i usunąć importy `infrastructure.dto` z `application.service` oraz `application.service.impl`. -2. Zostawić DTO transportowe wyłącznie w `infrastructure.dto`; adaptery HTTP powinny mapować modele aplikacyjne/domenowe na DTO transportowe i odwrotnie. -3. Przenieść szczegóły endpointów (`RequestTarget`, `Resource`, HTTP `Method` używane jako protokół transportowy) z domeny do infrastruktury lub do konfiguracji adapterów HTTP. -4. Zastąpić część generycznych portów dedykowanymi gatewayami opisującymi intencje domenowe, np. odczyt użytkowników, zapis danych runtime albo aktualizację programów pikseli. -5. Utrzymywać regułę zależności: domena nie importuje aplikacji ani infrastruktury; aplikacja importuje domenę i abstrakcje; infrastruktura implementuje porty i zależy od warstw wewnętrznych. -6. Traktować `LazyApplicationServices` jako wygodny composition root biblioteki. Aplikacja wyższego poziomu może użyć własnego kontenera DI, o ile nadal wstrzykuje zależności przez konstruktory. - -## Rekomendowany docelowy podział pakietów - -```text -pl.vtt.wpi.core -├── domain -│ ├── model -│ └── gateway # dedykowane porty/gatewaye w języku domeny -├── application -│ ├── command/query # modele wejściowe przypadków użycia, jeśli nie są domenowe -│ ├── service # interfejsy przypadków użycia -│ └── service.impl # implementacje przypadków użycia -└── infrastructure - ├── adapter.http # implementacje gatewayów/portów przez HTTP - ├── dto # DTO transportowe - └── factory # fabryki requestów i konfiguracja adapterów -``` - -## Podsumowanie - -Obecna architektura jest warstwowa i korzysta z elementów Ports & Adapters. Po przeniesieniu adapterów HTTP i DTO do infrastruktury domena jest czystsza, ale pełna Clean Architecture wymaga jeszcze usunięcia zależności aplikacji od infrastruktury oraz wyniesienia szczegółów HTTP z domeny. diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 502bc0f..36c5118 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -2,14 +2,15 @@ exports pl.vtt.wpi.core.application.context; exports pl.vtt.wpi.core.application.exception; exports pl.vtt.wpi.core.application.service; + exports pl.vtt.wpi.core.domain.dto; exports pl.vtt.wpi.core.domain.port.exception; exports pl.vtt.wpi.core.domain.model; exports pl.vtt.wpi.core.domain.model.color; exports pl.vtt.wpi.core.domain.model.device; exports pl.vtt.wpi.core.domain.model.endpoint; exports pl.vtt.wpi.core.domain.port; + exports pl.vtt.wpi.core.domain.port.input; + exports pl.vtt.wpi.core.domain.port.output; exports pl.vtt.wpi.core.infrastructure; - exports pl.vtt.wpi.core.infrastructure.adapter.http; - exports pl.vtt.wpi.core.infrastructure.dto; exports pl.vtt.wpi.core.infrastructure.exception; } diff --git a/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java b/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java index 5ef62dc..a1fa61f 100644 --- a/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java +++ b/src/main/java/pl/vtt/wpi/core/application/context/LazyApplicationServices.java @@ -21,9 +21,9 @@ import pl.vtt.wpi.core.application.service.impl.RebootServiceImpl; import pl.vtt.wpi.core.application.service.impl.RuntimeDataServiceImpl; import pl.vtt.wpi.core.application.service.impl.UserManagementServiceImpl; -import pl.vtt.wpi.core.infrastructure.dto.AdminPasswordResetRequest; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; -import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; +import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.model.Credentials; import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.model.device.CurrentState; diff --git a/src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java b/src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java index d7085bf..596e320 100644 --- a/src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java +++ b/src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java @@ -1,6 +1,6 @@ package pl.vtt.wpi.core.application.service; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.PasswordDto; import pl.vtt.wpi.core.application.exception.AdminPasswordResetException; public interface AdminPasswordService { diff --git a/src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java b/src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java index 830c683..10c3d6e 100644 --- a/src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java +++ b/src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java @@ -4,7 +4,7 @@ import pl.vtt.wpi.core.application.exception.UserAlreadyExistsException; import pl.vtt.wpi.core.application.exception.UserNotExistsException; import pl.vtt.wpi.core.application.exception.UserManagementOperationException; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.PasswordDto; import pl.vtt.wpi.core.domain.model.User; public interface UserManagementService { diff --git a/src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java b/src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java index a3afb89..2c742c3 100644 --- a/src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java +++ b/src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java @@ -3,8 +3,8 @@ import java.util.Objects; import pl.vtt.wpi.core.application.exception.AdminPasswordResetException; import pl.vtt.wpi.core.application.service.AdminPasswordService; -import pl.vtt.wpi.core.infrastructure.dto.AdminPasswordResetRequest; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; import pl.vtt.wpi.core.domain.port.InputPort; import pl.vtt.wpi.core.domain.port.exception.InputPortException; diff --git a/src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java b/src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java index 10e84c6..5ffc1de 100644 --- a/src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java +++ b/src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java @@ -9,8 +9,8 @@ import pl.vtt.wpi.core.application.exception.UserManagementOperationException; import pl.vtt.wpi.core.application.exception.UserNotExistsException; import pl.vtt.wpi.core.application.service.UserManagementService; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; -import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.port.InputPort; import pl.vtt.wpi.core.domain.port.OutputPort; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/dto/AdminPasswordResetRequest.java b/src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java similarity index 66% rename from src/main/java/pl/vtt/wpi/core/infrastructure/dto/AdminPasswordResetRequest.java rename to src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java index ca4c4f6..a4aef71 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/dto/AdminPasswordResetRequest.java +++ b/src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.dto; +package pl.vtt.wpi.core.domain.dto; public record AdminPasswordResetRequest(String secureKey, PasswordDto passwordDto) { } diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/dto/PasswordDto.java b/src/main/java/pl/vtt/wpi/core/domain/dto/PasswordDto.java similarity index 81% rename from src/main/java/pl/vtt/wpi/core/infrastructure/dto/PasswordDto.java rename to src/main/java/pl/vtt/wpi/core/domain/dto/PasswordDto.java index 6ffd593..5ebeee1 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/dto/PasswordDto.java +++ b/src/main/java/pl/vtt/wpi/core/domain/dto/PasswordDto.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.dto; +package pl.vtt.wpi.core.domain.dto; public record PasswordDto(String password, String passwordConfirmation) { @Override diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/dto/UserCreateRequest.java b/src/main/java/pl/vtt/wpi/core/domain/dto/UserCreateRequest.java similarity index 72% rename from src/main/java/pl/vtt/wpi/core/infrastructure/dto/UserCreateRequest.java rename to src/main/java/pl/vtt/wpi/core/domain/dto/UserCreateRequest.java index 2ad3480..a2830da 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/dto/UserCreateRequest.java +++ b/src/main/java/pl/vtt/wpi/core/domain/dto/UserCreateRequest.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.dto; +package pl.vtt.wpi.core.domain.dto; import pl.vtt.wpi.core.domain.model.User; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/LogsDeleteInputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/input/LogsDeleteInputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/LogsDeleteInputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/input/LogsDeleteInputPort.java index c586755..60239da 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/LogsDeleteInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/input/LogsDeleteInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.input; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.domain.port.InputPort; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsInputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/input/PixelProgramsInputPort.java similarity index 97% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsInputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/input/PixelProgramsInputPort.java index 18efc00..458b564 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/input/PixelProgramsInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.input; import java.util.List; import pl.vtt.wpi.core.infrastructure.RequestFactory; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RestartInputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/input/RestartInputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RestartInputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/input/RestartInputPort.java index ae7560b..6da2b22 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RestartInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/input/RestartInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.input; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.domain.port.InputPort; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataInputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/input/RuntimeDataInputPort.java similarity index 97% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataInputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/input/RuntimeDataInputPort.java index e170d34..1d66150 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/input/RuntimeDataInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.input; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.domain.port.InputPort; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UserCreateInputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java similarity index 92% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UserCreateInputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java index c967de2..e0c185e 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UserCreateInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java @@ -1,8 +1,8 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.input; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestSender; -import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; +import pl.vtt.wpi.core.domain.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.port.exception.InputPortException; import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.endpoint.Method; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/WifiConfigInputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/input/WifiConfigInputPort.java similarity index 96% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/WifiConfigInputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/input/WifiConfigInputPort.java index aa6bb93..ae1266a 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/WifiConfigInputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/input/WifiConfigInputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.input; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.domain.port.InputPort; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/AuthOutputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/output/AuthOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/AuthOutputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/output/AuthOutputPort.java index b15ccff..fbf7db3 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/AuthOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/output/AuthOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.output; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestHandler; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/CurrentStateOutputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/output/CurrentStateOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/CurrentStateOutputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/output/CurrentStateOutputPort.java index 8d92886..10a4251 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/CurrentStateOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/output/CurrentStateOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.output; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestHandler; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/DeviceInfoOutputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/output/DeviceInfoOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/DeviceInfoOutputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/output/DeviceInfoOutputPort.java index 140a7e6..144482a 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/DeviceInfoOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/output/DeviceInfoOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.output; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestHandler; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsOutputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/output/PixelProgramsOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsOutputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/output/PixelProgramsOutputPort.java index b714d37..bd6d072 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/PixelProgramsOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/output/PixelProgramsOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.output; import java.util.List; import pl.vtt.wpi.core.infrastructure.RequestFactory; diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/output/RuntimeDataOutputPort.java similarity index 87% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/output/RuntimeDataOutputPort.java index ff76a36..aa81a4e 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/RuntimeDataOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/output/RuntimeDataOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.output; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestHandler; @@ -23,9 +23,6 @@ public RuntimeData load() throws OutputPortException { try { return requestHandler.handle(requestFactory.create(null, Method.GET, RequestTarget.DATA_READ)); } catch (Exception e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } throw new OutputPortException("Cannot load runtime data", e); } } diff --git a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UsersOutputPort.java b/src/main/java/pl/vtt/wpi/core/domain/port/output/UsersOutputPort.java similarity index 95% rename from src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UsersOutputPort.java rename to src/main/java/pl/vtt/wpi/core/domain/port/output/UsersOutputPort.java index 6a941de..263842d 100644 --- a/src/main/java/pl/vtt/wpi/core/infrastructure/adapter/http/UsersOutputPort.java +++ b/src/main/java/pl/vtt/wpi/core/domain/port/output/UsersOutputPort.java @@ -1,4 +1,4 @@ -package pl.vtt.wpi.core.infrastructure.adapter.http; +package pl.vtt.wpi.core.domain.port.output; import java.util.List; import pl.vtt.wpi.core.infrastructure.RequestFactory; diff --git a/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java b/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java index 7d7a6cc..c0813c6 100644 --- a/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java +++ b/src/test/java/pl/vtt/wpi/core/application/context/LazyApplicationServicesTest.java @@ -4,9 +4,9 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import pl.vtt.wpi.core.infrastructure.dto.AdminPasswordResetRequest; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; -import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; +import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.model.Credentials; import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.model.device.CurrentState; diff --git a/src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java b/src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java index 5a487f1..d35dd8e 100644 --- a/src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java +++ b/src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java @@ -3,8 +3,8 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; import pl.vtt.wpi.core.application.exception.AdminPasswordResetException; -import pl.vtt.wpi.core.infrastructure.dto.AdminPasswordResetRequest; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.AdminPasswordResetRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; import pl.vtt.wpi.core.domain.port.InputPort; import pl.vtt.wpi.core.domain.port.exception.InputPortException; diff --git a/src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java b/src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java index dd4abb7..f7cd50e 100644 --- a/src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java +++ b/src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java @@ -9,8 +9,8 @@ import pl.vtt.wpi.core.application.exception.InvalidPasswordException; import pl.vtt.wpi.core.application.exception.UserAlreadyExistsException; import pl.vtt.wpi.core.application.exception.UserManagementOperationException; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; -import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.UserCreateRequest; import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.model.endpoint.UserGroup; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java index e84a0fe..8e84b5c 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/AuthOutputPortTest.java @@ -7,7 +7,7 @@ import pl.vtt.wpi.core.domain.model.Credentials; import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.infrastructure.adapter.http.AuthOutputPort; +import pl.vtt.wpi.core.domain.port.output.AuthOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java index 7115795..93288d0 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/CurrentStateOutputPortTest.java @@ -6,7 +6,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.CurrentState; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.infrastructure.adapter.http.CurrentStateOutputPort; +import pl.vtt.wpi.core.domain.port.output.CurrentStateOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java index 0f8e224..3d7b405 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/DeviceInfoOutputPortTest.java @@ -8,7 +8,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.DeviceInfo; import pl.vtt.wpi.core.domain.model.endpoint.RequestTarget; -import pl.vtt.wpi.core.infrastructure.adapter.http.DeviceInfoOutputPort; +import pl.vtt.wpi.core.domain.port.output.DeviceInfoOutputPort; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java index 41a9587..7efe5d3 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/LogsDeleteInputPortTest.java @@ -6,7 +6,7 @@ import pl.vtt.wpi.core.infrastructure.RequestSender; import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.infrastructure.adapter.http.LogsDeleteInputPort; +import pl.vtt.wpi.core.domain.port.input.LogsDeleteInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java index f317115..550f9f4 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsInputPortTest.java @@ -10,7 +10,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.PixelProgram; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.infrastructure.adapter.http.PixelProgramsInputPort; +import pl.vtt.wpi.core.domain.port.input.PixelProgramsInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java index 7b376e2..3115bbf 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/PixelProgramsOutputPortTest.java @@ -7,7 +7,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.PixelProgram; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.infrastructure.adapter.http.PixelProgramsOutputPort; +import pl.vtt.wpi.core.domain.port.output.PixelProgramsOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java index 68bb431..ed0afa3 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/RestartInputPortTest.java @@ -8,7 +8,7 @@ import pl.vtt.wpi.core.domain.port.exception.InputPortException; import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.infrastructure.adapter.http.RestartInputPort; +import pl.vtt.wpi.core.domain.port.input.RestartInputPort; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java index 8baf266..a5ea6ca 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataInputPortTest.java @@ -9,7 +9,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.RuntimeData; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.infrastructure.adapter.http.RuntimeDataInputPort; +import pl.vtt.wpi.core.domain.port.input.RuntimeDataInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java index a846eb0..e26774d 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/RuntimeDataOutputPortTest.java @@ -6,12 +6,9 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.RuntimeData; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.domain.port.exception.OutputPortException; -import pl.vtt.wpi.core.infrastructure.adapter.http.RuntimeDataOutputPort; +import pl.vtt.wpi.core.domain.port.output.RuntimeDataOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; class RuntimeDataOutputPortTest { @Test @@ -26,23 +23,4 @@ void load_usesGetMethod() throws Exception { assertEquals(10, port.load().nol()); } - - @Test - void load_preservesInterruptStatusWhenInterrupted() { - Thread.interrupted(); - RequestFactory requestFactory = (payload, method, target, _) -> - new Request<>(null, method, target.url("http://localhost"), null, payload); - RequestHandler requestHandler = _ -> { - throw new InterruptedException("interrupted"); - }; - RuntimeDataOutputPort port = new RuntimeDataOutputPort(requestFactory, requestHandler); - - try { - OutputPortException exception = assertThrows(OutputPortException.class, port::load); - assertEquals("interrupted", exception.getCause().getMessage()); - assertTrue(Thread.currentThread().isInterrupted()); - } finally { - Thread.interrupted(); - } - } } diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java index 8632257..f87f27c 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java @@ -3,8 +3,8 @@ import java.util.EnumSet; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; -import pl.vtt.wpi.core.infrastructure.dto.PasswordDto; -import pl.vtt.wpi.core.infrastructure.dto.UserCreateRequest; +import pl.vtt.wpi.core.domain.dto.PasswordDto; +import pl.vtt.wpi.core.domain.dto.UserCreateRequest; import pl.vtt.wpi.core.infrastructure.RequestFactory; import pl.vtt.wpi.core.infrastructure.RequestSender; import pl.vtt.wpi.core.domain.port.exception.InputPortException; @@ -13,7 +13,7 @@ import pl.vtt.wpi.core.domain.model.endpoint.Method; import pl.vtt.wpi.core.domain.model.endpoint.RequestTarget; import pl.vtt.wpi.core.domain.model.endpoint.UserGroup; -import pl.vtt.wpi.core.infrastructure.adapter.http.UserCreateInputPort; +import pl.vtt.wpi.core.domain.port.input.UserCreateInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java index 18cf1c8..1d89acb 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/UsersOutputPortTest.java @@ -9,7 +9,7 @@ import pl.vtt.wpi.core.domain.model.User; import pl.vtt.wpi.core.domain.model.endpoint.Method; import pl.vtt.wpi.core.domain.model.endpoint.UserGroup; -import pl.vtt.wpi.core.infrastructure.adapter.http.UsersOutputPort; +import pl.vtt.wpi.core.domain.port.output.UsersOutputPort; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java b/src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java index 3dd01ed..d8293a7 100644 --- a/src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java +++ b/src/test/java/pl/vtt/wpi/core/domain/port/WifiConfigInputPortTest.java @@ -8,7 +8,7 @@ import pl.vtt.wpi.core.infrastructure.Request; import pl.vtt.wpi.core.domain.model.device.WifiConfig; import pl.vtt.wpi.core.domain.model.endpoint.Method; -import pl.vtt.wpi.core.infrastructure.adapter.http.WifiConfigInputPort; +import pl.vtt.wpi.core.domain.port.input.WifiConfigInputPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; From c8d1eb36614568fb8bad91ac93452c8995bb3c9b Mon Sep 17 00:00:00 2001 From: Kamil Jan Mularski Date: Fri, 15 May 2026 12:27:50 +0200 Subject: [PATCH 6/6] Expand README architecture description --- README.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 69bcc23..a067797 100644 --- a/README.md +++ b/README.md @@ -48,26 +48,40 @@ Then reference it in your project's `pom.xml`: ## Architecture -The library follows a layered architecture: +The library follows a layered architecture with constructor-based dependency injection. The public service interfaces describe application use cases, service implementations orchestrate those use cases, domain ports isolate request execution, and infrastructure abstractions model low-level request handling. ```text pl.vtt.wpi.core ├── application │ ├── config # AuthorizationHolder (thread-local auth state) +│ ├── context # ApplicationServices facade + lazy composition root │ ├── exception # Application-level exceptions │ └── service │ ├── ... # Service interfaces (LoginService, RuntimeDataService, etc.) -│ └── impl # Internal service implementations +│ └── impl # Service implementations wired through constructors ├── domain -│ ├── dto -│ ├── model -│ └── port # Input/Output port contracts + endpoint-specific port implementations +│ ├── dto # Request DTOs used by application services and ports +│ ├── model # Domain values and device state models +│ └── port # Input/Output contracts + endpoint-specific port implementations └── infrastructure ├── Request / Response ├── RequestFactory / RequestHandler / RequestSender └── factory # SynchronizedRequestFactory ``` +### Layer responsibilities + +| Layer | Responsibility | +|---|---| +| `application.service` | Stable use-case API exposed to library consumers. | +| `application.service.impl` | Use-case orchestration, validation, exception mapping, and constructor-injected dependencies. | +| `application.context` | Optional composition helper that lazily creates default service implementations from supplied ports. | +| `domain.model` / `domain.dto` | Shared domain values, device data records, credentials, users, and request DTOs. | +| `domain.port` | Generic `InputPort` / `OutputPort` contracts and endpoint-specific port implementations. | +| `infrastructure` | Request/response abstractions and request factory/sender/handler contracts used by ports. | + +Application services do not create their own ports. Instead, callers either instantiate service implementations directly with constructor injection or use `LazyApplicationServices` as a small composition root. `LazyApplicationServices` accepts `Supplier` instances for all required ports, resolves each supplier only when a dependent service is first requested, and then reuses the created service instance. + ## Key Concepts ### Authentication @@ -116,7 +130,7 @@ Request request = new Request<>( ); ``` -### Application Services +### Application Services and lazy composition The package `pl.vtt.wpi.core.application.service` currently exposes interfaces for: @@ -130,7 +144,40 @@ The package `pl.vtt.wpi.core.application.service` currently exposes interfaces f - `DebugService` - `RebootService` -Concrete implementations are provided in `pl.vtt.wpi.core.application.service.impl`. +Concrete implementations are provided in `pl.vtt.wpi.core.application.service.impl`. They use constructor injection, so tests and applications can provide fake or real `InputPort` / `OutputPort` implementations explicitly. + +For applications that want a single access point, `pl.vtt.wpi.core.application.context` provides: + +- `ApplicationServices` — a facade exposing getters for all service interfaces. +- `LazyApplicationServices` — a builder-based implementation that creates service implementations on first use. +- `Lazy` — a thread-safe memoizing supplier used internally by the composition root. + +Example composition: + +```java +ApplicationServices services = LazyApplicationServices.builder() + .authOutputPort(() -> authOutputPort) + .adminPasswordResetInputPort(() -> adminPasswordResetInputPort) + .usersOutputPort(() -> usersOutputPort) + .userCreateRequestInputPort(() -> userCreateRequestInputPort) + .changePasswordInputPort(() -> changePasswordInputPort) + .removeUserInputPort(() -> removeUserInputPort) + .runtimeDataOutputPort(() -> runtimeDataOutputPort) + .pixelProgramsOutputPort(() -> pixelProgramsOutputPort) + .runtimeDataInputPort(() -> runtimeDataInputPort) + .pixelProgramsInputPort(() -> pixelProgramsInputPort) + .wifiConfigInputPort(() -> wifiConfigInputPort) + .deviceInfoOutputPort(() -> deviceInfoOutputPort) + .logsOutputPort(() -> logsOutputPort) + .logsDeleteInputPort(() -> logsDeleteInputPort) + .currentStateOutputPort(() -> currentStateOutputPort) + .rebootInputPort(() -> rebootInputPort) + .build(); + +RuntimeDataService runtimeDataService = services.runtimeDataService(); +``` + +Only `runtimeDataService`, `runtimeDataOutputPort`, `pixelProgramsOutputPort`, and `runtimeDataInputPort` are resolved by the final line in this example; unrelated ports remain uninitialized until another service is requested. ### Domain Ports