diff --git a/claude.md b/claude.md
index eff087d..e0c3ef1 100644
--- a/claude.md
+++ b/claude.md
@@ -1,4 +1,4 @@
-# AI-Assisted Architecture Guardrails (Copy-Paste Identical)
+# AI-Assisted Architecture Guardrails (Case-Service Architecture)
This repo will be developed by different AI tools across a group. Keep the architecture clean and predictable by following these conventions every time you add or change code.
@@ -6,209 +6,105 @@ This repo will be developed by different AI tools across a group. Keep the archi
## Goals (what we optimize for)
-1. Security first: authorization and data-access controls must be correct by construction.
-2. Clear layering: domain logic is independent of Spring/Web/DB/S3.
+1. Security first: authorization and data-access controls must be correct.
+2. Clear layering: separation of concerns between DTOs, Services, and Entities.
3. Auditability: every important activity produces an audit record.
-4. Real-time updates: changes are emitted as events and delivered to clients.
+4. Transactional Integrity: business operations are wrapped in SQL transactions.
---
## Project Stack
- Java 25
-- Spring Boot + Maven
+- Spring Boot (Web + Data JPA) + Maven
+- H2 Database (or SQL compatible)
- JUnit tests
-- Docker (local dev + integration)
-- Server-side templates: Thymeleaf or JTE-templates
+- Thymeleaf templates
-## Target Clean Architecture (Spring Boot)
+---
+
+## Core Architectural Components
-Use a consistent package split under your base package (`org.example.projektarendehantering`).
+For every feature (e.g., "Case"), we maintain a consistent set of components:
-### Recommended package layout
+### 1. `*Controller` (Presentation Layer)
+- **Role:** Handles incoming HTTP requests and interacts with the frontend.
+- **Location:** `...presentation.rest`
+- **Responsibility:** Translates between HTTP payloads and DTOs. Thin logic only.
+- **Injected with:** `*Service`.
-- `...domain`
- - Pure domain model: entities/aggregates, value objects, domain services, domain policies
- - No Spring annotations
- - No direct JDBC/JPA/S3/Web dependencies
+### 2. `*DTO` (Data Transfer Object)
+- **Role:** Temporary objects for frontend interaction.
+- **Location:** `...presentation.dto`
+- **Responsibility:** Placeholder for data before it is converted to an entity or returned to the client.
-- `...application`
- - Use cases (application services) that orchestrate domain + ports
- - Permission checks happen here (not only in controllers)
- - Defines port interfaces (e.g. `EventPublisher`, `CaseRepository`, `FileStorage`)
+### 3. `*Entity` (Infrastructure/Persistence Layer)
+- **Role:** The data object written to the database.
+- **Location:** `...infrastructure.persistence`
+- **Responsibility:** JPA-mapped object representing the database schema.
-- `...presentation`
- - Controllers (REST), WebSocket handlers, request/response DTOs
- - Translates between HTTP/Web payloads and application commands/queries
- - Keep controllers thin: no heavy logic, no direct persistence/S3 calls
+### 4. `*Mapper` (Application Layer)
+- **Role:** Utility for object conversion.
+- **Location:** `...application.service`
+- **Responsibility:** Mapping `DTO -> Entity` and `Entity -> DTO`.
-- `...infrastructure`
- - Adapters that implement application ports
- - Persistence (JPA repositories, migrations)
- - S3-compatible storage client/adapter
- - Real-time delivery adapter (WebSocket/SSE)
- - Security adapter integrating with Spring Security
- - Framework-specific configuration
+### 5. `*Service` (Application Layer)
+- **Role:** The core business logic handler.
+- **Location:** `...application.service`
+- **Responsibility:** Handles SQL transactions (using `@Transactional`).
+- **Injected with:** `*Repository` and `*Mapper`.
-- `...common` (optional but recommended)
- - Cross-cutting domain-independent utilities: IDs, error types, time abstraction, shared DTO base types
+### 6. `*Repository` (Infrastructure Layer)
+- **Role:** Database access.
+- **Location:** `...infrastructure.persistence`
+- **Responsibility:** Extends `JpaRepository` for SQL operations.
-### Folder structure (mirrors packages)
+---
-Maintain the following in `src/main/java`:
+## Package layout
```text
src/main/java/
org/example/projektarendehantering/
- ProjektArendehanteringApplication.java
- common/
- domain/
+ common/ (Cross-cutting utilities)
+ domain/ (Core business logic / legacy domain models)
application/
+ service/ (Services, Mappers)
+ ports/ (Interface boundaries)
presentation/
+ rest/ (Controllers)
+ dto/ (DTOs)
+ web/ (UI Controllers)
infrastructure/
+ persistence/ (Entities, Repositories)
+ config/ (Spring Config)
```
-Maintain the following in `src/test/java`:
-
-```text
-src/test/java/
- org/example/projektarendehantering/
- domain/ (unit tests)
- application/ (use-case tests with mocks/fakes)
- infrastructure/(adapter tests with testcontainers or fakes)
- presentation/ (controller tests)
-```
-
----
-
-## Naming conventions (enforce consistency)
-
-1. Use-case classes: suffix with `UseCase` or `Service` (pick one style and stick to it).
-2. Port interfaces (application boundary): name them as nouns + suffix `Port`.
- - Example: `FileStoragePort`, `CaseEventPublisherPort`
-3. Adapter implementations (infrastructure): suffix with `*Adapter` or `*JpaRepository` as appropriate.
- - Example: `S3FileStorageAdapter implements FileStoragePort`
-4. Controller classes: suffix with `*Controller`.
-5. DTOs:
- - Incoming: `*Request`
- - Outgoing: `*Response`
-6. Domain objects:
- - Entities: nouns (e.g. `Case`)
- - Value objects: `*Id`, `*Name`, `*Policy`, etc.
-
----
-
-## Security & Authorization (central requirement)
-
-### Where checks must happen
-
-- Authorization must be enforced in the `application` layer.
-- Controllers must not assume the user is allowed; they pass commands/queries to application services, which verify permissions.
-
-### How to structure authorization
-
-1. Define role/user concepts in `domain` (or `common` if shared).
-2. Create an application-level authorization component (port + implementation).
- - Example ports:
- - `CurrentUserPort` (who am I?)
- - `AuthorizationPolicyPort` (am I allowed?)
-3. Permission logic lives in domain/application policies, not spread across controllers.
-
-### Audit on security-sensitive actions
-
-- If access is denied, decide whether to audit it (at least log internally).
-- If access is granted and data is returned (e.g., file download), audit the event.
-
----
-
-## File handling & S3 (strict access)
-
-### Recommended approach
-
-1. Store only file metadata + S3 key in the database (not the file bytes).
-2. For download:
- - Application service authorizes the user for that specific case + document.
- - Infrastructure adapter streams from S3 only after successful authorization.
-3. For upload:
- - Application service authorizes the user for the target case.
- - Infrastructure adapter uploads bytes and returns the stored S3 key + metadata.
-
-### Ports
-
-Create ports in `application`:
-- `FileStoragePort` (upload/download/delete)
-- `DocumentMetadataRepositoryPort` (persist metadata + link to cases)
-
-Implement in `infrastructure`:
-- `S3FileStorageAdapter` (S3-compatible client)
-
---
-## Logging & Audit Trail (transparency)
-
-Every important activity must create an audit event.
-
-### Define an audit event model
-
-In `domain`, define:
-- `AuditEvent` (type, timestamp, actor, target identifiers, details)
-
-### Persist and publish audit
+## Naming conventions
-In `application`:
-- Use an application port like `AuditLogPort` to persist audit events
-- Optionally also publish to the real-time system
-
-In `infrastructure`:
-- Implement persistence (`AuditLogJpaAdapter`, etc.)
-- Optionally forward to external log/stream
+1. **Controllers:** Suffix with `Controller` (e.g., `CaseController`).
+2. **Services:** Suffix with `Service` (e.g., `CaseService`).
+3. **Mappers:** Suffix with `Mapper` (e.g., `CaseMapper`).
+4. **DTOs:** Suffix with `DTO` (e.g., `CaseDTO`).
+5. **Entities:** Suffix with `Entity` (e.g., `CaseEntity`).
+6. **Repositories:** Suffix with `Repository` (e.g., `CaseRepository`).
---
-## Real-time updates (comments, changes, lifecycle events)
-
-### Event-driven pattern
-
-1. Application services create domain events (or audit events that double as domain events).
-2. Application publishes events via an application port.
-3. Infrastructure delivery adapter sends them to clients.
-
-Example ports:
-- `CaseEventPublisherPort` (publish lifecycle/comment/file-activity events)
-
-Example delivery adapters:
-- `WebSocketCaseEventsAdapter`
-- `SseCaseEventsAdapter` (if using SSE)
+## Security & Authorization
----
-
-## AI Tool Usage Rules (how teammates should prompt/code)
-
-1. When you propose code, explain:
- - which layer you touched (`domain`/`application`/`presentation`/`infrastructure`)
- - what port/interface boundary is used
- - where authorization and audit are enforced
-2. Never add Spring annotations in `domain`.
-3. Never call S3 or DB directly from `presentation`. Use application ports/use cases.
-4. Prefer interfaces in `application`; implement them in `infrastructure`.
-5. Add tests:
- - domain unit tests for pure behavior
- - application tests for use-case orchestration (mock ports)
- - integration/adapters tests for S3/JPA/WebSocket only when needed
-6. Keep configuration:
- - in `src/main/resources` (application properties)
- - framework wiring in `infrastructure` config classes
+- Authorization must be enforced in the `Service` layer or via Spring Security.
+- Controllers should not perform heavy logic; they delegate to Services which verify permissions.
---
-## Initial module checklist (when we start implementing)
-
-At minimum, we will eventually add:
-
-1. `domain`: Case lifecycle model + permissions concepts
-2. `application`: use cases for create/follow/assign/update/close; plus ports for files, audit, events, auth context
-3. `infrastructure`: S3 adapter, persistence adapters, auth adapter, event delivery adapter
-4. `presentation`: REST endpoints + DTOs + WebSocket endpoints
-
-Start small: add one vertical slice (e.g., create case + audit + real-time notification) and repeat.
+## AI Tool Usage Rules
+1. **Always use DTOs** for public API communication.
+2. **Never expose Entities** directly to the web layer.
+3. **Use Mappers** to handle the translation between layers.
+4. **Ensure @Transactional** is used on Service methods that modify data.
+5. **Verify changes** with `mvnw compile` before finishing.
+6. ** DO NOT TOUCH pom.xml, docker-compose or application.properties.
diff --git a/cursor.md b/cursor.md
index eff087d..5b6e415 100644
--- a/cursor.md
+++ b/cursor.md
@@ -1,4 +1,4 @@
-# AI-Assisted Architecture Guardrails (Copy-Paste Identical)
+# AI-Assisted Architecture Guardrails (Case-Service Architecture)
This repo will be developed by different AI tools across a group. Keep the architecture clean and predictable by following these conventions every time you add or change code.
@@ -6,209 +6,105 @@ This repo will be developed by different AI tools across a group. Keep the archi
## Goals (what we optimize for)
-1. Security first: authorization and data-access controls must be correct by construction.
-2. Clear layering: domain logic is independent of Spring/Web/DB/S3.
+1. Security first: authorization and data-access controls must be correct.
+2. Clear layering: separation of concerns between DTOs, Services, and Entities.
3. Auditability: every important activity produces an audit record.
-4. Real-time updates: changes are emitted as events and delivered to clients.
+4. Transactional Integrity: business operations are wrapped in SQL transactions.
---
## Project Stack
- Java 25
-- Spring Boot + Maven
+- Spring Boot (Web + Data JPA) + Maven
+- H2 Database (or SQL compatible)
- JUnit tests
-- Docker (local dev + integration)
-- Server-side templates: Thymeleaf or JTE-templates
+- Thymeleaf templates
-## Target Clean Architecture (Spring Boot)
+---
+
+## Core Architectural Components
-Use a consistent package split under your base package (`org.example.projektarendehantering`).
+For every feature (e.g., "Case"), we maintain a consistent set of components:
-### Recommended package layout
+### 1. `*Controller` (Presentation Layer)
+- **Role:** Handles incoming HTTP requests and interacts with the frontend.
+- **Location:** `...presentation.rest`
+- **Responsibility:** Translates between HTTP payloads and DTOs. Thin logic only.
+- **Injected with:** `*Service`.
-- `...domain`
- - Pure domain model: entities/aggregates, value objects, domain services, domain policies
- - No Spring annotations
- - No direct JDBC/JPA/S3/Web dependencies
+### 2. `*DTO` (Data Transfer Object)
+- **Role:** Temporary objects for frontend interaction.
+- **Location:** `...presentation.dto`
+- **Responsibility:** Placeholder for data before it is converted to an entity or returned to the client.
-- `...application`
- - Use cases (application services) that orchestrate domain + ports
- - Permission checks happen here (not only in controllers)
- - Defines port interfaces (e.g. `EventPublisher`, `CaseRepository`, `FileStorage`)
+### 3. `*Entity` (Infrastructure/Persistence Layer)
+- **Role:** The data object written to the database.
+- **Location:** `...infrastructure.persistence`
+- **Responsibility:** JPA-mapped object representing the database schema.
-- `...presentation`
- - Controllers (REST), WebSocket handlers, request/response DTOs
- - Translates between HTTP/Web payloads and application commands/queries
- - Keep controllers thin: no heavy logic, no direct persistence/S3 calls
+### 4. `*Mapper` (Application Layer)
+- **Role:** Utility for object conversion.
+- **Location:** `...application.service`
+- **Responsibility:** Mapping `DTO -> Entity` and `Entity -> DTO`.
-- `...infrastructure`
- - Adapters that implement application ports
- - Persistence (JPA repositories, migrations)
- - S3-compatible storage client/adapter
- - Real-time delivery adapter (WebSocket/SSE)
- - Security adapter integrating with Spring Security
- - Framework-specific configuration
+### 5. `*Service` (Application Layer)
+- **Role:** The core business logic handler.
+- **Location:** `...application.service`
+- **Responsibility:** Handles SQL transactions (using `@Transactional`).
+- **Injected with:** `*Repository` and `*Mapper`.
-- `...common` (optional but recommended)
- - Cross-cutting domain-independent utilities: IDs, error types, time abstraction, shared DTO base types
+### 6. `*Repository` (Infrastructure Layer)
+- **Role:** Database access.
+- **Location:** `...infrastructure.persistence`
+- **Responsibility:** Extends `JpaRepository` for SQL operations.
-### Folder structure (mirrors packages)
+---
-Maintain the following in `src/main/java`:
+## Package layout
```text
src/main/java/
org/example/projektarendehantering/
- ProjektArendehanteringApplication.java
- common/
- domain/
+ common/ (Cross-cutting utilities)
+ domain/ (Core business logic / legacy domain models)
application/
+ service/ (Services, Mappers)
+ ports/ (Interface boundaries)
presentation/
+ rest/ (Controllers)
+ dto/ (DTOs)
+ web/ (UI Controllers)
infrastructure/
+ persistence/ (Entities, Repositories)
+ config/ (Spring Config)
```
-Maintain the following in `src/test/java`:
-
-```text
-src/test/java/
- org/example/projektarendehantering/
- domain/ (unit tests)
- application/ (use-case tests with mocks/fakes)
- infrastructure/(adapter tests with testcontainers or fakes)
- presentation/ (controller tests)
-```
-
----
-
-## Naming conventions (enforce consistency)
-
-1. Use-case classes: suffix with `UseCase` or `Service` (pick one style and stick to it).
-2. Port interfaces (application boundary): name them as nouns + suffix `Port`.
- - Example: `FileStoragePort`, `CaseEventPublisherPort`
-3. Adapter implementations (infrastructure): suffix with `*Adapter` or `*JpaRepository` as appropriate.
- - Example: `S3FileStorageAdapter implements FileStoragePort`
-4. Controller classes: suffix with `*Controller`.
-5. DTOs:
- - Incoming: `*Request`
- - Outgoing: `*Response`
-6. Domain objects:
- - Entities: nouns (e.g. `Case`)
- - Value objects: `*Id`, `*Name`, `*Policy`, etc.
-
----
-
-## Security & Authorization (central requirement)
-
-### Where checks must happen
-
-- Authorization must be enforced in the `application` layer.
-- Controllers must not assume the user is allowed; they pass commands/queries to application services, which verify permissions.
-
-### How to structure authorization
-
-1. Define role/user concepts in `domain` (or `common` if shared).
-2. Create an application-level authorization component (port + implementation).
- - Example ports:
- - `CurrentUserPort` (who am I?)
- - `AuthorizationPolicyPort` (am I allowed?)
-3. Permission logic lives in domain/application policies, not spread across controllers.
-
-### Audit on security-sensitive actions
-
-- If access is denied, decide whether to audit it (at least log internally).
-- If access is granted and data is returned (e.g., file download), audit the event.
-
----
-
-## File handling & S3 (strict access)
-
-### Recommended approach
-
-1. Store only file metadata + S3 key in the database (not the file bytes).
-2. For download:
- - Application service authorizes the user for that specific case + document.
- - Infrastructure adapter streams from S3 only after successful authorization.
-3. For upload:
- - Application service authorizes the user for the target case.
- - Infrastructure adapter uploads bytes and returns the stored S3 key + metadata.
-
-### Ports
-
-Create ports in `application`:
-- `FileStoragePort` (upload/download/delete)
-- `DocumentMetadataRepositoryPort` (persist metadata + link to cases)
-
-Implement in `infrastructure`:
-- `S3FileStorageAdapter` (S3-compatible client)
-
---
-## Logging & Audit Trail (transparency)
-
-Every important activity must create an audit event.
-
-### Define an audit event model
-
-In `domain`, define:
-- `AuditEvent` (type, timestamp, actor, target identifiers, details)
-
-### Persist and publish audit
+## Naming conventions
-In `application`:
-- Use an application port like `AuditLogPort` to persist audit events
-- Optionally also publish to the real-time system
-
-In `infrastructure`:
-- Implement persistence (`AuditLogJpaAdapter`, etc.)
-- Optionally forward to external log/stream
+1. **Controllers:** Suffix with `Controller` (e.g., `CaseController`).
+2. **Services:** Suffix with `Service` (e.g., `CaseService`).
+3. **Mappers:** Suffix with `Mapper` (e.g., `CaseMapper`).
+4. **DTOs:** Suffix with `DTO` (e.g., `CaseDTO`).
+5. **Entities:** Suffix with `Entity` (e.g., `CaseEntity`).
+6. **Repositories:** Suffix with `Repository` (e.g., `CaseRepository`).
---
-## Real-time updates (comments, changes, lifecycle events)
-
-### Event-driven pattern
-
-1. Application services create domain events (or audit events that double as domain events).
-2. Application publishes events via an application port.
-3. Infrastructure delivery adapter sends them to clients.
-
-Example ports:
-- `CaseEventPublisherPort` (publish lifecycle/comment/file-activity events)
-
-Example delivery adapters:
-- `WebSocketCaseEventsAdapter`
-- `SseCaseEventsAdapter` (if using SSE)
+## Security & Authorization
----
-
-## AI Tool Usage Rules (how teammates should prompt/code)
-
-1. When you propose code, explain:
- - which layer you touched (`domain`/`application`/`presentation`/`infrastructure`)
- - what port/interface boundary is used
- - where authorization and audit are enforced
-2. Never add Spring annotations in `domain`.
-3. Never call S3 or DB directly from `presentation`. Use application ports/use cases.
-4. Prefer interfaces in `application`; implement them in `infrastructure`.
-5. Add tests:
- - domain unit tests for pure behavior
- - application tests for use-case orchestration (mock ports)
- - integration/adapters tests for S3/JPA/WebSocket only when needed
-6. Keep configuration:
- - in `src/main/resources` (application properties)
- - framework wiring in `infrastructure` config classes
+- Authorization must be enforced in the `Service` layer or via Spring Security.
+- Controllers should not perform heavy logic; they delegate to Services which verify permissions.
---
-## Initial module checklist (when we start implementing)
-
-At minimum, we will eventually add:
-
-1. `domain`: Case lifecycle model + permissions concepts
-2. `application`: use cases for create/follow/assign/update/close; plus ports for files, audit, events, auth context
-3. `infrastructure`: S3 adapter, persistence adapters, auth adapter, event delivery adapter
-4. `presentation`: REST endpoints + DTOs + WebSocket endpoints
-
-Start small: add one vertical slice (e.g., create case + audit + real-time notification) and repeat.
+## AI Tool Usage Rules
+1. **Always use DTOs** for public API communication.
+2. **Never expose Entities** directly to the web layer.
+3. **Use Mappers** to handle the translation between layers.
+4. **Ensure @Transactional** is used on Service methods that modify data.
+5. **Verify changes** with `mvnw compile` before finishing.
+6. 6. ** DO NOT TOUCH pom.xml, docker-compose or application.properties.
diff --git a/docker-compose.yml b/docker-compose.yml
index 4b4db37..5f378e6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -20,3 +20,4 @@ services:
volumes:
postgres_data:
+
diff --git a/gemini.md b/gemini.md
index eff087d..e0c3ef1 100644
--- a/gemini.md
+++ b/gemini.md
@@ -1,4 +1,4 @@
-# AI-Assisted Architecture Guardrails (Copy-Paste Identical)
+# AI-Assisted Architecture Guardrails (Case-Service Architecture)
This repo will be developed by different AI tools across a group. Keep the architecture clean and predictable by following these conventions every time you add or change code.
@@ -6,209 +6,105 @@ This repo will be developed by different AI tools across a group. Keep the archi
## Goals (what we optimize for)
-1. Security first: authorization and data-access controls must be correct by construction.
-2. Clear layering: domain logic is independent of Spring/Web/DB/S3.
+1. Security first: authorization and data-access controls must be correct.
+2. Clear layering: separation of concerns between DTOs, Services, and Entities.
3. Auditability: every important activity produces an audit record.
-4. Real-time updates: changes are emitted as events and delivered to clients.
+4. Transactional Integrity: business operations are wrapped in SQL transactions.
---
## Project Stack
- Java 25
-- Spring Boot + Maven
+- Spring Boot (Web + Data JPA) + Maven
+- H2 Database (or SQL compatible)
- JUnit tests
-- Docker (local dev + integration)
-- Server-side templates: Thymeleaf or JTE-templates
+- Thymeleaf templates
-## Target Clean Architecture (Spring Boot)
+---
+
+## Core Architectural Components
-Use a consistent package split under your base package (`org.example.projektarendehantering`).
+For every feature (e.g., "Case"), we maintain a consistent set of components:
-### Recommended package layout
+### 1. `*Controller` (Presentation Layer)
+- **Role:** Handles incoming HTTP requests and interacts with the frontend.
+- **Location:** `...presentation.rest`
+- **Responsibility:** Translates between HTTP payloads and DTOs. Thin logic only.
+- **Injected with:** `*Service`.
-- `...domain`
- - Pure domain model: entities/aggregates, value objects, domain services, domain policies
- - No Spring annotations
- - No direct JDBC/JPA/S3/Web dependencies
+### 2. `*DTO` (Data Transfer Object)
+- **Role:** Temporary objects for frontend interaction.
+- **Location:** `...presentation.dto`
+- **Responsibility:** Placeholder for data before it is converted to an entity or returned to the client.
-- `...application`
- - Use cases (application services) that orchestrate domain + ports
- - Permission checks happen here (not only in controllers)
- - Defines port interfaces (e.g. `EventPublisher`, `CaseRepository`, `FileStorage`)
+### 3. `*Entity` (Infrastructure/Persistence Layer)
+- **Role:** The data object written to the database.
+- **Location:** `...infrastructure.persistence`
+- **Responsibility:** JPA-mapped object representing the database schema.
-- `...presentation`
- - Controllers (REST), WebSocket handlers, request/response DTOs
- - Translates between HTTP/Web payloads and application commands/queries
- - Keep controllers thin: no heavy logic, no direct persistence/S3 calls
+### 4. `*Mapper` (Application Layer)
+- **Role:** Utility for object conversion.
+- **Location:** `...application.service`
+- **Responsibility:** Mapping `DTO -> Entity` and `Entity -> DTO`.
-- `...infrastructure`
- - Adapters that implement application ports
- - Persistence (JPA repositories, migrations)
- - S3-compatible storage client/adapter
- - Real-time delivery adapter (WebSocket/SSE)
- - Security adapter integrating with Spring Security
- - Framework-specific configuration
+### 5. `*Service` (Application Layer)
+- **Role:** The core business logic handler.
+- **Location:** `...application.service`
+- **Responsibility:** Handles SQL transactions (using `@Transactional`).
+- **Injected with:** `*Repository` and `*Mapper`.
-- `...common` (optional but recommended)
- - Cross-cutting domain-independent utilities: IDs, error types, time abstraction, shared DTO base types
+### 6. `*Repository` (Infrastructure Layer)
+- **Role:** Database access.
+- **Location:** `...infrastructure.persistence`
+- **Responsibility:** Extends `JpaRepository` for SQL operations.
-### Folder structure (mirrors packages)
+---
-Maintain the following in `src/main/java`:
+## Package layout
```text
src/main/java/
org/example/projektarendehantering/
- ProjektArendehanteringApplication.java
- common/
- domain/
+ common/ (Cross-cutting utilities)
+ domain/ (Core business logic / legacy domain models)
application/
+ service/ (Services, Mappers)
+ ports/ (Interface boundaries)
presentation/
+ rest/ (Controllers)
+ dto/ (DTOs)
+ web/ (UI Controllers)
infrastructure/
+ persistence/ (Entities, Repositories)
+ config/ (Spring Config)
```
-Maintain the following in `src/test/java`:
-
-```text
-src/test/java/
- org/example/projektarendehantering/
- domain/ (unit tests)
- application/ (use-case tests with mocks/fakes)
- infrastructure/(adapter tests with testcontainers or fakes)
- presentation/ (controller tests)
-```
-
----
-
-## Naming conventions (enforce consistency)
-
-1. Use-case classes: suffix with `UseCase` or `Service` (pick one style and stick to it).
-2. Port interfaces (application boundary): name them as nouns + suffix `Port`.
- - Example: `FileStoragePort`, `CaseEventPublisherPort`
-3. Adapter implementations (infrastructure): suffix with `*Adapter` or `*JpaRepository` as appropriate.
- - Example: `S3FileStorageAdapter implements FileStoragePort`
-4. Controller classes: suffix with `*Controller`.
-5. DTOs:
- - Incoming: `*Request`
- - Outgoing: `*Response`
-6. Domain objects:
- - Entities: nouns (e.g. `Case`)
- - Value objects: `*Id`, `*Name`, `*Policy`, etc.
-
----
-
-## Security & Authorization (central requirement)
-
-### Where checks must happen
-
-- Authorization must be enforced in the `application` layer.
-- Controllers must not assume the user is allowed; they pass commands/queries to application services, which verify permissions.
-
-### How to structure authorization
-
-1. Define role/user concepts in `domain` (or `common` if shared).
-2. Create an application-level authorization component (port + implementation).
- - Example ports:
- - `CurrentUserPort` (who am I?)
- - `AuthorizationPolicyPort` (am I allowed?)
-3. Permission logic lives in domain/application policies, not spread across controllers.
-
-### Audit on security-sensitive actions
-
-- If access is denied, decide whether to audit it (at least log internally).
-- If access is granted and data is returned (e.g., file download), audit the event.
-
----
-
-## File handling & S3 (strict access)
-
-### Recommended approach
-
-1. Store only file metadata + S3 key in the database (not the file bytes).
-2. For download:
- - Application service authorizes the user for that specific case + document.
- - Infrastructure adapter streams from S3 only after successful authorization.
-3. For upload:
- - Application service authorizes the user for the target case.
- - Infrastructure adapter uploads bytes and returns the stored S3 key + metadata.
-
-### Ports
-
-Create ports in `application`:
-- `FileStoragePort` (upload/download/delete)
-- `DocumentMetadataRepositoryPort` (persist metadata + link to cases)
-
-Implement in `infrastructure`:
-- `S3FileStorageAdapter` (S3-compatible client)
-
---
-## Logging & Audit Trail (transparency)
-
-Every important activity must create an audit event.
-
-### Define an audit event model
-
-In `domain`, define:
-- `AuditEvent` (type, timestamp, actor, target identifiers, details)
-
-### Persist and publish audit
+## Naming conventions
-In `application`:
-- Use an application port like `AuditLogPort` to persist audit events
-- Optionally also publish to the real-time system
-
-In `infrastructure`:
-- Implement persistence (`AuditLogJpaAdapter`, etc.)
-- Optionally forward to external log/stream
+1. **Controllers:** Suffix with `Controller` (e.g., `CaseController`).
+2. **Services:** Suffix with `Service` (e.g., `CaseService`).
+3. **Mappers:** Suffix with `Mapper` (e.g., `CaseMapper`).
+4. **DTOs:** Suffix with `DTO` (e.g., `CaseDTO`).
+5. **Entities:** Suffix with `Entity` (e.g., `CaseEntity`).
+6. **Repositories:** Suffix with `Repository` (e.g., `CaseRepository`).
---
-## Real-time updates (comments, changes, lifecycle events)
-
-### Event-driven pattern
-
-1. Application services create domain events (or audit events that double as domain events).
-2. Application publishes events via an application port.
-3. Infrastructure delivery adapter sends them to clients.
-
-Example ports:
-- `CaseEventPublisherPort` (publish lifecycle/comment/file-activity events)
-
-Example delivery adapters:
-- `WebSocketCaseEventsAdapter`
-- `SseCaseEventsAdapter` (if using SSE)
+## Security & Authorization
----
-
-## AI Tool Usage Rules (how teammates should prompt/code)
-
-1. When you propose code, explain:
- - which layer you touched (`domain`/`application`/`presentation`/`infrastructure`)
- - what port/interface boundary is used
- - where authorization and audit are enforced
-2. Never add Spring annotations in `domain`.
-3. Never call S3 or DB directly from `presentation`. Use application ports/use cases.
-4. Prefer interfaces in `application`; implement them in `infrastructure`.
-5. Add tests:
- - domain unit tests for pure behavior
- - application tests for use-case orchestration (mock ports)
- - integration/adapters tests for S3/JPA/WebSocket only when needed
-6. Keep configuration:
- - in `src/main/resources` (application properties)
- - framework wiring in `infrastructure` config classes
+- Authorization must be enforced in the `Service` layer or via Spring Security.
+- Controllers should not perform heavy logic; they delegate to Services which verify permissions.
---
-## Initial module checklist (when we start implementing)
-
-At minimum, we will eventually add:
-
-1. `domain`: Case lifecycle model + permissions concepts
-2. `application`: use cases for create/follow/assign/update/close; plus ports for files, audit, events, auth context
-3. `infrastructure`: S3 adapter, persistence adapters, auth adapter, event delivery adapter
-4. `presentation`: REST endpoints + DTOs + WebSocket endpoints
-
-Start small: add one vertical slice (e.g., create case + audit + real-time notification) and repeat.
+## AI Tool Usage Rules
+1. **Always use DTOs** for public API communication.
+2. **Never expose Entities** directly to the web layer.
+3. **Use Mappers** to handle the translation between layers.
+4. **Ensure @Transactional** is used on Service methods that modify data.
+5. **Verify changes** with `mvnw compile` before finishing.
+6. ** DO NOT TOUCH pom.xml, docker-compose or application.properties.
diff --git a/pom.xml b/pom.xml
index ab78dcc..9e5ce89 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,6 +91,11 @@
spring-security-test
test
+
+ com.h2database
+ h2
+ test
+
org.thymeleaf.extras
thymeleaf-extras-springsecurity6
@@ -106,4 +111,4 @@
-
+
\ No newline at end of file
diff --git a/src/main/java/org/example/projektarendehantering/application/ports/CaseEventPublisherPort.java b/src/main/java/org/example/projektarendehantering/application/ports/CaseEventPublisherPort.java
index 1f7c9de..091f25f 100644
--- a/src/main/java/org/example/projektarendehantering/application/ports/CaseEventPublisherPort.java
+++ b/src/main/java/org/example/projektarendehantering/application/ports/CaseEventPublisherPort.java
@@ -7,3 +7,4 @@ public interface CaseEventPublisherPort {
void publishCaseEvent(CaseEvent event);
}
+// Kommentar för att kunna pusha igen
\ No newline at end of file
diff --git a/src/main/java/org/example/projektarendehantering/application/service/CaseMapper.java b/src/main/java/org/example/projektarendehantering/application/service/CaseMapper.java
new file mode 100644
index 0000000..eec79f9
--- /dev/null
+++ b/src/main/java/org/example/projektarendehantering/application/service/CaseMapper.java
@@ -0,0 +1,31 @@
+package org.example.projektarendehantering.application.service;
+
+import org.example.projektarendehantering.infrastructure.persistence.CaseEntity;
+import org.example.projektarendehantering.presentation.dto.CaseDTO;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CaseMapper {
+
+ public CaseDTO toDTO(CaseEntity entity) {
+ if (entity == null) return null;
+ return new CaseDTO(
+ entity.getId(),
+ entity.getStatus(),
+ entity.getTitle(),
+ entity.getDescription(),
+ entity.getCreatedAt()
+ );
+ }
+
+ public CaseEntity toEntity(CaseDTO dto) {
+ if (dto == null) return null;
+ CaseEntity entity = new CaseEntity();
+ entity.setId(dto.getId());
+ entity.setStatus(dto.getStatus());
+ entity.setTitle(dto.getTitle());
+ entity.setDescription(dto.getDescription());
+ entity.setCreatedAt(dto.getCreatedAt());
+ return entity;
+ }
+}
diff --git a/src/main/java/org/example/projektarendehantering/application/service/CaseService.java b/src/main/java/org/example/projektarendehantering/application/service/CaseService.java
new file mode 100644
index 0000000..ffd69e3
--- /dev/null
+++ b/src/main/java/org/example/projektarendehantering/application/service/CaseService.java
@@ -0,0 +1,50 @@
+package org.example.projektarendehantering.application.service;
+
+import org.example.projektarendehantering.infrastructure.persistence.CaseEntity;
+import org.example.projektarendehantering.infrastructure.persistence.CaseRepository;
+import org.example.projektarendehantering.presentation.dto.CaseDTO;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+@Service
+public class CaseService {
+
+ private final CaseRepository caseRepository;
+ private final CaseMapper caseMapper;
+
+ public CaseService(CaseRepository caseRepository, CaseMapper caseMapper) {
+ this.caseRepository = caseRepository;
+ this.caseMapper = caseMapper;
+ }
+
+ @Transactional
+ public CaseDTO createCase(CaseDTO caseDTO) {
+ CaseEntity entity = caseMapper.toEntity(caseDTO);
+ if (entity.getStatus() == null) {
+ entity.setStatus("OPEN");
+ }
+ if (entity.getCreatedAt() == null) {
+ entity.setCreatedAt(Instant.now());
+ }
+ CaseEntity savedEntity = caseRepository.save(entity);
+ return caseMapper.toDTO(savedEntity);
+ }
+
+ @Transactional(readOnly = true)
+ public Optional getCase(UUID id) {
+ return caseRepository.findById(id).map(caseMapper::toDTO);
+ }
+
+ @Transactional(readOnly = true)
+ public List getAllCases() {
+ return caseRepository.findAll().stream()
+ .map(caseMapper::toDTO)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseCommand.java b/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseCommand.java
deleted file mode 100644
index 5998775..0000000
--- a/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseCommand.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.example.projektarendehantering.application.usecase;
-
-import java.util.Objects;
-
-public record CreateCaseCommand(String title, String description) {
-
- public CreateCaseCommand {
- Objects.requireNonNull(title, "title");
- Objects.requireNonNull(description, "description");
- }
-}
-
diff --git a/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseResult.java b/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseResult.java
deleted file mode 100644
index 86bc60e..0000000
--- a/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseResult.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.example.projektarendehantering.application.usecase;
-
-import org.example.projektarendehantering.domain.CaseId;
-
-public record CreateCaseResult(CaseId caseId) {
-}
-
diff --git a/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseUseCase.java b/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseUseCase.java
deleted file mode 100644
index a19f71b..0000000
--- a/src/main/java/org/example/projektarendehantering/application/usecase/CreateCaseUseCase.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.example.projektarendehantering.application.usecase;
-
-import org.example.projektarendehantering.application.ports.AuditLogPort;
-import org.example.projektarendehantering.application.ports.CaseEventPublisherPort;
-import org.example.projektarendehantering.application.ports.CaseRepositoryPort;
-import org.example.projektarendehantering.application.ports.CurrentUserPort;
-import org.example.projektarendehantering.domain.AuditEvent;
-import org.example.projektarendehantering.domain.Case;
-import org.example.projektarendehantering.domain.CaseEvent;
-import org.example.projektarendehantering.domain.CaseId;
-import org.example.projektarendehantering.domain.CasePermissions;
-import org.example.projektarendehantering.common.Actor;
-import org.springframework.stereotype.Service;
-
-@Service
-public class CreateCaseUseCase {
-
- private final CurrentUserPort currentUserPort;
- private final CaseRepositoryPort caseRepositoryPort;
- private final AuditLogPort auditLogPort;
- private final CaseEventPublisherPort caseEventPublisherPort;
-
- public CreateCaseUseCase(
- CurrentUserPort currentUserPort,
- CaseRepositoryPort caseRepositoryPort,
- AuditLogPort auditLogPort,
- CaseEventPublisherPort caseEventPublisherPort
- ) {
- this.currentUserPort = currentUserPort;
- this.caseRepositoryPort = caseRepositoryPort;
- this.auditLogPort = auditLogPort;
- this.caseEventPublisherPort = caseEventPublisherPort;
- }
-
- public CreateCaseResult execute(CreateCaseCommand command) {
- Actor actor = currentUserPort.currentUser();
- CasePermissions.assertCanCreate(actor);
-
- Case caseToCreate = Case.create(actor, command.title(), command.description());
- CaseId caseId = caseToCreate.id();
- caseRepositoryPort.save(caseToCreate);
-
- AuditEvent auditEvent = AuditEvent.caseCreated(actor, caseId, command.title(), command.description());
- auditLogPort.append(auditEvent);
-
- CaseEvent caseEvent = CaseEvent.caseCreated(caseId);
- caseEventPublisherPort.publishCaseEvent(caseEvent);
-
- return new CreateCaseResult(caseId);
- }
-}
-
diff --git a/src/main/java/org/example/projektarendehantering/infrastructure/persistence/CaseEntity.java b/src/main/java/org/example/projektarendehantering/infrastructure/persistence/CaseEntity.java
new file mode 100644
index 0000000..2d57c78
--- /dev/null
+++ b/src/main/java/org/example/projektarendehantering/infrastructure/persistence/CaseEntity.java
@@ -0,0 +1,49 @@
+package org.example.projektarendehantering.infrastructure.persistence;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import java.time.Instant;
+import java.util.UUID;
+
+@Entity
+@Table(name = "cases")
+public class CaseEntity {
+
+ @Id
+ private UUID id;
+ private String status;
+ private UUID ownerId;
+ private String title;
+ private String description;
+ private Instant createdAt;
+
+ public CaseEntity() {}
+
+ public CaseEntity(UUID id, String status, UUID ownerId, String title, String description, Instant createdAt) {
+ this.id = id;
+ this.status = status;
+ this.ownerId = ownerId;
+ this.title = title;
+ this.description = description;
+ this.createdAt = createdAt;
+ }
+
+ public UUID getId() { return id; }
+ public void setId(UUID id) { this.id = id; }
+
+ public String getStatus() { return status; }
+ public void setStatus(String status) { this.status = status; }
+
+ public UUID getOwnerId() { return ownerId; }
+ public void setOwnerId(UUID ownerId) { this.ownerId = ownerId; }
+
+ public String getTitle() { return title; }
+ public void setTitle(String title) { this.title = title; }
+
+ public String getDescription() { return description; }
+ public void setDescription(String description) { this.description = description; }
+
+ public Instant getCreatedAt() { return createdAt; }
+ public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
+}
diff --git a/src/main/java/org/example/projektarendehantering/infrastructure/persistence/CaseRepository.java b/src/main/java/org/example/projektarendehantering/infrastructure/persistence/CaseRepository.java
new file mode 100644
index 0000000..0eb62c0
--- /dev/null
+++ b/src/main/java/org/example/projektarendehantering/infrastructure/persistence/CaseRepository.java
@@ -0,0 +1,7 @@
+package org.example.projektarendehantering.infrastructure.persistence;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.UUID;
+
+public interface CaseRepository extends JpaRepository {
+}
diff --git a/src/main/java/org/example/projektarendehantering/infrastructure/persistence/InMemoryCaseRepositoryAdapter.java b/src/main/java/org/example/projektarendehantering/infrastructure/persistence/InMemoryCaseRepositoryAdapter.java
index 83bb9ae..8a0ede4 100644
--- a/src/main/java/org/example/projektarendehantering/infrastructure/persistence/InMemoryCaseRepositoryAdapter.java
+++ b/src/main/java/org/example/projektarendehantering/infrastructure/persistence/InMemoryCaseRepositoryAdapter.java
@@ -3,22 +3,159 @@
import org.example.projektarendehantering.application.ports.CaseRepositoryPort;
import org.example.projektarendehantering.domain.Case;
import org.example.projektarendehantering.domain.CaseId;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.repository.query.FluentQuery;
import org.springframework.stereotype.Repository;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
@Repository
public class InMemoryCaseRepositoryAdapter implements CaseRepositoryPort {
- private final ConcurrentMap cases = new ConcurrentHashMap<>();
+ private final ConcurrentMap domainCases = new ConcurrentHashMap<>();
+ private final ConcurrentMap entities = new ConcurrentHashMap<>();
+ // Old Port implementation (for compatibility)
@Override
public Case save(Case caseToSave) {
Objects.requireNonNull(caseToSave, "caseToSave");
- cases.put(caseToSave.id(), caseToSave);
+ domainCases.put(caseToSave.id(), caseToSave);
return caseToSave;
}
-}
+ // New Repository implementation (keeping the methods but not implementing CaseRepository)
+ public CaseEntity save(CaseEntity entity) {
+ Objects.requireNonNull(entity, "entity");
+ if (entity.getId() == null) {
+ entity.setId(UUID.randomUUID());
+ }
+ entities.put(entity.getId(), entity);
+ return entity;
+ }
+
+ public Optional findById(UUID id) {
+ return Optional.ofNullable(entities.get(id));
+ }
+
+ public boolean existsById(UUID uuid) {
+ return false;
+ }
+
+ public List saveAll(Iterable entities) {
+ return List.of();
+ }
+
+ public List findAll() {
+ return new ArrayList<>(entities.values());
+ }
+
+ public List findAllById(Iterable uuids) {
+ return List.of();
+ }
+
+ public long count() {
+ return 0;
+ }
+
+ public void deleteById(UUID uuid) {
+
+ }
+
+ public void delete(CaseEntity entity) {
+
+ }
+
+ public void deleteAllById(Iterable extends UUID> uuids) {
+
+ }
+
+ public void deleteAll(Iterable extends CaseEntity> entities) {
+
+ }
+
+ public void deleteAll() {
+
+ }
+
+ public void flush() {
+
+ }
+
+ public S saveAndFlush(S entity) {
+ return null;
+ }
+
+ public List saveAllAndFlush(Iterable entities) {
+ return List.of();
+ }
+
+ public void deleteAllInBatch(Iterable entities) {
+
+ }
+
+ public void deleteAllByIdInBatch(Iterable uuids) {
+
+ }
+
+ public void deleteAllInBatch() {
+
+ }
+
+ public CaseEntity getOne(UUID uuid) {
+ return null;
+ }
+
+ public CaseEntity getById(UUID uuid) {
+ return null;
+ }
+
+ public CaseEntity getReferenceById(UUID uuid) {
+ return null;
+ }
+
+ public Optional findOne(Example example) {
+ return Optional.empty();
+ }
+
+ public List findAll(Example example) {
+ return List.of();
+ }
+
+ public List findAll(Example example, Sort sort) {
+ return List.of();
+ }
+
+ public Page findAll(Example example, Pageable pageable) {
+ return null;
+ }
+
+ public long count(Example example) {
+ return 0;
+ }
+
+ public boolean exists(Example example) {
+ return false;
+ }
+
+ public R findBy(Example example, Function, R> queryFunction) {
+ return null;
+ }
+
+ public List findAll(Sort sort) {
+ return List.of();
+ }
+
+ public Page findAll(Pageable pageable) {
+ return null;
+ }
+}
diff --git a/src/main/java/org/example/projektarendehantering/presentation/dto/CaseDTO.java b/src/main/java/org/example/projektarendehantering/presentation/dto/CaseDTO.java
new file mode 100644
index 0000000..050fcb9
--- /dev/null
+++ b/src/main/java/org/example/projektarendehantering/presentation/dto/CaseDTO.java
@@ -0,0 +1,38 @@
+package org.example.projektarendehantering.presentation.dto;
+
+import java.time.Instant;
+import java.util.UUID;
+
+public class CaseDTO {
+
+ private UUID id;
+ private String status;
+ private String title;
+ private String description;
+ private Instant createdAt;
+
+ public CaseDTO() {}
+
+ public CaseDTO(UUID id, String status, String title, String description, Instant createdAt) {
+ this.id = id;
+ this.status = status;
+ this.title = title;
+ this.description = description;
+ this.createdAt = createdAt;
+ }
+
+ public UUID getId() { return id; }
+ public void setId(UUID id) { this.id = id; }
+
+ public String getStatus() { return status; }
+ public void setStatus(String status) { this.status = status; }
+
+ public String getTitle() { return title; }
+ public void setTitle(String title) { this.title = title; }
+
+ public String getDescription() { return description; }
+ public void setDescription(String description) { this.description = description; }
+
+ public Instant getCreatedAt() { return createdAt; }
+ public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
+}
diff --git a/src/main/java/org/example/projektarendehantering/presentation/rest/CaseController.java b/src/main/java/org/example/projektarendehantering/presentation/rest/CaseController.java
new file mode 100644
index 0000000..9ffa43e
--- /dev/null
+++ b/src/main/java/org/example/projektarendehantering/presentation/rest/CaseController.java
@@ -0,0 +1,39 @@
+package org.example.projektarendehantering.presentation.rest;
+
+import jakarta.validation.Valid;
+import org.example.projektarendehantering.application.service.CaseService;
+import org.example.projektarendehantering.presentation.dto.CaseDTO;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.UUID;
+
+@RestController
+@RequestMapping("/api/cases")
+public class CaseController {
+
+ private final CaseService caseService;
+
+ public CaseController(CaseService caseService) {
+ this.caseService = caseService;
+ }
+
+ @PostMapping
+ public ResponseEntity createCase(@RequestBody @Valid CaseDTO caseDTO) {
+ CaseDTO created = caseService.createCase(caseDTO);
+ return ResponseEntity.ok(created);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getCase(@PathVariable UUID id) {
+ return caseService.getCase(id)
+ .map(ResponseEntity::ok)
+ .orElse(ResponseEntity.notFound().build());
+ }
+
+ @GetMapping
+ public ResponseEntity> getAllCases() {
+ return ResponseEntity.ok(caseService.getAllCases());
+ }
+}
diff --git a/src/main/java/org/example/projektarendehantering/presentation/rest/CaseRestController.java b/src/main/java/org/example/projektarendehantering/presentation/rest/CaseRestController.java
deleted file mode 100644
index 40e7e7e..0000000
--- a/src/main/java/org/example/projektarendehantering/presentation/rest/CaseRestController.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.example.projektarendehantering.presentation.rest;
-
-import jakarta.validation.Valid;
-import org.example.projektarendehantering.application.usecase.CreateCaseCommand;
-import org.example.projektarendehantering.application.usecase.CreateCaseResult;
-import org.example.projektarendehantering.application.usecase.CreateCaseUseCase;
-import org.example.projektarendehantering.presentation.dto.CreateCaseRequest;
-import org.example.projektarendehantering.presentation.dto.CreateCaseResponse;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequestMapping("/cases")
-public class CaseRestController {
-
- private final CreateCaseUseCase createCaseUseCase;
-
- public CaseRestController(CreateCaseUseCase createCaseUseCase) {
- this.createCaseUseCase = createCaseUseCase;
- }
-
- @PostMapping(
- consumes = MediaType.APPLICATION_JSON_VALUE,
- produces = MediaType.APPLICATION_JSON_VALUE
- )
- public CreateCaseResponse createCase(@RequestBody @Valid CreateCaseRequest request) {
- CreateCaseCommand command = new CreateCaseCommand(request.title(), request.description());
- CreateCaseResult result = createCaseUseCase.execute(command);
- return new CreateCaseResponse(result.caseId().value().toString());
- }
-}
-
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
new file mode 100644
index 0000000..c2ea3e9
--- /dev/null
+++ b/src/test/resources/application.properties
@@ -0,0 +1,17 @@
+spring.application.name=Projekt-arendehantering-test
+
+# Use H2 in-memory database for tests
+spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=PostgreSQL
+spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+
+# Create schema on startup
+spring.jpa.hibernate.ddl-auto=create-drop
+
+# Show SQL in logs for easier debugging during tests
+spring.jpa.show-sql=true
+
+# Disable Docker Compose auto-configuration during tests to avoid connection errors in CI
+spring.docker.compose.enabled=false