docs: Migrationsplan für Projekt-Restrukturierung hinzugefügt

- Detaillierter Plan zur Migration von alter zu neuer Modulstruktur
- Umfasst Überführung von shared-kernel zu core-Modulen
- Definiert Migration von Fachdomänen zu bounded contexts:
  * master-data → masterdata-Module
  * member-management → members-Module
  * horse-registry → horses-Module
  * event-management → events-Module
- Beschreibt Verlagerung von api-gateway zu infrastructure/gateway
- Strukturiert nach Domain-driven Design Prinzipien
- Berücksichtigt Clean Architecture Layering (domain, application, infrastructure, api)
This commit is contained in:
stefan
2025-07-25 13:05:42 +02:00
parent a4c7d53aa3
commit 65a0084f91
68 changed files with 13107 additions and 101 deletions
@@ -0,0 +1,71 @@
package at.mocode.members.application.usecase
import at.mocode.core.domain.model.ApiResponse
import at.mocode.core.domain.model.ErrorDto
import at.mocode.members.domain.model.Member
import at.mocode.members.domain.repository.MemberRepository
/**
* Use case for finding members with expiring memberships.
*
* This use case handles the business logic for finding members
* whose memberships are expiring within a specified number of days.
*/
class FindExpiringMembershipsUseCase(
private val memberRepository: MemberRepository
) {
/**
* Request data for finding expiring memberships.
*/
data class FindExpiringMembershipsRequest(
val daysAhead: Int = 30
)
/**
* Response data containing the list of members with expiring memberships.
*/
data class FindExpiringMembershipsResponse(
val members: List<Member>,
val count: Int
)
/**
* Executes the find expiring memberships use case.
*
* @param request The request containing the number of days to look ahead
* @return ApiResponse with the list of members or error information
*/
suspend fun execute(request: FindExpiringMembershipsRequest): ApiResponse<FindExpiringMembershipsResponse> {
return try {
// Validate input
if (request.daysAhead < 0) {
return ApiResponse(
success = false,
error = ErrorDto(
code = "INVALID_DAYS_AHEAD",
message = "Days ahead must be a positive number"
)
)
}
val members = memberRepository.findMembersWithExpiringMembership(request.daysAhead)
ApiResponse(
success = true,
data = FindExpiringMembershipsResponse(
members = members,
count = members.size
)
)
} catch (e: Exception) {
ApiResponse(
success = false,
error = ErrorDto(
code = "INTERNAL_ERROR",
message = "Failed to find expiring memberships: ${e.message}"
)
)
}
}
}
@@ -0,0 +1,93 @@
package at.mocode.members.application.usecase
import at.mocode.core.domain.model.ApiResponse
import at.mocode.core.domain.model.ErrorDto
import at.mocode.members.domain.model.Member
import at.mocode.members.domain.repository.MemberRepository
import kotlinx.datetime.LocalDate
/**
* Use case for finding members by date ranges.
*
* This use case handles the business logic for finding members
* based on their membership start or end date ranges.
*/
class FindMembersByDateRangeUseCase(
private val memberRepository: MemberRepository
) {
/**
* Request data for finding members by date range.
*/
data class FindMembersByDateRangeRequest(
val startDate: LocalDate,
val endDate: LocalDate,
val dateType: DateRangeType
)
/**
* Type of date range to search by.
*/
enum class DateRangeType {
MEMBERSHIP_START_DATE,
MEMBERSHIP_END_DATE
}
/**
* Response data containing the list of members within the date range.
*/
data class FindMembersByDateRangeResponse(
val members: List<Member>,
val count: Int,
val dateType: DateRangeType,
val startDate: LocalDate,
val endDate: LocalDate
)
/**
* Executes the find members by date range use case.
*
* @param request The request containing the date range and type
* @return ApiResponse with the list of members or error information
*/
suspend fun execute(request: FindMembersByDateRangeRequest): ApiResponse<FindMembersByDateRangeResponse> {
return try {
// Validate input
if (request.startDate > request.endDate) {
return ApiResponse(
success = false,
error = ErrorDto(
code = "INVALID_DATE_RANGE",
message = "Start date cannot be after end date"
)
)
}
val members = when (request.dateType) {
DateRangeType.MEMBERSHIP_START_DATE ->
memberRepository.findByMembershipStartDateRange(request.startDate, request.endDate)
DateRangeType.MEMBERSHIP_END_DATE ->
memberRepository.findByMembershipEndDateRange(request.startDate, request.endDate)
}
ApiResponse(
success = true,
data = FindMembersByDateRangeResponse(
members = members,
count = members.size,
dateType = request.dateType,
startDate = request.startDate,
endDate = request.endDate
)
)
} catch (e: Exception) {
ApiResponse(
success = false,
error = ErrorDto(
code = "INTERNAL_ERROR",
message = "Failed to find members by date range: ${e.message}"
)
)
}
}
}
@@ -0,0 +1,146 @@
package at.mocode.members.application.usecase
import at.mocode.core.domain.model.ApiResponse
import at.mocode.core.domain.model.ErrorDto
import at.mocode.members.domain.repository.MemberRepository
import com.benasher44.uuid.Uuid
/**
* Use case for validating member data.
*
* This use case handles the business logic for validating
* member data such as email and membership number uniqueness.
*/
class ValidateMemberDataUseCase(
private val memberRepository: MemberRepository
) {
/**
* Request data for validating email uniqueness.
*/
data class ValidateEmailRequest(
val email: String,
val excludeMemberId: Uuid? = null
)
/**
* Request data for validating membership number uniqueness.
*/
data class ValidateMembershipNumberRequest(
val membershipNumber: String,
val excludeMemberId: Uuid? = null
)
/**
* Response data for validation results.
*/
data class ValidationResponse(
val isValid: Boolean,
val exists: Boolean,
val message: String
)
/**
* Validates if an email address is unique.
*
* @param request The request containing email and optional member ID to exclude
* @return ApiResponse with validation result
*/
suspend fun validateEmail(request: ValidateEmailRequest): ApiResponse<ValidationResponse> {
return try {
// Basic email format validation
if (request.email.isBlank()) {
return ApiResponse(
success = true,
data = ValidationResponse(
isValid = false,
exists = false,
message = "Email is required"
)
)
}
if (!isValidEmailFormat(request.email)) {
return ApiResponse(
success = true,
data = ValidationResponse(
isValid = false,
exists = false,
message = "Email format is invalid"
)
)
}
val exists = memberRepository.existsByEmail(request.email, request.excludeMemberId)
ApiResponse(
success = true,
data = ValidationResponse(
isValid = !exists,
exists = exists,
message = if (exists) "Email already exists" else "Email is available"
)
)
} catch (e: Exception) {
ApiResponse(
success = false,
error = ErrorDto(
code = "INTERNAL_ERROR",
message = "Failed to validate email: ${e.message}"
)
)
}
}
/**
* Validates if a membership number is unique.
*
* @param request The request containing membership number and optional member ID to exclude
* @return ApiResponse with validation result
*/
suspend fun validateMembershipNumber(request: ValidateMembershipNumberRequest): ApiResponse<ValidationResponse> {
return try {
// Basic membership number validation
if (request.membershipNumber.isBlank()) {
return ApiResponse(
success = true,
data = ValidationResponse(
isValid = false,
exists = false,
message = "Membership number is required"
)
)
}
val exists = memberRepository.existsByMembershipNumber(request.membershipNumber, request.excludeMemberId)
ApiResponse(
success = true,
data = ValidationResponse(
isValid = !exists,
exists = exists,
message = if (exists) "Membership number already exists" else "Membership number is available"
)
)
} catch (e: Exception) {
ApiResponse(
success = false,
error = ErrorDto(
code = "INTERNAL_ERROR",
message = "Failed to validate membership number: ${e.message}"
)
)
}
}
/**
* Basic email format validation.
*/
private fun isValidEmailFormat(email: String): Boolean {
return email.contains("@") &&
email.contains(".") &&
email.indexOf("@") > 0 &&
email.lastIndexOf(".") > email.indexOf("@") &&
email.length > 5
}
}