diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-compose-4.0.0-commonMain-QYyzSQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-compose-4.0.0-commonMain-QYyzSQ.klib new file mode 100644 index 00000000..233c148a Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-compose-4.0.0-commonMain-QYyzSQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-compose-viewmodel-4.0.0-commonMain-Lzy-yA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-compose-viewmodel-4.0.0-commonMain-Lzy-yA.klib new file mode 100644 index 00000000..4bf9569d Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-compose-viewmodel-4.0.0-commonMain-Lzy-yA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-core-4.0.0-commonMain-CvvNiA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-core-4.0.0-commonMain-CvvNiA.klib new file mode 100644 index 00000000..f936634a Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-core-4.0.0-commonMain-CvvNiA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-core-viewmodel-4.0.0-commonMain-H79fAg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-core-viewmodel-4.0.0-commonMain-H79fAg.klib new file mode 100644 index 00000000..55dfd804 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.insert-koin-koin-core-viewmodel-4.0.0-commonMain-H79fAg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.core-core-bundle-1.0.0-commonMain-cb_PMQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.core-core-bundle-1.0.0-commonMain-cb_PMQ.klib new file mode 100644 index 00000000..6e820291 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.core-core-bundle-1.0.0-commonMain-cb_PMQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.core-core-bundle-1.0.0-jbMain-cb_PMQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.core-core-bundle-1.0.0-jbMain-cb_PMQ.klib new file mode 100644 index 00000000..5172f08c Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.core-core-bundle-1.0.0-jbMain-cb_PMQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-compose-2.8.0-commonMain-207ecg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-compose-2.8.0-commonMain-207ecg.klib new file mode 100644 index 00000000..b8bb10e8 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-compose-2.8.0-commonMain-207ecg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-compose-2.8.0-jbMain-207ecg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-compose-2.8.0-jbMain-207ecg.klib new file mode 100644 index 00000000..48437ca1 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-compose-2.8.0-jbMain-207ecg.klib differ diff --git a/Makefile b/Makefile index 2561a6ba..3ffad532 100644 --- a/Makefile +++ b/Makefile @@ -38,19 +38,19 @@ help: ## Show this help message dev-up: ## Start development environment (single compose) @echo "🚀 Starting development environment..." - $(COMPOSE) -f docker-compose.yml up -d + $(COMPOSE) -f docker/docker-compose.yml up -d @$(MAKE) dev-info dev-down: ## Stop development environment @echo "🛑 Stopping development environment..." - $(COMPOSE) -f docker-compose.yml down + $(COMPOSE) -f docker/docker-compose.yml down dev-restart: ## Restart full development environment @$(MAKE) dev-down @$(MAKE) dev-up dev-logs: ## Show logs for all development services - $(COMPOSE) -f docker-compose.yml logs -f + $(COMPOSE) -f docker/docker-compose.yml logs -f # =================================================================== # Layer-specific Commands @@ -58,7 +58,7 @@ dev-logs: ## Show logs for all development services infrastructure-up: ## Start only infrastructure services (postgres, redis, keycloak, consul) @echo "đŸ—ïž Starting infrastructure services..." - $(COMPOSE) -f docker-compose.yml up -d + $(COMPOSE) -f docker/docker-compose.yml up -d @echo "✅ Infrastructure services started" @echo "đŸ—„ïž PostgresQL: localhost:5432" @echo "🔮 Redis: localhost:6379" @@ -66,14 +66,14 @@ infrastructure-up: ## Start only infrastructure services (postgres, redis, keycl @echo "🧭 Consul: http://localhost:8500" infrastructure-down: ## Stop infrastructure services - $(COMPOSE) -f docker-compose.yml down + $(COMPOSE) -f docker/docker-compose.yml down infrastructure-logs: ## Show infrastructure logs - $(COMPOSE) -f docker-compose.yml logs -f + $(COMPOSE) -f docker/docker-compose.yml logs -f services-up: ## Start application services (infrastructure + microservices) @echo "⚙ Starting application services..." - $(COMPOSE) -f docker-compose.yml -f docker-compose.services.yml up -d + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.services.yml up -d @echo "✅ Application services started" @echo "🔗 Gateway: http://localhost:8081" @echo "🏓 Ping Service: http://localhost:8082" @@ -83,32 +83,32 @@ services-up: ## Start application services (infrastructure + microservices) @echo "📊 Master Service: http://localhost:8086" services-down: ## Stop application services - $(COMPOSE) -f docker-compose.yml -f docker-compose.services.yml down + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.services.yml down services-restart: ## Restart application services @$(MAKE) services-down @$(MAKE) services-up services-logs: ## Show application services logs - $(COMPOSE) -f docker-compose.yml -f docker-compose.services.yml logs -f + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.services.yml logs -f clients-up: ## Start client applications (infrastructure + clients) @echo "đŸ’» Starting client applications..." - $(COMPOSE) -f docker-compose.yml -f docker-compose.clients.yml up -d + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.clients.yml up -d @echo "✅ Client applications started" @echo "🌐 Web App: http://localhost:4000" @echo "🔐 Auth Server: http://localhost:8087" @echo "📈 Monitoring: http://localhost:8088" clients-down: ## Stop client applications - $(COMPOSE) -f docker-compose.yml -f docker-compose.clients.yml down + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.clients.yml down clients-restart: ## Restart client applications @$(MAKE) clients-down @$(MAKE) clients-up clients-logs: ## Show client application logs - $(COMPOSE) -f docker-compose.yml -f docker-compose.clients.yml logs -f + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.clients.yml logs -f # =================================================================== # Full System Commands @@ -116,7 +116,7 @@ clients-logs: ## Show client application logs full-up: ## Start complete system (infrastructure + services + clients) @echo "🚀 Starting complete Meldestelle system..." - $(COMPOSE) -f docker-compose.yml -f docker-compose.services.yml -f docker-compose.clients.yml up -d + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.services.yml -f docker/docker-compose.clients.yml up -d @echo "✅ Complete system started" @echo "" @echo "🌐 Frontend & APIs:" @@ -141,14 +141,14 @@ full-up: ## Start complete system (infrastructure + services + clients) @echo " Monitoring: http://localhost:8088" full-down: ## Stop complete system - $(COMPOSE) -f docker-compose.yml -f docker-compose.services.yml -f docker-compose.clients.yml down + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.services.yml -f docker/docker-compose.clients.yml down full-restart: ## Restart complete system @$(MAKE) full-down @$(MAKE) full-up full-logs: ## Show all system logs - $(COMPOSE) -f docker-compose.yml -f docker-compose.services.yml -f docker-compose.clients.yml logs -f + $(COMPOSE) -f docker/docker-compose.yml -f docker/docker-compose.services.yml -f docker/docker-compose.clients.yml logs -f # =================================================================== # SSoT Developer UX (optional helpers) diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 00000000..1e834ba3 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,7 @@ +# Backend + +DomĂ€nenspezifische Services und Gateway. + +- gateway: API Gateway/Auth/Routing +- discovery: Service Registry/Discovery (optional) +- services: Microservices, pro DomĂ€ne ein Service diff --git a/infrastructure/gateway/build.gradle.kts b/backend/gateway/build.gradle.kts similarity index 100% rename from infrastructure/gateway/build.gradle.kts rename to backend/gateway/build.gradle.kts diff --git a/infrastructure/gateway/docs/index.html b/backend/gateway/docs/index.html similarity index 100% rename from infrastructure/gateway/docs/index.html rename to backend/gateway/docs/index.html diff --git a/infrastructure/gateway/CONFIGURATION.md b/backend/gateway/gateway/CONFIGURATION.md similarity index 100% rename from infrastructure/gateway/CONFIGURATION.md rename to backend/gateway/gateway/CONFIGURATION.md diff --git a/infrastructure/gateway/README-INFRA-GATEWAY.md b/backend/gateway/gateway/README-INFRA-GATEWAY.md similarity index 100% rename from infrastructure/gateway/README-INFRA-GATEWAY.md rename to backend/gateway/gateway/README-INFRA-GATEWAY.md diff --git a/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/GatewayApplication.kt b/backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/GatewayApplication.kt similarity index 100% rename from infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/GatewayApplication.kt rename to backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/GatewayApplication.kt diff --git a/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/GatewayConfig.kt b/backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/GatewayConfig.kt similarity index 100% rename from infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/GatewayConfig.kt rename to backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/GatewayConfig.kt diff --git a/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/controller/FallbackController.kt b/backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/controller/FallbackController.kt similarity index 100% rename from infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/controller/FallbackController.kt rename to backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/controller/FallbackController.kt diff --git a/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/health/GatewayHealthIndicator.kt b/backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/health/GatewayHealthIndicator.kt similarity index 100% rename from infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/health/GatewayHealthIndicator.kt rename to backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/health/GatewayHealthIndicator.kt diff --git a/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/metrics/GatewayMetricsConfig.kt b/backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/metrics/GatewayMetricsConfig.kt similarity index 100% rename from infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/metrics/GatewayMetricsConfig.kt rename to backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/metrics/GatewayMetricsConfig.kt diff --git a/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/security/SecurityConfig.kt b/backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/security/SecurityConfig.kt similarity index 100% rename from infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/security/SecurityConfig.kt rename to backend/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/security/SecurityConfig.kt diff --git a/infrastructure/gateway/src/main/resources/application-keycloak.yml b/backend/gateway/src/main/resources/application-keycloak.yml similarity index 100% rename from infrastructure/gateway/src/main/resources/application-keycloak.yml rename to backend/gateway/src/main/resources/application-keycloak.yml diff --git a/infrastructure/gateway/src/main/resources/application.conf b/backend/gateway/src/main/resources/application.conf similarity index 100% rename from infrastructure/gateway/src/main/resources/application.conf rename to backend/gateway/src/main/resources/application.conf diff --git a/infrastructure/gateway/src/main/resources/application.yml b/backend/gateway/src/main/resources/application.yml similarity index 100% rename from infrastructure/gateway/src/main/resources/application.yml rename to backend/gateway/src/main/resources/application.yml diff --git a/infrastructure/gateway/src/main/resources/logback-spring.xml b/backend/gateway/src/main/resources/logback-spring.xml similarity index 100% rename from infrastructure/gateway/src/main/resources/logback-spring.xml rename to backend/gateway/src/main/resources/logback-spring.xml diff --git a/infrastructure/gateway/src/main/resources/logback.xml b/backend/gateway/src/main/resources/logback.xml similarity index 100% rename from infrastructure/gateway/src/main/resources/logback.xml rename to backend/gateway/src/main/resources/logback.xml diff --git a/infrastructure/gateway/src/main/resources/openapi/documentation.yaml b/backend/gateway/src/main/resources/openapi/documentation.yaml similarity index 100% rename from infrastructure/gateway/src/main/resources/openapi/documentation.yaml rename to backend/gateway/src/main/resources/openapi/documentation.yaml diff --git a/infrastructure/gateway/src/main/resources/static/docs/index.html b/backend/gateway/src/main/resources/static/docs/index.html similarity index 100% rename from infrastructure/gateway/src/main/resources/static/docs/index.html rename to backend/gateway/src/main/resources/static/docs/index.html diff --git a/infrastructure/gateway/src/main/resources/static/docs/postman/Meldestelle_API_Collection.json b/backend/gateway/src/main/resources/static/docs/postman/Meldestelle_API_Collection.json similarity index 100% rename from infrastructure/gateway/src/main/resources/static/docs/postman/Meldestelle_API_Collection.json rename to backend/gateway/src/main/resources/static/docs/postman/Meldestelle_API_Collection.json diff --git a/infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/FallbackControllerTests.kt b/backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/FallbackControllerTests.kt similarity index 100% rename from infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/FallbackControllerTests.kt rename to backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/FallbackControllerTests.kt diff --git a/infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayApplicationTests.kt b/backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayApplicationTests.kt similarity index 100% rename from infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayApplicationTests.kt rename to backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayApplicationTests.kt diff --git a/infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayFiltersTests.kt b/backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayFiltersTests.kt similarity index 100% rename from infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayFiltersTests.kt rename to backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayFiltersTests.kt diff --git a/infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayRoutingTests.kt b/backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayRoutingTests.kt similarity index 100% rename from infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayRoutingTests.kt rename to backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewayRoutingTests.kt diff --git a/infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewaySecurityTests.kt b/backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewaySecurityTests.kt similarity index 100% rename from infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewaySecurityTests.kt rename to backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/GatewaySecurityTests.kt diff --git a/infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/KeycloakGatewayIntegrationTest.kt b/backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/KeycloakGatewayIntegrationTest.kt similarity index 100% rename from infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/KeycloakGatewayIntegrationTest.kt rename to backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/KeycloakGatewayIntegrationTest.kt diff --git a/infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/config/TestSecurityConfig.kt b/backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/config/TestSecurityConfig.kt similarity index 100% rename from infrastructure/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/config/TestSecurityConfig.kt rename to backend/gateway/src/test/kotlin/at/mocode/infrastructure/gateway/config/TestSecurityConfig.kt diff --git a/infrastructure/gateway/src/test/resources/application-dev.yml b/backend/gateway/src/test/resources/application-dev.yml similarity index 100% rename from infrastructure/gateway/src/test/resources/application-dev.yml rename to backend/gateway/src/test/resources/application-dev.yml diff --git a/infrastructure/gateway/src/test/resources/application-keycloak-integration-test.yml b/backend/gateway/src/test/resources/application-keycloak-integration-test.yml similarity index 100% rename from infrastructure/gateway/src/test/resources/application-keycloak-integration-test.yml rename to backend/gateway/src/test/resources/application-keycloak-integration-test.yml diff --git a/infrastructure/gateway/src/test/resources/application-test.yml b/backend/gateway/src/test/resources/application-test.yml similarity index 100% rename from infrastructure/gateway/src/test/resources/application-test.yml rename to backend/gateway/src/test/resources/application-test.yml diff --git a/infrastructure/gateway/src/test/resources/logback-test.xml b/backend/gateway/src/test/resources/logback-test.xml similarity index 100% rename from infrastructure/gateway/src/test/resources/logback-test.xml rename to backend/gateway/src/test/resources/logback-test.xml diff --git a/infrastructure/gateway/src/test/resources/test-init-keycloak-schema.sql b/backend/gateway/src/test/resources/test-init-keycloak-schema.sql similarity index 100% rename from infrastructure/gateway/src/test/resources/test-init-keycloak-schema.sql rename to backend/gateway/src/test/resources/test-init-keycloak-schema.sql diff --git a/services/ping/ping-api/build.gradle.kts b/backend/services/ping/ping-api/build.gradle.kts similarity index 100% rename from services/ping/ping-api/build.gradle.kts rename to backend/services/ping/ping-api/build.gradle.kts diff --git a/services/ping/ping-api/src/commonMain/kotlin/at/mocode/ping/api/PingApi.kt b/backend/services/ping/ping-api/src/commonMain/kotlin/at/mocode/ping/api/PingApi.kt similarity index 100% rename from services/ping/ping-api/src/commonMain/kotlin/at/mocode/ping/api/PingApi.kt rename to backend/services/ping/ping-api/src/commonMain/kotlin/at/mocode/ping/api/PingApi.kt diff --git a/services/ping/ping-api/src/commonMain/kotlin/at/mocode/ping/api/PingData.kt b/backend/services/ping/ping-api/src/commonMain/kotlin/at/mocode/ping/api/PingData.kt similarity index 100% rename from services/ping/ping-api/src/commonMain/kotlin/at/mocode/ping/api/PingData.kt rename to backend/services/ping/ping-api/src/commonMain/kotlin/at/mocode/ping/api/PingData.kt diff --git a/services/ping/ping-service/build.gradle.kts b/backend/services/ping/ping-service/build.gradle.kts similarity index 97% rename from services/ping/ping-service/build.gradle.kts rename to backend/services/ping/ping-service/build.gradle.kts index 8d1489e0..1476ac67 100644 --- a/services/ping/ping-service/build.gradle.kts +++ b/backend/services/ping/ping-service/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { // Platform und Core Dependencies implementation(projects.platform.platformDependencies) - implementation(projects.services.ping.pingApi) + implementation(project(":backend:services:ping:ping-api")) implementation(projects.infrastructure.monitoring.monitoringClient) // Spring Boot Service Complete Bundle diff --git a/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingController.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingController.kt similarity index 100% rename from services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingController.kt rename to backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingController.kt diff --git a/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceApplication.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceApplication.kt similarity index 100% rename from services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceApplication.kt rename to backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceApplication.kt diff --git a/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceCircuitBreaker.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceCircuitBreaker.kt similarity index 100% rename from services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceCircuitBreaker.kt rename to backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceCircuitBreaker.kt diff --git a/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/config/SecurityConfiguration.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/config/SecurityConfiguration.kt similarity index 100% rename from services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/config/SecurityConfiguration.kt rename to backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/config/SecurityConfiguration.kt diff --git a/services/ping/ping-service/src/main/resources/application.yml b/backend/services/ping/ping-service/src/main/resources/application.yml similarity index 100% rename from services/ping/ping-service/src/main/resources/application.yml rename to backend/services/ping/ping-service/src/main/resources/application.yml diff --git a/services/ping/ping-service/src/main/resources/logback-spring.xml b/backend/services/ping/ping-service/src/main/resources/logback-spring.xml similarity index 100% rename from services/ping/ping-service/src/main/resources/logback-spring.xml rename to backend/services/ping/ping-service/src/main/resources/logback-spring.xml diff --git a/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingControllerIntegrationTest.kt b/backend/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingControllerIntegrationTest.kt similarity index 100% rename from services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingControllerIntegrationTest.kt rename to backend/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingControllerIntegrationTest.kt diff --git a/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingControllerTest.kt b/backend/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingControllerTest.kt similarity index 100% rename from services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingControllerTest.kt rename to backend/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingControllerTest.kt diff --git a/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingServiceCircuitBreakerTest.kt b/backend/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingServiceCircuitBreakerTest.kt similarity index 100% rename from services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingServiceCircuitBreakerTest.kt rename to backend/services/ping/ping-service/src/test/kotlin/at/mocode/ping/service/PingServiceCircuitBreakerTest.kt diff --git a/services/ping/ping-service/src/test/resources/application-test.yml b/backend/services/ping/ping-service/src/test/resources/application-test.yml similarity index 100% rename from services/ping/ping-service/src/test/resources/application-test.yml rename to backend/services/ping/ping-service/src/test/resources/application-test.yml diff --git a/build.gradle.kts b/build.gradle.kts index abd54f18..ce143113 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,23 +1,26 @@ - plugins { - // Version management plugin for dependency updates - id("com.github.ben-manes.versions") version "0.51.0" + // Version management plugin for dependency updates + id("com.github.ben-manes.versions") version "0.51.0" - // Kotlin plugins declared here with 'apply false' to centralize version management - // This prevents "plugin loaded multiple times" errors in Gradle 9.1.0+ - // Subprojects apply these plugins via version catalog: alias(libs.plugins.kotlinJvm) - alias(libs.plugins.kotlinJvm) apply false - alias(libs.plugins.kotlinMultiplatform) apply false - alias(libs.plugins.kotlinSerialization) apply false - alias(libs.plugins.kotlinSpring) apply false - alias(libs.plugins.kotlinJpa) apply false - alias(libs.plugins.composeMultiplatform) apply false - alias(libs.plugins.composeCompiler) apply false - alias(libs.plugins.spring.boot) apply false - alias(libs.plugins.spring.dependencyManagement) apply false + // Kotlin plugins declared here with 'apply false' to centralize version management + // This prevents "plugin loaded multiple times" errors in Gradle 9.1.0+ + // Subprojects apply these plugins via version catalog: alias(libs.plugins.kotlinJvm) + alias(libs.plugins.kotlinJvm) apply false + alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.kotlinSerialization) apply false + alias(libs.plugins.kotlinSpring) apply false + alias(libs.plugins.kotlinJpa) apply false + alias(libs.plugins.composeMultiplatform) apply false + alias(libs.plugins.composeCompiler) apply false + alias(libs.plugins.spring.boot) apply false + alias(libs.plugins.spring.dependencyManagement) apply false - // Dokka plugin applied at root to create multi-module collector tasks - alias(libs.plugins.dokka) + // Dokka plugin applied at root to create multi-module collector tasks + alias(libs.plugins.dokka) + + // Static analysis (enabled at root and inherited by subprojects) + id("io.gitlab.arturbosch.detekt") version "1.23.6" + id("org.jlleitschuh.gradle.ktlint") version "12.1.1" } // ################################################################## @@ -25,90 +28,196 @@ plugins { // ################################################################## allprojects { - group = "at.mocode" - version = "1.0.0-SNAPSHOT" + group = "at.mocode" + version = "1.0.0-SNAPSHOT" - // Apply common repository configuration - repositories { - mavenCentral() - google() - maven { url = uri("https://jitpack.io") } - maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } - maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") } - maven { url = uri("https://us-central1-maven.pkg.dev/varabyte-repos/public") } - } + // Apply common repository configuration + repositories { + mavenCentral() + google() + maven { url = uri("https://jitpack.io") } + maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } + maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") } + maven { url = uri("https://us-central1-maven.pkg.dev/varabyte-repos/public") } + } } subprojects { - // Note: Kotlin compiler configuration is handled by individual modules - // a Root project doesn't apply Kotlin plugins, so we can't configure KotlinCompile tasks here + // Note: Kotlin compiler configuration is handled by individual modules + // a Root project doesn't apply Kotlin plugins, so we can't configure KotlinCompile tasks here - tasks.withType().configureEach { - useJUnitPlatform { - excludeTags("perf") - } - // Configure CDS in auto-mode to prevent bootstrap classpath warnings - jvmArgs("-Xshare:auto", "-Djdk.instrument.traceUsage=false") - // Increase test JVM memory with a stable configuration - minHeapSize = "512m" - maxHeapSize = "2g" - // Parallel test execution for better performance - maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) - // Removed byte-buddy-agent configuration to fix Gradle 9.0.0 deprecation warning - // The agent configuration was causing Task.project access at execution time + tasks.withType().configureEach { + useJUnitPlatform { + excludeTags("perf") } + // Configure CDS in auto-mode to prevent bootstrap classpath warnings + jvmArgs("-Xshare:auto", "-Djdk.instrument.traceUsage=false") + // Increase test JVM memory with a stable configuration + minHeapSize = "512m" + maxHeapSize = "2g" + // Parallel test execution for better performance + maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) + // Removed byte-buddy-agent configuration to fix Gradle 9.0.0 deprecation warning + // The agent configuration was causing Task.project access at execution time + } - // Erzwinge eine stabile Version von kotlinx-serialization-json fĂŒr alle Konfigurationen, - // um Auflösungsfehler (z.B. 1.10.2, nicht verfĂŒgbar auf Maven Central) zu vermeiden - configurations.configureEach { - resolutionStrategy { - force("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") - } + // Erzwinge eine stabile Version von kotlinx-serialization-json fĂŒr alle Konfigurationen, + // um Auflösungsfehler (z.B. 1.10.2, nicht verfĂŒgbar auf Maven Central) zu vermeiden + configurations.configureEach { + resolutionStrategy { + force("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") } + } - // Dedicated performance test task per JVM subproject - plugins.withId("java") { - val javaExt = extensions.getByType() - // Ensure a full JDK toolchain with compiler is available (Gradle will auto-download if missing) - javaExt.toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + // Dedicated performance test task per JVM subproject + plugins.withId("java") { + val javaExt = extensions.getByType() + // Ensure a full JDK toolchain with compiler is available (Gradle will auto-download if missing) + javaExt.toolchain.languageVersion.set(JavaLanguageVersion.of(21)) - tasks.register("perfTest") { - description = "Runs tests tagged with 'perf'" - group = "verification" - // Use the regular test source set outputs - testClassesDirs = javaExt.sourceSets.getByName("test").output.classesDirs - classpath = javaExt.sourceSets.getByName("test").runtimeClasspath - useJUnitPlatform { - includeTags("perf") + tasks.register("perfTest") { + description = "Runs tests tagged with 'perf'" + group = "verification" + // Use the regular test source set outputs + testClassesDirs = javaExt.sourceSets.getByName("test").output.classesDirs + classpath = javaExt.sourceSets.getByName("test").runtimeClasspath + useJUnitPlatform { + includeTags("perf") + } + shouldRunAfter("test") + // Keep the same JVM settings for consistency + jvmArgs("-Xshare:auto", "-Djdk.instrument.traceUsage=false") + maxHeapSize = "2g" + dependsOn("testClasses") + } + } + + // Suppress Node.js deprecation warnings (e.g., DEP0040 punycode) during Kotlin/JS npm/yarn tasks + // Applies to all Exec-based tasks (covers Yarn/NPM invocations used by Kotlin JS plugin) + tasks.withType().configureEach { + // Merge existing NODE_OPTIONS with --no-deprecation + val current = (environment["NODE_OPTIONS"] as String?) ?: System.getenv("NODE_OPTIONS") + val merged = if (current.isNullOrBlank()) "--no-deprecation" else "$current --no-deprecation" + environment("NODE_OPTIONS", merged) + // Also set the legacy switch to silence warnings entirely + environment("NODE_NO_WARNINGS", "1") + // Set Chrome binary path to avoid snap permission issues + environment("CHROME_BIN", "/usr/bin/google-chrome-stable") + environment("CHROMIUM_BIN", "/usr/bin/chromium") + environment("PUPPETEER_EXECUTABLE_PATH", "/usr/bin/chromium") + } + + tasks.withType { + compilerOptions { + freeCompilerArgs.add("-Xannotation-default-target=param-property") + } + } + + // ------------------------------ + // Detekt & Ktlint default setup + // ------------------------------ + plugins.withId("io.gitlab.arturbosch.detekt") { + extensions.configure(io.gitlab.arturbosch.detekt.extensions.DetektExtension::class.java) { + buildUponDefaultConfig = true + allRules = false + autoCorrect = false + config.setFrom(files(rootProject.file("config/detekt/detekt.yml"))) + basePath = rootDir.absolutePath + } + tasks.withType().configureEach { + jvmTarget = "21" + reports { + xml.required.set(false) + txt.required.set(false) + sarif.required.set(false) + html.required.set(true) + } + } + } + + plugins.withId("org.jlleitschuh.gradle.ktlint") { + extensions.configure(org.jlleitschuh.gradle.ktlint.KtlintExtension::class.java) { + android.set(false) + outputToConsole.set(true) + ignoreFailures.set(false) + reporters { + reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE) + reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.PLAIN) + } + } + } +} + +// ================================================================== +// Architecture Guards (lightweight, fast checks) +// ================================================================== + +// Fails if any source file contains manual Authorization header setting. +// Policy: Authorization must be injected by the DI-provided HttpClient (apiClient). +tasks.register("archGuardForbiddenAuthorizationHeader") { + group = "verification" + description = "Fail build if code sets Authorization header manually." + doLast { + val forbiddenPatterns = + listOf( + ".header(\"Authorization\"", + "setHeader(\"Authorization\"", + "headers[\"Authorization\"]", + "headers[\'Authorization\']", + ) + // Scope: Frontend-only enforcement. Backend/Test code is excluded. + val srcDirs = listOf("clients", "frontend") + val violations = mutableListOf() + srcDirs.map { file(it) } + .filter { it.exists() } + .forEach { rootDir -> + rootDir.walkTopDown() + .filter { it.isFile && (it.extension == "kt" || it.extension == "kts") } + .forEach { f -> + val text = f.readText() + // Skip test sources + val path = f.invariantSeparatorsPath + val isTest = + path.contains("/src/commonTest/") || + path.contains("/src/jsTest/") || + path.contains("/src/jvmTest/") || + path.contains("/src/test/") + if (!isTest && forbiddenPatterns.any { text.contains(it) }) { + violations += f } - shouldRunAfter("test") - // Keep the same JVM settings for consistency - jvmArgs("-Xshare:auto", "-Djdk.instrument.traceUsage=false") - maxHeapSize = "2g" - dependsOn("testClasses") + } + } + if (violations.isNotEmpty()) { + val msg = + buildString { + appendLine("Forbidden manual Authorization header usage found in:") + violations.take(50).forEach { appendLine(" - ${it.path}") } + if (violations.size > 50) appendLine(" ... and ${violations.size - 50} more files") + appendLine() + appendLine("Policy: Use DI-provided apiClient (Koin named \"apiClient\").") } + throw GradleException(msg) } + } +} - // Suppress Node.js deprecation warnings (e.g., DEP0040 punycode) during Kotlin/JS npm/yarn tasks - // Applies to all Exec-based tasks (covers Yarn/NPM invocations used by Kotlin JS plugin) - tasks.withType().configureEach { - // Merge existing NODE_OPTIONS with --no-deprecation - val current = (environment["NODE_OPTIONS"] as String?) ?: System.getenv("NODE_OPTIONS") - val merged = if (current.isNullOrBlank()) "--no-deprecation" else "$current --no-deprecation" - environment("NODE_OPTIONS", merged) - // Also set the legacy switch to silence warnings entirely - environment("NODE_NO_WARNINGS", "1") - // Set Chrome binary path to avoid snap permission issues - environment("CHROME_BIN", "/usr/bin/google-chrome-stable") - environment("CHROMIUM_BIN", "/usr/bin/chromium") - environment("PUPPETEER_EXECUTABLE_PATH", "/usr/bin/chromium") - } +// Aggregate convenience task +tasks.register("archGuards") { + group = "verification" + description = "Run all architecture guard checks" + dependsOn("archGuardForbiddenAuthorizationHeader") +} - tasks.withType { - compilerOptions { - freeCompilerArgs.add("-Xannotation-default-target=param-property") - } - } +// Composite verification task including static analyzers if present +tasks.register("staticAnalysis") { + group = "verification" + description = "Run static analysis (detekt, ktlint) and architecture guards" + // These tasks are provided by plugins; only depend if tasks exist + dependsOn( + tasks.matching { it.name == "detekt" }, + tasks.matching { it.name == "ktlintCheck" }, + tasks.named("archGuards"), + ) } // ################################################################## @@ -117,70 +226,80 @@ subprojects { // Apply Dokka automatically to Kotlin subprojects to enable per-module docs subprojects { - plugins.withId("org.jetbrains.kotlin.jvm") { - apply(plugin = "org.jetbrains.dokka") - } - plugins.withId("org.jetbrains.kotlin.multiplatform") { - apply(plugin = "org.jetbrains.dokka") - } + plugins.withId("org.jetbrains.kotlin.jvm") { + apply(plugin = "org.jetbrains.dokka") + } + plugins.withId("org.jetbrains.kotlin.multiplatform") { + apply(plugin = "org.jetbrains.dokka") + } - // Minimal sourceLink configuration when running in GitHub Actions - tasks.withType(org.jetbrains.dokka.gradle.DokkaTask::class.java).configureEach { - dokkaSourceSets.configureEach { - val repo = System.getenv("GITHUB_REPOSITORY") - if (!repo.isNullOrBlank()) { - sourceLink { - localDirectory.set(project.file("src")) - remoteUrl.set(java.net.URI.create("https://github.com/$repo/blob/main/" + project.path.trimStart(':').replace(':', '/') + "/src").toURL()) - } - } - // Keep module names short and stable - moduleName.set(project.path.trimStart(':')) + // Minimal sourceLink configuration when running in GitHub Actions + tasks.withType(org.jetbrains.dokka.gradle.DokkaTask::class.java).configureEach { + dokkaSourceSets.configureEach { + val repo = System.getenv("GITHUB_REPOSITORY") + if (!repo.isNullOrBlank()) { + sourceLink { + localDirectory.set(project.file("src")) + remoteUrl.set( + java.net.URI.create( + "https://github.com/$repo/blob/main/" + project.path.trimStart(':').replace(':', '/') + "/src", + ).toURL(), + ) } + } + // Keep module names short and stable + moduleName.set(project.path.trimStart(':')) } + } } // Aggregate tasks to build multi-module docs in Markdown (GFM) and HTML -val dokkaGfmAll = tasks.register("dokkaGfmAll") { +val dokkaGfmAll = + tasks.register("dokkaGfmAll") { group = "documentation" description = "Builds Dokka GFM for all modules and aggregates outputs under build/dokka/gfm" // Depend on all dokkaGfm tasks that exist in subprojects - dependsOn(subprojects + dependsOn( + subprojects .filter { it.plugins.hasPlugin("org.jetbrains.dokka") } - .map { "${it.path}:dokkaGfm" }) + .map { "${it.path}:dokkaGfm" }, + ) doLast { - val dest = layout.buildDirectory.dir("dokka/gfm").get().asFile - if (dest.exists()) dest.deleteRecursively() - dest.mkdirs() - subprojects.filter { it.plugins.hasPlugin("org.jetbrains.dokka") }.forEach { p -> - val out = p.layout.buildDirectory.dir("dokka/gfm").get().asFile - if (out.exists()) { - out.copyRecursively(File(dest, p.path.trimStart(':').replace(':', '/')), overwrite = true) - } + val dest = layout.buildDirectory.dir("dokka/gfm").get().asFile + if (dest.exists()) dest.deleteRecursively() + dest.mkdirs() + subprojects.filter { it.plugins.hasPlugin("org.jetbrains.dokka") }.forEach { p -> + val out = p.layout.buildDirectory.dir("dokka/gfm").get().asFile + if (out.exists()) { + out.copyRecursively(File(dest, p.path.trimStart(':').replace(':', '/')), overwrite = true) } - println("[DOKKA] Aggregated GFM into ${dest.absolutePath}") + } + println("[DOKKA] Aggregated GFM into ${dest.absolutePath}") } -} + } -val dokkaHtmlAll = tasks.register("dokkaHtmlAll") { +val dokkaHtmlAll = + tasks.register("dokkaHtmlAll") { group = "documentation" description = "Builds Dokka HTML for all modules and aggregates outputs under build/dokka/html" - dependsOn(subprojects + dependsOn( + subprojects .filter { it.plugins.hasPlugin("org.jetbrains.dokka") } - .map { "${it.path}:dokkaHtml" }) + .map { "${it.path}:dokkaHtml" }, + ) doLast { - val dest = layout.buildDirectory.dir("dokka/html").get().asFile - if (dest.exists()) dest.deleteRecursively() - dest.mkdirs() - subprojects.filter { it.plugins.hasPlugin("org.jetbrains.dokka") }.forEach { p -> - val out = p.layout.buildDirectory.dir("dokka/html").get().asFile - if (out.exists()) { - out.copyRecursively(File(dest, p.path.trimStart(':').replace(':', '/')), overwrite = true) - } + val dest = layout.buildDirectory.dir("dokka/html").get().asFile + if (dest.exists()) dest.deleteRecursively() + dest.mkdirs() + subprojects.filter { it.plugins.hasPlugin("org.jetbrains.dokka") }.forEach { p -> + val out = p.layout.buildDirectory.dir("dokka/html").get().asFile + if (out.exists()) { + out.copyRecursively(File(dest, p.path.trimStart(':').replace(':', '/')), overwrite = true) } - println("[DOKKA] Aggregated HTML into ${dest.absolutePath}") + } + println("[DOKKA] Aggregated HTML into ${dest.absolutePath}") } -} + } // ################################################################## // ### DOKU-AGGREGATOR ### @@ -188,26 +307,26 @@ val dokkaHtmlAll = tasks.register("dokkaHtmlAll") { // Leichter Aggregator im Root-Projekt, ruft die eigentlichen Tasks im :docs Subprojekt auf tasks.register("docs") { - description = "Aggregates documentation tasks from :docs" - group = "documentation" - dependsOn(":docs:generateAllDocs") + description = "Aggregates documentation tasks from :docs" + group = "documentation" + dependsOn(":docs:generateAllDocs") } // Wrapper-Konfiguration // Apply Node warning suppression on root project Exec tasks as well // Ensures aggregated Kotlin/JS tasks created at root (e.g., kotlinNpmInstall) inherit the env tasks.withType().configureEach { - val current = (environment["NODE_OPTIONS"] as String?) ?: System.getenv("NODE_OPTIONS") - val merged = if (current.isNullOrBlank()) "--no-deprecation" else "$current --no-deprecation" - environment("NODE_OPTIONS", merged) - environment("NODE_NO_WARNINGS", "1") - // Set Chrome binary path to avoid snap permission issues - environment("CHROME_BIN", "/usr/bin/google-chrome-stable") - environment("CHROMIUM_BIN", "/usr/bin/chromium") - environment("PUPPETEER_EXECUTABLE_PATH", "/usr/bin/chromium") + val current = (environment["NODE_OPTIONS"] as String?) ?: System.getenv("NODE_OPTIONS") + val merged = if (current.isNullOrBlank()) "--no-deprecation" else "$current --no-deprecation" + environment("NODE_OPTIONS", merged) + environment("NODE_NO_WARNINGS", "1") + // Set Chrome binary path to avoid snap permission issues + environment("CHROME_BIN", "/usr/bin/google-chrome-stable") + environment("CHROMIUM_BIN", "/usr/bin/chromium") + environment("PUPPETEER_EXECUTABLE_PATH", "/usr/bin/chromium") } tasks.wrapper { - gradleVersion = "9.1.0" - distributionType = Wrapper.DistributionType.BIN + gradleVersion = "9.1.0" + distributionType = Wrapper.DistributionType.BIN } diff --git a/clients/auth-feature/build.gradle.kts b/clients/auth-feature/build.gradle.kts index 4bd935e0..620d2db3 100644 --- a/clients/auth-feature/build.gradle.kts +++ b/clients/auth-feature/build.gradle.kts @@ -42,8 +42,8 @@ kotlin { sourceSets { commonMain.dependencies { - // UI Kit - implementation(project(":clients:shared:common-ui")) + // UI Kit (Design System) + implementation(project(":frontend:core:design-system")) // Shared Konfig & Utilities (AppConfig + BuildConfig) implementation(project(":clients:shared")) diff --git a/clients/ping-feature/build.gradle.kts b/clients/ping-feature/build.gradle.kts index 25beb5c3..681cda1b 100644 --- a/clients/ping-feature/build.gradle.kts +++ b/clients/ping-feature/build.gradle.kts @@ -43,10 +43,10 @@ kotlin { sourceSets { commonMain.dependencies { // Contract from backend - implementation(projects.services.ping.pingApi) + implementation(project(":backend:services:ping:ping-api")) - // UI Kit - implementation(project(":clients:shared:common-ui")) + // UI Kit (Design System) + implementation(project(":frontend:core:design-system")) // Shared Konfig & Utilities implementation(project(":clients:shared")) @@ -65,6 +65,9 @@ kotlin { // Coroutines and serialization implementation(libs.bundles.kotlinx.core) + // DI (Koin) for resolving apiClient from container + implementation(libs.koin.core) + // ViewModel lifecycle implementation(libs.bundles.compose.common) diff --git a/clients/shared/build.gradle.kts b/clients/shared/build.gradle.kts index 35b6c6eb..48b71da0 100644 --- a/clients/shared/build.gradle.kts +++ b/clients/shared/build.gradle.kts @@ -63,6 +63,9 @@ kotlin { implementation(libs.koin.compose) implementation(libs.koin.compose.viewmodel) + // Network module (provides DI `apiClient`) + implementation(project(":frontend:core:network")) + // Compose fĂŒr shared UI components (common) implementation(compose.runtime) implementation(compose.foundation) diff --git a/clients/shared/src/commonMain/kotlin/at/mocode/clients/shared/di/SharedModule.kt b/clients/shared/src/commonMain/kotlin/at/mocode/clients/shared/di/SharedModule.kt index f548bf43..10202c64 100644 --- a/clients/shared/src/commonMain/kotlin/at/mocode/clients/shared/di/SharedModule.kt +++ b/clients/shared/src/commonMain/kotlin/at/mocode/clients/shared/di/SharedModule.kt @@ -1,6 +1,7 @@ package at.mocode.clients.shared.di import at.mocode.clients.shared.core.devConfig +import at.mocode.frontend.core.network.networkModule import org.koin.core.context.startKoin import org.koin.dsl.KoinAppDeclaration import org.koin.dsl.module @@ -10,14 +11,16 @@ val configModule = module { single { devConfig } // SpĂ€ter können wir hier PROD/DEV umschalten } -// Alle Module zusammen -val sharedModules = listOf( +// Basismodule, die immer geladen werden sollen (ohne Feature/Core-Cross-Imports) +val baseSharedModules = listOf( configModule, + // Network module provides DI-only HttpClient (safe to be shared across features) networkModule ) // Helper zum Starten von Koin (wird von der App aufgerufen) +// Weitere Module (z. B. networkModule) können ĂŒber appDeclaration hinzugefĂŒgt werden. fun initKoin(appDeclaration: KoinAppDeclaration = {}) = startKoin { + modules(baseSharedModules) appDeclaration() - modules(sharedModules) } diff --git a/compose.hardcoded.yaml b/compose.hardcoded.yaml deleted file mode 100644 index 9f8138ba..00000000 --- a/compose.hardcoded.yaml +++ /dev/null @@ -1,190 +0,0 @@ -name: meldestelle-hardcoded - -services: - # --- DATENBANK --- - postgres: - image: postgres:16-alpine - container_name: meldestelle-postgres - restart: unless-stopped - ports: - - "5432:5432" - environment: - POSTGRES_USER: pg-user - POSTGRES_PASSWORD: pg-password - POSTGRES_DB: meldestelle - volumes: - - postgres-data:/var/lib/postgresql/data - # Falls du Init-Scripte hast, lassen wir die erstmal weg, - # um Fehlerquellen zu reduzieren, oder lassen den Pfad, falls er existiert: - - ./docker/core/postgres:/docker-entrypoint-initdb.d:Z - healthcheck: - test: [ "CMD-SHELL", "pg_isready -U pg-user -d meldestelle" ] - interval: 1s - timeout: 5s - retries: 3 - start_period: 30s - networks: - - meldestelle-network - - # --- DATENBANK-MANAGEMENT-TOOL --- - pgadmin: - image: dpage/pgadmin4:8 - container_name: pgadmin4_container - restart: unless-stopped - ports: - - "8888:80" - environment: - PGADMIN_DEFAULT_EMAIL: user@domain.com - PGADMIN_DEFAULT_PASSWORD: strong-password - volumes: - - pgadmin-data:/var/lib/pgadmin - healthcheck: - test: [ "CMD-SHELL", "wget --spider -q http://localhost:80/ || exit 1" ] - interval: 1s - timeout: 5s - retries: 3 - start_period: 30s - networks: - - meldestelle-network - - # --- CACHE --- - redis: - image: redis:7-alpine - container_name: meldestelle-redis - restart: unless-stopped - ports: - - "6379:6379" - volumes: - - redis-data:/data - command: redis-server --appendonly yes - healthcheck: - test: [ "CMD", "redis-cli" ] - interval: 1s - timeout: 5s - retries: 3 - networks: - - meldestelle-network - - # --- IDENTITY PROVIDER (Wartet auf Postgres) --- - keycloak: - image: quay.io/keycloak/keycloak:26.4 - container_name: meldestelle-keycloak - restart: unless-stopped - environment: - KC_HEALTH_ENABLED: true - KC_METRICS_ENABLED: true - KC_BOOTSTRAP_ADMIN_USERNAME: kc-admin - KC_BOOTSTRAP_ADMIN_PASSWORD: kc-password - KC_DB: postgres - KC_DB_URL: jdbc:postgresql://postgres:5432/meldestelle - KC_DB_USERNAME: pg-user - KC_DB_PASSWORD: pg-password - KC_HOSTNAME: localhost - ports: - - "8180:8080" - depends_on: - postgres: - condition: service_healthy - volumes: - - ./docker/core/keycloak:/opt/keycloak/data/import:Z - command: start-dev --import-realm - healthcheck: - test: [ "CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000" ] - interval: 20s - timeout: 10s - retries: 5 - start_period: 60s - networks: - - meldestelle-network - - # --- MONITORING --- - prometheus: - image: prom/prometheus:v2.54.1 - container_name: meldestelle-prometheus - restart: unless-stopped - ports: - - "9090:9090" - volumes: - - prometheus-data:/prometheus - - ./docker/monitoring/prometheus:/etc/prometheus:Z - command: - - --config.file=/etc/prometheus/prometheus.yml - - --storage.tsdb.retention.time=15d - healthcheck: - test: [ "CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy" ] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - networks: - - meldestelle-network - - grafana: - image: grafana/grafana:11.3.0 - container_name: meldestelle-grafana - environment: - GF_SECURITY_ADMIN_USER: gf-admin - GF_SECURITY_ADMIN_PASSWORD: gf-password - ports: - - "3000:3000" - volumes: - - grafana-data:/var/lib/grafana - - ./docker/monitoring/grafana:/etc/grafana/provisioning:Z - depends_on: - - prometheus - healthcheck: - test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health" ] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - networks: - - meldestelle-network - - # --- CLIENTS: WEB APP (Kotlin/JS, no WASM) --- - web-app: - build: - context: . - dockerfile: dockerfiles/clients/web-app/Dockerfile - args: - GRADLE_VERSION: 9.1.0 - JAVA_VERSION: 21 - NODE_VERSION: 22.21.0 - NGINX_IMAGE_TAG: 1.28.0-alpine - WEB_BUILD_PROFILE: dev - container_name: meldestelle-web-app - restart: unless-stopped - ports: - - "4000:4000" - depends_on: - - api-gateway - networks: - - meldestelle-network - - # --- CLIENTS: DESKTOP APP (VNC + noVNC) --- - desktop-app: - build: - context: . - dockerfile: dockerfiles/clients/desktop-app/Dockerfile - container_name: meldestelle-desktop-app - restart: unless-stopped - environment: - - API_BASE_URL=http://api-gateway:8081 - ports: - - "5901:5901" # VNC - - "6080:6080" # noVNC - depends_on: - - api-gateway - networks: - - meldestelle-network - -volumes: - postgres-data: - pgadmin-data: - redis-data: - prometheus-data: - grafana-data: - -networks: - meldestelle-network: - driver: bridge diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 00000000..441e1f8c --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,43 @@ +# Core project name used as prefix for container names +COMPOSE_PROJECT_NAME=meldestelle + +# Ports +POSTGRES_PORT=5432:5432 +REDIS_PORT=6379:6379 +KC_PORT=8180:8080 +CONSUL_PORT=8500:8500 +PROMETHEUS_PORT=9090:9090 +GF_PORT=3000:3000 +WEB_APP_PORT=4000:80 +PING_SERVICE_PORT=8082:8082 +PING_DEBUG_PORT=5006:5006 +GATEWAY_PORT=8081:8081 +GATEWAY_DEBUG_PORT=5005:5005 +GATEWAY_SERVER_PORT=8081 +DESKTOP_APP_VNC_PORT=5900:5900 +DESKTOP_APP_NOVNC_PORT=6080:6080 + +# Postgres +POSTGRES_USER=meldestelle +POSTGRES_PASSWORD=meldestelle +POSTGRES_DB=meldestelle + +# Keycloak +KC_ADMIN_USER=admin +KC_ADMIN_PASSWORD=admin +KC_HOSTNAME=localhost + +# PgAdmin +PGADMIN_EMAIL=admin@example.com +PGADMIN_PASSWORD=admin + +# Grafana +GF_ADMIN_USER=admin +GF_ADMIN_PASSWORD=admin + +# Docker build versions (optional overrides) +DOCKER_GRADLE_VERSION=9.1.0 +DOCKER_JAVA_VERSION=21 +DOCKER_NODE_VERSION=22.21.0 +DOCKER_NGINX_VERSION=1.28.0-alpine +WEB_BUILD_PROFILE=dev diff --git a/docker/docker-compose.clients.yml b/docker/docker-compose.clients.yml new file mode 100644 index 00000000..ad189ddb --- /dev/null +++ b/docker/docker-compose.clients.yml @@ -0,0 +1 @@ +services: {} diff --git a/docker/docker-compose.services.yml b/docker/docker-compose.services.yml new file mode 100644 index 00000000..ad189ddb --- /dev/null +++ b/docker/docker-compose.services.yml @@ -0,0 +1 @@ +services: {} diff --git a/compose.yaml b/docker/docker-compose.yml similarity index 94% rename from compose.yaml rename to docker/docker-compose.yml index 4d3e2fc6..8ff0bfba 100644 --- a/compose.yaml +++ b/docker/docker-compose.yml @@ -16,7 +16,7 @@ services: POSTGRES_DB: ${POSTGRES_DB} volumes: - postgres-data:/var/lib/postgresql/data - - ./docker/core/postgres:/docker-entrypoint-initdb.d:Z + - ./core/postgres:/docker-entrypoint-initdb.d:Z healthcheck: test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}" ] interval: 5s @@ -72,7 +72,7 @@ services: postgres: condition: service_healthy volumes: - - ./docker/core/keycloak:/opt/keycloak/data/import:Z + - ./core/keycloak:/opt/keycloak/data/import:Z command: start-dev --import-realm healthcheck: test: [ "CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000" ] @@ -112,7 +112,7 @@ services: - "${PROMETHEUS_PORT}" volumes: - prometheus-data:/prometheus - - ./docker/monitoring/prometheus:/etc/prometheus:Z + - ./monitoring/prometheus:/etc/prometheus:Z command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.retention.time=15d @@ -138,7 +138,7 @@ services: - "${GF_PORT}" volumes: - grafana-data:/var/lib/grafana - - ./docker/monitoring/grafana:/etc/grafana/provisioning:Z + - ./monitoring/grafana:/etc/grafana/provisioning:Z depends_on: - prometheus healthcheck: @@ -175,7 +175,7 @@ services: api-gateway: build: - context: . + context: .. dockerfile: dockerfiles/infrastructure/gateway/Dockerfile args: # Build-Args aus deinen .env Dateien (werden hier statisch benötigt fĂŒr den Build) @@ -224,7 +224,7 @@ services: # ========================================== ping-service: build: - context: . + context: .. dockerfile: dockerfiles/services/ping-service/Dockerfile args: GRADLE_VERSION: 9.1.0 @@ -246,16 +246,13 @@ services: SPRING_CLOUD_CONSUL_PORT: 8500 SPRING_CLOUD_CONSUL_DISCOVERY_HOSTNAME: ping-service - # --- DATENBANK VERBINDUNG --- - # Wir nutzen die Container-Namen aus deiner .env Variable + # - DATENBANK VERBINDUNG - SPRING_DATASOURCE_URL: jdbc:postgresql://${COMPOSE_PROJECT_NAME}-postgres:5432/${POSTGRES_DB} SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER} SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD} - # WICHTIG: Wir wollen nur validieren, nichts erstellen. SPRING_JPA_HIBERNATE_DDL_AUTO: validate # --- REDIS --- - # Wir nutzen den Service-Namen, genau wie bei Postgres SPRING_DATA_REDIS_HOST: ${COMPOSE_PROJECT_NAME}-redis SPRING_DATA_REDIS_PORT: 6379 depends_on: @@ -275,7 +272,7 @@ services: # ========================================== web-app: build: - context: . + context: .. dockerfile: dockerfiles/clients/web-app/Dockerfile args: GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.1.0} @@ -297,7 +294,7 @@ services: desktop-app: build: - context: . + context: .. dockerfile: dockerfiles/clients/desktop-app/Dockerfile container_name: ${COMPOSE_PROJECT_NAME}-desktop-app restart: unless-stopped diff --git a/dockerfiles/clients/desktop-app/Dockerfile b/dockerfiles/clients/desktop-app/Dockerfile index 09ada4d9..38b69ff7 100644 --- a/dockerfiles/clients/desktop-app/Dockerfile +++ b/dockerfiles/clients/desktop-app/Dockerfile @@ -15,22 +15,21 @@ COPY gradle ./gradle COPY gradlew ./ # Kopiere alle notwendigen Module fĂŒr Multi-Modul-Projekt -COPY clients ./clients +COPY frontend ./frontend +COPY backend ./backend COPY core ./core COPY domains ./domains COPY platform ./platform -COPY infrastructure ./infrastructure -COPY services ./services COPY docs ./docs # Setze Gradle-Wrapper Berechtigung RUN chmod +x ./gradlew # Dependencies downloaden (fĂŒr besseres Caching) -RUN ./gradlew :clients:app:dependencies --no-configure-on-demand +RUN ./gradlew :frontend:shells:meldestelle-portal:dependencies --no-configure-on-demand # Desktop-App kompilieren (createDistributable fĂŒr native Distribution) -RUN ./gradlew :clients:app:createDistributable --no-configure-on-demand +RUN ./gradlew :frontend:shells:meldestelle-portal:createDistributable --no-configure-on-demand # =================================================================== # Stage 2: Runtime Stage - Ubuntu mit VNC + noVNC @@ -59,7 +58,7 @@ RUN apt-get update && apt-get install -y \ WORKDIR /app # Kopiere kompilierte Desktop-App von Build-Stage -COPY --from=builder /app/clients/app/build/compose/binaries/main/desktop/ ./desktop-app/ +COPY --from=builder /app/frontend/shells/meldestelle-portal/build/compose/binaries/main/desktop/ ./desktop-app/ # Kopiere Scripts COPY dockerfiles/clients/desktop-app/entrypoint.sh /entrypoint.sh diff --git a/dockerfiles/clients/web-app/Dockerfile b/dockerfiles/clients/web-app/Dockerfile index 951ef685..0d5f9a13 100644 --- a/dockerfiles/clients/web-app/Dockerfile +++ b/dockerfiles/clients/web-app/Dockerfile @@ -30,29 +30,28 @@ COPY gradle ./gradle COPY gradlew ./ # Kopiere alle notwendigen Module fĂŒr Multi-Modul-Projekt -COPY clients ./clients +COPY frontend ./frontend +COPY backend ./backend COPY core ./core COPY domains ./domains COPY platform ./platform -COPY infrastructure ./infrastructure -COPY services ./services COPY docs ./docs # Setze Gradle-Wrapper Berechtigung RUN chmod +x ./gradlew # Dependencies downloaden (fĂŒr besseres Caching) -RUN ./gradlew :clients:app:dependencies --no-configure-on-demand +RUN ./gradlew :frontend:shells:meldestelle-portal:dependencies --no-configure-on-demand # Kotlin/JS Web-App kompilieren (Profil wĂ€hlbar ĂŒber WEB_BUILD_PROFILE) # - dev → jsBrowserDevelopmentExecutable (schneller, Source Maps) # - prod → jsBrowserDistribution (minifiziert, optimiert) RUN if [ "$WEB_BUILD_PROFILE" = "prod" ]; then \ - ./gradlew :clients:app:jsBrowserDistribution --no-configure-on-demand -Pproduction=true; \ - mkdir -p /app/web-dist && cp -r clients/app/build/dist/js/productionExecutable/* /app/web-dist/; \ + ./gradlew :frontend:shells:meldestelle-portal:jsBrowserDistribution --no-configure-on-demand -Pproduction=true; \ + mkdir -p /app/web-dist && cp -r frontend/shells/meldestelle-portal/build/dist/js/productionExecutable/* /app/web-dist/; \ else \ - ./gradlew :clients:app:jsBrowserDevelopmentExecutable --no-configure-on-demand; \ - mkdir -p /app/web-dist && cp -r clients/app/build/dist/js/developmentExecutable/* /app/web-dist/; \ + ./gradlew :frontend:shells:meldestelle-portal:jsBrowserDevelopmentExecutable --no-configure-on-demand; \ + mkdir -p /app/web-dist && cp -r frontend/shells/meldestelle-portal/build/dist/js/developmentExecutable/* /app/web-dist/; \ fi # =================================================================== diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 00000000..efb52dc3 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,67 @@ +Repository-Architektur (MP-22) + +Dieses Dokument beschreibt die Zielstruktur und das Mapping vom bisherigen Stand (Ist) zur neuen Struktur (Soll). Es begleitet Epic 2 (MP-22). + +Zielstruktur (Top-Level) + +backend/ Gateway, Discovery (optional), Services + gateway + discovery + services +frontend/ KMP Frontend + shells AusfĂŒhrbare Apps (Assembler) + features Vertical Slices (kein Feature→Feature) + core Shared Foundation (Design-System, Network, Local-DB, Auth, Domain) +docker/ Docker Compose, .env.example, Monitoring-/Core-Konfiguration +docs/ Architektur, ADRs, C4-Modelle, Guides + +Ist → Soll Mapping (erste Tranche) + +- Frontend + - clients/app → frontend/shells/meldestelle-portal (verschieben in Folge-Commit) + - clients/shared/common-ui → frontend/core/design-system (verschieben in Folge-Commit) + - clients/shared/navigation → frontend/core/navigation (verschieben in Folge-Commit) + +- Backend + - infrastructure/gateway → backend/gateway (verschieben in Folge-Commit) + - services/* → backend/services/* (verschieben in Folge-Commit) + - Discovery (falls genutzt) → backend/discovery + +- Docker + - compose.yaml → docker/docker-compose.yml (neu angelegt, Makefile angepasst) + - .env Handling → docker/.env.example (neu, als Template) + +Build/Gradle + +- settings.gradle.kts bleibt vorerst unverĂ€ndert. Modul-Verschiebungen folgen in einem separaten Schritt mit angepassten include-Pfaden. +- Version Catalog (gradle/libs.versions.toml) bleibt die einzige Quelle der Versionswahrheit. + +Richtlinien (Kurzfassung) + +- Features kommunizieren ausschließlich ĂŒber Routen (Navigation) und Shared-Modelle in frontend/core/domain. +- Kein manueller Authorization-Header – nur der DI-verwaltete apiClient aus frontend/core/network (Koin Named Binding). +- SQLDelight als Offline-SSoT: Schema/Migrationen zentral versionieren, UI liest stets lokal und synchronisiert im Hintergrund. + +DI-Policy & Architecture Guards (MP-23) + +- DI-Policy (Frontend) + - Http‑Requests erfolgen ausschließlich ĂŒber den via Koin bereitgestellten `apiClient` (named Binding) aus `:frontend:core:network`. + - Manuelles Setzen des `Authorization`‑Headers ist verboten. Token‑Handling wird zentral im `apiClient` konfiguriert (Auth‑Plugin/Interceptor). + - Basis‑URL wird plattformspezifisch aufgelöst: + - JVM/Desktop: Env `API_BASE_URL` (Fallback `http://localhost:8081`). + - Web/JS: `globalThis.API_BASE_URL` (z. B. per `index.html` oder Proxy), sonst `window.location.origin`, Fallback `http://localhost:8081`. + +- Architecture Guards (Frontend‑Scope) + - Root‑Task `archGuards` bricht den Build ab, wenn verbotene Muster gefunden werden (manuelle `Authorization`‑Header). Tests sind ausgenommen; Backend ist ausgenommen. + - Statische Analyse verfĂŒgbar ĂŒber `detekt` und `ktlintCheck`; Aggregator `staticAnalysis` fĂŒhrt alles zusammen. + +- Hinweise fĂŒr Features + - Features importieren keine anderen Features (Kommunikation ĂŒber Navigation + Shared‑Domain‑Modelle). Eine explizite Detekt‑Regel folgt. + - Netzwerkzugriffe in Features nutzen Koin ĂŒber die App‑Shell (DI‑Bootstrap). FĂŒr schrittweise Migration kann eine Factory den `apiClient` optional beziehen. + +NĂ€chste Schritte (MP-22 Folgetasks) + +1. Physisches Verschieben der Frontend-Module gemĂ€ĂŸ Mapping und Anpassung von settings.gradle.kts. +2. Physisches Verschieben der Backend-Komponenten in backend/* inkl. evtl. Package-Pfade, sofern notwendig. +3. ErgĂ€nzung von docker-compose.services.yml und docker-compose.clients.yml mit echten Overlays. +4. Erstellen der ersten ADRs unter docs/adr (Koin, SQLDelight, Optimistic Locking, Freshness UI, Core Domain). diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 00000000..9d51fc60 --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,13 @@ +Architecture Decision Records (ADRs) + +Dieses Verzeichnis enthĂ€lt Architekturentscheidungen in kurzer, ĂŒberprĂŒfbarer Form. + +Namensschema: ADR-XXX-title.md mit fortlaufender Nummerierung. + +- ADR-001 Koin als DI +- ADR-002 SQLDelight als Offline-DB +- ADR-003 Optimistic Locking (409) als Konfliktstrategie +- ADR-004 Freshness UI (Ampel) +- ADR-005 Core Domain & Feature Isolation + +Siehe Template: ADR-000-template.md. diff --git a/docs/clients/visionen/AntwortenOffenerFragenArchitekturReview.md b/docs/clients/visionen/AntwortenOffenerFragenArchitekturReview.md new file mode 100644 index 00000000..2e47083a --- /dev/null +++ b/docs/clients/visionen/AntwortenOffenerFragenArchitekturReview.md @@ -0,0 +1,160 @@ +### 1\. Welche DI-Lösung? (Dependency Injection) + +**Entscheidung:** Wir nutzen **Koin**. + +**BegrĂŒndung (ADR):** + +* **Warum nicht Dagger/Hilt?** Hilt ist stark auf Android (Context, Lifecycles) fixiert. Dagger ist extrem komplex im Setup fĂŒr Multiplatform (Kapt/KSP Setup ĂŒber alle Targets). +* **Warum Koin?** Es ist ein reines Kotlin-Framework ("Service Locator" Pattern). Es funktioniert identisch auf JVM (Desktop), JS (Web) und Android. Es benötigt keine Annotation-Processing-Magie, was die Build-Zeiten im Monorepo niedrig hĂ€lt. + +**Eintrag im Guide:** + +```kotlin +// GUIDELINE: Dependency Injection +// Wir nutzen Koin. Module werden im `di` Package des Features definiert. + +// 1. Definition (Feature Module) +val inventoryModule = module { + // Singletons fĂŒr Services + single { InventoryRepositoryImpl(get(), get()) } + + // ViewModels (Factory scope) + viewModel { InventoryViewModel(get()) } +} + +// 2. Nutzung des ApiClients (Best Practice) +// Wir injizieren IMMER den "apiClient" (mit Auth-Header), niemals den Default Client. +val networkModule = module { + single(named("apiClient")) { ... } // Konfiguriert in :core:network +} + +val myFeatureModule = module { + single { + // Explizites Holen des authentifizierten Clients + MyFeatureApi(httpClient = get(named("apiClient"))) + } +} +``` + +----- + +### 2\. Welche Offline-DB/ORM? + +**Entscheidung:** Wir nutzen **SQLDelight**. + +**BegrĂŒndung (ADR):** + +* **Warum nicht Room (KMP)?** Room ist fĂŒr KMP noch sehr neu (Alpha/Beta Status) und bringt viel Overhead mit sich (SQLite Bundling etc.). +* **Warum SQLDelight?** + 1. **Schema First:** Du schreibst SQL (`.sq`), und Kotlin-Code wird *generiert*. Das zwingt Entwickler dazu, ĂŒber ihr Datenmodell nachzudenken, bevor sie Code schreiben. + 2. **Performance:** Es ist extrem leichtgewichtig und typ-sicher. + 3. **Migrationen:** SQLDelight hat ein exzellentes System fĂŒr Schema-Migrationen (`1.sqm`, `2.sqm`), was fĂŒr Desktop-Apps (die nicht einfach "neu geladen" werden können wie Webseiten) essenziell ist. + +**Eintrag im Guide:** + +> **DB-Guideline:** +> +> * Jedes Feature definiert sein Schema in `:frontend:core:local-db/src/commonMain/sqldelight/...`. +> * Business-Logik darf niemals SQL-Strings enthalten. Nutze die generierten `Queries`-Objekte. +> * Migrationen sind Pflicht bei Schema-Änderungen\! (Kein `DROP TABLE` in Production). + +----- + +### 3\. Konfliktstrategie bei Sync? + +**Entscheidung:** **Optimistic Locking** (Server Wins). + +**BegrĂŒndung (ADR):** + +* In einem System mit Offline-Clients ist "Last Write Wins" gefĂ€hrlich (Lagerbestand wird ĂŒberschrieben). +* **Strategie:** + 1. Jedes Entity hat eine `lastUpdated` (Timestamp) Spalte. + 2. Der Client sendet beim Update die Version mit, die er *kennt*. + 3. Wenn Server-Version \> Client-Version → **HTTP 409 Conflict**. + 4. Client muss Daten neu laden (Refresh) und User fragen/informieren. + +**Eintrag im Guide:** + +```kotlin +// GUIDELINE: Sync & Conflicts +// Das Frontend fĂŒhrt KEIN komplexes Merging durch. + +suspend fun updateStock(item: Item) { + try { + api.update(item.id, item.newStock, currentVersion = item.version) + // Happy Path: DB Update + } catch (e: ConflictException) { // HTTP 409 + // 1. Markiere Item in UI als "Out of Sync" (Rot) + // 2. Trigger automatischen Refresh vom Server + // 3. Zeige User Toast: "Daten waren veraltet. Bitte prĂŒfen." + repo.refreshSingleItem(item.id) + } +} +``` + +----- + +### 4\. Error Budgets / SLIs (Stale Data Indikatoren) + +**Entscheidung:** **Visual Freshness Indicators** (Ampel-System). + +**BegrĂŒndung (ADR):** + +* Ein User muss wissen, ob der Lagerbestand "live" ist oder "von gestern". +* Wir definieren keine harten Timeouts (App blockieren), sondern weiche UI-Hinweise. + +**Eintrag im Guide:** + +> **UI-Regel "Data Freshness":** +> Jedes Entity in der lokalen DB hat ein Feld `lastSyncedAt`. Das UI reagiert darauf: +> +> * **\< 5 min:** ✅ Normalzustand (Kein Indikator). +> * **\> 5 min:** ⚠ Kleines gelbes "Wolke"-Icon oder ausgegrauter Text (Warnung). +> * **\> 1 Stunde:** ❌ Roter Banner "Offline-Daten: Bestand nicht garantiert". +> * **Aktion:** Schreibende Operationen sind bei "Rot" fĂŒr kritische Bereiche (z.B. Inventur-Abschluss) gesperrt, fĂŒr unkritische (z.B. Notiz anlegen) erlaubt (Queue). + +----- + +### 5\. API-VertrĂ€ge und Kapselung der Feature-Teams + +**Entscheidung:** **Loose Coupling via Navigation Routes & Shared Data Models (Core)**. + +**BegrĂŒndung (ADR):** + +* Wir wollen vermeiden, dass Team A (Inventory) direkt Klassen von Team B (Checkout) importiert. Das fĂŒhrt zum "Monolithen-Klumpen". +* Wir nutzen **keine** separaten Gradle-Module pro Feature-API (`:inventory-api`, `:inventory-impl`), da dies den Build-Graph unnötig aufblĂ€ht ("Gradle Overhead"). + +**Strategie:** + +1. **Schnittstelle:** Die einzige "Public API" eines Features ist sein `EntryPoint` (Composable) und seine `Route` (String). +2. **Datenaustausch:** + * *Minimal:* Über URL-Parameter (IDs). `navigator.navigate("inventory/details/123")`. + * *Objekte:* Wenn komplexe Objekte geteilt werden mĂŒssen (z.B. `UserProfile`), gehören diese in **`:frontend:core:domain`** (Shared Kernel). + +**Eintrag im Guide:** + +```kotlin +// GUIDELINE: Feature Isolation +// 1. Features importieren NIEMALS andere Features im `build.gradle.kts`. +// 2. Kommunikation nur ĂŒber Navigation (Router). +// 3. Gemeinsam genutzte Datenobjekte (z.B. UserID, ShopID) liegen in :core:domain. + +// FALSCH: +import com.project.features.billing.Invoice // AbhĂ€ngigkeit zu anderem Feature! + +// RICHTIG: +// Feature A navigiert zu Feature B via Route +navigator.navigateTo("billing/create?orderId=123") +``` + +----- + +### Zusammenfassung fĂŒr dein Dokument + +Diese 5 Punkte schließen den Kreis: + +1. **Koin** hĂ€lt den Code sauber. +2. **SQLDelight** hĂ€lt die Daten sicher. +3. **Optimistic Locking** verhindert DatenmĂŒll. +4. **Freshness UI** managed die Erwartungshaltung des Users. +5. **Core Domain** verhindert Spaghetti-Code zwischen Features. diff --git a/docs/clients/visionen/ProjectArchitecture_StructureGuide.md b/docs/clients/visionen/ProjectArchitecture_StructureGuide.md new file mode 100644 index 00000000..d24c541d --- /dev/null +++ b/docs/clients/visionen/ProjectArchitecture_StructureGuide.md @@ -0,0 +1,155 @@ +# 🏗 Project Architecture & Structure Guide + +> **"Code is liability. Structure is asset."** +> Wir bauen dieses System nicht fĂŒr den schnellsten Start, sondern fĂŒr die **Wartbarkeit ĂŒber Jahre**, Offline-FĂ€higkeit und Skalierbarkeit ĂŒber mehrere Teams hinweg. + +----- + +## 1\. Die Große Übersicht: The Monorepo Strategy + +Wir organisieren Backend und Frontend in einem einzigen Repository (Monorepo). + +### **Warum Monorepo? (Decision Record)** + +* ❌ **Alternative:** Getrennte Repositories fĂŒr Backend, Web-Frontend, Desktop-App. +* **Problem dabei:** "Version Hell". Backend Ă€ndert API v1 zu v2, aber Frontend-Repo ist noch auf v1. Refactorings ĂŒber die ganze Kette sind schmerzhaft. +* ✅ **Unsere Entscheidung:** Monorepo. + * **Atomic Commits:** Ein Pull Request enthĂ€lt Backend-Änderungen UND die dazugehörige Frontend-Anpassung. + * **Single Versioning:** Wir nutzen `gradle/libs.versions.toml` als einzige Quelle der Wahrheit fĂŒr Library-Versionen (z.B. Kotlin Version) ĂŒber das gesamte System hinweg. + +----- + +## 2\. Der "Deep Dive" in die Ordnerstruktur + +Hier ist der detaillierte Aufriss unseres Dateisystems. Jeder Ordner hat einen spezifischen architektonischen Zweck. + +```text +/my-project-root +│ +├── ⚙ docker-compose.yml <-- Die lokale "Cloud". Startet DBs, Gateway & Services. +├── 📄 settings.gradle.kts <-- Definiert die Module (Frontend & Backend). +├── 📂 gradle +│ └── libs.versions.toml <-- 🛑 STOP! Hier werden Versionen definiert. Nirgendwo sonst. +│ +├── 📂 backend <-- ARCHITEKTUR: Hexagonal / DDD +│ ├── 📂 gateway <-- Der "TĂŒrsteher". Routing & Auth-Check. +│ ├── 📂 discovery <-- Das "Telefonbuch" (Consul/Service Registry). +│ └── 📂 services <-- Die Business Logic (Microservices) +│ ├── 📂 inventory-service +│ │ ├── 📄 Dockerfile <-- Jedes Service ist ein isolierter Container! +│ │ └── 📂 src/main/kotlin/.../domain <-- Reine Logik, kein Spring! +│ └── 📂 auth-service +│ +└── 📂 frontend <-- ARCHITEKTUR: Kotlin Multiplatform (KMP) + │ + ├── 📂 shells <-- 💡 CONCEPT: "The Assembler" + │ │ Das sind die ausfĂŒhrbaren Anwendungen. Sie enthalten KEINE Logik. + │ │ Sie "kleben" nur Features zusammen und konfigurieren DI. + │ │ + │ ├── 📂 warehouse-app <-- Desktop-App (Windows/Linux) fĂŒr Lageristen + │ │ └── build.gradle.kts (bindet :features:inventory ein) + │ └── 📂 admin-portal <-- Web-App (JS/Wasm) fĂŒr Management + │ └── build.gradle.kts (bindet alle Features ein) + │ + ├── 📂 features <-- 💡 CONCEPT: "Vertical Slices" (Micro-Frontends) + │ │ Hier passiert die Arbeit. Ein Feature gehört einem Team. + │ │ + │ ├── 📂 inventory-feature + │ │ ├── 📂 src/commonMain + │ │ │ ├── 📂 api <-- Public Interface (Der Vertrag nach außen) + │ │ │ ├── 📂 ui <-- Screens & Components (Internal) + │ │ │ └── 📂 data <-- Repository & SSoT (Internal) + │ │ └── build.gradle.kts + │ └── 📂 auth-feature + │ + └── 📂 core <-- 💡 CONCEPT: "Shared Foundation" + │ Code, der sich selten Ă€ndert, aber ĂŒberall genutzt wird. + │ + ├── 📂 design-system <-- UI-Baukasten (Farben, Typo, Buttons) + ├── 📂 network <-- HTTP Clients & Auth-Interceptor + ├── 📂 local-db <-- SQLDelight Schemas (Die Offline-Wahrheit) + └── 📂 auth <-- OAuth2 Logik (Browser Bridge fĂŒr Desktop) +``` + +----- + +## 3\. Architectural Decision Records (ADRs) + +Warum haben wir das so gebaut? Hier sind die Antworten auf die "Warum nicht X?" Fragen. + +### ADR 001: Kotlin Multiplatform vs. Electron / Web-Wrapper + +* **Kontext:** Wir brauchen eine Web-App UND eine Desktop-App. +* **Entscheidung:** Wir nutzen **Kotlin Multiplatform (Compose)**. +* **BegrĂŒndung:** + * *Performance:* Electron braucht pro App \~200MB RAM (Chromium Instanz). Unsere Desktop-Apps (Lager, Kasse) laufen auf schwacher Hardware. JVM/Native ist effizienter. + * *Type Safety:* Wir teilen Business-Logik (Validation, SSoT) zwischen Web und Desktop. Mit JS/Electron mĂŒssten wir Logik duplizieren oder transpilen. + * *Offline:* Echte SQL-Datenbank (SQLite) Integration ist in nativem Code robuster als im Browser-Storage. + +### ADR 002: Multiple App Shells vs. One "Super-App" + +* **Kontext:** Wir haben Lagerarbeiter, Kassierer und Manager. +* **Entscheidung:** Wir bauen **pro Rolle eine eigene "Shell"** (Executable). +* **BegrĂŒndung:** + * *Security (Web):* "Tree Shaking". Wenn der Code fĂŒr "Admin-User-Löschen" gar nicht erst in der `warehouse-app.js` enthalten ist, kann er auch nicht gehackt werden. + * *Focus (Desktop):* Die Lager-App startet schneller und hat weniger Bugs, weil sie den Code fĂŒr das Rechnungswesen gar nicht lĂ€dt. + * *FlexibilitĂ€t:* Wir können Features wiederverwenden. Das Feature `auth-feature` ist in ALLEN Apps, `inventory-feature` nur in zweien. + +### ADR 003: Single Source of Truth (SSoT) via Database + +* **Kontext:** Desktop-Apps werden in Hallen mit schlechtem WLAN genutzt. +* **Entscheidung:** **Database First Architecture**. +* **BegrĂŒndung:** + * Klassisch (`UI -> API -> UI`) fĂŒhrt zu weißen Screens und Ladekreisen bei Netzschwankungen. + * Wir nutzen `UI -> Local DB <- Sync -> API`. + * Das UI zeigt **immer** Daten an (auch wenn sie 10 Minuten alt sind). Der User kann arbeiten. Sync passiert transparent im Hintergrund. + +### ADR 004: Docker fĂŒr alles (außer Desktop Runtime) + +* **Kontext:** "Bei mir lĂ€uft's aber..." Probleme. +* **Entscheidung:** Das gesamte Backend + Web-Frontend Build-Pipeline lĂ€uft in Docker. +* **BegrĂŒndung:** + * Die `docker-compose.yml` ist die Wahrheit. + * FĂŒr die Desktop-Entwicklung nutzen wir Gradle lokal, aber der Server, gegen den entwickelt wird, lĂ€uft im Container. Das garantiert IdentitĂ€t zwischen Dev und Prod. + +----- + +## 4\. Guidelines: Wo gehört mein Code hin? + +Wenn du neuen Code schreibst, stelle dir diese Fragen: + +### Q1: Ist es Business Logik (z.B. "Preis berechnen")? + +* âžĄïž Gehört in **`/backend/services/.../domain`** (Server-Side Validierung ist Pflicht). +* âžĄïž UND optional in **`/frontend/features/.../domain`** (fĂŒr schnelle UI-Feedback, aber Server hat das letzte Wort). + +### Q2: Ist es ein UI-Element (z.B. "Runder Button")? + +* âžĄïž Gehört in **`/frontend/core/design-system`**. +* 🛑 *Stop\!* Baue keine Custom Buttons in deinem Feature-Ordner. Nutze das Design System. Wenn etwas fehlt, erweitere das Design System. + +### Q3: Ich brauche Daten von einem anderen Service. + +* **Szenario:** Im "Checkout" (Kasse) brauche ich den Produktnamen aus dem "Inventory". +* ❌ **Falsch:** `CheckoutService` ruft `InventoryService` Datenbank direkt ab. +* ✅ **Richtig (Backend):** `CheckoutService` ruft `InventoryService` via REST/gRPC ĂŒber das Gateway. +* ✅ **Richtig (Frontend):** Das `Checkout-Feature` kennt das `Inventory-Feature` nicht. Es bekommt nur eine `productId`. Wenn es Details anzeigen muss, nutzt es entweder ein eigenes minimales Datenmodell oder fragt das Backend. + +### Q4: Auth Token Handling + +* ❌ **Niemals:** `httpClient.header("Authorization", token)` manuell aufrufen. +* ✅ **Immer:** Nutze den konfigurierten Client aus dem DI-Container: `get(named("apiClient"))`. Die Architektur kĂŒmmert sich um Refresh und Injection. + +----- + +## 5\. Das "Mental Model" fĂŒr Entwickler + +Stell dir unsere App wie einen **Lego-Baukasten** vor. + +1. **Core (Platte):** Das Fundament (Auth, Network, Design). Muss immer da sein. +2. **Features (Steine):** Bunte Bausteine (Inventory, Cart, Profile). Sie berĂŒhren sich seitlich nicht (keine direkten AbhĂ€ngigkeiten). +3. **Shells (Modelle):** Das fertige Haus. + * Haus A (Admin Portal) nutzt alle Steine. + * Haus B (Lager App) nutzt nur die grĂŒnen Steine (Inventory). + +Dein Job als Entwickler ist es meistens, **einen neuen Stein (Feature)** zu bauen oder einen bestehenden zu verbessern. Du musst dich selten um das Fundament oder das fertige Haus kĂŒmmern. diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 00000000..3f12a19d --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,7 @@ +# Frontend + +Kotlin Multiplatform Frontend layer. + +- shells: ausfĂŒhrbare Anwendungen (Assembler) +- features: Vertical Slices (kein Feature→Feature Import) +- core: gemeinsame Basis (Design-System, Network, Local-DB, Auth, Domain) diff --git a/frontend/core/.gitkeep b/frontend/core/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/clients/shared/common-ui/build.gradle.kts b/frontend/core/design-system/build.gradle.kts similarity index 100% rename from clients/shared/common-ui/build.gradle.kts rename to frontend/core/design-system/build.gradle.kts diff --git a/clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppFooter.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppFooter.kt similarity index 100% rename from clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppFooter.kt rename to frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppFooter.kt diff --git a/clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppHeader.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppHeader.kt similarity index 100% rename from clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppHeader.kt rename to frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppHeader.kt diff --git a/clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppScaffold.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppScaffold.kt similarity index 100% rename from clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppScaffold.kt rename to frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/AppScaffold.kt diff --git a/clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/LoadingIndicator.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/LoadingIndicator.kt similarity index 100% rename from clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/LoadingIndicator.kt rename to frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/LoadingIndicator.kt diff --git a/clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/MeldestelleButton.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/MeldestelleButton.kt similarity index 100% rename from clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/MeldestelleButton.kt rename to frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/MeldestelleButton.kt diff --git a/clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/MeldestelleTextField.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/MeldestelleTextField.kt similarity index 100% rename from clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/MeldestelleTextField.kt rename to frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/MeldestelleTextField.kt diff --git a/clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/NotificationCard.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/NotificationCard.kt similarity index 100% rename from clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/NotificationCard.kt rename to frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/components/NotificationCard.kt diff --git a/clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/theme/AppTheme.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/theme/AppTheme.kt similarity index 100% rename from clients/shared/common-ui/src/commonMain/kotlin/at/mocode/clients/shared/commonui/theme/AppTheme.kt rename to frontend/core/design-system/src/commonMain/kotlin/at/mocode/clients/shared/commonui/theme/AppTheme.kt diff --git a/clients/shared/navigation/build.gradle.kts b/frontend/core/navigation/build.gradle.kts similarity index 100% rename from clients/shared/navigation/build.gradle.kts rename to frontend/core/navigation/build.gradle.kts diff --git a/clients/shared/navigation/src/commonMain/kotlin/at/mocode/clients/shared/navigation/AppScreen.kt b/frontend/core/navigation/src/commonMain/kotlin/at/mocode/clients/shared/navigation/AppScreen.kt similarity index 100% rename from clients/shared/navigation/src/commonMain/kotlin/at/mocode/clients/shared/navigation/AppScreen.kt rename to frontend/core/navigation/src/commonMain/kotlin/at/mocode/clients/shared/navigation/AppScreen.kt diff --git a/frontend/features/.gitkeep b/frontend/features/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/clients/app/build.gradle.kts b/frontend/shells/meldestelle-portal/build.gradle.kts similarity index 93% rename from clients/app/build.gradle.kts rename to frontend/shells/meldestelle-portal/build.gradle.kts index bf8828be..92b80868 100644 --- a/clients/app/build.gradle.kts +++ b/frontend/shells/meldestelle-portal/build.gradle.kts @@ -76,11 +76,15 @@ kotlin { commonMain.dependencies { // Shared modules implementation(project(":clients:shared")) - implementation(project(":clients:shared:common-ui")) - implementation(project(":clients:shared:navigation")) + implementation(project(":frontend:core:design-system")) + implementation(project(":frontend:core:navigation")) + implementation(project(":frontend:core:network")) implementation(project(":clients:auth-feature")) implementation(project(":clients:ping-feature")) + // DI (Koin) needed to call initKoin { modules(...) } + implementation(libs.koin.core) + // Compose Multiplatform implementation(compose.runtime) implementation(compose.foundation) @@ -100,6 +104,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(libs.kotlinx.coroutines.swing) implementation(libs.kotlinx.coroutines.core) + implementation(libs.koin.core) } jsMain.dependencies { diff --git a/clients/app/src/commonMain/kotlin/DevelopmentMode.kt b/frontend/shells/meldestelle-portal/src/commonMain/kotlin/DevelopmentMode.kt similarity index 100% rename from clients/app/src/commonMain/kotlin/DevelopmentMode.kt rename to frontend/shells/meldestelle-portal/src/commonMain/kotlin/DevelopmentMode.kt diff --git a/clients/app/src/commonMain/kotlin/MainApp.kt b/frontend/shells/meldestelle-portal/src/commonMain/kotlin/MainApp.kt similarity index 100% rename from clients/app/src/commonMain/kotlin/MainApp.kt rename to frontend/shells/meldestelle-portal/src/commonMain/kotlin/MainApp.kt diff --git a/clients/app/src/commonTest/kotlin/ComposeAppCommonTest.kt b/frontend/shells/meldestelle-portal/src/commonTest/kotlin/ComposeAppCommonTest.kt similarity index 100% rename from clients/app/src/commonTest/kotlin/ComposeAppCommonTest.kt rename to frontend/shells/meldestelle-portal/src/commonTest/kotlin/ComposeAppCommonTest.kt diff --git a/clients/app/src/jsMain/kotlin/DevelopmentMode.js.kt b/frontend/shells/meldestelle-portal/src/jsMain/kotlin/DevelopmentMode.js.kt similarity index 100% rename from clients/app/src/jsMain/kotlin/DevelopmentMode.js.kt rename to frontend/shells/meldestelle-portal/src/jsMain/kotlin/DevelopmentMode.js.kt diff --git a/clients/app/src/jsMain/kotlin/main.kt b/frontend/shells/meldestelle-portal/src/jsMain/kotlin/main.kt similarity index 59% rename from clients/app/src/jsMain/kotlin/main.kt rename to frontend/shells/meldestelle-portal/src/jsMain/kotlin/main.kt index 7c71a485..69f1f198 100644 --- a/clients/app/src/jsMain/kotlin/main.kt +++ b/frontend/shells/meldestelle-portal/src/jsMain/kotlin/main.kt @@ -2,10 +2,40 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.window.ComposeViewport import kotlinx.browser.document import org.w3c.dom.HTMLElement +import at.mocode.clients.shared.di.initKoin +import at.mocode.frontend.core.network.networkModule +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import org.koin.core.context.GlobalContext +import org.koin.core.qualifier.named +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.request.get @OptIn(ExperimentalComposeUiApi::class) fun main() { console.log("[WebApp] main() entered") + // Initialize DI (Koin) with shared modules + network module + try { + initKoin { modules(networkModule) } + console.log("[WebApp] Koin initialized with networkModule") + } catch (e: dynamic) { + console.warn("[WebApp] Koin initialization warning:", e) + } + // Simple smoke request using DI apiClient + try { + val client = GlobalContext.get().get(named("apiClient")) + MainScope().launch { + try { + val resp: String = client.get("/api/ping/health").body() + console.log("[WebApp] /api/ping/health → ", resp) + } catch (e: dynamic) { + console.warn("[WebApp] /api/ping/health failed:", e?.message ?: e) + } + } + } catch (e: dynamic) { + console.warn("[WebApp] Unable to resolve apiClient from Koin:", e) + } fun startApp() { try { console.log("[WebApp] startApp(): readyState=", document.asDynamic().readyState) diff --git a/clients/app/src/jsMain/resources/icons/icon-192.png b/frontend/shells/meldestelle-portal/src/jsMain/resources/icons/icon-192.png similarity index 100% rename from clients/app/src/jsMain/resources/icons/icon-192.png rename to frontend/shells/meldestelle-portal/src/jsMain/resources/icons/icon-192.png diff --git a/clients/app/src/jsMain/resources/icons/icon-512.png b/frontend/shells/meldestelle-portal/src/jsMain/resources/icons/icon-512.png similarity index 100% rename from clients/app/src/jsMain/resources/icons/icon-512.png rename to frontend/shells/meldestelle-portal/src/jsMain/resources/icons/icon-512.png diff --git a/clients/app/src/jsMain/resources/index.html b/frontend/shells/meldestelle-portal/src/jsMain/resources/index.html similarity index 56% rename from clients/app/src/jsMain/resources/index.html rename to frontend/shells/meldestelle-portal/src/jsMain/resources/index.html index 47fb586b..9c7f76f0 100644 --- a/clients/app/src/jsMain/resources/index.html +++ b/frontend/shells/meldestelle-portal/src/jsMain/resources/index.html @@ -12,6 +12,24 @@
Loading...
+