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:
+71
@@ -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}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -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}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+146
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user