# Meldestelle Development Guidelines **Version:** 1.0 **Date:** 2025-08-15 **Status:** Active This document outlines the development guidelines for the Meldestelle project, covering coding conventions, code organization, and testing approaches. --- ## 1. Coding Conventions ### 1.1 Language Standards - **Primary Language:** Kotlin (JVM/Multiplatform) - **Java Compatibility:** Target Java 21+ - **Kotlin Version:** Latest stable version - **Code Style:** Official Kotlin coding conventions ### 1.2 Naming Conventions #### Classes and Interfaces ```kotlin // Use PascalCase for classes and interfaces class MemberService interface EventRepository data class MemberRegistration sealed class AuthResult // Use descriptive names that reflect domain concepts class HorseRegistrationService // Good class HRS // Avoid abbreviations ``` #### Functions and Variables ```kotlin // Use camelCase for functions and variables fun authenticateUser(): AuthResult val memberRepository: MemberRepository suspend fun findByEmail(email: EmailAddress): Result // Use descriptive test method names with "should" statements @Test fun `authenticate should return Success for valid credentials`() ``` #### Constants and Enums ```kotlin // Use SCREAMING_SNAKE_CASE for constants const val MAX_RETRY_ATTEMPTS = 3 const val DEFAULT_TIMEOUT_MS = 5000L // Use PascalCase for enum values enum class MemberStatus { ACTIVE, INACTIVE, SUSPENDED } ``` ### 1.3 Code Structure Principles #### Result Pattern Usage ```kotlin // Always use Result pattern for operations that can fail interface MemberRepository { suspend fun findById(id: MemberId): Result suspend fun save(member: Member): Result } // Result extensions for error handling inline fun Result.mapError(transform: (E) -> R): Result = when (this) { is Result.Success -> Result.Success(value) is Result.Failure -> Result.Failure(transform(error)) } ``` #### Coroutines and Async Programming ```kotlin // Use suspend functions for async operations suspend fun processEventBatch(events: List): Result // Prefer structured concurrency class EventProcessor { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) suspend fun processEvents() = withContext(scope.coroutineContext) { // Implementation } } ``` #### Documentation Standards ```kotlin /** * Authenticates a user with the given credentials. * * @param credentials The user credentials containing username and password * @return AuthResult.Success with user data if authentication succeeds, * AuthResult.Failure with error details if it fails */ suspend fun authenticate(credentials: UserCredentials): AuthResult ``` --- ## 2. Code Organization and Package Structure ### 2.1 Overall Architecture The project follows a **microservices architecture** with **Domain-Driven Design (DDD)** principles and **Clean Architecture** patterns. #### High-Level Structure ``` Meldestelle/ ├── core/ # Shared kernel - fundamental building blocks │ ├── core-domain/ # Common domain types and interfaces │ └── core-utils/ # Shared utilities and extensions ├── infrastructure/ # Cross-cutting infrastructure services │ ├── auth/ # Authentication & authorization │ ├── messaging/ # Event messaging (Kafka) │ ├── cache/ # Distributed caching (Redis) │ ├── gateway/ # API Gateway │ └── monitoring/ # Observability and monitoring ├── [domain-services]/ # Domain-specific microservices │ ├── members/ # Member management │ ├── events/ # Event management │ ├── horses/ # Horse registry │ └── masterdata/ # Master data management ├── client/ # Client applications │ ├── common-ui/ # Shared UI components (KMP) │ ├── desktop-app/ # Desktop application │ └── web-app/ # Web application └── platform/ # Build and dependency management ``` ### 2.2 Microservice Structure (Clean Architecture) Each domain service follows a **4-layer architecture**: ``` domain-service/ ├── domain-api/ # REST controllers, DTOs, API contracts ├── domain-application/ # Use cases, application logic, orchestration ├── domain-domain/ # Domain models, business rules, interfaces └── domain-infrastructure/ # Technical implementations (DB, external APIs) ``` #### Layer Responsibilities **`:domain-api` Layer:** ```kotlin // REST Controllers @RestController @RequestMapping("/api/v1/members") class MemberController(private val memberService: MemberService) // DTOs for external communication data class MemberRegistrationRequest( val firstName: String, val lastName: String, val email: String ) ``` **`:domain-application` Layer:** ```kotlin // Use cases and application services class MemberApplicationService( private val memberRepository: MemberRepository, private val eventPublisher: EventPublisher ) { suspend fun registerMember(command: RegisterMemberCommand): Result } ``` **`:domain-domain` Layer:** ```kotlin // Domain models and business logic data class Member( val id: MemberId, val personalInfo: PersonalInfo, val membershipStatus: MembershipStatus ) { fun activate(): Member = copy(membershipStatus = MembershipStatus.ACTIVE) } // Repository interfaces (implemented in infrastructure) interface MemberRepository { suspend fun findById(id: MemberId): Result suspend fun save(member: Member): Result } ``` **`:domain-infrastructure` Layer:** ```kotlin // Technical implementations class ExposedMemberRepository( private val database: Database ) : MemberRepository { override suspend fun findById(id: MemberId): Result { // Database implementation using Exposed ORM } } ``` ### 2.3 Package Naming Conventions ```kotlin // Base package structure at.mocode.[layer].[domain].[component] // Examples at.mocode.members.domain.model // Domain models at.mocode.members.application.service // Application services at.mocode.members.infrastructure.persistence // Persistence layer at.mocode.infrastructure.messaging.kafka // Infrastructure components at.mocode.core.utils.result // Core utilities ``` ### 2.4 Dependency Rules - **Core modules** must not depend on any other modules - **Domain layer** must not depend on infrastructure or application layers - **Application layer** can depend on domain layer only - **Infrastructure layer** can depend on domain and application layers - **API layer** orchestrates calls between application and infrastructure --- ## 3. Unit and Integration Testing Approaches ### 3.1 Testing Strategy Overview The project follows a **comprehensive testing strategy** with multiple testing levels: 1. **Unit Tests** - Fast, isolated tests for individual components 2. **Integration Tests** - Tests for component interactions 3. **Performance Tests** - Load and throughput testing 4. **End-to-End Tests** - Full system workflow testing ### 3.2 Testing Stack #### Core Testing Libraries ```kotlin // Unit testing testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") testImplementation("io.mockk:mockk:1.13.8") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") // Integration testing testImplementation("org.testcontainers:junit-jupiter:1.19.1") testImplementation("org.testcontainers:kafka:1.19.1") testImplementation("org.testcontainers:postgresql:1.19.1") // Performance testing testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") ``` ### 3.3 Unit Testing Conventions #### Test Structure and Naming ```kotlin class AuthenticationServiceTest { @BeforeEach fun setUp() { // Test setup } @Test fun `authenticate should return Success for valid credentials`() = runTest { // Given val credentials = UserCredentials("user@example.com", "validPassword") coEvery { userRepository.findByEmail(any()) } returns Result.Success(testUser) // When val result = authenticationService.authenticate(credentials) // Then assertTrue(result is AuthResult.Success) assertEquals(testUser.id, result.user.id) } @Test fun `authenticate should return Failure for invalid credentials`() = runTest { // Given - When - Then pattern } } ``` #### Mocking Best Practices ```kotlin class MemberServiceTest { private val memberRepository = mockk() private val eventPublisher = mockk() private val memberService = MemberService(memberRepository, eventPublisher) @Test fun `should publish event when member is registered`() = runTest { // Mock repository responses coEvery { memberRepository.save(any()) } returns Result.Success(Unit) coEvery { eventPublisher.publish(any()) } returns Result.Success(Unit) // Test implementation val result = memberService.registerMember(validCommand) // Verify interactions coVerify { eventPublisher.publish(any()) } } } ``` ### 3.4 Integration Testing Approaches #### Database Integration Tests ```kotlin @Testcontainers class MemberRepositoryIntegrationTest { companion object { @Container val postgres = PostgreSQLContainer("postgres:15-alpine") } @Test fun `should persist and retrieve member correctly`() = runTest { // Test with real database using Testcontainers val member = createTestMember() val saveResult = memberRepository.save(member) assertTrue(saveResult.isSuccess()) val retrievedResult = memberRepository.findById(member.id) assertTrue(retrievedResult.isSuccess()) assertEquals(member, retrievedResult.getOrNull()) } } ``` #### Messaging Integration Tests ```kotlin @Testcontainers class KafkaEventPublisherIntegrationTest { companion object { @Container val kafka = KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")) } @Test fun `should publish and consume events correctly`() = runTest { val event = MemberRegisteredEvent(memberId = MemberId.generate()) val publishResult = eventPublisher.publish(event) assertTrue(publishResult.isSuccess()) // Verify event was consumed val consumedEvents = eventConsumer.consumeEvents(timeout = 5.seconds) assertTrue(consumedEvents.any { it.memberId == event.memberId }) } } ``` ### 3.5 Performance Testing #### Batch Processing Performance Tests ```kotlin class KafkaBatchPerformanceTest { @Test fun `should process large batches within acceptable time limits`() = runTest { val batchSize = 1000 val events = generateTestEvents(batchSize) val startTime = System.currentTimeMillis() val results = eventProcessor.processBatch(events) val processingTime = System.currentTimeMillis() - startTime assertTrue(results.all { it.isSuccess() }) assertTrue(processingTime < 5000) // Should complete within 5 seconds println("[DEBUG_LOG] Processed $batchSize events in ${processingTime}ms") } } ``` ### 3.6 Test Organization #### Directory Structure ``` src/ ├── main/kotlin/ # Production code └── test/kotlin/ # Test code ├── unit/ # Unit tests (optional sub-organization) ├── integration/ # Integration tests └── performance/ # Performance tests ``` #### Test Categories and Execution ```kotlin // Use JUnit 5 tags for test categorization @Tag("unit") class MemberServiceTest @Tag("integration") class MemberRepositoryIntegrationTest @Tag("performance") class KafkaBatchPerformanceTest ``` ### 3.7 Testing Guidelines #### Best Practices 1. **Test Method Naming:** Use descriptive names with "should" statements 2. **AAA Pattern:** Arrange, Act, Assert structure 3. **One Assertion Per Test:** Focus on single behavior 4. **Test Data Builders:** Use factory methods for test data creation 5. **Coroutine Testing:** Use `runTest` for suspend functions 6. **Mock Verification:** Verify important interactions, not implementation details #### Coverage Goals - **Unit Tests:** 80%+ code coverage for domain and application layers - **Integration Tests:** Cover all repository implementations and external integrations - **Performance Tests:** Cover critical batch operations and high-load scenarios #### Debugging Support ```kotlin // Always prefix debug messages with [DEBUG_LOG] @Test fun `should handle concurrent requests`() = runTest { println("[DEBUG_LOG] Starting concurrent request test with ${requestCount} requests") // Test implementation println("[DEBUG_LOG] Completed test. Success rate: ${successCount}/${requestCount}") } ``` --- ## 4. Additional Development Standards ### 4.1 Error Handling - Use `Result` pattern consistently for operations that can fail - Define domain-specific error types - Avoid throwing exceptions in domain logic ### 4.2 Logging and Monitoring - Use structured logging with appropriate log levels - Include correlation IDs for request tracing - Monitor key business metrics and technical performance ### 4.3 Security Considerations - Validate all external inputs - Use JWT tokens for authentication - Implement proper authorization checks - Secure sensitive configuration data --- This guideline is a living document and should be updated as the project evolves and new patterns emerge.