feat: jpa, messaging, notification, mongo, graphql 어댑터터 구현체 추가

This commit is contained in:
DongHyeonka
2026-08-15 13:01:58 +09:00
parent ac874e49e6
commit 2f5d2fc219
909 changed files with 62510 additions and 6354 deletions
+399
View File
@@ -0,0 +1,399 @@
# GraphQL leaf public API surface — every public top-level type in src/main/java.
# A public type in a single-jar leaf is reachable from every adopter's code, so
# additions are reviewed rather than discovered. `api` and `spi` are the intended
# external surface; the rest are candidates to become internal when this leaf is
# split into capability artifacts.
# Update only after review with:
# ./gradlew :adapter:inbound:graphql:updateGraphQlApiSurface -PapproveGraphQlApiSurfaceChange
# types: 391
dev.caskeleton.adapter.inbound.graphql.HealthGraphqlController
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlAdminPrincipal
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationAdminAuthorization
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationAdminDeniedException
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationAdminPort
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationAdminService
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationAudit
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationBlockCommand
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationRemovalGate
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationRemovalRejectedException
dev.caskeleton.adapter.inbound.graphql.advanced.admin.GraphQlPersistedOperationUsage
dev.caskeleton.adapter.inbound.graphql.advanced.bootstrap.GraphQlAdvancedCapability
dev.caskeleton.adapter.inbound.graphql.advanced.bootstrap.GraphQlAdvancedCapabilityDisabledException
dev.caskeleton.adapter.inbound.graphql.advanced.bootstrap.GraphQlAdvancedCapabilityGrade
dev.caskeleton.adapter.inbound.graphql.advanced.bootstrap.GraphQlAdvancedDependencyRules
dev.caskeleton.adapter.inbound.graphql.advanced.bootstrap.GraphQlAdvancedFeatureFlags
dev.caskeleton.adapter.inbound.graphql.advanced.bootstrap.GraphQlAdvancedModuleGuard
dev.caskeleton.adapter.inbound.graphql.advanced.chaining.GraphQlChainedDataLoaderPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.chaining.GraphQlChainedDispatchConfigurer
dev.caskeleton.adapter.inbound.graphql.advanced.chaining.GraphQlChainedLoaderMetrics
dev.caskeleton.adapter.inbound.graphql.advanced.chaining.GraphQlDataLoaderCycleDetector
dev.caskeleton.adapter.inbound.graphql.advanced.chaining.GraphQlDataLoaderDependencyCycleException
dev.caskeleton.adapter.inbound.graphql.advanced.chaining.GraphQlDataLoaderDependencyGraph
dev.caskeleton.adapter.inbound.graphql.advanced.codegen.GraphQlClientOperationGenerator
dev.caskeleton.adapter.inbound.graphql.advanced.codegen.GraphQlCodegenBoundaryException
dev.caskeleton.adapter.inbound.graphql.advanced.codegen.GraphQlCodegenProfile
dev.caskeleton.adapter.inbound.graphql.advanced.codegen.GraphQlGeneratedCompatibilityGate
dev.caskeleton.adapter.inbound.graphql.advanced.codegen.GraphQlGeneratedSourceBoundary
dev.caskeleton.adapter.inbound.graphql.advanced.codegen.GraphQlOperationValidator
dev.caskeleton.adapter.inbound.graphql.advanced.codegen.GraphQlScalarMapping
dev.caskeleton.adapter.inbound.graphql.advanced.codegen.GraphQlTransportTypeGenerator
dev.caskeleton.adapter.inbound.graphql.advanced.composition.GraphQlFederationCompositionGate
dev.caskeleton.adapter.inbound.graphql.advanced.composition.GraphQlFederationCompositionResult
dev.caskeleton.adapter.inbound.graphql.advanced.composition.GraphQlFederationDeploymentOrder
dev.caskeleton.adapter.inbound.graphql.advanced.composition.GraphQlFederationLatencyBudget
dev.caskeleton.adapter.inbound.graphql.advanced.composition.GraphQlFederationReleaseEvidence
dev.caskeleton.adapter.inbound.graphql.advanced.composition.GraphQlFederationReleaseRejectedException
dev.caskeleton.adapter.inbound.graphql.advanced.composition.GraphQlFederationUsageReport
dev.caskeleton.adapter.inbound.graphql.advanced.composition.GraphQlSubgraphContract
dev.caskeleton.adapter.inbound.graphql.advanced.federation.GraphQlFederationBatchResolver
dev.caskeleton.adapter.inbound.graphql.advanced.federation.GraphQlFederationCapability
dev.caskeleton.adapter.inbound.graphql.advanced.federation.GraphQlFederationEntityKey
dev.caskeleton.adapter.inbound.graphql.advanced.federation.GraphQlFederationEntityResolver
dev.caskeleton.adapter.inbound.graphql.advanced.federation.GraphQlFederationProperties
dev.caskeleton.adapter.inbound.graphql.advanced.federation.GraphQlFederationRepresentationException
dev.caskeleton.adapter.inbound.graphql.advanced.federation.GraphQlFederationSchemaFactory
dev.caskeleton.adapter.inbound.graphql.advanced.get.GraphQlHttpDraftCompatibilityReport
dev.caskeleton.adapter.inbound.graphql.advanced.get.GraphQlHttpGetCachePolicy
dev.caskeleton.adapter.inbound.graphql.advanced.get.GraphQlHttpGetCsrfPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.get.GraphQlHttpGetOperationPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.get.GraphQlHttpGetProfile
dev.caskeleton.adapter.inbound.graphql.advanced.get.GraphQlHttpGetRejectedException
dev.caskeleton.adapter.inbound.graphql.advanced.get.GraphQlHttpGetRequestParser
dev.caskeleton.adapter.inbound.graphql.advanced.incremental.GraphQlIncrementalCancellation
dev.caskeleton.adapter.inbound.graphql.advanced.incremental.GraphQlIncrementalCompatibilityGate
dev.caskeleton.adapter.inbound.graphql.advanced.incremental.GraphQlIncrementalDeliveryCapability
dev.caskeleton.adapter.inbound.graphql.advanced.incremental.GraphQlIncrementalDeliveryProfile
dev.caskeleton.adapter.inbound.graphql.advanced.incremental.GraphQlIncrementalDeliveryRejectedException
dev.caskeleton.adapter.inbound.graphql.advanced.incremental.GraphQlIncrementalPatch
dev.caskeleton.adapter.inbound.graphql.advanced.incremental.GraphQlIncrementalTransportPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperation
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationConflictException
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationId
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationInterceptor
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationLookup
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationNotFoundException
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationRecordMapping
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationRegistry
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationRejectedException
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationRequest
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationStatus
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedOperationTransition
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.GraphQlPersistedPreparsedBridge
dev.caskeleton.adapter.inbound.graphql.advanced.persisted.OperationalStoreGraphQlPersistedOperationRegistry
dev.caskeleton.adapter.inbound.graphql.advanced.release.GraphQlAdvancedCompatibilityMatrix
dev.caskeleton.adapter.inbound.graphql.advanced.release.GraphQlAdvancedPromotionDecision
dev.caskeleton.adapter.inbound.graphql.advanced.release.GraphQlAdvancedReleaseEvidence
dev.caskeleton.adapter.inbound.graphql.advanced.release.GraphQlAdvancedReleaseFailure
dev.caskeleton.adapter.inbound.graphql.advanced.release.GraphQlAdvancedReleaseGate
dev.caskeleton.adapter.inbound.graphql.advanced.release.GraphQlAdvancedRunbookIndex
dev.caskeleton.adapter.inbound.graphql.advanced.release.GraphQlAdvancedSoakScenario
dev.caskeleton.adapter.inbound.graphql.advanced.replay.GraphQlReplayAuthorization
dev.caskeleton.adapter.inbound.graphql.advanced.replay.GraphQlReplayAuthorizationException
dev.caskeleton.adapter.inbound.graphql.advanced.replay.GraphQlReplayGapException
dev.caskeleton.adapter.inbound.graphql.advanced.replay.GraphQlReplayHistoryLostException
dev.caskeleton.adapter.inbound.graphql.advanced.replay.GraphQlReplayPosition
dev.caskeleton.adapter.inbound.graphql.advanced.replay.GraphQlReplaySource
dev.caskeleton.adapter.inbound.graphql.advanced.replay.GraphQlSnapshotLiveHandoff
dev.caskeleton.adapter.inbound.graphql.advanced.replay.GraphQlSubscriptionCursor
dev.caskeleton.adapter.inbound.graphql.advanced.rsocket.GraphQlRSocketAdmission
dev.caskeleton.adapter.inbound.graphql.advanced.rsocket.GraphQlRSocketAuthentication
dev.caskeleton.adapter.inbound.graphql.advanced.rsocket.GraphQlRSocketCapability
dev.caskeleton.adapter.inbound.graphql.advanced.rsocket.GraphQlRSocketErrorMapper
dev.caskeleton.adapter.inbound.graphql.advanced.rsocket.GraphQlRSocketProperties
dev.caskeleton.adapter.inbound.graphql.advanced.rsocket.GraphQlRSocketRoutePolicy
dev.caskeleton.adapter.inbound.graphql.advanced.rsocket.GraphQlRSocketRouteRejectedException
dev.caskeleton.adapter.inbound.graphql.advanced.security.GraphQlSubscriptionAuthorizationPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.security.GraphQlWebSocketAuthenticationException
dev.caskeleton.adapter.inbound.graphql.advanced.security.GraphQlWebSocketAuthenticationInterceptor
dev.caskeleton.adapter.inbound.graphql.advanced.security.GraphQlWebSocketCloseReason
dev.caskeleton.adapter.inbound.graphql.advanced.security.GraphQlWebSocketCredentialExpiry
dev.caskeleton.adapter.inbound.graphql.advanced.security.GraphQlWebSocketPrincipal
dev.caskeleton.adapter.inbound.graphql.advanced.security.GraphQlWebSocketRevocationSignal
dev.caskeleton.adapter.inbound.graphql.advanced.sse.GraphQlSseAdmission
dev.caskeleton.adapter.inbound.graphql.advanced.sse.GraphQlSseConnectionPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.sse.GraphQlSseHeartbeat
dev.caskeleton.adapter.inbound.graphql.advanced.sse.GraphQlSseProperties
dev.caskeleton.adapter.inbound.graphql.advanced.sse.GraphQlSseRejectedException
dev.caskeleton.adapter.inbound.graphql.advanced.sse.GraphQlSseTermination
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSlowConsumerPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionBufferPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionCancellation
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionContext
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionDispatcher
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionDrainCoordinator
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionDrainPhase
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionDrainingException
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionEvent
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionExecutionPolicy
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionLease
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionMetrics
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionOrderingProfile
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionSource
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionState
dev.caskeleton.adapter.inbound.graphql.advanced.subscription.GraphQlSubscriptionTermination
dev.caskeleton.adapter.inbound.graphql.advanced.websocket.GraphQlWebSocketAdmission
dev.caskeleton.adapter.inbound.graphql.advanced.websocket.GraphQlWebSocketConnectionId
dev.caskeleton.adapter.inbound.graphql.advanced.websocket.GraphQlWebSocketLifecycle
dev.caskeleton.adapter.inbound.graphql.advanced.websocket.GraphQlWebSocketProperties
dev.caskeleton.adapter.inbound.graphql.advanced.websocket.GraphQlWebSocketProtocol
dev.caskeleton.adapter.inbound.graphql.advanced.websocket.GraphQlWebSocketProtocolError
dev.caskeleton.adapter.inbound.graphql.api.GraphQlClientProfile
dev.caskeleton.adapter.inbound.graphql.api.GraphQlClientProfileName
dev.caskeleton.adapter.inbound.graphql.api.GraphQlOperationId
dev.caskeleton.adapter.inbound.graphql.api.GraphQlOperationName
dev.caskeleton.adapter.inbound.graphql.api.GraphQlSchemaCoordinate
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlAsyncReturnShape
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlControllerContractException
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlControllerInspector
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlControllerTransactionRule
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlInputTypePolicy
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlResolverBoundaryRules
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlReturnTypePolicy
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlTransportTypeRules
dev.caskeleton.adapter.inbound.graphql.architecture.GraphQlTypeGraph
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlPlatformActuatorEndpoint
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlPlatformAutoConfiguration
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlPlatformConfigurationException
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlPlatformConfigurationReport
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlPlatformEnvironment
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlPlatformProperties
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlPlatformRuntime
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlPlatformStartupValidator
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlRuntimeTransport
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlChangeKind
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlClientOwnerApproval
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlCompatibilityImpact
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlCompatibilityPolicy
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlCompatibilityReport
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlDeprecationGate
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlRemovalDecision
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlRemovalRequest
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlSchemaChange
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlSchemaComparator
dev.caskeleton.adapter.inbound.graphql.compat.GraphQlSchemaUsage
dev.caskeleton.adapter.inbound.graphql.context.ActorRef
dev.caskeleton.adapter.inbound.graphql.context.GraphQlCommandAttribution
dev.caskeleton.adapter.inbound.graphql.context.GraphQlDeadline
dev.caskeleton.adapter.inbound.graphql.context.GraphQlRequestContext
dev.caskeleton.adapter.inbound.graphql.context.TenantContext
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlComplexityCalculator
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlComplexityRejectedException
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlComplexityResult
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlCostCatalog
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlDocumentComplexityScorer
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlDocumentShape
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlDocumentShapeAnalyzer
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlFieldCostDescriptor
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlParserLimitPolicy
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlParserLimits
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlParserOptionsFactory
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlParserRejectedException
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlResolverWeight
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlResponseByteLimiter
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlResponseNodeCounter
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlRuntimeBudget
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlRuntimeBudgetExceededException
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlRuntimeBudgetTracker
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlStructuralLimitPolicy
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlStructuralLimitViolation
dev.caskeleton.adapter.inbound.graphql.cost.GraphQlStructuralLimits
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchChunker
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchContext
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchErrorPolicy
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchExecutor
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchLoadException
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchObservation
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchPolicy
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchPolicyRegistry
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchResult
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchResultMapper
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchTimeoutException
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlBatchValue
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlDataLoaderFactory
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlDataLoaderName
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlDataLoaderRequestRegistry
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlMissingKeyException
dev.caskeleton.adapter.inbound.graphql.dataloader.GraphQlMissingKeyPolicy
dev.caskeleton.adapter.inbound.graphql.error.GraphQlErrorCategory
dev.caskeleton.adapter.inbound.graphql.error.GraphQlErrorCode
dev.caskeleton.adapter.inbound.graphql.error.GraphQlErrorContext
dev.caskeleton.adapter.inbound.graphql.error.GraphQlExceptionResolver
dev.caskeleton.adapter.inbound.graphql.error.GraphQlFailureBoundary
dev.caskeleton.adapter.inbound.graphql.error.GraphQlInternalErrorMasker
dev.caskeleton.adapter.inbound.graphql.error.GraphQlNullabilityContract
dev.caskeleton.adapter.inbound.graphql.error.GraphQlRequestErrorMapper
dev.caskeleton.adapter.inbound.graphql.error.GraphQlSubscriptionExceptionResolver
dev.caskeleton.adapter.inbound.graphql.error.GraphQlWireError
dev.caskeleton.adapter.inbound.graphql.execution.BoundedPreparsedDocumentProvider
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlAnonymousOperationException
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlCancellation
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlDeadlinePropagator
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlExecutionPipeline
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlExecutionPipelineException
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlExecutionPipelineValidator
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlExecutionProfile
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlExecutionProfileException
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlExecutionProfileValidator
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlExecutionStage
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlOperationNameInterceptor
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlOperationNamePolicy
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlOperationSelection
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlPreparsedCacheKey
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlPreparsedCacheMetrics
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlPreparsedCachePolicy
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlRequestCancelledException
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlResolverBudget
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlResolverCatalog
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlResolverDescriptor
dev.caskeleton.adapter.inbound.graphql.execution.GraphQlTimeoutPolicy
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlFetchProfile
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlFetchProfileClassifier
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlFetchProfileName
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlFetchProfileRegistry
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlFetchProfileRule
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlFetchProfileValidationException
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlSelectionCoordinate
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlSelectionSetView
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlSelectionSignature
dev.caskeleton.adapter.inbound.graphql.fetch.GraphQlUnmappedSelectionException
dev.caskeleton.adapter.inbound.graphql.http.GraphQlAcceptHeader
dev.caskeleton.adapter.inbound.graphql.http.GraphQlExecutionOutcome
dev.caskeleton.adapter.inbound.graphql.http.GraphQlExtensionsPolicy
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpContractException
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpExecutor
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpOutcome
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpProfile
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpRequestEnvelope
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpResponse
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpResponseFactory
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpResponsePolicy
dev.caskeleton.adapter.inbound.graphql.http.GraphQlHttpStatusMapper
dev.caskeleton.adapter.inbound.graphql.http.GraphQlJsonStructurePolicy
dev.caskeleton.adapter.inbound.graphql.http.GraphQlJsonValues
dev.caskeleton.adapter.inbound.graphql.http.GraphQlMediaTypes
dev.caskeleton.adapter.inbound.graphql.http.GraphQlRequestEnvelopeValidator
dev.caskeleton.adapter.inbound.graphql.http.GraphQlRequestFormatException
dev.caskeleton.adapter.inbound.graphql.http.GraphQlRequestSize
dev.caskeleton.adapter.inbound.graphql.http.GraphQlRequestTooLargeException
dev.caskeleton.adapter.inbound.graphql.moduleboundary.GraphQlAdvancedModule
dev.caskeleton.adapter.inbound.graphql.moduleboundary.GraphQlModuleBoundary
dev.caskeleton.adapter.inbound.graphql.moduleboundary.GraphQlModulePurity
dev.caskeleton.adapter.inbound.graphql.moduleboundary.GraphQlStableModule
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlBatchMutationItemResult
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlBusinessResult
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlCanonicalInput
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlExpectedVersion
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlIdempotencyConflictException
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlIdempotencyKey
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlMutationContractException
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlMutationContractValidator
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlMutationCoordinate
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlMutationFingerprint
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlMutationIdempotencyContext
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlMutationIdempotencyInterceptor
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlMutationPayload
dev.caskeleton.adapter.inbound.graphql.mutation.GraphQlMutationResultMapper
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlDataLoaderObservationConvention
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlMetricCardinalityPolicy
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlObservationContractException
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlObservationNames
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlOperationNameCardinality
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlProfilerAccessPolicy
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlRequestObservationConvention
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlResolverObservationConvention
dev.caskeleton.adapter.inbound.graphql.observation.GraphQlSensitiveAttributeFilter
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlConnection
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlConnectionAssembler
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlConnectionException
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlConnectionPolicy
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlConnectionRequest
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlCursorCodec
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlCursorException
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlCursorFraming
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlCursorKeyRing
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlCursorKeyset
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlCursorPayload
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlCursorScope
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlCursorVersion
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlEdge
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlKeysetWindow
dev.caskeleton.adapter.inbound.graphql.pagination.GraphQlPageInfo
dev.caskeleton.adapter.inbound.graphql.pagination.HmacGraphQlCursorCodec
dev.caskeleton.adapter.inbound.graphql.policy.GraphQlClientPolicy
dev.caskeleton.adapter.inbound.graphql.policy.GraphQlClientPolicyManifest
dev.caskeleton.adapter.inbound.graphql.policy.GraphQlOperationCatalog
dev.caskeleton.adapter.inbound.graphql.policy.GraphQlOperationPolicy
dev.caskeleton.adapter.inbound.graphql.policy.GraphQlOperationType
dev.caskeleton.adapter.inbound.graphql.policy.GraphQlPolicyViolation
dev.caskeleton.adapter.inbound.graphql.policy.GraphQlUnknownClientProfileException
dev.caskeleton.adapter.inbound.graphql.policy.GraphQlUnknownOperationException
dev.caskeleton.adapter.inbound.graphql.policy.ResolverExecutionType
dev.caskeleton.adapter.inbound.graphql.release.GraphQlCompatibilityMatrix
dev.caskeleton.adapter.inbound.graphql.release.GraphQlFaultScenario
dev.caskeleton.adapter.inbound.graphql.release.GraphQlPerformanceScenario
dev.caskeleton.adapter.inbound.graphql.release.GraphQlReleaseEvidence
dev.caskeleton.adapter.inbound.graphql.release.GraphQlReleaseFailure
dev.caskeleton.adapter.inbound.graphql.release.GraphQlReleaseGate
dev.caskeleton.adapter.inbound.graphql.release.GraphQlReleaseOverride
dev.caskeleton.adapter.inbound.graphql.release.GraphQlReleaseReportWriter
dev.caskeleton.adapter.inbound.graphql.release.GraphQlStableCapabilityManifest
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlBatchLoaderRegistrar
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlBlockingBridge
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlBlockingBridgeFullException
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlCostBudgetHandler
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlDataFetcherExceptionResolver
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlDocumentAuthorizationHandler
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlExecutionChain
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlExecutionContext
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlExecutionHandler
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlExecutionRequest
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlOperationSelectionHandler
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlPlatformInstrumentation
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlPlatformRejectionMapper
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlPlatformWebInterceptor
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlPreparsedDocumentAdapter
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlPrincipalResolver
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlWireErrorMapper
dev.caskeleton.adapter.inbound.graphql.runtime.GraphQlWireErrors
dev.caskeleton.adapter.inbound.graphql.runtime.servlet.GraphQlRequestBodyLimitFilter
dev.caskeleton.adapter.inbound.graphql.scalar.BigDecimalScalar
dev.caskeleton.adapter.inbound.graphql.scalar.DateScalar
dev.caskeleton.adapter.inbound.graphql.scalar.GraphQlDecimalBounds
dev.caskeleton.adapter.inbound.graphql.scalar.GraphQlScalarWiringConfigurer
dev.caskeleton.adapter.inbound.graphql.scalar.InstantScalar
dev.caskeleton.adapter.inbound.graphql.scalar.LongScalar
dev.caskeleton.adapter.inbound.graphql.scalar.UuidScalar
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlContractVersion
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlMappingInspectionGate
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlMappingIssue
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlMappingPolicy
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlOneOfInputValidator
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlOneOfPolicy
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlOneOfSchemaGate
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlOneOfViolationException
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlScalarDefinition
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlScalarManifest
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlScalarPolicy
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlSchemaAssembler
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlSchemaAssemblyException
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlSchemaAssemblyResult
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlSchemaContract
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlSchemaHash
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlSchemaMappingException
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlSchemaOwnership
dev.caskeleton.adapter.inbound.graphql.schema.GraphQlSchemaResource
dev.caskeleton.adapter.inbound.graphql.security.GraphQlAuthenticatedPrincipal
dev.caskeleton.adapter.inbound.graphql.security.GraphQlAuthenticationContextFactory
dev.caskeleton.adapter.inbound.graphql.security.GraphQlAuthenticationException
dev.caskeleton.adapter.inbound.graphql.security.GraphQlAuthorizationDecision
dev.caskeleton.adapter.inbound.graphql.security.GraphQlAuthorizationDeniedException
dev.caskeleton.adapter.inbound.graphql.security.GraphQlAuthorizationInterceptor
dev.caskeleton.adapter.inbound.graphql.security.GraphQlAuthorizationPolicy
dev.caskeleton.adapter.inbound.graphql.security.GraphQlBatchContext
dev.caskeleton.adapter.inbound.graphql.security.GraphQlClientProfileResolver
dev.caskeleton.adapter.inbound.graphql.security.GraphQlContextCleanup
dev.caskeleton.adapter.inbound.graphql.security.GraphQlContextPropagator
dev.caskeleton.adapter.inbound.graphql.security.GraphQlObjectAuthorizationPort
dev.caskeleton.adapter.inbound.graphql.security.GraphQlTenantIsolationException
dev.caskeleton.adapter.inbound.graphql.security.GraphQlTenantIsolationPolicy
+349
View File
@@ -0,0 +1,349 @@
# MongoDB leaf public API surface — every public top-level type in src/main/java.
# A public type in a single-jar leaf is reachable from every adopter's code, so
# additions are reviewed rather than discovered. `api` is the intended external
# surface; the rest is implementation that has not been moved under an internal
# root yet.
# Update only after review with:
# ./gradlew :adapter:outbound:persistence-mongo:updateMongoApiSurface -PapproveMongoApiSurfaceChange
# types: 341
dev.caskeleton.adapter.outbound.mongo.MongoOptInAutoConfigurationImportFilter
dev.caskeleton.adapter.outbound.mongo.MongoPersistenceConfig
dev.caskeleton.adapter.outbound.mongo.MongoPersistenceProperties
dev.caskeleton.adapter.outbound.mongo.advanced.MongoAdvancedCapabilityFlags
dev.caskeleton.adapter.outbound.mongo.advanced.MongoAdvancedCapabilityGuard
dev.caskeleton.adapter.outbound.mongo.advanced.MongoAdvancedEntryPoint
dev.caskeleton.adapter.outbound.mongo.advanced.MongoAdvancedPolicy
dev.caskeleton.adapter.outbound.mongo.advanced.MongoAdvancedPromotionEvidence
dev.caskeleton.adapter.outbound.mongo.advanced.MongoAdvancedPromotionGate
dev.caskeleton.adapter.outbound.mongo.advanced.autoconfigure.MongoAdvancedConfiguration
dev.caskeleton.adapter.outbound.mongo.advanced.autoconfigure.MongoAdvancedProperties
dev.caskeleton.adapter.outbound.mongo.advanced.bridge.MongoBridgeCheckpointPolicy
dev.caskeleton.adapter.outbound.mongo.advanced.bridge.MongoBridgeOutboxPolicy
dev.caskeleton.adapter.outbound.mongo.advanced.bridge.MongoChangeMessagingBridge
dev.caskeleton.adapter.outbound.mongo.advanced.bridge.MongoChangeToIntegrationEventMapper
dev.caskeleton.adapter.outbound.mongo.advanced.bridge.MongoIntegrationEventEnvelope
dev.caskeleton.adapter.outbound.mongo.advanced.bridge.MongoIntegrationEventPublisher
dev.caskeleton.adapter.outbound.mongo.advanced.bridge.MongoPublishResult
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.csfle.MongoCsfleClientFactory
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.csfle.MongoCsfleFieldPolicy
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.csfle.MongoCsfleMode
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.csfle.MongoCsfleProfile
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.csfle.MongoDataKeyResolver
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.qe.MongoEncryptedFieldDescriptor
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.qe.MongoEncryptionMetadataOwnership
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.qe.MongoQueryShape
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.qe.MongoQueryShapeSupport
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.qe.MongoQueryableEncryptionCollectionManager
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.qe.MongoQueryableEncryptionProfile
dev.caskeleton.adapter.outbound.mongo.advanced.encryption.qe.MongoQueryableEncryptionQueryType
dev.caskeleton.adapter.outbound.mongo.advanced.gridfs.MongoGridFsCompatibilityReader
dev.caskeleton.adapter.outbound.mongo.advanced.gridfs.MongoGridFsMigrationCheckpoint
dev.caskeleton.adapter.outbound.mongo.advanced.gridfs.MongoGridFsMigrationJob
dev.caskeleton.adapter.outbound.mongo.advanced.gridfs.MongoGridFsObjectReference
dev.caskeleton.adapter.outbound.mongo.advanced.search.MongoSearchIndexDescriptor
dev.caskeleton.adapter.outbound.mongo.advanced.search.MongoSearchIndexState
dev.caskeleton.adapter.outbound.mongo.advanced.search.MongoSearchOperations
dev.caskeleton.adapter.outbound.mongo.advanced.search.MongoSearchQuery
dev.caskeleton.adapter.outbound.mongo.advanced.search.MongoSearchReadinessGate
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.MongoRoutingClassification
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.ShardAwareQueryValidator
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.ShardKeyDescriptor
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.ShardKeyPart
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.ShardStrategy
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.admin.MongoShardingAdminGateway
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.admin.ReshardApproval
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.admin.ShardKeyAnalyzer
dev.caskeleton.adapter.outbound.mongo.advanced.sharding.admin.ShardKeyReadinessReport
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.database.MongoTenantClientRegistry
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.database.MongoTenantDatabaseResolver
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.database.MongoTenantLifecyclePolicy
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.database.MongoTenantMigrationCheckpointStore
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.database.MongoTenantMigrationCoordinator
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.shared.MongoTenantContext
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.shared.MongoTenantManifestValidator
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.shared.MongoTenantPredicateInjector
dev.caskeleton.adapter.outbound.mongo.advanced.tenancy.shared.TenantScopedMongoOperations
dev.caskeleton.adapter.outbound.mongo.advanced.timeseries.MongoTimeSeriesCapability
dev.caskeleton.adapter.outbound.mongo.advanced.timeseries.MongoTimeSeriesCapabilityValidator
dev.caskeleton.adapter.outbound.mongo.advanced.timeseries.MongoTimeSeriesDescriptor
dev.caskeleton.adapter.outbound.mongo.advanced.timeseries.MongoTimeSeriesGranularity
dev.caskeleton.adapter.outbound.mongo.advanced.timeseries.MongoTimeSeriesOperations
dev.caskeleton.adapter.outbound.mongo.advanced.timeseries.MongoTimeSeriesSupport
dev.caskeleton.adapter.outbound.mongo.advanced.vector.MongoEmbedding
dev.caskeleton.adapter.outbound.mongo.advanced.vector.MongoVectorIndexDescriptor
dev.caskeleton.adapter.outbound.mongo.advanced.vector.MongoVectorQuery
dev.caskeleton.adapter.outbound.mongo.advanced.vector.MongoVectorSearchBenchmarkGate
dev.caskeleton.adapter.outbound.mongo.advanced.vector.MongoVectorSearchOperations
dev.caskeleton.adapter.outbound.mongo.aggregation.MongoAggregationPlan
dev.caskeleton.adapter.outbound.mongo.aggregation.MongoAggregationProfile
dev.caskeleton.adapter.outbound.mongo.aggregation.MongoAggregationRisk
dev.caskeleton.adapter.outbound.mongo.aggregation.MongoAggregationStageDescriptor
dev.caskeleton.adapter.outbound.mongo.aggregation.PolicyAwareMongoAggregationExecutor
dev.caskeleton.adapter.outbound.mongo.api.CollectionProfileName
dev.caskeleton.adapter.outbound.mongo.api.DatabaseProfileName
dev.caskeleton.adapter.outbound.mongo.api.MongoOperationContext
dev.caskeleton.adapter.outbound.mongo.api.MongoOperationName
dev.caskeleton.adapter.outbound.mongo.api.MongoOperationScope
dev.caskeleton.adapter.outbound.mongo.api.MongoOperationType
dev.caskeleton.adapter.outbound.mongo.api.capability.MongoCapability
dev.caskeleton.adapter.outbound.mongo.api.capability.MongoCapabilitySet
dev.caskeleton.adapter.outbound.mongo.api.capability.MongoCapabilitySupport
dev.caskeleton.adapter.outbound.mongo.api.capability.MongoServerVersion
dev.caskeleton.adapter.outbound.mongo.api.capability.MongoSupportLevel
dev.caskeleton.adapter.outbound.mongo.api.consistency.MongoConsistencyDescriptor
dev.caskeleton.adapter.outbound.mongo.api.consistency.MongoConsistencyGuarantee
dev.caskeleton.adapter.outbound.mongo.api.consistency.MongoConsistencyProfile
dev.caskeleton.adapter.outbound.mongo.api.consistency.MongoConsistencyRegistry
dev.caskeleton.adapter.outbound.mongo.api.error.MongoBulkPartialFailureException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoConnectionException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoCursorException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoDataSchemaUnsupportedException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoDocumentTooLargeException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoDuplicateKeyException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoEncryptionException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoExecutionOutcome
dev.caskeleton.adapter.outbound.mongo.api.error.MongoFailureCategory
dev.caskeleton.adapter.outbound.mongo.api.error.MongoFailureContext
dev.caskeleton.adapter.outbound.mongo.api.error.MongoOperationRejectedException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoOptimisticConflictException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoPersistenceException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoReadConcernException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoResumeException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoRetryScope
dev.caskeleton.adapter.outbound.mongo.api.error.MongoSchemaValidationException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoServerSelectionException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoShardRoutingException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoTimeoutException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoTransactionCommitUnknownException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoTransactionTransientException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoUnclassifiedFailureException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoWriteConcernException
dev.caskeleton.adapter.outbound.mongo.api.error.MongoWriteConflictException
dev.caskeleton.adapter.outbound.mongo.api.mapping.DomainDocumentId
dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoBigIntegerRepresentation
dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoDecimalRepresentation
dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoEnumRepresentation
dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoIdRepresentation
dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoTemporalRepresentation
dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoTypeMetadataPolicy
dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoTypeRepresentationManifest
dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoUuidRepresentation
dev.caskeleton.adapter.outbound.mongo.api.observation.MongoOperationObservation
dev.caskeleton.adapter.outbound.mongo.api.observation.MongoOperationObserver
dev.caskeleton.adapter.outbound.mongo.api.profile.MongoClientPlane
dev.caskeleton.adapter.outbound.mongo.api.profile.MongoRuntimeProfile
dev.caskeleton.adapter.outbound.mongo.api.profile.MongoStableApiProfile
dev.caskeleton.adapter.outbound.mongo.api.profile.MongoTopology
dev.caskeleton.adapter.outbound.mongo.api.profile.MongoTopologyRequirement
dev.caskeleton.adapter.outbound.mongo.api.schema.DocumentSchemaVersion
dev.caskeleton.adapter.outbound.mongo.api.schema.MongoSchemaVersionPolicy
dev.caskeleton.adapter.outbound.mongo.api.schema.MongoSchemaVersionRange
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoClientGeneration
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoClientGenerationRegistry
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoDriverObservabilityAutoConfiguration
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoPlatformAutoConfiguration
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoPlatformHealthIndicator
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoPlatformProperties
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoProfileProperties
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoStableReleaseEvidence
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoStableReleaseGate
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoStartupValidator
dev.caskeleton.adapter.outbound.mongo.autoconfigure.MongoTopologyProbe
dev.caskeleton.adapter.outbound.mongo.changestream.MongoChangeEventIdentity
dev.caskeleton.adapter.outbound.mongo.changestream.MongoChangeStreamPipeline
dev.caskeleton.adapter.outbound.mongo.changestream.MongoChangeStreamState
dev.caskeleton.adapter.outbound.mongo.changestream.MongoChangeStreamSubscription
dev.caskeleton.adapter.outbound.mongo.changestream.MongoClusterTime
dev.caskeleton.adapter.outbound.mongo.changestream.MongoResumeCheckpoint
dev.caskeleton.adapter.outbound.mongo.changestream.MongoResumeCheckpointStore
dev.caskeleton.adapter.outbound.mongo.changestream.MongoResumePosition
dev.caskeleton.adapter.outbound.mongo.changestream.projector.MongoChangeClaim
dev.caskeleton.adapter.outbound.mongo.changestream.projector.MongoChangeDeduplicationStore
dev.caskeleton.adapter.outbound.mongo.changestream.projector.MongoChangeProjectionResult
dev.caskeleton.adapter.outbound.mongo.changestream.projector.MongoChangeProjector
dev.caskeleton.adapter.outbound.mongo.changestream.projector.MongoChangeStreamRunner
dev.caskeleton.adapter.outbound.mongo.changestream.recovery.MongoChangeHistoryLostException
dev.caskeleton.adapter.outbound.mongo.changestream.recovery.MongoChangeStreamRecoveryDecision
dev.caskeleton.adapter.outbound.mongo.changestream.recovery.MongoChangeStreamRecoveryPolicy
dev.caskeleton.adapter.outbound.mongo.changestream.recovery.MongoInvalidateRecovery
dev.caskeleton.adapter.outbound.mongo.failure.DefaultMongoFailureClassifier
dev.caskeleton.adapter.outbound.mongo.failure.DefaultMongoFailureTranslator
dev.caskeleton.adapter.outbound.mongo.failure.MongoDriverFailureView
dev.caskeleton.adapter.outbound.mongo.failure.MongoFailureClassification
dev.caskeleton.adapter.outbound.mongo.failure.MongoFailureClassifier
dev.caskeleton.adapter.outbound.mongo.failure.MongoFailureExtractor
dev.caskeleton.adapter.outbound.mongo.failure.MongoFailurePhase
dev.caskeleton.adapter.outbound.mongo.failure.MongoFailureTranslator
dev.caskeleton.adapter.outbound.mongo.geo.MongoGeoDistance
dev.caskeleton.adapter.outbound.mongo.geo.MongoGeoPoint
dev.caskeleton.adapter.outbound.mongo.geo.MongoGeoQuery
dev.caskeleton.adapter.outbound.mongo.geo.MongoGeospatialOperations
dev.caskeleton.adapter.outbound.mongo.geo.SpringMongoGeospatialOperations
dev.caskeleton.adapter.outbound.mongo.imperative.DefaultMongoImperativeExecutor
dev.caskeleton.adapter.outbound.mongo.imperative.MongoCollectionAccess
dev.caskeleton.adapter.outbound.mongo.imperative.MongoCollectionProfileRegistry
dev.caskeleton.adapter.outbound.mongo.imperative.MongoCompletion
dev.caskeleton.adapter.outbound.mongo.imperative.MongoConsistencyBinder
dev.caskeleton.adapter.outbound.mongo.imperative.MongoImperativeCallback
dev.caskeleton.adapter.outbound.mongo.imperative.MongoImperativeExecutor
dev.caskeleton.adapter.outbound.mongo.imperative.MongoOperationResult
dev.caskeleton.adapter.outbound.mongo.imperative.MongoPlatformCallback
dev.caskeleton.adapter.outbound.mongo.imperative.MongoPlatformCollectionAccess
dev.caskeleton.adapter.outbound.mongo.imperative.MongoTemplateSupportContract
dev.caskeleton.adapter.outbound.mongo.imperative.ScopedMongoOperations
dev.caskeleton.adapter.outbound.mongo.imperative.atomic.AtomicFilter
dev.caskeleton.adapter.outbound.mongo.imperative.atomic.AtomicUpdate
dev.caskeleton.adapter.outbound.mongo.imperative.atomic.AtomicUpdateResult
dev.caskeleton.adapter.outbound.mongo.imperative.atomic.MongoAtomicOperations
dev.caskeleton.adapter.outbound.mongo.imperative.atomic.MongoAtomicOperationsTemplate
dev.caskeleton.adapter.outbound.mongo.imperative.atomic.MongoAtomicPolicy
dev.caskeleton.adapter.outbound.mongo.imperative.atomic.MongoUpdateOperator
dev.caskeleton.adapter.outbound.mongo.imperative.atomic.ReturnDocumentMode
dev.caskeleton.adapter.outbound.mongo.imperative.bulk.MongoBulkExecutor
dev.caskeleton.adapter.outbound.mongo.imperative.bulk.MongoBulkItemFailure
dev.caskeleton.adapter.outbound.mongo.imperative.bulk.MongoBulkItemOutcome
dev.caskeleton.adapter.outbound.mongo.imperative.bulk.MongoBulkMode
dev.caskeleton.adapter.outbound.mongo.imperative.bulk.MongoBulkResult
dev.caskeleton.adapter.outbound.mongo.imperative.bulk.MongoBulkWritePlan
dev.caskeleton.adapter.outbound.mongo.imperative.bulk.SpringDataBulkFailureExtractor
dev.caskeleton.adapter.outbound.mongo.imperative.revision.MongoDocumentNotFoundException
dev.caskeleton.adapter.outbound.mongo.imperative.revision.MongoOptimisticConflictTranslator
dev.caskeleton.adapter.outbound.mongo.imperative.revision.MongoRevision
dev.caskeleton.adapter.outbound.mongo.imperative.revision.VersionedMongoUpdater
dev.caskeleton.adapter.outbound.mongo.imperative.revision.VersionedUpdateCommand
dev.caskeleton.adapter.outbound.mongo.mapping.BigDecimalToDecimal128Converter
dev.caskeleton.adapter.outbound.mongo.mapping.BigIntegerRepresentationConverters
dev.caskeleton.adapter.outbound.mongo.mapping.Decimal128ToBigDecimalConverter
dev.caskeleton.adapter.outbound.mongo.mapping.DomainIdReadConverter
dev.caskeleton.adapter.outbound.mongo.mapping.DomainIdWriteConverter
dev.caskeleton.adapter.outbound.mongo.mapping.LocalDateTimeMappingGuard
dev.caskeleton.adapter.outbound.mongo.mapping.MongoCustomConversionsFactory
dev.caskeleton.adapter.outbound.mongo.mapping.MongoMappingConfiguration
dev.caskeleton.adapter.outbound.mongo.mapping.MongoTypeMetadataConfigurer
dev.caskeleton.adapter.outbound.mongo.mapping.type.LongLivedMongoDocument
dev.caskeleton.adapter.outbound.mongo.mapping.type.MongoTypeMetadataDescriptor
dev.caskeleton.adapter.outbound.mongo.mapping.type.MongoTypeMetadataRegistry
dev.caskeleton.adapter.outbound.mongo.mapping.type.PolicyAwareMongoTypeMapper
dev.caskeleton.adapter.outbound.mongo.migration.MongoCollectionMigrationLedger
dev.caskeleton.adapter.outbound.mongo.migration.MongoCollectionMigrationLock
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigration
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationCheckpoint
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationChecksum
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationContext
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationHeartbeat
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationId
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationLedger
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationLock
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationPostcondition
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationPrecondition
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationResult
dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationRunner
dev.caskeleton.adapter.outbound.mongo.migration.flamingock.FlamingockChangeUnitView
dev.caskeleton.adapter.outbound.mongo.migration.flamingock.FlamingockLedgerAdapter
dev.caskeleton.adapter.outbound.mongo.migration.flamingock.FlamingockLockAdapter
dev.caskeleton.adapter.outbound.mongo.migration.flamingock.FlamingockMigrationConfiguration
dev.caskeleton.adapter.outbound.mongo.migration.flamingock.FlamingockMongoMigrationAdapter
dev.caskeleton.adapter.outbound.mongo.nativecap.ApprovedMongoNativeOperation
dev.caskeleton.adapter.outbound.mongo.nativecap.MongoNativeCapabilityGateway
dev.caskeleton.adapter.outbound.mongo.nativecap.MongoNativeCommandCategory
dev.caskeleton.adapter.outbound.mongo.nativecap.MongoNativeOperationPolicy
dev.caskeleton.adapter.outbound.mongo.nativecap.PolicyAwareMongoNativeGateway
dev.caskeleton.adapter.outbound.mongo.observation.MicrometerMongoOperationObserver
dev.caskeleton.adapter.outbound.mongo.observation.MongoCommandObservationListener
dev.caskeleton.adapter.outbound.mongo.observation.MongoDriverObservabilityConfiguration
dev.caskeleton.adapter.outbound.mongo.observation.MongoObservationConvention
dev.caskeleton.adapter.outbound.mongo.observation.MongoObservationRedactor
dev.caskeleton.adapter.outbound.mongo.observation.MongoPoolObservationListener
dev.caskeleton.adapter.outbound.mongo.observation.MongoSdamObservationListener
dev.caskeleton.adapter.outbound.mongo.query.MongoFieldDescriptor
dev.caskeleton.adapter.outbound.mongo.query.MongoOperator
dev.caskeleton.adapter.outbound.mongo.query.MongoQueryPolicy
dev.caskeleton.adapter.outbound.mongo.query.MongoRegexPolicy
dev.caskeleton.adapter.outbound.mongo.query.MongoSortDescriptor
dev.caskeleton.adapter.outbound.mongo.query.PolicyAwareMongoQueryBuilder
dev.caskeleton.adapter.outbound.mongo.query.budget.MongoBudgetEnforcer
dev.caskeleton.adapter.outbound.mongo.query.budget.MongoBudgetPolicyRegistry
dev.caskeleton.adapter.outbound.mongo.query.budget.MongoOperationBudget
dev.caskeleton.adapter.outbound.mongo.query.pagination.MongoKeysetCursor
dev.caskeleton.adapter.outbound.mongo.query.pagination.MongoKeysetCursorCodec
dev.caskeleton.adapter.outbound.mongo.query.pagination.MongoKeysetPageRequest
dev.caskeleton.adapter.outbound.mongo.query.pagination.MongoKeysetQueryBuilder
dev.caskeleton.adapter.outbound.mongo.query.pagination.MongoKeysetSlice
dev.caskeleton.adapter.outbound.mongo.query.pagination.MongoKeysetSort
dev.caskeleton.adapter.outbound.mongo.query.pagination.MongoNullSortOrdering
dev.caskeleton.adapter.outbound.mongo.reactive.DefaultReactiveMongoExecutor
dev.caskeleton.adapter.outbound.mongo.reactive.ReactiveMongoCallback
dev.caskeleton.adapter.outbound.mongo.reactive.ReactiveMongoCollectionAccess
dev.caskeleton.adapter.outbound.mongo.reactive.ReactiveMongoConsistencyBinder
dev.caskeleton.adapter.outbound.mongo.reactive.ReactiveMongoContextKeys
dev.caskeleton.adapter.outbound.mongo.reactive.ReactiveMongoExecutor
dev.caskeleton.adapter.outbound.mongo.reactive.ReactiveScopedMongoOperations
dev.caskeleton.adapter.outbound.mongo.reactive.cursor.MongoCursorGuard
dev.caskeleton.adapter.outbound.mongo.reactive.cursor.MongoCursorLease
dev.caskeleton.adapter.outbound.mongo.reactive.cursor.MongoCursorTermination
dev.caskeleton.adapter.outbound.mongo.reactive.cursor.MongoReactiveCursorPublisher
dev.caskeleton.adapter.outbound.mongo.reactive.cursor.MongoResultBudgetTracker
dev.caskeleton.adapter.outbound.mongo.schema.index.MongoIndexApplyPolicy
dev.caskeleton.adapter.outbound.mongo.schema.index.MongoIndexDescriptorView
dev.caskeleton.adapter.outbound.mongo.schema.index.MongoIndexDiff
dev.caskeleton.adapter.outbound.mongo.schema.index.MongoIndexDiffEngine
dev.caskeleton.adapter.outbound.mongo.schema.index.MongoIndexRetirementPlan
dev.caskeleton.adapter.outbound.mongo.schema.index.MongoIndexRetirementState
dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoCollectionManifest
dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoIndexDirection
dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoIndexKey
dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoIndexManifest
dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoManifestRegistry
dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoMetadataOwnership
dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoSchemaManifest
dev.caskeleton.adapter.outbound.mongo.schema.model.EmbeddedCollectionDescriptor
dev.caskeleton.adapter.outbound.mongo.schema.model.MongoBinaryFieldDescriptor
dev.caskeleton.adapter.outbound.mongo.schema.model.MongoDocumentModelManifest
dev.caskeleton.adapter.outbound.mongo.schema.model.MongoDocumentModelValidator
dev.caskeleton.adapter.outbound.mongo.schema.model.MongoDocumentSizeBudget
dev.caskeleton.adapter.outbound.mongo.schema.model.MongoReferenceDescriptor
dev.caskeleton.adapter.outbound.mongo.schema.model.MongoReferenceLifecycle
dev.caskeleton.adapter.outbound.mongo.schema.ttl.MongoExpirationAccessPolicy
dev.caskeleton.adapter.outbound.mongo.schema.ttl.MongoTtlIndexDescriptor
dev.caskeleton.adapter.outbound.mongo.schema.ttl.MongoTtlPolicy
dev.caskeleton.adapter.outbound.mongo.schema.ttl.MongoTtlPolicyValidator
dev.caskeleton.adapter.outbound.mongo.schema.validation.MongoValidationAction
dev.caskeleton.adapter.outbound.mongo.schema.validation.MongoValidationLevel
dev.caskeleton.adapter.outbound.mongo.schema.validation.MongoValidatorApplyPolicy
dev.caskeleton.adapter.outbound.mongo.schema.validation.MongoValidatorDescriptor
dev.caskeleton.adapter.outbound.mongo.schema.validation.MongoValidatorDiff
dev.caskeleton.adapter.outbound.mongo.schema.validation.MongoValidatorDiffEngine
dev.caskeleton.adapter.outbound.mongo.security.MongoCredentialReference
dev.caskeleton.adapter.outbound.mongo.security.MongoCredentialResolver
dev.caskeleton.adapter.outbound.mongo.security.MongoCredentialRotationPolicy
dev.caskeleton.adapter.outbound.mongo.security.MongoPrincipalRole
dev.caskeleton.adapter.outbound.mongo.security.MongoSecurityProfile
dev.caskeleton.adapter.outbound.mongo.security.MongoSecurityProfileValidator
dev.caskeleton.adapter.outbound.mongo.security.admin.MongoAdminApproval
dev.caskeleton.adapter.outbound.mongo.security.admin.MongoAdminAuditPhase
dev.caskeleton.adapter.outbound.mongo.security.admin.MongoAdminAuditRecord
dev.caskeleton.adapter.outbound.mongo.security.admin.MongoAdminAuthorization
dev.caskeleton.adapter.outbound.mongo.security.admin.MongoAdminCommand
dev.caskeleton.adapter.outbound.mongo.security.admin.MongoAdminGateway
dev.caskeleton.adapter.outbound.mongo.security.admin.MongoAdminOperation
dev.caskeleton.adapter.outbound.mongo.security.admin.MongoAdminRuntimeGuard
dev.caskeleton.adapter.outbound.mongo.transaction.MongoTransactionExecutor
dev.caskeleton.adapter.outbound.mongo.transaction.MongoTransactionProfile
dev.caskeleton.adapter.outbound.mongo.transaction.MongoTransactionScope
dev.caskeleton.adapter.outbound.mongo.transaction.MongoTransactionSession
dev.caskeleton.adapter.outbound.mongo.transaction.MongoTransactionSessionFactory
dev.caskeleton.adapter.outbound.mongo.transaction.ReactiveMongoTransactionExecutor
dev.caskeleton.adapter.outbound.mongo.transaction.ReactiveMongoTransactionSession
dev.caskeleton.adapter.outbound.mongo.transaction.ReactiveMongoTransactionSessionFactory
dev.caskeleton.adapter.outbound.mongo.transaction.SpringMongoTransactionExecutor
dev.caskeleton.adapter.outbound.mongo.transaction.SpringMongoTransactionSessionFactory
dev.caskeleton.adapter.outbound.mongo.transaction.SpringReactiveMongoTransactionExecutor
dev.caskeleton.adapter.outbound.mongo.transaction.SpringReactiveMongoTransactionSessionFactory
dev.caskeleton.adapter.outbound.mongo.transaction.retry.MongoCommitReconciler
dev.caskeleton.adapter.outbound.mongo.transaction.retry.MongoRetryBudget
dev.caskeleton.adapter.outbound.mongo.transaction.retry.MongoRetryDecision
dev.caskeleton.adapter.outbound.mongo.transaction.retry.MongoTransactionRetryCoordinator
dev.caskeleton.adapter.outbound.mongo.transaction.session.MongoCausalSessionContext
dev.caskeleton.adapter.outbound.mongo.transaction.session.MongoCausalSessionExecutor
dev.caskeleton.adapter.outbound.mongo.transaction.session.ReactiveMongoCausalSessionExecutor
dev.caskeleton.adapter.outbound.mongo.transaction.session.SpringMongoCausalSessionExecutor
+13 -2
View File
@@ -35,8 +35,19 @@ This is the same adaptation already applied to the HTTP client platform
| `jpa-spring-boot-starter` | `:app-bootstrap` (`dev.caskeleton.bootstrap.autoconfigure.jpa`) | This repository's composition root owns wiring, startup validation, and actuator surface; an adapter leaf must not auto-configure itself. `AGENTS.md` assigns composition to `app-bootstrap`. |
| `jpa-testkit`, `jpa-testkit-postgresql`, `jpa-testkit-migration`, `jpa-testkit-queryplan` | `:adapter:outbound:persistence-jpa` `src/testkit/java/**/testkit` | The plan forbids production modules depending on the testkit. A source set whose dependencies are declared only on test configurations gives the same guarantee without a new Gradle project, and more than one lane consumes it. |
The package boundary is enforced by `JpaModuleBoundaryTest`, which reproduces the plan's
§3 module dependency map as package rules.
The package boundary is enforced by `JpaModuleBoundaryTest`. It holds a closed catalog of the
production root's direct child packages, compares that catalog against the tree for exact equality,
checks every observed top-level edge against the declared ones, and rejects cycles.
This used to be a stronger claim than the test. The catalog listed thirteen packages while the tree
held twenty-two, so nine — `audit`, `config`, `failure`, `fileserver`, `h2`, `idempotency`, `lock`,
`notification`, `outbox` — were governed by nothing, and a `transaction → postgresql` /
`postgresql → transaction` cycle passed. Both are closed now, and the catalog's exact-equality check
is what keeps a new package from being green by omission.
**Known gap.** The catalog governs top-level packages. Sub-package edges inside one top-level
package are not checked, and the target tree in the review's JPA-023 (a `capability/*` layout) is
not implemented — the notification configuration facade is the first step toward it.
## 2. Package mapping
+40 -11
View File
@@ -1,23 +1,52 @@
# JPA Persistence Platform — Support Matrix
The machine-readable source for `JpaReleaseManifest`. A release gate parses this file, so a version
or gate that stops being named here stops being claimed — and the build fails rather than the
document quietly drifting from the code.
**This document is a rendering. The machine-readable source is
[`src/config/jpa/release-registry.json`](../../src/config/jpa/release-registry.json).**
`JpaReleaseManifest` used to parse this file with regular expressions: every `PostgreSQL NN` it
mentioned became a supported version, whatever table or sentence produced the match. An Experimental
major joined the Stable list, a version named once in prose counted as supported, and demoting a
major changed nothing so long as the string survived somewhere in the document. Now the registry
declares a support level per major as a field, each gate names the Gradle task that produces its
evidence, and this document describes what the registry says.
## Database
| Database | Support | Evidence |
|---|---|---|
| PostgreSQL 16 | Stable | full contract suite, release lane |
| PostgreSQL 17 | Stable | full contract suite, release lane |
| PostgreSQL 18 | Stable | full contract suite, release lane |
| PostgreSQL 16 | Stable | full contract suite, release lane (own matrix job) |
| PostgreSQL 17 | Stable | full contract suite, release lane (own matrix job) |
| PostgreSQL 18 | Stable | full contract suite, release lane (own matrix job) |
| PostgreSQL 19 | Experimental | compatibility lane only; promotion requires an ADR |
| H2 | Local convenience | **never** evidence of PostgreSQL behaviour |
H2 is not a second production target. It reports different SQLSTATEs for the same violation, has no
`SKIP LOCKED` guarantee the platform relies on, no JSONB operators, no range types, and no
concurrent index builds. A green H2 run is evidence that the code compiles and runs, and nothing
more.
Each major gets its **own release job**, because for a while it did not. The release lane passed
`-Pjpa.matrix.versions=16,17,18` to a `JpaPlatformContractSupport.start()` that used
`selectedVersions().get(0)`, so the whole integration suite ran against PostgreSQL 16 and this table
recorded 17 and 18 as fully covered on the strength of a three-assertion smoke test. `start()` now
refuses a multi-version selection outright, `jpa-release.yml` fans out to one job per major, and a
promotion job requires all three majors' evidence to carry the same commit SHA — so a removed major
removes the release, not the evidence for it.
**Provider baseline.** The gates run against the Hibernate version the Spring Boot BOM resolves —
**7.1.8.Final** — which the registry records as `stable-tested-baseline`. This document previously
called 7.4 the Stable baseline and the pagination gate was named `hibernate-7.4-fetch-pagination`,
so every run of that gate produced evidence labelled with a provider it had never executed against.
7.4 is recorded as `compatibility-target`; it becomes the baseline when a full lane has actually run
on it.
H2 is not a second production target. It reports different SQLSTATEs for the same violation, no JSONB
operators, no range types, and no concurrent index builds. A green H2 run is evidence that the code
compiles and runs, and nothing more.
`SKIP LOCKED` needs its own sentence, because two documents said different things about it. The
module's `CLAUDE.md` records a measurement: H2 2.4.240 accepts `FOR UPDATE SKIP LOCKED` and does
genuinely skip locked rows, which is why the outbox claim SQL is identical on both vendors. This
document previously said H2 has no such guarantee. Both are right about different questions, and
the distinction is the point: **observed behaviour in the version we measured is not a production
guarantee, and it is never PostgreSQL contract evidence.** The measurement is why the claim SQL
needs no vendor branch; the absence of a guarantee is why every concurrency contract still runs
against a real PostgreSQL.
## Specification and provider
@@ -67,7 +96,7 @@ Each row is a way the platform could pass its tests and still be wrong in produc
| `osiv-disabled` | gate | lazy loading from the view layer, one query per rendered row |
| `flyway-validate` | gate | Hibernate mutating a deployed schema, or running against one it was not built for |
| `runtime-role-no-ddl` | gate | the application's own credential being able to alter or drop schema objects |
| `hibernate-7.4-fetch-pagination` | gate | a paged collection fetch silently reading the whole table and paginating in memory |
| `collection-fetch-pagination` | gate | a paged collection fetch silently reading the whole table and paginating in memory |
## Explicitly unsupported
+20 -6
View File
@@ -1,9 +1,18 @@
# 설정 레퍼런스
> **Prefix.** Every property below binds under `app.messaging`, which is the prefix the deployed
> runtime and the `APP_MESSAGING_*` environment variables already use. Earlier revisions of this
> page documented a bare `messaging` prefix and the starter bound `backend.messaging`; neither
> bound what this page describes, so a deployment configured from it changed nothing. A key under
> either of the old prefixes now fails startup with a message naming the key — see
> `MessagingPrefixMigrationValidator`.
## Destination profile
```yaml
messaging:
app:
messaging:
destinations:
order-events:
broker: kafka-primary
@@ -77,7 +86,8 @@ messaging:
### Kafka
```yaml
messaging:
app:
messaging:
brokers:
kafka-primary:
type: kafka
@@ -96,7 +106,8 @@ messaging:
### RabbitMQ
```yaml
messaging:
app:
messaging:
brokers:
rabbit-primary:
type: rabbitmq
@@ -117,7 +128,8 @@ messaging:
## 보안
```yaml
messaging:
app:
messaging:
security:
kafka-primary:
producer: { type: SASL_SCRAM, credential-id: kafka-producer }
@@ -135,7 +147,8 @@ messaging:
기본값은 전부 `false`다.
```yaml
messaging:
app:
messaging:
experimental:
kafka-share: false
pulsar: false
@@ -147,7 +160,8 @@ messaging:
## Backpressure
```yaml
messaging:
app:
messaging:
backpressure:
global-limit: 512
per-destination-limit: 64 # global-limit 이하여야 한다
+34
View File
@@ -0,0 +1,34 @@
# 기존 runtime → 신규 messaging platform cutover (MSG-015)
## 왜 기계적 매핑이 안 되는가
두 outbox 모델의 enum 이름이 겹치는데 의미가 반대다.
| 모델 | retryable | terminal |
|---|---|---|
| 기존 `OutboxEventStatus` | `FAILED` (`next_attempt_at` 보유) | `DEAD` |
| 신규 `OutboxStatus` | `AMBIGUOUS` | `FAILED`, `EXHAUSTED` |
이름으로 매핑하면 **확정 거절이 무한 재시도**가 되고 **불확정이 park**된다. 그래서 application은
자기 어휘(`OutboxPublishOutcome`)만 쓰고, 변환은 bridge adapter가 한다.
## 지금 반영된 것
- `OutboxPublishOutcome``CONFIRMED` / `AMBIGUOUS` / `REJECTED_BEFORE_SEND` /
`REJECTED_AFTER_BROKER`. application이 소유하는 canonical 결과 타입이며, "리턴 or throw"만 가능한
기존 어댑터를 위해 `OutboxMessagePublishPort.publishForOutcome`의 default가 `CONFIRMED`를 돌려준다.
- `APPLICATION_DOES_NOT_DEPEND_ON_THE_MESSAGING_PLATFORM` ArchUnit 규칙 — application-core가
`dev.caskeleton.messaging..`를 import하면 빌드가 깨진다.
- 반대 방향(신규 `PublishResult` → application outcome) 매핑 규칙을 테스트로 고정.
## 남은 것
- `messaging-platform-bridge` outbound leaf: validated application event → platform envelope,
`PublishResult``OutboxPublishOutcome`. registry에 leaf를 추가하는 변경이라 별도 커밋.
- golden contract 테스트: event/message ID, type, schema revision, partition/order/correlation/
causation/tenant/trace, payload digest, wire version이 bytes 단위로 보존되는지.
- 단일 publication authority: 기존 `OutboxPublicationAuthority` fence를 재사용해 writer/relay가
동시에 ACTIVE가 되지 않도록. **dual write/publish는 금지** — 한 business fact가 두 durable store와
두 relay로 나가는 상태가 cutover에서 가장 위험하다.
- 첫 cutover 범위는 **transport만** 교체(저장소는 기존 유지). storage migration은 shadow read →
authority switch → old backlog drain 순서로 별도 release.
+18 -1
View File
@@ -41,9 +41,26 @@ failed로 표시하면 broker가 이미 가지고 있을 수 있는 메시지를
### lease
```text
status IN ('PENDING','AMBIGUOUS') AND (lease_expires_at IS NULL OR lease_expires_at <= now)
status IN ('PENDING','AMBIGUOUS','IN_FLIGHT')
AND (lease_expires_at IS NULL OR lease_expires_at <= now)
AND next_attempt_at <= now
AND attempts < maxAttempts
```
`IN_FLIGHT`가 목록에 있는 것이 핵심이다. relay가 publish 도중 죽으면 row는 `IN_FLIGHT`로 남는데,
이를 제외하면 그 메시지는 **영원히** 발행되지 않는다 — outbox가 막으려던 바로 그 실패다. 대신
lease가 만료됐을 때만 회수하므로, 살아 있는 relay가 들고 있는 row는 회수되지 않는다.
회수는 **같은 `message_id`로** 이루어지고 `lease_token`이 1 증가한다. 새 id를 발급하면 "전달됐을
수도 있는 메시지"가 "확실히 두 번째"가 되기 때문이다 (위의 AMBIGUOUS 논의와 같은 이유).
이 문단의 근거는 실제 PostgreSQL 컨테이너 레인이다:
- `OutboxPostgresIT#anExpiredLeaseBecomesClaimableAgain` — 만료된 lease의 재회수
- `OutboxPostgresIT#anExpiryReclaimKeepsTheMessageIdAndAdvancesTheToken` — 같은 id, 증가한 token
- `OutboxPostgresIT#aLeasedRowIsInvisibleToASecondRelayInstance` — 살아 있는 lease는 회수 불가
- `OutboxPostgresIT#aSupersededRelayCannotOverwriteTheOutcomeOfTheOneThatReplacedIt` — fencing
partial index `ix_messaging_outbox_claimable`이 이 쿼리를 backlog 크기에 비례하게 유지한다.
PUBLISHED row는 retention job이 지울 때까지 쌓이기 때문이다.
+18 -1
View File
@@ -3,11 +3,28 @@
플랫폼이 **무엇을 보장하는지**와 **무엇을 보장하지 않는지**를 브로커별로 고정한다.
여기 없는 조합은 지원되지 않는다.
> **인증 근거.** 이 표의 버전은 이 저장소의 컨테이너 레인이 실제로 실행한 이미지다. 이전 판은
> Kafka 4.2/4.3을 선언했지만 fixture는 `apache/kafka:4.1.0`, lockfile client는 4.1.1이었다 — 표와
> 코드 상수가 서로 일치했을 뿐 어느 쪽도 실행된 적이 없었다. 장애 시나리오 커버리지도 마찬가지로
> `BrokerFailureMatrix.shipped()` 하드코딩이 아니라 레인이 낸 증거(`BrokerCertificationEvidence`)에서
> 나온다. 증거가 없는 조합은 `NOT_COVERED`다 (MSG-014).
> **모듈 이름과 런타임 편입.** `messaging-outbox-jdbc-postgresql` / `messaging-inbox-jdbc-postgresql`은
> 이전에 `-jpa`로 불렸다. 구현은 Spring JDBC이고 SQL은 PostgreSQL 전용(`?::jsonb`,
> `FOR UPDATE SKIP LOCKED`, `ON CONFLICT`, `TIMESTAMPTZ`)이므로, 그 이름은 쓰지 않는 기술을
> 광고하고 vendor 중립 port(`messaging-reliability-api`)의 위치를 가렸다 (MSG-023).
>
> 또한 registry의 messaging leaf는 모두 `runtime_memberships`가 비어 있다. 이는 **build-only /
> incubating** — 어느 composition root에도 편입되지 않았다는 뜻이며, 아래의 등급과는 다른 축이다.
> 등급은 "무엇이 증명되었는가", membership은 "무엇이 실행되는가"를 말한다. 애플리케이션에 배선하려면
> registry를 먼저 바꾸고 `verifyRuntimeModuleMembership`을 통과시켜야 한다. 자세한 규칙은
> `src/messaging/CLAUDE.md`가 소유한다.
## 브로커 등급
| 브로커 | 등급 | 인증 기준 | Stable 기능 | 제한 |
|---|---|---|---|---|
| Kafka | Stable | 4.2+ / 4.3.x | producer idempotence, consumer group, batch, pause/resume, replay, transaction capability | Share Group은 Experimental |
| Kafka | Stable | 4.1.x | producer idempotence, consumer group, batch, pause/resume, replay, transaction capability | Share Group은 Experimental |
| RabbitMQ | Stable | 4.3.x | exchange/routing, publisher confirm, mandatory return, manual ACK, quorum queue, retry queue, DLQ | stream 및 특수 plugin 미지원 |
| Pulsar | Experimental | 4.0 LTS + 4.2 | typed publish/consume, Shared, Key_Shared, schema | transaction 미승격, 기본 비활성 |
| NATS JetStream | Experimental | 2.14.x | stream, durable consumer, explicit ACK, dedupe, replay | native DLQ 없음(플랫폼이 대행), 기본 비활성 |
+14
View File
@@ -0,0 +1,14 @@
# Advanced capability sign-off
`scripts/verify-mongodb-advanced.sh` treats a file in this directory as the evidence that a review
happened:
- `security.md` — per-capability privilege review, naming the roles granted and by whom.
- `migration.md` — per-capability migration path, naming what an existing deployment has to do.
These were previously appended to the gate's missing-evidence list unconditionally, so the gate had
no passing state at all. A gate that can never pass is one nobody can act on, and the thing it was
waiting for — a human review — has an artefact. This is that artefact.
A file here asserts the review was done. Adding one without doing it is the failure mode; that is a
review-process problem, and no script can tell the difference.
+10 -2
View File
@@ -21,8 +21,16 @@ Gradle projects would violate HARD-STOP #5 in `AGENTS.md`.
Therefore the design's 31 modules become **package boundaries inside the registered leaf**
`:adapter:outbound:persistence-mongo`, following the precedent already set by
[docs/httpclient/repository-adaptation.md](../httpclient/repository-adaptation.md). The design's
module dependency table (§6.3) is reproduced as ten ArchUnit rules in `MongoModuleBoundaryTest`, so
a forbidden edge fails the build the same way a missing Gradle dependency would.
module dependency table (§6.3) is enforced by `MongoModuleBoundaryTest` as a **closed edge matrix**:
every top-level package is declared with the packages it may import, the matrix is compared against
the tree for exact equality, and every observed edge must appear in it. A forbidden edge fails the
build the same way a missing Gradle dependency would, and so does a new package nobody registered.
This used to be a stronger claim than the test. The rules forbade a handful of reverse dependencies
and said nothing about the rest, so four edges outside the design's DAG existed and passed:
`reactive → imperative`, `reactive → query`, `transaction → reactive` and `geo → imperative`. They
are declared in the matrix now rather than removed — each is a real coupling the code relies on, and
the point of recording them is that the next one is a decision instead of an accident.
## 2. Package mapping
@@ -0,0 +1,69 @@
# NOTIF-ADR-005 — 어느 notification API가 canonical인가
## 상태
Accepted (2026-08-15). NTF-018 대응.
## 문제
같은 저장소에 notification 모델이 **두 개** 있다.
| 세대 | 위치 | 규모 |
| --- | --- | --- |
| R0 legacy | `adapter:outbound:notification`의 router/provider seam | 삭제 예정 |
| R1 | `dev.caskeleton.application.notification` (직속) | public type 100개 |
| Platform | `dev.caskeleton.application.notification.platform..` | 신규 |
`application-core/CLAUDE.md`는 R1을 "R1 canonical"이라 부르고,
`docs/notification/migration-guide.md`는 R0 → platform 이행만 설명하며 R1의 처분을 전혀 다루지 않는다.
`Channel`, plan, dispatch, receipt/evidence 모델이 두 namespace에 중복 존재하고 둘 사이에 production
bridge도 import도 없다.
**실패 모드는 "무엇이 깨지는가"가 아니라 "무엇을 써야 하는가"다.** 새 consumer가 어느 API를 쓸지 알 수
없고, 두 모델이 각자 진화하며, R0를 지운 뒤에도 R1 graph가 고아로 남거나 platform이 R1 정책을 우회하는
이중 canonical이 된다.
## 결정
**Platform이 canonical이다.** R1은 유지되지만 새 production consumer를 받지 않는다.
이유는 능력이 아니라 증거다. platform은 durable acceptance, fenced claim, event ledger, projection,
reconciliation을 실제 PostgreSQL 레인으로 증명한다(NOTIF-ADR-001~003). R1은 fake로 증명된 R1 계약이며
스스로 그렇게 선언한다 — `application-core/CLAUDE.md`가 "R1 application contract proven with fakes.
It does not claim PostgreSQL schema/locking, provider protocol, cryptographic verifier, or runtime
wiring qualification"이라고 적어 둔 그대로다.
## Disposition
R1 public type 100개의 처분은 네 가지 중 하나다.
| 처분 | 의미 | 대상 |
| --- | --- | --- |
| `replace` | platform에 동등물이 있다. 새 consumer는 platform을 쓴다 | `Channel`, plan/dispatch/receipt/evidence 계열 |
| `bridge` | 변환이 필요하다. 변환은 ACL 한 곳에만 둔다 | writer-cutover / receipt 적용 경로 |
| `retain` | platform이 다루지 않는 관심사다. 그대로 둔다 | consent/quiescence verifier port |
| `delete` | R0와 함께 사라진다 | R0 router가 쓰던 seam |
전수 분류표는 이 ADR이 아니라 `docs/notification/module-mapping.md`가 소유한다. ADR은 규칙을,
mapping 문서는 목록을 소유한다 — 목록을 두 곳에 복제하면 드리프트하는 쪽이 늘어난다.
## 강제
두 namespace 사이의 production dependency는 **0건**이며, 이것은 문서가 아니라 ArchUnit 규칙이 지킨다
(`CleanArchitectureTest``NOTIFICATION_R1_AND_PLATFORM_DO_NOT_DEPEND_ON_EACH_OTHER`).
변환이 필요해지면 `dev.caskeleton.application.notification.compatibility.r1` 한 패키지에만 두고, 그
패키지만 규칙에서 예외로 인정한다. 예외를 한 곳으로 모으는 것이 목적이다 — 두 모델이 서로를 아는
지점이 여러 곳이면 "어느 쪽이 canonical인가"라는 질문에 코드가 답하지 못한다.
## 결과
- 새 production consumer는 `..notification.platform..`만 쓴다.
- R1 type은 남지만, 새 코드가 그것을 import하면 ArchUnit이 막는다.
- R0 삭제는 이 ADR과 무관하게 진행된다. R1은 R0와 함께 사라지지 않는다.
## 하지 않은 것
R1 100개 type에 `@Deprecated(forRemoval = true)`를 붙이지 않았다. 제거 시점이 정해지지 않았고,
`forRemoval`은 "이 릴리스 이후 사라진다"는 약속이라 시점 없이 붙이면 그 자체가 거짓 신호다. 경계는
ArchUnit이 강제하고, deprecation은 제거 계획이 생길 때 붙인다.
+572
View File
@@ -0,0 +1,572 @@
# NTF-022 — public type surface of the notification platform.
# Every top-level public type under the platform packages. Growth is a reviewed
# change: ./gradlew updateNotificationApiSurface -PapproveNotificationApiChange
dev.caskeleton.adapter.outbound.notification.NotificationConfig
dev.caskeleton.adapter.outbound.notification.NotificationRoutesSettings
dev.caskeleton.adapter.outbound.notification.catalog.CompiledNotificationBinding
dev.caskeleton.adapter.outbound.notification.catalog.NotificationBindingCompiler
dev.caskeleton.adapter.outbound.notification.catalog.NotificationCanonicalRouteCatalog
dev.caskeleton.adapter.outbound.notification.catalog.NotificationCatalogException
dev.caskeleton.adapter.outbound.notification.catalog.NotificationCutoverRouteCatalog
dev.caskeleton.adapter.outbound.notification.catalog.NotificationPlanAdapter
dev.caskeleton.adapter.outbound.notification.catalog.NotificationProviderCapabilityCard
dev.caskeleton.adapter.outbound.notification.catalog.NotificationProviderCapabilityDescriptorSource
dev.caskeleton.adapter.outbound.notification.catalog.NotificationProviderDescriptor
dev.caskeleton.adapter.outbound.notification.catalog.NotificationProviderRuntimeProfile
dev.caskeleton.adapter.outbound.notification.catalog.NotificationRouteDescriptor
dev.caskeleton.adapter.outbound.notification.catalog.NotificationTemplateDescriptor
dev.caskeleton.adapter.outbound.notification.core.FailOpenNotificationProvider
dev.caskeleton.adapter.outbound.notification.core.NotificationProvider
dev.caskeleton.adapter.outbound.notification.core.RoutingNotifier
dev.caskeleton.adapter.outbound.notification.email.google.GoogleEmailClient
dev.caskeleton.adapter.outbound.notification.email.google.GoogleEmailNotificationAdapterConfig
dev.caskeleton.adapter.outbound.notification.email.google.GoogleEmailProvider
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.AssembledProvider
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationPlatformAutoConfiguration
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationPlatformMode
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationPlatformSettings
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationProviderAssembly
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.ProviderRuntimeAssembler
dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.ProviderType
dev.caskeleton.adapter.outbound.notification.platform.dispatch.AttemptPermit
dev.caskeleton.adapter.outbound.notification.platform.dispatch.CapabilityReconciliationGateway
dev.caskeleton.adapter.outbound.notification.platform.dispatch.ConfiguredProfileCatalog
dev.caskeleton.adapter.outbound.notification.platform.dispatch.CredentialProbe
dev.caskeleton.adapter.outbound.notification.platform.dispatch.CredentialValidationException
dev.caskeleton.adapter.outbound.notification.platform.dispatch.JacksonRoutingPlanCodec
dev.caskeleton.adapter.outbound.notification.platform.dispatch.LeaseRecoveryService
dev.caskeleton.adapter.outbound.notification.platform.dispatch.LoggingInboxSignalPublisher
dev.caskeleton.adapter.outbound.notification.platform.dispatch.MapTemplateRendererRegistry
dev.caskeleton.adapter.outbound.notification.platform.dispatch.NotificationBackgroundWorkers
dev.caskeleton.adapter.outbound.notification.platform.dispatch.NotificationDispatchProperties
dev.caskeleton.adapter.outbound.notification.platform.dispatch.NotificationSchedulerWorker
dev.caskeleton.adapter.outbound.notification.platform.dispatch.ProviderAttemptLimiter
dev.caskeleton.adapter.outbound.notification.platform.dispatch.ProviderEventReplayWorker
dev.caskeleton.adapter.outbound.notification.platform.dispatch.ProviderRuntime
dev.caskeleton.adapter.outbound.notification.platform.dispatch.ProviderRuntimeRegistry
dev.caskeleton.adapter.outbound.notification.platform.dispatch.ProviderRuntimeRotator
dev.caskeleton.adapter.outbound.notification.platform.dispatch.ReconciliationJobWorker
dev.caskeleton.adapter.outbound.notification.platform.dispatch.RegistryProviderDispatchGateway
dev.caskeleton.adapter.outbound.notification.platform.dispatch.RegistryProviderRuntimeControl
dev.caskeleton.adapter.outbound.notification.platform.dispatch.RuntimeDrainCoordinator
dev.caskeleton.adapter.outbound.notification.platform.dispatch.SingleTenantContext
dev.caskeleton.adapter.outbound.notification.platform.dispatch.UuidV7Generator
dev.caskeleton.adapter.outbound.notification.platform.observation.LoggingNotificationAudit
dev.caskeleton.adapter.outbound.notification.platform.observation.LoggingNotificationMetrics
dev.caskeleton.adapter.outbound.notification.platform.observation.NotificationHealthReporter
dev.caskeleton.adapter.outbound.notification.platform.observation.NotificationHealthSnapshot
dev.caskeleton.adapter.outbound.notification.platform.observation.NotificationServingThresholds
dev.caskeleton.adapter.outbound.notification.platform.provider.ProviderResults
dev.caskeleton.adapter.outbound.notification.platform.provider.UnconfiguredAttachmentResolver
dev.caskeleton.adapter.outbound.notification.platform.provider.apns.ApnsFailureClassifier
dev.caskeleton.adapter.outbound.notification.platform.provider.apns.ApnsNotificationProviderAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.apns.ApnsProviderProperties
dev.caskeleton.adapter.outbound.notification.platform.provider.apns.ApnsRequestMapper
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmBatchCoordinator
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmBatchResult
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmContactPointUpdater
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmFailureClassifier
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmGateway
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmMessageMapper
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmNotificationProviderAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmProviderProperties
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmTargetMapper
dev.caskeleton.adapter.outbound.notification.platform.provider.fcm.FcmWireTarget
dev.caskeleton.adapter.outbound.notification.platform.provider.http.JdkNotificationHttpGateway
dev.caskeleton.adapter.outbound.notification.platform.provider.http.NotificationEndpoints
dev.caskeleton.adapter.outbound.notification.platform.provider.http.NotificationHttpGateway
dev.caskeleton.adapter.outbound.notification.platform.provider.http.NotificationHttpRequest
dev.caskeleton.adapter.outbound.notification.platform.provider.http.NotificationHttpResponse
dev.caskeleton.adapter.outbound.notification.platform.provider.http.NotificationHttpTransportException
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.AwsSignatureV4Signer
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesCallbackAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesDeliveryProjector
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesEventNormalizer
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesFailureClassifier
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesNotificationProviderAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesProviderProperties
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesRequestMapper
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SesSuppressionUpdater
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SnsCertificateProvider
dev.caskeleton.adapter.outbound.notification.platform.provider.ses.SnsSignatureVerifier
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpDispatch
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpDispatchException
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpFailureClassifier
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpMimeMessageFactory
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpNotificationProviderAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.smtp.SmtpProviderProperties
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioCallbackAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioDeliveryProjector
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioFailureClassifier
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioProviderProperties
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioReconciliationCapability
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioRequestMapper
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioSignatureValidator
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioSmsProviderAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.twilio.TwilioStatusNormalizer
dev.caskeleton.adapter.outbound.notification.platform.provider.webhook.WebhookNotificationProviderAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.webhook.WebhookSignatureStrategy
dev.caskeleton.adapter.outbound.notification.platform.provider.webhook.WebhookSubscription
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.EncryptedWebPushPayload
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.Rfc8291Aes128GcmEncryptor
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.VapidAuthorizationProvider
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.VapidJwtSigner
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.VapidKeyRegistry
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.WebPushFailureClassifier
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.WebPushNotificationProviderAdapter
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.WebPushProviderProperties
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.WebPushReceiptCapability
dev.caskeleton.adapter.outbound.notification.platform.provider.webpush.WebPushRequestMapper
dev.caskeleton.adapter.outbound.notification.platform.reactor.ReactiveNotificationOrchestrator
dev.caskeleton.adapter.outbound.notification.platform.reactor.ReactorContextBridge
dev.caskeleton.adapter.outbound.notification.platform.reactor.ReactorNotificationOrchestrator
dev.caskeleton.adapter.outbound.notification.platform.security.AesGcmCallbackPayloadProtection
dev.caskeleton.adapter.outbound.notification.platform.security.AesGcmContactPointProtector
dev.caskeleton.adapter.outbound.notification.platform.security.CredentialGeneration
dev.caskeleton.adapter.outbound.notification.platform.security.HmacProviderRequestIdHasher
dev.caskeleton.adapter.outbound.notification.platform.security.ProviderCredentialManager
dev.caskeleton.adapter.outbound.notification.platform.security.SettingsSecretMaterialProvider
dev.caskeleton.adapter.outbound.notification.platform.template.CanonicalNotificationRenderer
dev.caskeleton.adapter.outbound.notification.platform.template.JacksonInboxContentCodec
dev.caskeleton.adapter.outbound.notification.platform.template.JacksonNotificationVariablesCodec
dev.caskeleton.adapter.outbound.notification.platform.template.JacksonTemplateContentCodec
dev.caskeleton.adapter.outbound.notification.platform.template.JsonSchemaVariableValidator
dev.caskeleton.adapter.outbound.notification.platform.template.NotificationDigest
dev.caskeleton.adapter.outbound.notification.platform.template.NotificationJsonMapper
dev.caskeleton.adapter.outbound.notification.platform.template.NotificationTemplateEngine
dev.caskeleton.adapter.outbound.notification.platform.template.PlaceholderTemplateEngine
dev.caskeleton.adapter.outbound.notification.platform.template.Sha256MessageDigestAdapter
dev.caskeleton.adapter.outbound.notification.platform.template.TemplateSlotMode
dev.caskeleton.adapter.outbound.notification.platform.template.ThymeleafNotificationRenderer
dev.caskeleton.adapter.outbound.notification.platform.template.ThymeleafStringTemplateEngine
dev.caskeleton.adapter.outbound.notification.provider.AttemptCorrelationId
dev.caskeleton.adapter.outbound.notification.provider.InlineNotificationAttemptAdapter
dev.caskeleton.adapter.outbound.notification.provider.NotificationAdmissionReadinessAdapter
dev.caskeleton.adapter.outbound.notification.provider.NotificationAttemptContext
dev.caskeleton.adapter.outbound.notification.provider.NotificationProviderAttemptAdapter
dev.caskeleton.adapter.outbound.notification.provider.NotificationProviderAttemptClient
dev.caskeleton.adapter.outbound.notification.provider.NotificationProviderRateAdmission
dev.caskeleton.adapter.outbound.notification.provider.NotificationProviderReadinessProbe
dev.caskeleton.adapter.outbound.notification.provider.NotificationProviderReadinessSnapshot
dev.caskeleton.adapter.outbound.notification.provider.NotificationProviderSecretMaterialProvider
dev.caskeleton.adapter.outbound.notification.provider.NotificationReconciliationAdapter
dev.caskeleton.adapter.outbound.notification.provider.NotificationSecretMaterialHandle
dev.caskeleton.adapter.outbound.notification.provider.PreparedNotificationAttempt
dev.caskeleton.adapter.outbound.notification.provider.ProviderMessageReference
dev.caskeleton.adapter.outbound.notification.provider.ReconciliationLookupMode
dev.caskeleton.adapter.outbound.notification.slack.webhook.SlackClient
dev.caskeleton.adapter.outbound.notification.slack.webhook.SlackNotificationAdapterConfig
dev.caskeleton.adapter.outbound.notification.slack.webhook.SlackWebhookProvider
dev.caskeleton.adapter.outbound.notification.template.LocalEmailRenderer
dev.caskeleton.adapter.outbound.notification.template.NotificationTemplateCatalog
dev.caskeleton.adapter.outbound.notification.template.NotificationTemplateManifest
dev.caskeleton.adapter.outbound.notification.template.NotificationTemplateRenderer
dev.caskeleton.adapter.outbound.notification.template.RenderedNotification
dev.caskeleton.adapter.outbound.notification.template.SlackBlockKitRenderer
dev.caskeleton.adapter.outbound.notification.template.TemplateRenderingException
dev.caskeleton.application.notification.ApplyNotificationReceiptCommand
dev.caskeleton.application.notification.ApplyNotificationReceiptResult
dev.caskeleton.application.notification.ApplyNotificationReceiptUseCase
dev.caskeleton.application.notification.Channel
dev.caskeleton.application.notification.ConsentCheckMode
dev.caskeleton.application.notification.EmailRecipientReference
dev.caskeleton.application.notification.InitializeNotificationWriterFencesCommand
dev.caskeleton.application.notification.InitializeNotificationWriterFencesOperation
dev.caskeleton.application.notification.InitializeNotificationWriterFencesResult
dev.caskeleton.application.notification.InitializeNotificationWriterFencesUseCase
dev.caskeleton.application.notification.InlineNotificationAttemptPort
dev.caskeleton.application.notification.NormalizedNotificationReceiptCommand
dev.caskeleton.application.notification.Notification
dev.caskeleton.application.notification.NotificationAdmissionClass
dev.caskeleton.application.notification.NotificationAdmissionGateCommand
dev.caskeleton.application.notification.NotificationAdmissionGateUseCase
dev.caskeleton.application.notification.NotificationAdmissionReadinessPort
dev.caskeleton.application.notification.NotificationAppendResult
dev.caskeleton.application.notification.NotificationApplicationException
dev.caskeleton.application.notification.NotificationAttemptId
dev.caskeleton.application.notification.NotificationCanonicalWriterFenceGuard
dev.caskeleton.application.notification.NotificationCanonicalWriterFencePort
dev.caskeleton.application.notification.NotificationCanonicalWriterRouteSet
dev.caskeleton.application.notification.NotificationCapabilityCompatibilityValidator
dev.caskeleton.application.notification.NotificationChannel
dev.caskeleton.application.notification.NotificationDeliveryId
dev.caskeleton.application.notification.NotificationDeliveryStorePort
dev.caskeleton.application.notification.NotificationDispatchCommand
dev.caskeleton.application.notification.NotificationDispatchResult
dev.caskeleton.application.notification.NotificationDispatchUseCase
dev.caskeleton.application.notification.NotificationEvidenceTrustSnapshot
dev.caskeleton.application.notification.NotificationFaultScope
dev.caskeleton.application.notification.NotificationFrozenPlan
dev.caskeleton.application.notification.NotificationIntentAppendPort
dev.caskeleton.application.notification.NotificationIntentDraft
dev.caskeleton.application.notification.NotificationIntentId
dev.caskeleton.application.notification.NotificationKindId
dev.caskeleton.application.notification.NotificationKindPolicy
dev.caskeleton.application.notification.NotificationLegacyWriterPermitCommand
dev.caskeleton.application.notification.NotificationLegacyWriterPermitResult
dev.caskeleton.application.notification.NotificationLegacyWriterPermitUseCase
dev.caskeleton.application.notification.NotificationMaintenanceCommand
dev.caskeleton.application.notification.NotificationMaintenanceResult
dev.caskeleton.application.notification.NotificationMaintenanceStorePort
dev.caskeleton.application.notification.NotificationMaintenanceUseCase
dev.caskeleton.application.notification.NotificationMode
dev.caskeleton.application.notification.NotificationOperationsSnapshot
dev.caskeleton.application.notification.NotificationOperationsSnapshotPort
dev.caskeleton.application.notification.NotificationOperationsSnapshotQuery
dev.caskeleton.application.notification.NotificationOperationsSnapshotUseCase
dev.caskeleton.application.notification.NotificationPlanPort
dev.caskeleton.application.notification.NotificationPlanningResult
dev.caskeleton.application.notification.NotificationPort
dev.caskeleton.application.notification.NotificationProviderAttemptPort
dev.caskeleton.application.notification.NotificationProviderCapabilityDescriptor
dev.caskeleton.application.notification.NotificationReasonCode
dev.caskeleton.application.notification.NotificationReceiptEventId
dev.caskeleton.application.notification.NotificationReceiptFact
dev.caskeleton.application.notification.NotificationReceiptIngressCapabilityDescriptor
dev.caskeleton.application.notification.NotificationReceiptProjection
dev.caskeleton.application.notification.NotificationReceiptStorePort
dev.caskeleton.application.notification.NotificationRecipientReference
dev.caskeleton.application.notification.NotificationReconciliationPort
dev.caskeleton.application.notification.NotificationRequestResult
dev.caskeleton.application.notification.NotificationRouteId
dev.caskeleton.application.notification.NotificationRouteStrategy
dev.caskeleton.application.notification.NotificationSignedEvidenceHeader
dev.caskeleton.application.notification.NotificationStoreCapabilityDescriptor
dev.caskeleton.application.notification.NotificationTechnicalSuppressionPort
dev.caskeleton.application.notification.NotificationTemplateParameters
dev.caskeleton.application.notification.NotificationTemplateRef
dev.caskeleton.application.notification.NotificationTemplateValue
dev.caskeleton.application.notification.NotificationWriterCutoverPort
dev.caskeleton.application.notification.NotificationWriterInventoryEvidence
dev.caskeleton.application.notification.NotificationWriterInventoryEvidenceVerifierPort
dev.caskeleton.application.notification.NotificationWriterOwnership
dev.caskeleton.application.notification.NotificationWriterQuiescenceAttestationPort
dev.caskeleton.application.notification.NotificationWriterRouteSet
dev.caskeleton.application.notification.ProviderAttemptOutcome
dev.caskeleton.application.notification.ReconcileNotificationDeliveriesCommand
dev.caskeleton.application.notification.ReconcileNotificationDeliveriesResult
dev.caskeleton.application.notification.ReconcileNotificationDeliveriesUseCase
dev.caskeleton.application.notification.RecordNotificationWriterQuiescenceAttestationCommand
dev.caskeleton.application.notification.RecordNotificationWriterQuiescenceAttestationOperation
dev.caskeleton.application.notification.RecordNotificationWriterQuiescenceAttestationResult
dev.caskeleton.application.notification.RecordNotificationWriterQuiescenceAttestationUseCase
dev.caskeleton.application.notification.RetryDisposition
dev.caskeleton.application.notification.SignedNotificationWriterInventoryManifest
dev.caskeleton.application.notification.SignedNotificationWriterQuiescenceManifest
dev.caskeleton.application.notification.SlackAudienceReference
dev.caskeleton.application.notification.SubmissionCertainty
dev.caskeleton.application.notification.SwitchNotificationWriterOwnershipCommand
dev.caskeleton.application.notification.SwitchNotificationWriterOwnershipOperation
dev.caskeleton.application.notification.SwitchNotificationWriterOwnershipResult
dev.caskeleton.application.notification.SwitchNotificationWriterOwnershipUseCase
dev.caskeleton.application.notification.TargetAttemptOutcome
dev.caskeleton.application.notification.TerminalizeExpiredNotificationWriterPermitsCommand
dev.caskeleton.application.notification.TerminalizeExpiredNotificationWriterPermitsOperation
dev.caskeleton.application.notification.TerminalizeExpiredNotificationWriterPermitsResult
dev.caskeleton.application.notification.TerminalizeExpiredNotificationWriterPermitsUseCase
dev.caskeleton.application.notification.platform.admin.AdminAccessDeniedException
dev.caskeleton.application.notification.platform.admin.AdminActor
dev.caskeleton.application.notification.platform.admin.AdminAuthorizationGuard
dev.caskeleton.application.notification.platform.admin.AdminOperationClaim
dev.caskeleton.application.notification.platform.admin.AdminOperationResult
dev.caskeleton.application.notification.platform.admin.AdminOperationStorePort
dev.caskeleton.application.notification.platform.admin.DuplicateRiskApprovalRequiredException
dev.caskeleton.application.notification.platform.admin.DuplicateRiskGuard
dev.caskeleton.application.notification.platform.admin.NotificationAdminApplicationService
dev.caskeleton.application.notification.platform.admin.NotificationAdminAuthority
dev.caskeleton.application.notification.platform.admin.NotificationAdminService
dev.caskeleton.application.notification.platform.admin.ProviderRuntimeControlPort
dev.caskeleton.application.notification.platform.admin.ReconcileCommand
dev.caskeleton.application.notification.platform.admin.RedriveCommand
dev.caskeleton.application.notification.platform.admin.SetProviderStateCommand
dev.caskeleton.application.notification.platform.admin.SuppressCommand
dev.caskeleton.application.notification.platform.api.CallbackIngestionResult
dev.caskeleton.application.notification.platform.api.CallbackRequest
dev.caskeleton.application.notification.platform.api.CancelCommand
dev.caskeleton.application.notification.platform.api.CancelResult
dev.caskeleton.application.notification.platform.api.ChannelPreferenceOverride
dev.caskeleton.application.notification.platform.api.CollapseScope
dev.caskeleton.application.notification.platform.api.CollapseSpec
dev.caskeleton.application.notification.platform.api.ContactPointId
dev.caskeleton.application.notification.platform.api.ContactPointSelector
dev.caskeleton.application.notification.platform.api.CorrelationId
dev.caskeleton.application.notification.platform.api.DeduplicationAction
dev.caskeleton.application.notification.platform.api.DeduplicationSpec
dev.caskeleton.application.notification.platform.api.DeliveryAttemptId
dev.caskeleton.application.notification.platform.api.EncodedNotificationPlan
dev.caskeleton.application.notification.platform.api.IdempotencyKey
dev.caskeleton.application.notification.platform.api.NotificationAcceptance
dev.caskeleton.application.notification.platform.api.NotificationId
dev.caskeleton.application.notification.platform.api.NotificationOrchestrator
dev.caskeleton.application.notification.platform.api.NotificationPlan
dev.caskeleton.application.notification.platform.api.NotificationReceipt
dev.caskeleton.application.notification.platform.api.NotificationSnapshot
dev.caskeleton.application.notification.platform.api.NotificationVariable
dev.caskeleton.application.notification.platform.api.ProviderEventId
dev.caskeleton.application.notification.platform.api.ProviderId
dev.caskeleton.application.notification.platform.api.ProviderProfileId
dev.caskeleton.application.notification.platform.api.RecipientDeliveryId
dev.caskeleton.application.notification.platform.api.RecipientSpec
dev.caskeleton.application.notification.platform.api.RequestStatus
dev.caskeleton.application.notification.platform.api.TemplateSelection
dev.caskeleton.application.notification.platform.api.TenantId
dev.caskeleton.application.notification.platform.api.content.AttachmentDisposition
dev.caskeleton.application.notification.platform.api.content.AttachmentRef
dev.caskeleton.application.notification.platform.api.content.EmailContent
dev.caskeleton.application.notification.platform.api.content.EmailOptions
dev.caskeleton.application.notification.platform.api.content.InAppAction
dev.caskeleton.application.notification.platform.api.content.InAppContent
dev.caskeleton.application.notification.platform.api.content.MobilePushContent
dev.caskeleton.application.notification.platform.api.content.NotificationContent
dev.caskeleton.application.notification.platform.api.content.PushPresentation
dev.caskeleton.application.notification.platform.api.content.SmsContent
dev.caskeleton.application.notification.platform.api.content.SmsOptions
dev.caskeleton.application.notification.platform.api.content.WebPushContent
dev.caskeleton.application.notification.platform.api.content.WebPushOptions
dev.caskeleton.application.notification.platform.api.delivery.AttemptConfirmation
dev.caskeleton.application.notification.platform.api.delivery.DeliveryOutcome
dev.caskeleton.application.notification.platform.api.delivery.EvidenceLevel
dev.caskeleton.application.notification.platform.api.delivery.RecipientDeliveryState
dev.caskeleton.application.notification.platform.api.delivery.SubmissionOutcome
dev.caskeleton.application.notification.platform.api.error.AmbiguousSubmissionException
dev.caskeleton.application.notification.platform.api.error.AttachmentIntegrityException
dev.caskeleton.application.notification.platform.api.error.AttachmentUnavailableException
dev.caskeleton.application.notification.platform.api.error.CallbackProjectionException
dev.caskeleton.application.notification.platform.api.error.CallbackValidationException
dev.caskeleton.application.notification.platform.api.error.FailureCategory
dev.caskeleton.application.notification.platform.api.error.IdempotencyConflictException
dev.caskeleton.application.notification.platform.api.error.InvalidContactPointException
dev.caskeleton.application.notification.platform.api.error.NotificationCapacityException
dev.caskeleton.application.notification.platform.api.error.NotificationException
dev.caskeleton.application.notification.platform.api.error.NotificationExpiredException
dev.caskeleton.application.notification.platform.api.error.NotificationFailureCode
dev.caskeleton.application.notification.platform.api.error.NotificationFailureDescriptor
dev.caskeleton.application.notification.platform.api.error.NotificationSuppressedException
dev.caskeleton.application.notification.platform.api.error.NotificationValidationException
dev.caskeleton.application.notification.platform.api.error.ProviderAuthenticationException
dev.caskeleton.application.notification.platform.api.error.ProviderAuthorizationException
dev.caskeleton.application.notification.platform.api.error.ProviderConfigurationException
dev.caskeleton.application.notification.platform.api.error.ProviderPayloadLimitException
dev.caskeleton.application.notification.platform.api.error.ProviderPermanentException
dev.caskeleton.application.notification.platform.api.error.ProviderRejectedException
dev.caskeleton.application.notification.platform.api.error.ProviderThrottledException
dev.caskeleton.application.notification.platform.api.error.ProviderTransientException
dev.caskeleton.application.notification.platform.api.error.ProviderUnavailableException
dev.caskeleton.application.notification.platform.api.error.ReconciliationException
dev.caskeleton.application.notification.platform.api.error.TemplateNotFoundException
dev.caskeleton.application.notification.platform.api.error.TemplateRenderingException
dev.caskeleton.application.notification.platform.api.error.TemplateVariableValidationException
dev.caskeleton.application.notification.platform.api.routing.Channel
dev.caskeleton.application.notification.platform.api.routing.DeliveryStrategy
dev.caskeleton.application.notification.platform.api.routing.ExplicitChannel
dev.caskeleton.application.notification.platform.api.routing.OrderedFallback
dev.caskeleton.application.notification.platform.callback.AppendEventResult
dev.caskeleton.application.notification.platform.callback.CallbackLimits
dev.caskeleton.application.notification.platform.callback.CallbackPayloadProtectionPort
dev.caskeleton.application.notification.platform.callback.CallbackVerificationResult
dev.caskeleton.application.notification.platform.callback.DeliveryAttemptResolverPort
dev.caskeleton.application.notification.platform.callback.DeliveryAttemptSnapshot
dev.caskeleton.application.notification.platform.callback.DeliveryProjection
dev.caskeleton.application.notification.platform.callback.DeliveryProjectionStorePort
dev.caskeleton.application.notification.platform.callback.EngagementFacts
dev.caskeleton.application.notification.platform.callback.IngestProviderCallbackApplicationUseCase
dev.caskeleton.application.notification.platform.callback.NormalizedEventType
dev.caskeleton.application.notification.platform.callback.NormalizedProviderEvent
dev.caskeleton.application.notification.platform.callback.NotificationSideEffectPort
dev.caskeleton.application.notification.platform.callback.ProjectionResult
dev.caskeleton.application.notification.platform.callback.ProjectionStatus
dev.caskeleton.application.notification.platform.callback.ProviderCallbackAdapter
dev.caskeleton.application.notification.platform.callback.ProviderCallbackAdapterRegistry
dev.caskeleton.application.notification.platform.callback.ProviderEventLedger
dev.caskeleton.application.notification.platform.callback.ProviderEventProjectionService
dev.caskeleton.application.notification.platform.callback.ProviderEventProjector
dev.caskeleton.application.notification.platform.callback.ProviderEventProjectorRegistry
dev.caskeleton.application.notification.platform.callback.ProviderEventRecord
dev.caskeleton.application.notification.platform.callback.ProviderEventRecordId
dev.caskeleton.application.notification.platform.callback.ProviderEventSource
dev.caskeleton.application.notification.platform.callback.StandardDeliveryProjector
dev.caskeleton.application.notification.platform.callback.SuppressionFacts
dev.caskeleton.application.notification.platform.callback.VerifiedCallback
dev.caskeleton.application.notification.platform.callback.VerifiedProviderEvent
dev.caskeleton.application.notification.platform.contact.ApnsDeviceToken
dev.caskeleton.application.notification.platform.contact.ApnsEnvironment
dev.caskeleton.application.notification.platform.contact.ContactPointStatus
dev.caskeleton.application.notification.platform.contact.ContactPointType
dev.caskeleton.application.notification.platform.contact.ContactPointValue
dev.caskeleton.application.notification.platform.contact.EmailAddress
dev.caskeleton.application.notification.platform.contact.FcmInstallationId
dev.caskeleton.application.notification.platform.contact.InAppRecipientRef
dev.caskeleton.application.notification.platform.contact.LegacyFcmRegistrationToken
dev.caskeleton.application.notification.platform.contact.MobilePushTarget
dev.caskeleton.application.notification.platform.contact.PhoneNumber
dev.caskeleton.application.notification.platform.contact.WebPushSubscriptionValue
dev.caskeleton.application.notification.platform.dispatch.ApplicationReceiptServiceImpl
dev.caskeleton.application.notification.platform.dispatch.AttachmentIntegrityGuard
dev.caskeleton.application.notification.platform.dispatch.CancelNotificationApplicationUseCase
dev.caskeleton.application.notification.platform.dispatch.CanonicalNotificationPlanEncoder
dev.caskeleton.application.notification.platform.dispatch.CanonicalNotificationPlanWriter
dev.caskeleton.application.notification.platform.dispatch.ContactPointRecord
dev.caskeleton.application.notification.platform.dispatch.ContactPointStorePort
dev.caskeleton.application.notification.platform.dispatch.DeliveryAttemptFactory
dev.caskeleton.application.notification.platform.dispatch.DeliveryAttemptRecord
dev.caskeleton.application.notification.platform.dispatch.DeliveryAttemptStorePort
dev.caskeleton.application.notification.platform.dispatch.DispatchGuardOutcome
dev.caskeleton.application.notification.platform.dispatch.DispatchOutcomeRecorder
dev.caskeleton.application.notification.platform.dispatch.DispatchPipeline
dev.caskeleton.application.notification.platform.dispatch.DuplicateIdempotencyKeyException
dev.caskeleton.application.notification.platform.dispatch.GetNotificationApplicationUseCase
dev.caskeleton.application.notification.platform.dispatch.MessageDigestPort
dev.caskeleton.application.notification.platform.dispatch.NotificationDispatchService
dev.caskeleton.application.notification.platform.dispatch.NotificationIdGeneratorPort
dev.caskeleton.application.notification.platform.dispatch.NotificationRequestInsertOutcome
dev.caskeleton.application.notification.platform.dispatch.NotificationRequestRecord
dev.caskeleton.application.notification.platform.dispatch.NotificationRequestStatusPolicy
dev.caskeleton.application.notification.platform.dispatch.NotificationRequestStorePort
dev.caskeleton.application.notification.platform.dispatch.NotificationRoutePlannerPort
dev.caskeleton.application.notification.platform.dispatch.NotificationRoutingPlanCodecPort
dev.caskeleton.application.notification.platform.dispatch.NotificationSubmissionService
dev.caskeleton.application.notification.platform.dispatch.NotificationVariablesCodecPort
dev.caskeleton.application.notification.platform.dispatch.PolicyRoutePlanner
dev.caskeleton.application.notification.platform.dispatch.ProviderDispatchGatewayPort
dev.caskeleton.application.notification.platform.dispatch.ProviderProfileCatalogPort
dev.caskeleton.application.notification.platform.dispatch.ProviderRequestIdHasherPort
dev.caskeleton.application.notification.platform.dispatch.RecipientDeliveryRecord
dev.caskeleton.application.notification.platform.dispatch.RecipientDeliveryStorePort
dev.caskeleton.application.notification.platform.dispatch.RecipientLease
dev.caskeleton.application.notification.platform.dispatch.RecipientLeaseStorePort
dev.caskeleton.application.notification.platform.dispatch.ReconciliationGatewayPort
dev.caskeleton.application.notification.platform.dispatch.ReconciliationJob
dev.caskeleton.application.notification.platform.dispatch.ReconciliationJobStorePort
dev.caskeleton.application.notification.platform.dispatch.ReconciliationService
dev.caskeleton.application.notification.platform.dispatch.RequestFingerprint
dev.caskeleton.application.notification.platform.dispatch.ScheduleNotificationApplicationUseCase
dev.caskeleton.application.notification.platform.dispatch.SubmitNotificationApplicationUseCase
dev.caskeleton.application.notification.platform.dispatch.SyntheticEventFingerprint
dev.caskeleton.application.notification.platform.dispatch.TemplateRendererRegistry
dev.caskeleton.application.notification.platform.dispatch.TenantContextPort
dev.caskeleton.application.notification.platform.email.EmailNotification
dev.caskeleton.application.notification.platform.email.EmailNotifier
dev.caskeleton.application.notification.platform.inbox.CreateInboxItemCommand
dev.caskeleton.application.notification.platform.inbox.InboxContentCodecPort
dev.caskeleton.application.notification.platform.inbox.InboxCursor
dev.caskeleton.application.notification.platform.inbox.InboxItem
dev.caskeleton.application.notification.platform.inbox.InboxItemCreated
dev.caskeleton.application.notification.platform.inbox.InboxItemId
dev.caskeleton.application.notification.platform.inbox.InboxItemState
dev.caskeleton.application.notification.platform.inbox.InboxMutationResult
dev.caskeleton.application.notification.platform.inbox.InboxPage
dev.caskeleton.application.notification.platform.inbox.InboxPrincipal
dev.caskeleton.application.notification.platform.inbox.InboxQuery
dev.caskeleton.application.notification.platform.inbox.MarkAllReadCommand
dev.caskeleton.application.notification.platform.inbox.NotificationInbox
dev.caskeleton.application.notification.platform.inbox.NotificationInboxSignalPort
dev.caskeleton.application.notification.platform.observation.CardinalityGuard
dev.caskeleton.application.notification.platform.observation.IllegalMetricTagException
dev.caskeleton.application.notification.platform.observation.NotificationAuditEvent
dev.caskeleton.application.notification.platform.observation.NotificationAuditPort
dev.caskeleton.application.notification.platform.observation.NotificationMetricName
dev.caskeleton.application.notification.platform.observation.NotificationMetricsPort
dev.caskeleton.application.notification.platform.observation.NotificationSecurityAuditPort
dev.caskeleton.application.notification.platform.observation.NotificationServingState
dev.caskeleton.application.notification.platform.observation.NotificationServingStatePort
dev.caskeleton.application.notification.platform.observation.SensitiveValueDetector
dev.caskeleton.application.notification.platform.policy.CompositeNotificationEligibilityPolicy
dev.caskeleton.application.notification.platform.policy.ConsentRecord
dev.caskeleton.application.notification.platform.policy.ConsentStorePort
dev.caskeleton.application.notification.platform.policy.DeduplicationResult
dev.caskeleton.application.notification.platform.policy.DeduplicationService
dev.caskeleton.application.notification.platform.policy.DeduplicationStorePort
dev.caskeleton.application.notification.platform.policy.DefaultNotificationRetryPolicy
dev.caskeleton.application.notification.platform.policy.EligibilityResult
dev.caskeleton.application.notification.platform.policy.JitterSource
dev.caskeleton.application.notification.platform.policy.NotificationContext
dev.caskeleton.application.notification.platform.policy.NotificationEligibilityPolicy
dev.caskeleton.application.notification.platform.policy.NotificationRetryPolicy
dev.caskeleton.application.notification.platform.policy.PreferenceRecord
dev.caskeleton.application.notification.platform.policy.PreferenceStorePort
dev.caskeleton.application.notification.platform.policy.RecipientIdentity
dev.caskeleton.application.notification.platform.policy.RetryBackoff
dev.caskeleton.application.notification.platform.policy.RetryBudget
dev.caskeleton.application.notification.platform.policy.RetryContext
dev.caskeleton.application.notification.platform.policy.RetryDecision
dev.caskeleton.application.notification.platform.policy.RouteCandidate
dev.caskeleton.application.notification.platform.policy.RoutingContext
dev.caskeleton.application.notification.platform.policy.RoutingDecision
dev.caskeleton.application.notification.platform.policy.RoutingDecisionEngine
dev.caskeleton.application.notification.platform.policy.SuppressionEntry
dev.caskeleton.application.notification.platform.policy.SuppressionId
dev.caskeleton.application.notification.platform.policy.SuppressionReason
dev.caskeleton.application.notification.platform.policy.SuppressionScope
dev.caskeleton.application.notification.platform.policy.SuppressionSource
dev.caskeleton.application.notification.platform.policy.SuppressionStorePort
dev.caskeleton.application.notification.platform.port.in.CancelNotificationCommand
dev.caskeleton.application.notification.platform.port.in.CancelNotificationUseCase
dev.caskeleton.application.notification.platform.port.in.GetNotificationQuery
dev.caskeleton.application.notification.platform.port.in.GetNotificationUseCase
dev.caskeleton.application.notification.platform.port.in.IngestProviderCallbackCommand
dev.caskeleton.application.notification.platform.port.in.IngestProviderCallbackUseCase
dev.caskeleton.application.notification.platform.port.in.ScheduleNotificationCommand
dev.caskeleton.application.notification.platform.port.in.ScheduleNotificationUseCase
dev.caskeleton.application.notification.platform.port.in.SubmitNotificationCommand
dev.caskeleton.application.notification.platform.port.in.SubmitNotificationUseCase
dev.caskeleton.application.notification.platform.provider.AttachmentAccessContext
dev.caskeleton.application.notification.platform.provider.AttachmentResolver
dev.caskeleton.application.notification.platform.provider.BatchNotificationProviderAdapter
dev.caskeleton.application.notification.platform.provider.CollapseCapability
dev.caskeleton.application.notification.platform.provider.EvidenceCertainty
dev.caskeleton.application.notification.platform.provider.EvidenceFact
dev.caskeleton.application.notification.platform.provider.NotificationProviderAdapter
dev.caskeleton.application.notification.platform.provider.ProviderCallNotStartedException
dev.caskeleton.application.notification.platform.provider.ProviderCapabilities
dev.caskeleton.application.notification.platform.provider.ProviderCollapseMapping
dev.caskeleton.application.notification.platform.provider.ProviderExecutionEvidence
dev.caskeleton.application.notification.platform.provider.ProviderFailure
dev.caskeleton.application.notification.platform.provider.ProviderProfileSnapshot
dev.caskeleton.application.notification.platform.provider.ProviderRuntimeState
dev.caskeleton.application.notification.platform.provider.ProviderSubmission
dev.caskeleton.application.notification.platform.provider.ProviderSubmissionResult
dev.caskeleton.application.notification.platform.provider.ReconciliationCapability
dev.caskeleton.application.notification.platform.provider.ReconciliationResult
dev.caskeleton.application.notification.platform.provider.ResolvedAttachment
dev.caskeleton.application.notification.platform.provider.TraceContext
dev.caskeleton.application.notification.platform.push.ApplicationIdentity
dev.caskeleton.application.notification.platform.push.ApplicationReceipt
dev.caskeleton.application.notification.platform.push.ApplicationReceiptService
dev.caskeleton.application.notification.platform.push.MobilePushNotification
dev.caskeleton.application.notification.platform.push.MobilePushNotifier
dev.caskeleton.application.notification.platform.push.ReceiptAuthorizationException
dev.caskeleton.application.notification.platform.push.ReceiptKind
dev.caskeleton.application.notification.platform.push.ReceiptResult
dev.caskeleton.application.notification.platform.security.AccessContext
dev.caskeleton.application.notification.platform.security.ContactPointProtector
dev.caskeleton.application.notification.platform.security.NotificationRedactor
dev.caskeleton.application.notification.platform.security.ProtectedContactPoint
dev.caskeleton.application.notification.platform.security.SafeDiagnosticContext
dev.caskeleton.application.notification.platform.security.SecretKeyMaterial
dev.caskeleton.application.notification.platform.security.SecretMaterialProvider
dev.caskeleton.application.notification.platform.security.SecretPurpose
dev.caskeleton.application.notification.platform.security.SensitiveValueClassifier
dev.caskeleton.application.notification.platform.security.SensitiveValueKind
dev.caskeleton.application.notification.platform.security.UnsafeDiagnosticFieldException
dev.caskeleton.application.notification.platform.sms.E164PhoneNumberParser
dev.caskeleton.application.notification.platform.sms.GsmAlphabet
dev.caskeleton.application.notification.platform.sms.InvalidPhoneNumberException
dev.caskeleton.application.notification.platform.sms.SmsEncoding
dev.caskeleton.application.notification.platform.sms.SmsEstimate
dev.caskeleton.application.notification.platform.sms.SmsNotification
dev.caskeleton.application.notification.platform.sms.SmsNotifier
dev.caskeleton.application.notification.platform.sms.SmsSegmentEstimator
dev.caskeleton.application.notification.platform.template.NotificationTemplateRenderer
dev.caskeleton.application.notification.platform.template.NotificationTemplateVersion
dev.caskeleton.application.notification.platform.template.RenderCommand
dev.caskeleton.application.notification.platform.template.RenderedNotificationContent
dev.caskeleton.application.notification.platform.template.TemplateContentCodecPort
dev.caskeleton.application.notification.platform.template.TemplateContentDefinition
dev.caskeleton.application.notification.platform.template.TemplateRegistry
dev.caskeleton.application.notification.platform.template.TemplateSlot
dev.caskeleton.application.notification.platform.template.TemplateStatus
dev.caskeleton.application.notification.platform.template.TemplateVariableValidator
dev.caskeleton.application.notification.platform.template.TemplateVersionConflictException
dev.caskeleton.application.notification.platform.template.VariableSchema
dev.caskeleton.application.notification.platform.webpush.WebPushNotification
dev.caskeleton.application.notification.platform.webpush.WebPushNotifier
+70 -12
View File
@@ -1,23 +1,73 @@
# Configuration reference
The notification delivery platform binds under `ca-skeleton.notification.platform`. The tree lives in
`src/app-bootstrap/src/main/resources/application.yml`, disabled by default, and every value carries
an inline default so a deployment that leaves the platform off supplies nothing.
Until 2026-08-15 this page named properties the binding did not have — `max-retry-concurrency`,
`scheduler-poll-interval`, `callback-worker-concurrency` — and omitted three it did. There was no
tree in `application.yml` at all, so the only way to configure the platform was to guess environment
variable names from Boot's relaxed binding. `./gradlew verifyNotificationConfiguration` now fails
when this page, the YAML tree and `docs/registries/env-keys.yaml` disagree.
## Master switch
| Property | Environment variable | Default | Meaning |
|---|---|---|---|
| `enabled` | `APP_NOTIFICATION_PLATFORM_ENABLED` | `false` | Binds nothing at all while false: no runtime, no schema check, no scheduler thread, no secret required |
| `mode` | `APP_NOTIFICATION_PLATFORM_MODE` | `SERVING` | `SERVING` refuses to start without a working provider; `ACCEPT_ONLY` stores requests and does not dispatch |
## Dispatch
| Property | Meaning | Bound |
|---|---|---|
| `claim-batch-size` | Rows claimed per scheduler tick | 1..1000 |
| `lease-duration` | How long a claimed job stays owned | positive, finite |
| `max-global-concurrency` | Ceiling across all providers | positive |
| `max-queue-age` | Age at which a job is escalated | positive |
| `max-retry-concurrency` | Ceiling for retry work | positive |
| `scheduler-poll-interval` | Queue poll cadence | positive |
| `callback-worker-concurrency` | Callback projection workers | positive |
| Property | Environment variable | Default | Bound |
|---|---|---|---|
| `dispatch.claim-batch-size` | `APP_NOTIFICATION_PLATFORM_CLAIM_BATCH_SIZE` | `50` | 1..1000 |
| `dispatch.lease-duration` | `APP_NOTIFICATION_PLATFORM_LEASE_DURATION` | `2m` | positive, finite |
| `dispatch.poll-interval` | `APP_NOTIFICATION_PLATFORM_POLL_INTERVAL` | `1s` | positive, finite |
| `dispatch.max-global-concurrency` | `APP_NOTIFICATION_PLATFORM_MAX_CONCURRENCY` | `64` | positive |
| `dispatch.max-additional-attempts` | `APP_NOTIFICATION_PLATFORM_MAX_ADDITIONAL_ATTEMPTS` | `4` | non-negative |
| `dispatch.max-queue-age` | `APP_NOTIFICATION_PLATFORM_MAX_QUEUE_AGE` | `24h` | positive |
| `dispatch.allow-ambiguous-fallback` | `APP_NOTIFICATION_PLATFORM_ALLOW_AMBIGUOUS_FALLBACK` | `false` | boolean |
Every value is bounded. "Unlimited" is not an accepted configuration.
The lease must outlast a provider call plus its timeout. Below that, a delivery a live worker is
still waiting on gets claimed by a second worker, and the recipient receives the notification twice.
## Callbacks
| Property | Environment variable | Default | Bound |
|---|---|---|---|
| `callbacks.enabled` | `APP_NOTIFICATION_PLATFORM_CALLBACKS_ENABLED` | `false` | boolean |
| `callbacks.max-body-bytes` | `APP_NOTIFICATION_PLATFORM_CALLBACK_MAX_BODY_BYTES` | `65508` | 1..65508 |
| `callbacks.replay-skew` | `APP_NOTIFICATION_PLATFORM_CALLBACK_REPLAY_SKEW` | `5m` | positive |
65508 is not a round number by accident: it is the ciphertext column's 65536 bytes minus the AES-GCM
nonce and tag. A larger configured value would pass every check above the database and fail the
`CHECK` constraint after the callback had already been acknowledged to the provider.
## Provider profiles
A profile pins provider type, environment, credential profile, timeouts, concurrency, rate limit,
retry policy and callback profile. Sender identity and credential profile are separate concerns.
Profiles are a map under `providers`, keyed by profile id. There are no environment variables for
them, because the keys are deployment-chosen; supply them as YAML or as
`CA_SKELETON_NOTIFICATION_PLATFORM_PROVIDERS_<ID>_<FIELD>`.
| Field | Meaning |
|---|---|
| `type` | `APNS`, `FCM`, `SES`, `SMTP`, `TWILIO`, `WEB_PUSH`, `WEBHOOK` — a closed enum, so an unknown value fails binding rather than assembling into nothing |
| `enabled` | A disabled profile is bound and validated but contributes no runtime |
| `primary-for-channel` | Exactly one primary per channel |
| `environment` | Required when enabled |
| `credential-profile` | Resolved through `SecretMaterialProvider`; never an inline secret |
| `topic` | APNs bundle id |
| `vapid-public-key` | Web Push application server key |
| `callback-signing-secret-ref` | Reference, not material |
| `timeout` | Positive and finite |
| `max-concurrency` | Positive |
| `rate-per-second` | Positive |
A profile pins provider type, environment, credential profile, timeouts, concurrency and rate limit.
Sender identity and credential profile are separate concerns.
## Startup failures
@@ -32,10 +82,18 @@ Startup fails rather than degrading when:
- a Web Push profile is missing its VAPID key
- two provider profiles share an id
- a route points only at disabled providers
- ambiguous fallback is enabled by default
- `mode` is `SERVING` and no provider profile is enabled
- the notification schema stream is not applied and promoted
## Secrets
All key material arrives through `SecretMaterialProvider`. Nothing is read from source, from a
committed file, or from a plaintext log. Contact point encryption and lookup HMAC keys must be
distinct, and the encryption key must be exactly 256 bits.
## Readiness
The platform contributes a `notifications` actuator endpoint and a health indicator. It reports DOWN
when a provider's credentials were rejected, when a configured provider has no channel route, and
when the measured backlog, stuck-lease count, projection lag or reconciliation lag passes the
thresholds in `NotificationServingThresholds`. See [operations.md](operations.md).
+77
View File
@@ -0,0 +1,77 @@
{
"$comment": [
"NTF-024 — what each support grade requires, as executable artifacts rather than prose.",
"A grade in support-matrix.md is a promise about production behaviour. The workflow that was",
"supposed to back those promises ran a unit subset on PR, a job named 'restart recovery' that",
"ran no restart, and a provider sandbox job whose entire body was two echo statements behind",
"continue-on-error. So the strongest claim in the document rested on the weakest evidence in",
"the pipeline, and nothing connected the two.",
"verifyNotificationEvidence reads this file, checks that every claim marked satisfied names",
"test classes that exist, and refuses a grade whose claims are not all satisfied."
],
"claims": {
"durable": {
"requires": "PostgreSQL migration, CRUD against the real schema, and survival of a restart",
"status": "satisfied",
"lane": ":adapter:outbound:persistence-jpa:jpaPlatformContractTest",
"evidence": [
"adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/notification/RecipientClaimContractTest.java",
"adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/notification/ProjectionFactDurabilityContractTest.java",
"adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/notification/ServingStateContractTest.java"
]
},
"multi-worker-safe": {
"requires": "two workers racing the same claim against a real database, with lease fencing",
"status": "satisfied",
"lane": ":adapter:outbound:persistence-jpa:jpaPlatformContractTest",
"evidence": [
"adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/notification/RecipientClaimContractTest.java",
"adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/readiness/PostgreSqlRecipientLeaseFencingIntegrationTest.java"
]
},
"callback-supported": {
"requires": "signature verification, duplicate suppression, and an event that arrives before its attempt is stored",
"status": "satisfied",
"lane": ":adapter:outbound:persistence-jpa:jpaPlatformContractTest",
"evidence": [
"adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/notification/LateEventBindingContractTest.java",
"application-core/src/test/java/dev/caskeleton/application/notification/platform/callback/CallbackIngestionAtomicityTest.java"
]
},
"recoverable": {
"requires": "a process-kill matrix covering each dispatch phase, proving no delivery is lost or duplicated",
"status": "satisfied",
"lane": ":adapter:outbound:persistence-jpa:jpaPlatformContractTest",
"evidence": [
"adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/notification/WorkerCrashRecoveryContractTest.java"
]
},
"provider-wire-qualified": {
"requires": "a real provider sandbox call producing an immutable, uploaded evidence artifact with a correlation id",
"status": "unsatisfied",
"lane": null,
"evidence": [],
"gap": "The provider-sandbox job runs two echo statements behind continue-on-error. No request has ever left the process, so no provider protocol is qualified against its real endpoint."
}
},
"grades": {
"Stable": [
"durable",
"multi-worker-safe",
"callback-supported",
"recoverable",
"provider-wire-qualified"
],
"Contract implemented / runtime unqualified": [
"durable",
"multi-worker-safe",
"callback-supported"
],
"Optional stable": [
"durable"
],
"Extension": [],
"Experimental": []
},
"matrixDocument": "docs/notification/support-matrix.md"
}
+13
View File
@@ -29,3 +29,16 @@ platform exists to avoid.
Registration tokens keep working through `LegacyFcmRegistrationToken`. New registrations should use
`FcmInstallationId`. The two are distinct types, so a migration is a compile-time task rather than a
runtime guess.
## R1의 처분 (NOTIF-ADR-005)
이 문서는 R0 router → platform 이행만 설명해 왔고, `dev.caskeleton.application.notification` 직속의
R1 public type 100개를 어떻게 할 것인지 다루지 않았다. 그래서 새 consumer가 어느 API를 써야 하는지
문서 어디에도 답이 없었다.
- **canonical은 `..notification.platform..`이다.** NOTIF-ADR-005가 근거와 함께 정한다.
- **R1은 남지만 새 production consumer를 받지 않는다.** 삭제 계획은 별개이며, R0 삭제와 함께
사라지지 않는다.
- **전수 분류표는 `docs/notification/module-mapping.md`에 있다** (replace / bridge / retain / delete).
- **두 namespace 간 production dependency는 0건이며 ArchUnit이 강제한다.** 변환이 필요하면
`dev.caskeleton.application.notification.compatibility.r1` 한 곳에만 둔다.
+22
View File
@@ -92,3 +92,25 @@ Two plan edges cannot be expressed as project edges in this repository, and are
`AGENTS.md` pins commit policy to `human-only`. Step 5 (`git add` / `git commit`) of every plan task
is therefore intentionally **not** executed by the agent; the working tree carries the change and the
human owner commits.
## R1 public type disposition (NOTIF-ADR-005)
NOTIF-ADR-005 makes `..notification.platform..` canonical and keeps the R1 namespace in place
without new consumers. The ADR owns the rule; this table owns the list, so the two do not drift
apart by being written twice.
`NOTIFICATION_R1_AND_PLATFORM_DO_NOT_DEPEND_ON_EACH_OTHER` in `CleanArchitectureTest` enforces the
boundary: production dependencies between the two namespaces are zero, and the only permitted
exception is `dev.caskeleton.application.notification.compatibility.r1`.
| Disposition | Meaning | Types |
| --- | --- | --- |
| `replace` (27) | the platform has an equivalent; new consumers use it | `ApplyNotificationReceiptCommand`, `ApplyNotificationReceiptResult`, `ApplyNotificationReceiptUseCase`, `Channel`, `InlineNotificationAttemptPort`, `NormalizedNotificationReceiptCommand`, `NotificationAttemptId`, `NotificationDeliveryId`, `NotificationDeliveryStorePort`, `NotificationDispatchCommand`, `NotificationDispatchResult`, `NotificationDispatchUseCase`, `NotificationEvidenceTrustSnapshot`, `NotificationFrozenPlan`, `NotificationPlanPort`, `NotificationPlanningResult`, `NotificationProviderAttemptPort`, `NotificationReceiptEventId`, `NotificationReceiptFact`, `NotificationReceiptIngressCapabilityDescriptor`, `NotificationReceiptProjection`, `NotificationReceiptStorePort`, `NotificationSignedEvidenceHeader`, `NotificationWriterInventoryEvidence`, `NotificationWriterInventoryEvidenceVerifierPort`, `ProviderAttemptOutcome`, `TargetAttemptOutcome` |
| `bridge` (28) | conversion needed if an R1 caller remains; conversion lives only in the ACL | `InitializeNotificationWriterFencesCommand`, `InitializeNotificationWriterFencesOperation`, `InitializeNotificationWriterFencesResult`, `InitializeNotificationWriterFencesUseCase`, `NotificationAdmissionGateCommand`, `NotificationAdmissionGateUseCase`, `NotificationAppendResult`, `NotificationLegacyWriterPermitCommand`, `NotificationLegacyWriterPermitResult`, `NotificationLegacyWriterPermitUseCase`, `NotificationMaintenanceCommand`, `NotificationMaintenanceResult`, `NotificationMaintenanceUseCase`, `NotificationOperationsSnapshot`, `NotificationOperationsSnapshotQuery`, `NotificationOperationsSnapshotUseCase`, `NotificationRequestResult`, `ReconcileNotificationDeliveriesCommand`, `ReconcileNotificationDeliveriesResult`, `ReconcileNotificationDeliveriesUseCase`, `SwitchNotificationWriterOwnershipCommand`, `SwitchNotificationWriterOwnershipOperation`, `SwitchNotificationWriterOwnershipResult`, `SwitchNotificationWriterOwnershipUseCase`, `TerminalizeExpiredNotificationWriterPermitsCommand`, `TerminalizeExpiredNotificationWriterPermitsOperation`, `TerminalizeExpiredNotificationWriterPermitsResult`, `TerminalizeExpiredNotificationWriterPermitsUseCase` |
| `retain` (45) | a concern the platform does not cover; left as it is | `ConsentCheckMode`, `EmailRecipientReference`, `Notification`, `NotificationAdmissionClass`, `NotificationAdmissionReadinessPort`, `NotificationApplicationException`, `NotificationCanonicalWriterFenceGuard`, `NotificationCanonicalWriterFencePort`, `NotificationCanonicalWriterRouteSet`, `NotificationCapabilityCompatibilityValidator`, `NotificationChannel`, `NotificationFaultScope`, `NotificationIntentAppendPort`, `NotificationIntentDraft`, `NotificationIntentId`, `NotificationKindId`, `NotificationKindPolicy`, `NotificationMaintenanceStorePort`, `NotificationMode`, `NotificationOperationsSnapshotPort`, `NotificationPort`, `NotificationProviderCapabilityDescriptor`, `NotificationReasonCode`, `NotificationRecipientReference`, `NotificationReconciliationPort`, `NotificationRouteId`, `NotificationRouteStrategy`, `NotificationStoreCapabilityDescriptor`, `NotificationTechnicalSuppressionPort`, `NotificationTemplateParameters`, `NotificationTemplateRef`, `NotificationTemplateValue`, `NotificationWriterCutoverPort`, `NotificationWriterOwnership`, `NotificationWriterQuiescenceAttestationPort`, `NotificationWriterRouteSet`, `RecordNotificationWriterQuiescenceAttestationCommand`, `RecordNotificationWriterQuiescenceAttestationOperation`, `RecordNotificationWriterQuiescenceAttestationResult`, `RecordNotificationWriterQuiescenceAttestationUseCase`, `RetryDisposition`, `SignedNotificationWriterInventoryManifest`, `SignedNotificationWriterQuiescenceManifest`, `SlackAudienceReference`, `SubmissionCertainty` |
Total: 100 public types, every one classified.
No type carries `@Deprecated(forRemoval = true)`: no removal release is fixed, and
`forRemoval` without a date is a promise the codebase cannot keep. The boundary is
enforced by the ArchUnit rule instead.
+35 -5
View File
@@ -2,18 +2,48 @@
What each channel can actually prove, and what the platform refuses to claim.
The grade column is not an opinion. `docs/notification/evidence-manifest.json` declares which claims
each grade requires and which executable artifact proves each claim, and
`./gradlew verifyNotificationEvidence` refuses a grade whose claims are not all backed by a file that
exists. Raising a grade means adding the artifact first.
Five channels read `Stable` until 2026-08-15. Nothing in the pipeline had ever sent a request to a
provider — the sandbox job's whole body was two `echo` statements behind `continue-on-error` — and
no test killed a worker mid-dispatch. The protocols are implemented and their contracts are proven
against real PostgreSQL; the wire and the crash are not. That is what the grade now says.
## Channels
| Channel | Reference implementation | Grade | Strongest evidence the platform records by default |
|---|---|---|---|
| Email | SMTP, Amazon SES API | Stable | Provider acceptance; recipient mail-server delivery, bounce and complaint when the provider publishes events |
| SMS | Twilio Programmable Messaging | Stable | `accepted`/`queued`, `sent`, and carrier-DLR `delivered`/`undelivered` |
| Mobile push (Android and cross-platform) | FCM, FID-first with legacy registration token compatibility | Stable | FCM acceptance and explicit failures |
| Mobile push (Apple) | APNs HTTP/2 provider API | Stable | APNs acceptance |
| Web Push | RFC 8030, RFC 8291, RFC 8292 | Stable | Push-service acceptance; user-agent acknowledgement only where the service offers receipts |
| Email | SMTP, Amazon SES API | Contract implemented / runtime unqualified | Provider acceptance; recipient mail-server delivery, bounce and complaint when the provider publishes events |
| SMS | Twilio Programmable Messaging | Contract implemented / runtime unqualified | `accepted`/`queued`, `sent`, and carrier-DLR `delivered`/`undelivered` |
| Mobile push (Android and cross-platform) | FCM, FID-first with legacy registration token compatibility | Contract implemented / runtime unqualified | FCM acceptance and explicit failures |
| Mobile push (Apple) | APNs HTTP/2 provider API | Contract implemented / runtime unqualified | APNs acceptance |
| Web Push | RFC 8030, RFC 8291, RFC 8292 | Contract implemented / runtime unqualified | Push-service acceptance; user-agent acknowledgement only where the service offers receipts |
| In-app inbox | Own database | Optional stable | `PERSISTED`, `SEEN`, `READ` |
| Webhook | HTTP client platform | Extension | Whatever the receiving HTTP contract states |
## What each grade requires
| Grade | Requires |
|---|---|
| Stable | durable, multi-worker-safe, callback-supported, recoverable, provider-wire-qualified |
| Contract implemented / runtime unqualified | durable, multi-worker-safe, callback-supported |
| Optional stable | durable |
| Extension | nothing; the receiving contract owns its own guarantees |
| Experimental | nothing; the grade is the warning |
`recoverable` was met on 2026-08-15 by `WorkerCrashRecoveryContractTest`, which walks the four
phases a dispatch passes through — claimed, attempt written, request started, body committed — and
asserts for each that the delivery becomes claimable again or becomes a question for the provider,
never both and never neither.
The remaining unmet claim, and what would meet it:
- **provider-wire-qualified** — a real provider sandbox call producing an immutable, uploaded
artifact with a correlation id. No request has ever left the process in CI.
## Evidence levels
`NONE``PLATFORM_QUEUED``PROVIDER_ACCEPTED``NETWORK_OR_CARRIER_ACCEPTED`
+161
View File
@@ -4244,3 +4244,164 @@ env_keys:
validation: positive_int_bounded
compatibility_impact: behavior-change
required_test: async-contract:executor-queue-bounded
# === Notification delivery platform (NTF-025 — configuration surface) ===
# The tree exists in application.yml as ca-skeleton.notification.platform, disabled by
# default. Every key carries an inline default so a deployment that leaves the platform off
# supplies nothing. Reference: docs/notification/configuration.md.
- name: APP_NOTIFICATION_PLATFORM_ENABLED
# source: NTF-025 — master switch for the notification delivery platform; false binds nothing at all
type: boolean
default: false
allowed_values: [true, false]
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: boolean
compatibility_impact: behavior-change
required_test: adapter-contract:notification-platform-disabled-safe
- name: APP_NOTIFICATION_PLATFORM_MODE
# source: NTF-025 — SERVING refuses to start without a working provider; ACCEPT_ONLY stores and does not dispatch
type: enum
default: SERVING
allowed_values: [SERVING, ACCEPT_ONLY]
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: enum_of_notification_platform_mode
compatibility_impact: behavior-change
required_test: adapter-contract:notification-platform-mode
- name: APP_NOTIFICATION_PLATFORM_CLAIM_BATCH_SIZE
# source: NTF-025 — how many recipient deliveries one scheduler pass claims; 1..1000, refused outside that at binding
type: integer
default: 50
allowed_values: null
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: integer_1_to_1000
compatibility_impact: behavior-change
required_test: adapter-contract:notification-dispatch-bounds
- name: APP_NOTIFICATION_PLATFORM_LEASE_DURATION
# source: NTF-025 — must outlast a provider call plus its timeout, or a live worker's delivery is claimed by a second one
type: duration
default: 2m
allowed_values: null
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: positive_duration
compatibility_impact: behavior-change
required_test: adapter-contract:notification-lease-fencing
- name: APP_NOTIFICATION_PLATFORM_POLL_INTERVAL
# source: NTF-025 — how often the scheduler asks for work when the last pass claimed nothing
type: duration
default: 1s
allowed_values: null
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: positive_duration
compatibility_impact: behavior-change
required_test: adapter-contract:notification-dispatch-bounds
- name: APP_NOTIFICATION_PLATFORM_MAX_CONCURRENCY
# source: NTF-025 — ceiling on in-flight provider calls across the whole process
type: integer
default: 64
allowed_values: null
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: positive_integer
compatibility_impact: behavior-change
required_test: adapter-contract:notification-dispatch-bounds
- name: APP_NOTIFICATION_PLATFORM_MAX_ADDITIONAL_ATTEMPTS
# source: NTF-025 — retries after the first attempt; 0 means one attempt and no retry
type: integer
default: 4
allowed_values: null
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: non_negative_integer
compatibility_impact: behavior-change
required_test: adapter-contract:notification-retry-policy
- name: APP_NOTIFICATION_PLATFORM_MAX_QUEUE_AGE
# source: NTF-025 — after this, a queued delivery expires rather than being sent late
type: duration
default: 24h
allowed_values: null
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: positive_duration
compatibility_impact: behavior-change
required_test: adapter-contract:notification-expiry
- name: APP_NOTIFICATION_PLATFORM_ALLOW_AMBIGUOUS_FALLBACK
# source: NTF-025 — an ambiguous attempt reached the provider with an unread outcome; falling back risks a duplicate send
type: boolean
default: false
allowed_values: [true, false]
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: boolean
compatibility_impact: behavior-change
required_test: adapter-contract:notification-ambiguity
- name: APP_NOTIFICATION_PLATFORM_CALLBACKS_ENABLED
# source: NTF-025 — whether the platform exposes provider callback endpoints
type: boolean
default: false
allowed_values: [true, false]
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: boolean
compatibility_impact: behavior-change
required_test: adapter-contract:notification-callback-ingestion
- name: APP_NOTIFICATION_PLATFORM_CALLBACK_MAX_BODY_BYTES
# source: NTF-025 — ceiling is 65508 = ciphertext column minus AES-GCM nonce and tag; larger is refused at binding
type: integer
default: 65508
allowed_values: null
classification: public-config
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: integer_1_to_65508
compatibility_impact: behavior-change
required_test: adapter-contract:notification-callback-body-bound
- name: APP_NOTIFICATION_PLATFORM_CALLBACK_REPLAY_SKEW
# source: NTF-025 — how far a callback timestamp may differ from local time before it is treated as a replay
type: duration
default: 5m
allowed_values: null
classification: security-relevant
required: false
reload_policy: restart-only
owner_branch: worktree-notification-platform
validation: positive_duration
compatibility_impact: behavior-change
required_test: adapter-contract:notification-callback-replay
@@ -0,0 +1,889 @@
# GraphQL 인바운드 모듈 상세 코드·아키텍처 리뷰
- 기준 일자: 2026-08-14
- 기준 Git HEAD: `ac874e49e608b35429f82aa098574b52a68f2069`
- 대상 Gradle leaf: `:adapter:inbound:graphql`
- 주 대상 경로: `src/adapter/inbound/graphql`
- 교차 확인 경로: `src/config/architecture/modules.json`, `src/gradle/graphql-platform-conventions.gradle`, `src/.gitignore`
- 판정: **CHANGES REQUIRED / 현재 컴파일 불가**
- 검토 방식: 전체 파일·import·production reference inventory, 핵심 실행 경로 정독, 세 개의 독립 병렬 리뷰, Gradle focused/architecture 검증
- 변경 범위: 이 리뷰 문서만 추가했다. production/test 코드는 수정하지 않았다.
리뷰 도중 HEAD가 `c3043e530a604315c4df341b87b5470c7617ea03`에서 위 commit으로 이동했지만,
GraphQL tree hash는 두 revision 모두 `bd8307364e2312814995e5f3bb1386cee37b1498`이고
`src/.gitignore`, GraphQL convention, architecture registry에도 delta가 없음을 확인했다.
## 1. 결론
현재 GraphQL leaf는 373개 production Java 파일과 75개 test Java 파일을 가진 큰 실행 플랫폼 후보지만,
두 층의 문제가 겹쳐 있다.
첫 번째는 즉시 고쳐야 하는 **빌드 차단**이다. `src/.gitignore`의 unanchored `build/` 규칙이 Gradle
산출물뿐 아니라 Java source package인 `...graphql.build`까지 무시한다. 문서와 production code는
`GraphQlBuildModel`, `GraphQlStableModule`, `GraphQlAdvancedModule`, `GraphQlModuleBoundaryTest`가 있다고
주장하지만 실제 checkout에는 없다. 그 결과 focused test는 test 실행 전에 `compileJava`에서 7개
오류로 실패하고, 단일 leaf 안의 Stable/Advanced 경계를 지킨다는 핵심 안전망도 함께 사라졌다.
두 번째는 더 근본적인 **런타임 진실성 문제**다. cost, authorization, DataLoader, cursor,
idempotency, observation, persisted operation, subscription 등 많은 정책과 값 객체가 구현되어 있지만,
대부분 Spring GraphQL이 실제 `/graphql` 요청을 처리하는 extension point에 연결되지 않는다. 현재 HTTP
qualification은 Spring Boot 기본 endpoint와 health controller/error resolver를 검증할 뿐, 이 플랫폼의
pipeline을 거치지 않는다. 따라서 unit test가 복구되어 green이 되더라도 “정책 객체가 맞다”는 증거와
“실제 요청에 정책이 강제된다”는 증거를 분리해야 한다.
즉시 적용할 원칙은 다음과 같다.
1. GQL-001을 단독 PR로 먼저 처리해 compile과 내부 경계 검사를 복구한다.
2. 복구 전후 모두 현재 artifact를 `runtime-ready GraphQL execution platform`으로 승격하지 않는다.
3. Spring 기본 `/graphql`을 canonical transport로 정하고, 정책을 공식 extension point에 연결한다.
4. 자체 MVC/WebFlux adapter를 실제 endpoint로 만들 계획이 없다면 제거한다. 평행 실행 경로를 두지 않는다.
5. repository 자동 노출은 Advanced라도 제거한다. application use case를 우회하는 예외를 만들지 않는다.
6. correctness/security red test를 먼저 고정한 뒤 public API와 Gradle leaf를 단계적으로 분리한다.
7. 실제 random-port request가 정책에 의해 거부되고 resolver/use case가 0회 호출됨을 promotion 증거로 삼는다.
## 2. 범위와 증거 경계
### 2.1 현재 규모
| 항목 | 현재 값 |
|---|---:|
| production Java 파일 | 373 |
| production Java LOC | 20,155 |
| test Java 파일 | 75 |
| test Java LOC | 8,424 |
| test annotation (`@Test`, `@ParameterizedTest`) | 524 |
| 최상위 production package | 21 |
| main resource | `graphql/skeleton.graphqls` 1개 |
| test resource | qualification schema 1개 |
| 외부 Spring/GraphQL/Reactor import를 가진 production Java | 19 |
최상위 package는 `advanced`, `api`, `architecture`, `autoconfigure`, `compat`, `context`, `cost`,
`dataloader`, `error`, `execution`, `fetch`, `http`, `mutation`, `observation`, `pagination`, `policy`,
`release`, `scalar`, `schema`, `security`, `testkit`이다.
373개 중 354개가 Spring/GraphQL Java/Reactor type을 직접 import하지 않는다는 점은 framework-free policy
model을 추출할 여지가 크다는 뜻이다. 동시에 거의 모든 최상위 type이 public이어서 현재 한 jar가
사실상 수백 개의 API를 노출한다.
### 2.2 검토 깊이
| Path | Status | Evidence | Extracted facts |
|---|---|---|---|
| `src/adapter/inbound/graphql/build.gradle` | READ_FULL | 1-54 | servlet runtime 의존, WebFlux compile-only, test lane 등록 |
| `src/gradle/graphql-platform-conventions.gradle` | READ_FULL | 1-100 | Stable/contract/Advanced/performance lane과 누락된 boundary model 주장 |
| `src/adapter/inbound/graphql/CLAUDE.md` | READ_FULL | 1-143 | 단일 leaf 내부 28 bounded package, runtime opt-in, 실행 범위·검증 주장 |
| `src/adapter/inbound/graphql/README.md` | READ_FULL | 1-189 | health endpoint, error mapping, 설정·경계·Advanced 설계 근거 |
| `src/config/architecture/modules.json` GraphQL record | READ_FULL | GraphQL leaf record | 허용 project edge와 빈 runtime membership |
| `src/.gitignore` | READ_FULL | 1-16 + `git check-ignore` | `build/`가 Java source package까지 무시하는 직접 원인 |
| root controller/error resolver/schema | READ_FULL | production + 대응 tests | 현재 실제 Spring GraphQL endpoint 표면 |
| `autoconfigure`, `http`, `execution`, `architecture` | READ_FULL | production 핵심 경로 + 대응 tests | auto-config 등록, 실행 연결, transport, 경계 검사 |
| `cost`, `security`, `dataloader`, `pagination`, `mutation` | READ_FULL | 핵심 policy/codec/executor + 대응 tests | 구조 제한, tenant/auth, batch, cursor, idempotency correctness |
| `schema`, `compat`, `scalar`, `error`, `observation` | READ_FULL | production 핵심 경로 + 대응 tests | schema 조립/호환, scalar, wire error, cardinality |
| `advanced/**` | READ_PARTIAL | public entry/state transition/production reference scan + 주요 tests | persisted/admin/codegen/subscription/federation/transport seam |
| `release/**`, `testkit/**` | READ_PARTIAL | public contract/lane/reference scan + suite tests | self-reported evidence와 production artifact 오염 |
| production 373개/test 75개 전체 | READ_PARTIAL | inventory/import/reference/public-surface scan | 파일·package·사용처·실행 연결의 전수 정적 탐색 |
이 문서는 28,579 LOC의 모든 method를 line-by-line 승인한 결과가 아니다. 전체 inventory와 reference scan을
바탕으로 실행 seam과 고위험 policy를 정독한 구조·correctness 리뷰다. `advanced/**`, release/testkit의
세부 알고리즘은 명시한 범위 밖에서 `UNVERIFIED`이며, 실제 adopter/runtime·load·fault evidence도 없다.
## 3. 유지할 설계
리팩터링 과정에서 다음은 보존할 가치가 있다.
- registry상 GraphQL leaf의 production project dependency가 Clean Architecture 방향을 벗어나지 않는다.
- `runtime_memberships`가 비어 있어 현재 app-bootstrap/sample runtime에 조용히 유입되지 않는다.
- 실제 `HealthGraphqlController`는 얇고 feature/domain/repository 지식이 없다.
- 실제 Spring exception resolver는 shared error code만 노출하고 raw exception message를 사용하지 않는다.
- schema compatibility를 SDL 문자열 diff가 아니라 AST로 비교하고 결과를 결정적으로 정렬한다.
- partial data map에 null을 허용하는 defensive copy를 사용한다. 이를 `Map.copyOf`로 바꾸면 안 된다.
- cursor HMAC을 `MessageDigest.isEqual`로 비교하고 query/filter에 bind하려는 방향은 맞다.
- DataLoader 결과에서 `Present`, `Missing`, `Failed`를 구분하려는 결과 algebra는 유용하다.
- document traversal은 fragment cycle과 방문 node budget을 고려한다.
- Advanced capability가 기본 비활성이고 experimental production activation을 명시적으로 거부한다.
- test lane이 빈 performance evidence를 success로 위장하지 않으려는 fail-closed 의도는 좋다.
- broad static import scan에서 Stable package가 `...graphql.advanced`를 직접 import하는 edge와
production repository/JPA/Spring Data 직접 사용은 발견되지 않았다.
## 4. 우선순위 요약
| ID | 우선순위 | 주제 | 완료 조건 |
|---|---|---|---|
| GQL-001 | P0 | ignored `build` source package 때문에 compile 및 경계 모델 소실 | 비-ignore package로 모델 복구, compile/test/boundary negative fixture 통과 |
| GQL-002 | P0 | 플랫폼 정책이 실제 `/graphql` 실행 경로에 미연결 | real interceptor/instrumentation/DataLoader/wiring E2E에서 정책 거부 증명 |
| GQL-003 | P1 | auto-configuration 등록·binding default·실제 bean 검증 불일치 | 무설정 boot, imports metadata, 실제 override bean validation 통과 |
| GQL-004 | P1 | servlet artifact가 reactive profile도 표방 | MVC/WebFlux runtime classpath와 context가 별도 leaf에서 독립 통과 |
| GQL-005 | P1 | request byte 제한 미강제와 valid null variable 거부 | decode 전 body cap, null/omitted/value E2E 통과 |
| GQL-006 | P1 | `Accept` q-value/q=0 무시 | quality/specificity 기반 negotiation contract 통과 |
| GQL-007 | P1 | named fragment introspection 우회와 variable nesting 공백 | reachable fragment/variable JSON budget 거부 E2E 통과 |
| GQL-008 | P1 | resolver 경계 검사가 generic/JAR/subpackage를 놓치고 reactive type을 오판 | actual controller graph와 recursive generic negative fixture 통과 |
| GQL-009 | P1 | Advanced repository 자동 노출이 canonical hard-stop과 충돌 | repository exposure API 제거, application handler만 허용 |
| GQL-010 | P1 | cursor framing·rotation·direction·tenant binding 결함 | versioned codec property tests와 active-key/scope rejection 통과 |
| GQL-011 | P1 | mutation fingerprint collision과 tenant 없는 idempotency scope | typed canonical serialization과 tenant/version scope 테스트 통과 |
| GQL-012 | P1 | error resolver와 category contract가 두 벌 | 하나의 mapper를 모든 Spring/transport path가 사용 |
| GQL-013 | P1 | persisted-operation admin 상태·감사·인가가 durable하지 않음 | authenticated principal, CAS state machine, atomic audit contract 통과 |
| GQL-014 | P1 | codegen이 operation을 검증하지 않고 generator도 code를 생성하지 않음 | executable document validation 또는 정직한 planner 명명 |
| GQL-015 | P1 | schema comparator가 kind/default/extension/applied directive를 놓침 | breaking matrix와 extension ownership tests 통과 |
| GQL-016 | P1 | custom DataLoader와 timeout이 실제 loader 실행을 강제하지 않음 | Spring registry 연결과 real query-count/deadline test 통과 |
| GQL-017 | P1 | MVC concurrency/context와 WebFlux blocking bridge가 안전하지 않음 | bounded admission, context propagation, event-loop nonblocking 증명 |
| GQL-018 | P1 | pipeline stage 순서가 필요한 정보와 모순 | authenticate→parse/select→authorize→cost→execute executable chain |
| GQL-019 | P1 | subscription/replay/drain lifecycle의 race와 scope 공백 | atomic state/lease, actor+tenant+subscription binding 경쟁 test 통과 |
| GQL-020 | P2 | preparsed cache expiry 미사용·global miss serialization | expiry/single-flight/parallel-key test 통과 |
| GQL-021 | P2 | cancellation hook 하나가 나머지 cleanup을 막음 | all-hooks-once + suppressed exception contract 통과 |
| GQL-022 | P2 | scalar input/output bounds와 expansion limit 불일치 | BigDecimal/Long 양방향 boundary test 통과 |
| GQL-023 | P2 | raw operation name metric cardinality와 실제 Observation 미연결 | registered-name/`other` bound와 real MeterRegistry test 통과 |
| GQL-024 | P2 | testkit/fixed secret/in-memory 구현이 main jar에 포함 | test fixtures/optional leaf 분리 및 jar surface gate 통과 |
| GQL-025 | P2 | 373개 type의 과도한 public surface와 한 leaf의 낮은 응집도 | api/spi allowlist와 6~8 capability leaf 독립 compile/test |
| GQL-026 | P2 | GraphQL context/storage SPI ownership이 dependency 방향과 충돌 | inbound-local mapping과 transport-neutral operational port로 분리 |
| GQL-027 | P3 | 문서·설정 namespace·test count·runtime 지원 주장 drift | generated metadata/runtime adoption test 기반 문서 동기화 |
## 5. 상세 발견 사항과 구현 명세
### GQL-001 — `build` Java package가 `.gitignore`에 걸려 compile과 경계 검사가 함께 사라졌다
**근거**
- `src/.gitignore:2`는 root에 고정되지 않은 `build/` 패턴이다.
- `git check-ignore -v --no-index
src/adapter/inbound/graphql/src/main/java/dev/caskeleton/adapter/inbound/graphql/build/GraphQlBuildModel.java`
는 `src/.gitignore:2:build/`를 반환한다.
- `GraphQlPlatformAutoConfiguration.java:3,107-108`과
`advanced/bootstrap/GraphQlAdvancedDependencyRules.java:3,26-28,43`은 존재하지 않는
`dev...graphql.build.GraphQlBuildModel`을 참조한다.
- `CLAUDE.md:36-40`, `README.md:108-115`, `graphql-platform-conventions.gradle:11-13`은
`GraphQlStableModule`, `GraphQlAdvancedModule`, `GraphQlBuildModel`,
`GraphQlModuleBoundaryTest`가 실제 tree를 검사한다고 기록하지만 네 파일은 main/test tree에 없다.
- focused `:test`는 `compileJava`에서 해당 package/class 관련 7개 오류로 실패했다.
**실패 모드**
로컬 작성자가 ignored package 아래 파일을 생성하면 파일이 보이므로 잠시 compile될 수 있지만 commit에
들어가지 않는다. fresh checkout/CI에서는 소스가 사라져 compile이 깨진다. 더 위험한 변형은 production
참조를 지웠을 때다. build는 green이 될 수 있지만 Stable→Advanced/core purity/등록 package 검사가 없는
false green이 된다.
**구현 결정**
1. source package 이름을 `...graphql.build`가 아니라 `...graphql.moduleboundary`로 바꾼다. `.gitignore`
예외보다 역할이 분명하고 다른 도구의 `build` 디렉터리 규칙과 충돌하지 않는다.
2. 세 production model을 복원한다. 단, source-tree scanner가 runtime에 필요하지 않으면
`GraphQlBuildModel`을 test/build logic으로 이동하고 `GraphQlPlatformAutoConfiguration`의 runtime
source scan을 제거한다.
3. `GraphQlModuleBoundaryTest`는 실제 source/import graph를 검사하며 다음 세 negative fixture를 가진다.
Stable→Advanced import, core package의 Spring/GraphQL/Reactor import, 등록되지 않은 package.
4. `graphqlStableTest`가 해당 FQCN을 명시적으로 포함하고, test가 0개면 실패하게 유지한다.
5. repository-level gate에 `src/**/src/{main,test}/java/**/build/**` 같은 ignored source-package를
탐지하는 검사를 추가한다. Git에 존재하지 않는 파일을 CI가 찾을 수 없으므로, package naming rule과
required boundary-class existence 검사를 함께 둔다.
**필수 테스트/검증**
```bash
cd src
./gradlew :adapter:inbound:graphql:compileJava --console=plain
./gradlew :adapter:inbound:graphql:test --console=plain
./gradlew :adapter:inbound:graphql:graphqlStableTest --console=plain
./gradlew :adapter:inbound:graphql:test \
--tests '*GraphQlModuleBoundaryTest' --rerun-tasks --console=plain
```
### GQL-002 — 정책 카탈로그는 크지만 실제 `/graphql` 요청에는 실행되지 않는다
**근거**
- `GraphQlPlatformAutoConfiguration.java:43-103`은 startup validator, stage 목록, mapping gate,
observation convention POJO를 bean으로 만들지만 실제 request hook을 등록하지 않는다.
- `GraphQlExecutionPipeline`은 실행 가능한 Chain of Responsibility가 아니라 enum stage 순서 record다.
- MVC/WebFlux transport adapter의 `handle()`은 controller/router/filter가 아니며 production 호출처가 없다.
- production에는 `WebGraphQlInterceptor`, GraphQL Java `Instrumentation`, Spring
`BatchLoaderRegistry`, 실제 `PreparsedDocumentProvider` 연결이 없다.
- `GraphQlScalarWiringConfigurer`는 올바른 `RuntimeWiringConfigurer` 구현이지만 production bean이 아니다.
- HTTP qualification은 Spring Boot 기본 `/graphql`, health controller, root exception resolver와
test-only security를 검증한다. platform auto-configuration과 custom adapters를 import하지 않는다.
**실패 모드**
adopter가 최대 depth/complexity, introspection, authorization, timeout, DataLoader policy를 설정하고
안전하다고 판단해도, Spring 기본 endpoint는 이 객체들을 호출하지 않는다. unit test는 각 policy 함수가
정상임만 증명하고 endpoint adoption을 증명하지 못한다.
**구현 결정: Spring-native 단일 실행 경로**
1. Spring 기본 `/graphql`을 canonical HTTP transport로 유지한다.
2. `GraphQlPlatformWebInterceptor implements WebGraphQlInterceptor`에서 인증 principal을 검증된
request context로 매핑하고 GraphQL/Reactor context에 넣는다.
3. `GraphQlPlatformInstrumentation` 또는 `ExecutionGraphQlService` decorator에서 document
parse/selection, introspection, authorization, cost, deadline/cancellation을 실행한다.
4. `RuntimeWiringConfigurer`, `BatchLoaderRegistry` 등록/decorator, actual preparsed document provider,
canonical exception resolver를 auto-configuration이 bean으로 조립한다.
5. 현재 MVC/WebFlux custom adapters는 제거한다. 자체 transport가 반드시 필요하다면 Spring 기본
handler를 끄고 실제 route를 소유하게 하며, 두 경로를 동시에 두지 않는다.
6. 모든 policy stage는 `GraphQlExecutionRequest`와 `GraphQlExecutionContext`를 입력·출력하는 실행 가능한
handler로 바꾼다. 단순 stage catalog는 문서/검증 view로만 파생한다.
**필수 E2E**
- random-port servlet `/graphql`에서 depth/cost/alias/introspection/oversize/authz/timeout 거부.
- 각 거부에서 controller, use case, batch loader 호출 횟수 0.
- actor/tenant/deadline이 controller와 DataLoader에 동일하게 전달됨.
- custom scalar를 포함한 schema boot 및 실제 coercion.
- 같은 document cache hit, request별 DataLoader cache 격리.
- reactive artifact를 유지한다면 동일 contract를 reactive random-port에서도 실행.
Spring GraphQL이 제공하는 공식 연결점은
[`WebGraphQlInterceptor`](https://docs.spring.io/spring-graphql/reference/1.3/request-execution.html),
[`RuntimeWiringConfigurer`](https://docs.spring.io/spring-graphql/docs/current/api/org/springframework/graphql/execution/RuntimeWiringConfigurer.html),
[`BatchLoaderRegistry`](https://docs.spring.io/spring-graphql/docs/current/api/org/springframework/graphql/execution/BatchLoaderRegistry.html)다.
구현 시 repository lock의 Spring GraphQL 2.0.0/Boot 4.0.0 API signature로 다시 확인한다.
### GQL-003 — auto-configuration, binding default, 실제 override 검증이 각각 다른 계약이다
**근거**
- `GraphQlPlatformAutoConfiguration`은 이름과 달리 `@Configuration`이며 auto-configuration imports
metadata가 없다. main resource는 schema 한 개뿐이다.
- `GraphQlPlatformProperties` primitive binding default는 `maximumPageSize=0`,
`maximumComplexity=0`인데 startup validator는 양수만 허용한다.
- `productionDefaults()` factory는 Spring binder default가 아니다.
- custom `backend.graphql.graphiql-enabled/introspection-enabled`와 실제
`spring.graphql.*` framework flags가 분리되어 있다.
- startup check는 주입된 override pipeline이 아니라 `GraphQlExecutionPipeline.stable()` 상수를 검증한다.
**구현 결정**
1. 재사용 starter라면 `@AutoConfiguration(after = GraphQlAutoConfiguration.class)`과
`META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`를 추가한다.
내부 composition 전용이면 이름을 `GraphQlPlatformConfiguration`으로 바꾸고 app-bootstrap에서 명시 import한다.
2. properties를 nested record/class로 나누고 binder가 실제로 사용하는 default를 선언한다.
3. framework `GraphQlProperties`를 SSOT로 삼거나 custom flag와의 불일치를 startup failure로 만든다.
4. startup validator는 실제 주입된 pipeline, scalar manifest, client policies, key ring을 검증한다.
5. `ApplicationContextRunner`로 enabled/disabled/servlet/reactive/unsafe override matrix를 고정한다.
**필수 테스트**
- 아무 `backend.graphql.*`도 없는 context가 safe default로 부팅한다.
- custom/framework GraphiQL·introspection 값이 모순되면 부팅 실패한다.
- unsafe custom pipeline override가 startup에서 거부된다.
- auto-configuration imports와 configuration metadata에 모든 property가 존재한다.
### GQL-004 — 하나의 artifact가 servlet runtime을 강제하면서 reactive profile도 표방한다
**근거**
- `build.gradle:19-20`은 `spring-boot-starter-web`을 production implementation으로 둔다.
- WebFlux는 `compileOnly`라 reactive runtime에는 없다.
- `GraphQlWebFluxAutoConfiguration`은 application이 이미 reactive일 때만 활성화된다.
- reactive config는 blocking executor를 `Mono.fromCallable`로 감싸며 scheduler를 바꾸지 않는다.
- request validator 기본 bean은 MVC config 안에 있어 reactive context에서 기본 생성되지 않는다.
**구현 결정**
공통 artifact에 두 runtime을 넣지 말고 다음처럼 나눈다.
- `graphql-spring-execution`: Spring GraphQL execution/interceptor/wiring. servlet/reactive server 없음.
- `graphql-transport-mvc`: 위 leaf + `starter-web`.
- `graphql-transport-webflux`: 위 leaf + `starter-webflux`.
reactive profile은 `GraphQlReactiveExecutor` 전용 interface를 필수로 한다. blocking bridge가 필요하면 명시적
opt-in, bounded scheduler, bulkhead, lifecycle bean과 thread assertion을 함께 둔다.
### GQL-005 — request limit은 역직렬화 전에 강제되지 않고 valid null variable은 NPE가 된다
**근거**
- size policy 메서드는 존재하지만 custom adapters는 `validateEnvelope()`만 호출한다.
- 이미 materialized된 `GraphQlHttpRequestEnvelope`를 받으므로 JSON allocation 전 body cap을 적용할 수 없다.
- `GraphQlHttpRequestEnvelope:23-25`는 variables/extensions에 `Map.copyOf`를 사용해 null value를 거부한다.
- nested map/list는 shallow copy여서 생성 뒤 mutation 가능한 TOCTOU도 남는다.
**구현 결정**
1. servlet filter/reactive web filter 또는 bounded decoder에서 raw HTTP body byte cap을 먼저 적용한다.
2. JSON decode 후 query/variables/extensions를 UTF-8 byte 기준으로 검증한다.
3. variables/extensions는 null-preserving deep immutable JSON value copy를 사용한다.
4. JSON nesting, object key 수, list length에도 별도 bound를 둔다.
**필수 테스트**
- ASCII와 다중바이트 UTF-8의 exact limit/limit+1.
- variables의 omitted/explicit null/non-null 세 의미가 actual coercion까지 보존됨.
- nested original map/list 변경이 envelope에 반영되지 않음.
- oversize body는 decoder/controller/use case 0회와 413.
### GQL-006 — `Accept` 협상에서 client priority와 명시적 거부를 무시한다
**근거**
`GraphQlMediaTypes:45-63`은 parameter를 제거하고 server preference를 먼저 순회한다. 따라서
`application/graphql-response+json;q=0, application/json;q=1`에도 q=0인 첫 media type을 반환한다.
**구현 결정**
Spring `MediaType.parseMediaTypes`로 parse하고 quality/specificity를 정렬한 뒤 q=0을 제외한다. GraphQL
over HTTP profile이 생산 가능한 두 type과 client order의 교집합을 선택하고, malformed/empty/wildcard
정책을 명시한다. GraphQL over HTTP draft도 client가 제시한 우선순위를 존중하도록 요구한다
([GraphQL over HTTP draft](https://graphql.github.io/graphql-over-http/draft/)).
### GQL-007 — named fragment가 custom introspection gate를 우회하고 variable 입력 구조는 측정하지 않는다
**근거**
- `GraphQlDocumentShapeAnalyzer:96-132`의 introspection walk는 Field/InlineFragment만 처리한다.
- 같은 class의 일반 shape walk는 `FragmentSpread`와 cycle path를 처리한다.
- input nesting은 document literal만 보며 variables JSON은 보지 않는다.
- analyzer는 selected operation이 아니라 document의 모든 operation을 합산한다.
**실패 입력**
```graphql
query Q { ...I }
fragment I on Query { __schema { types { name } } }
```
**구현 결정**
operationName으로 선택한 operation과 reachable fragment graph만 하나의 budgeted walker가 순회하도록
합친다. introspection은 custom walker 하나만 신뢰하지 말고 engine validation/field visibility에서도
차단한다. variables는 streaming JSON constraint로 별도 제한한다.
### GQL-008 — resolver 경계 검사는 실제 adopter graph를 보지 못하고 valid reactive query도 거부한다
**근거**
- controller inspector와 boundary rules는 raw `Class<?>`만 검사해 `List<Entity>`, `Mono<Entity>`,
`Optional<Repository>`의 generic 내부 타입을 놓친다.
- package scan은 `file:` protocol, 직접 자식 `.class`만 지원해 JAR/subpackage를 건너뛴다.
- tests는 고정 fixture package만 직접 호출하고 production startup caller가 없다.
- `Publisher`를 subscription 외에서 모두 거부하지만 Spring GraphQL controller는 Query/Mutation에서
`Mono<T>`와 async return을 지원한다
([Spring GraphQL annotated controllers](https://docs.spring.io/spring-graphql/reference/controllers.html)).
**구현 결정**
1. build-time에는 ArchUnit/bytecode scan으로 실제 configured controller packages를 재귀 검사한다.
2. runtime에는 ApplicationContext의 실제 GraphQL controller bean/method를 startup 검사한다.
3. Java `Type`을 재귀 순회해 parameterized/array/wildcard/type-variable bound를 본다.
4. Query/Mutation에는 single-value async(`Mono`, `CompletionStage`)를 허용하고 multi-value publisher만
Subscription에 제한한다.
5. suffix-only `Repository/Dao` 휴리스틱은 보조 신호로 낮추고 package/assignability/annotation 증거를 쓴다.
### GQL-009 — repository 자동 노출은 Advanced여도 이 저장소의 Clean Architecture를 위반한다
**근거**
`advanced/compat/GraphQlRepositoryExposureValidator`와 `GraphQlRepositoryAllowlist`는 allowlisted
repository가 GraphQL field를 직접 back하는 경로를 지원하고 test도 이를 정상으로 고정한다. 현재 실제
controller가 repository를 직접 호출하는 위반은 없지만, 지원 계약 자체가 root HARD-STOP과 충돌한다.
**구현 결정**
- `SPRING_DATA_COMPAT`, repository exposure API와 정상 test를 제거한다.
- 자동 resolver 대상은 application query/use-case handler로 한정한다.
- 생성 resolver의 constructor/field/method generic graph에 repository, Spring Data interface,
persistence entity가 있으면 allowlist와 무관하게 실패한다.
- business transaction과 authorization은 application use case에 남긴다.
### GQL-010 — cursor는 서명되지만 정상 payload가 round-trip되지 않고 rotation/scope 검증도 불완전하다
**근거**
- keyset은 `;`, `=`, `|`, `\`를 escape하지만 decoder는 escape-aware하지 않은 `split`을 먼저 한다.
- queryProfile/filterFingerprint/keyId는 escape조차 하지 않는다.
- `GraphQlCursorKeyRing.activeKeyId()`는 production/test에서 사용처가 없고 기본 factory는 항상
`cursor-key-1`을 payload에 넣는다.
- connection request decode는 payload direction과 request direction을 비교하지 않는다.
- cursor는 tenant/actor scope를 bind하지 않는다.
- `keyIds()`는 mutable backing key set을 반환한다.
**구현 결정: versioned Codec Strategy**
1. v2 payload를 canonical JSON/CBOR 또는 length-prefixed typed framing으로 만든다.
2. codec이 active key id를 선택하고 envelope에 기록한다. caller payload가 signing key를 선택하지 않는다.
3. decode 입력에 expected query/filter/direction/tenant-scope fingerprint를 포함한다.
4. v1 decode를 migration 기간에만 유지하고 v2만 발급한다.
5. key ring map/key set을 완전 불변으로 만들고 secret clone은 유지한다.
6. `forTests()`와 fixed secret은 test fixtures로 이동한다.
**필수 property tests**
- 모든 string field의 delimiter/backslash/unicode round-trip.
- active key2로 신규 발급, key1 과거 cursor 검증, unknown/retired key 거부.
- forward↔backward, tenant A↔B, filter/query 변경 거부.
- tamper, truncation, oversized token, malformed Base64 거부.
### GQL-011 — mutation fingerprint canonical form이 충돌하고 tenant를 scope에 포함하지 않는다
**근거**
`GraphQlMutationFingerprint:29-33`은 top-level key만 정렬해 `key=value;`를 연결한다. 예를 들어
`{a:"b;c=d"}``{a:"b", c:"d"}`가 같은 canonical text가 된다. nested map은 재귀 정렬되지 않는다.
idempotency scope는 actor/coordinate/key만 포함하고 tenant와 contract version은 없다.
**구현 결정**
- recursive key sorting, JSON type, length framing, null/number normalization을 가진 canonical serializer를
하나의 port/service로 둔다.
- tenant fingerprint와 contract version을 scope에 포함한다.
- actor/tenant의 단순 SHA-256 prefix를 비가역이라고 부르지 않는다. 저엔트로피 identifier에는
rotation 가능한 HMAC fingerprint를 사용하고 metric label에는 넣지 않는다.
- `requireSingleUseCase`는 정확히 1을 요구하거나 실제 architecture gate로 교체한다.
### GQL-012 — error contract가 두 resolver와 여러 category vocabulary로 분기한다
**근거**
- root `GraphqlExceptionResolver`만 실제 Spring `DataFetcherExceptionResolverAdapter``@Component`다.
- `error/GraphQlExceptionResolver`는 richer masking/mapping을 제공하지만 Spring path에 연결되지 않는다.
- auth/cursor/idempotency/batch/timeout 예외의 code/category/retryable/executionId 계약이 경로마다 다르다.
- 대소문자만 다른 두 class 이름은 import 실수를 유발한다.
**구현 결정: Mapper + Adapter**
`GraphQlWireErrorMapper`를 canonical pure mapper로 두고 `GraphQlDataFetcherExceptionResolver`가 Spring
`GraphQLError`로 adapt한다. request-level HTTP failure와 field failure는 별도 strategy를 쓰되 code,
category, retryability, masking catalog는 공유한다. unknown failure의 raw message는 어떤 path에서도
노출하지 않는다.
### GQL-013 — persisted operation admin은 존재하지 않는 변경을 성공으로 audit할 수 있다
**근거**
- in-memory registry의 absent `updateStatus`는 no-op인데 admin service는 `ABSENT→BLOCKED/DEPRECATED` audit을 남긴다.
- `remove()`는 실제 삭제가 아니라 BLOCKED 전환이다.
- BLOCKED에서 DEPRECATED로 바꿔 다시 executable하게 만들 수 있는 transition guard가 없다.
- raw operator 문자열 allowlist를 받고 credential kind 거부 메서드는 service가 호출하지 않는다.
- registry 변경과 in-memory `ArrayList` audit은 원자적이지 않고 thread-safe하지 않다.
**구현 결정: State + authenticated command + durable transaction**
1. transport가 만든 `GraphQlAdminPrincipal`만 service에 전달한다.
2. lifecycle transition table을 두고 BLOCKED는 explicit audited unblock 전까지 terminal로 취급한다.
3. registry command는 updated record/version을 반환하거나 not-found/conflict를 던진다.
4. mutation과 audit append를 하나의 durable transactional port로 묶는다.
5. soft delete가 의도면 `remove``retireAndBlock`으로 이름 바꾼다.
### GQL-014 — codegen validator는 operation document를 읽지 않고 generator는 source를 만들지 않는다
**근거**
`GraphQlClientOperationGenerator.validateOperation`은 nonblank만 확인한 뒤 schema를 자기 자신과 비교한다.
`operationDocument`는 검증에 쓰지 않는다. invalid syntax나 unknown field operation이 통과한다. 다른
generator/factory도 실제 handler/source가 아니라 metadata set/report만 반환하는 사례가 많다.
**구현 결정**
- schema를 executable schema로 만들고 GraphQL Java parser/validator로 selected operation을 검증한다.
- 실제 source writer/Gradle task가 없다면 class/package를 `codegen-plan` 또는 `compatibility-policy`
정직하게 이름 바꾼다.
- invalid syntax, unknown field/argument/type, operation name ambiguity, valid fragment operation을 테스트한다.
### GQL-015 — schema compatibility와 ownership이 breaking change를 놓친다
**근거**
- 동일 이름의 `type Foo``input Foo` 같은 kind change를 먼저 비교하지 않는다.
- 기존 argument/input field의 default 추가·제거·변경을 비교하지 않는다.
- `extend type/interface/input/enum/union`의 field/member ownership과 duplicate를 충분히 기록하지 않는다.
- applied directive 변경이 아니라 directive definition만 비교한다.
- scalar SDL print 차이를 coercion change라 부르지만 실제 `Coercing` 구현 교체는 보지 못하고 description
변화는 오탐할 수 있다.
**구현 결정**
1. registry를 extension까지 normalize하거나 executable schema로 compile한 canonical model을 비교한다.
2. `TYPE_KIND_CHANGED`, `INPUT_DEFAULT_REMOVED/CHANGED/ADDED`, applied-directive change를 명시한다.
3. nested list/non-null 변화는 input/output position별 방향성을 재귀 분류한다.
4. scalar coercion compatibility는 SDL이 아니라 scalar manifest codec/version 계약으로 분리한다.
### GQL-016 — custom DataLoader contract는 실제 N+1과 timeout을 보장하지 않는다
**근거**
- `GraphQlDataLoaderRequestRegistry``Object` map이며 Spring/Java DataLoader registry에 연결되지 않는다.
- contract suite는 caller가 전달한 observed query count를 검사하고 test는 임의 숫자 1/2를 넘긴다.
- batch executor는 synchronous chunk 호출 전에만 시간을 보고 long/final chunk를 중단하지 못한다.
- mapped loader의 null은 `Present(null)`, ordered loader의 null은 `Missing`으로 해석되어 의미가 다르다.
- result cardinality가 같아도 requested key 대신 다른 key가 들어간 map을 검출하지 못한다.
**구현 결정: Spring registry adapter + Decorator**
Spring `BatchLoaderRegistry`에 실제 loader를 등록하고 chunk/timeout/auth scope/observation을 loader decorator로
적용한다. loader는 `CompletionStage`/`Mono`로 deadline/cancellation을 전달한다. null 의미는 하나로
정하고 requested key set/cardinality를 검증한다.
**필수 E2E**
- 50개 parent/child query의 fake application port 호출이 1회 또는 bounded chunk 수.
- request 간 cache 비공유, 같은 request duplicate key dedupe, actor/tenant scope 분리.
- never-completing loader timeout/cancel, 첫 chunk budget 소진 뒤 다음 chunk 0회.
- missing/failed/null/wrong-key map 계약.
### GQL-017 — MVC는 bounded라고 설명하지만 concurrency/queue가 unbounded이고 context도 전달하지 않는다
**근거**
- virtual-thread-per-task executor는 task admission을 제한하지 않는다.
- fixed thread pool은 기본 unbounded `LinkedBlockingQueue`를 사용한다.
- MVC adapter가 submit한 task를 `GraphQlContextPropagator.wrap`으로 감싸지 않는다.
- WebFlux blocking fallback은 `subscribeOn`이 없어 subscriber/event-loop thread에서 실행될 수 있다.
**구현 결정**
- executor 앞에 semaphore/bulkhead 또는 bounded `ThreadPoolExecutor` queue/rejection을 둔다.
- Spring GraphQL annotated controller executor를 canonical하게 구성해 double scheduling/wait을 피한다.
- context는 ThreadLocal만 믿지 말고 GraphQLContext/Reactor Context를 SSOT로 삼고 blocking bridge에서만
snapshot/wrap한다.
- timeout은 interrupt가 아니라 downstream deadline propagation과 함께 검증한다.
### GQL-018 — pipeline stage 순서는 authorization에 필요한 정보를 만들기 전에 authorize한다
**근거**
pipeline은 `AUTHORIZATION``PARSE_VALIDATE`보다 앞에 두지만 field authorization은 schema coordinate와
selected operation을 필요로 한다. 현재 pipeline이 실행되지 않아 장애는 잠복해 있지만 그대로 wiring할
수 없는 순서다.
**구현 결정: 실제 Chain of Responsibility**
```text
authenticate transport principal
→ create request context
→ persisted lookup / raw document admission
→ parse + validate + select operation
→ document/coordinate authorization
→ structural + complexity budget
→ execute + field/object authorization + DataLoader
→ map errors + observe + cleanup
```
각 handler는 입력 상태와 산출 상태를 typed record로 표현하고, 필요한 이전 stage가 없으면 compile-time 또는
startup validation에서 실패하게 한다.
### GQL-019 — subscription/replay/drain policy는 concurrent runtime state machine이 아니다
**근거**
- replay cursor는 expected subscription과 tenant를 검증하지 않는다.
- subscription event byte estimate는 실제 serialized bytes가 아니라 `payload.toString()`을 사용한다.
- drain coordinator는 draining check와 registration increment 사이 race가 있고, state publication 순서에
따라 startedAt을 null로 볼 수 있다.
- cancellation/listener collections와 protocol lifecycle의 thread-safety/ownership이 명시되지 않았다.
- WebSocket/SSE/RSocket “handler factory”는 실제 Spring transport handler가 아니라 policy 객체를 반환한다.
**구현 결정**
atomic immutable state 또는 lock-protected State pattern으로 `ACCEPTING→DRAINING→CLOSED`를 모델링한다.
registration은 lease를 받아 close 시 release한다. replay cursor는 actor+tenant+subscription profile에
bind한다. queue byte bound는 실제 serializer 결과로 계산한다. 실제 handler가 없으면 factory 명명과
지원 등급을 policy/catalog로 낮춘다.
### GQL-020 — preparsed cache의 expiry policy가 사용되지 않고 unrelated miss가 직렬화된다
`GraphQlPreparsedCachePolicy`의 expire-after-access 값은 provider에서 사용되지 않는다. cache miss parse가
synchronized block 안에서 실행되어 서로 다른 document도 직렬화된다. injected Clock/Ticker를 쓰는 bounded
cache와 per-key single-flight를 적용하고 expiry/access-refresh/same-key-once/different-key-parallel을 테스트한다.
### GQL-021 — cancellation hook 하나의 실패가 나머지 cleanup을 막는다
request/subscription cancellation listener loop가 exception을 aggregate하지 않는다. 세 hook 중 두 번째가
throw해도 세 개 모두 정확히 한 번 실행하고 첫 실패에 나머지를 suppressed로 붙이는 공통 cancellation
primitive로 합친다. 이미 `GraphQlContextCleanup`이 가진 all-cleanups 실행 의미를 재사용한다.
### GQL-022 — scalar input/output limit이 대칭이 아니고 작은 입력이 큰 출력을 만들 수 있다
- BigDecimal은 precision/scale/exponent/serialized length 제한 없이 parse 후 `toPlainString()`을 사용한다.
작은 `1E+1000000`이 매우 큰 output allocation을 만들 수 있다.
- custom Long scalar는 parse에 configured min/max를 적용하지만 serialize/valueToLiteral은 그 범위를 무시한다.
lexical length, precision, absolute scale, output length를 먼저 제한하고 Long의 input/output에 같은 range를
적용한다. coercion error에는 raw input을 포함하지 않는 기존 원칙을 유지한다.
### GQL-023 — operation name을 low-cardinality tag라고 가정할 수 없다
operation name은 길이/문법만 제한되어 client가 매번 임의 이름을 만들 수 있고 observation convention은 raw
name을 tag로 사용한다. 실제 Micrometer/Spring Observation interface 연결도 없다. persisted/registered
operation만 이름 tag로 사용하고 나머지는 `other`로 collapse하거나 production에서 anonymous/unregistered
operation을 거부한다. 10,000개 임의 name을 actual MeterRegistry에 넣어 series bound를 검증한다.
### GQL-024 — production jar가 testkit, fixed secret, in-memory development 구현을 함께 배포한다
main source에는 `testkit` 12개 class, `GraphQlConnectionAssembler.forTests()`의 fixed signing secret,
`GraphQlAuthenticationContextFactory.testContext`, test error context, in-memory persisted registry가 있다.
`java-test-fixtures` 또는 별도 `graphql-testkit` leaf로 옮기고 production jar에 `.testkit.`, `forTests`,
`testContext`, fixed secret이 없는 jar content gate를 둔다.
### GQL-025 — 373개 public 중심 type과 Stable/Advanced/testkit/release의 한 jar 결합은 변경 비용이 크다
package import graph에 명백한 cycle이 없는 방향성은 좋지만 package만으로 외부 API와 classpath isolation을
보장하지 못한다. 이번 ignored boundary package가 그 취약성을 실제로 보여 줬다. package-private를 default로
하고 explicit `api`/`spi`만 public으로 허용하는 API surface snapshot을 둔다. Gradle 분리는 28개를 한 번에
늘리지 않고 §7의 6~8개 capability 단위로 진행한다.
### GQL-026 — GraphQL request context와 storage SPI가 inbound에 있어 downstream 구현 방향과 충돌한다
문서는 `GraphQlRequestContext`/deadline을 application/JPA/Mongo/HTTP client까지 전달하고 persisted registry를
외부 durable store가 구현한다고 설명한다. application/outbound가 inbound leaf type을 구현하면 의존 방향이
뒤집힌다.
- GraphQL context는 inbound-local로 유지하고 application command의 actor/tenant/deadline 값으로 명시 매핑한다.
- object authorization은 application-core의 transport-neutral use case로 두고 GraphQL bridge가 호출한다.
- persisted operation 저장은 generic operational store/cache port를 neutral contract owner에 두고 GraphQL
adapter가 key/value mapping만 소유한다. inbound→outbound 직접 edge는 만들지 않는다.
- composition root는 연결만 하고 business/storage policy를 소유하지 않는다.
### GQL-027 — 문서와 실제 설정·테스트·지원 등급이 drift했다
- README는 custom `@ConfigurationProperties`가 없다고 하지만 `backend.graphql` properties가 있다.
- build comment는 `spring.graphql.platform.*`를 언급하지만 실제 prefix는 `backend.graphql`이다.
- CLAUDE/README는 누락된 boundary classes/tests가 있다고 기록한다.
- “더 이상 미구현이 아니다”라는 표현은 policy object 존재와 runtime integration을 구분하지 않는다.
- test count는 compile이 깨진 현재 실행 증거가 아니라 과거/문서 count다.
generated configuration metadata, actual bean inventory, random-port adoption test, task JUnit XML에서 문서를
생성/검증한다. capability마다 `modelled`, `wired`, `integration-verified`, `production-verified`를 분리하고
현재 수준 이상으로 표현하지 않는다.
## 6. 디자인 패턴 적용 제안
### 6.1 적용할 패턴
| 위치 | 패턴 | 적용 형태 | 해결하는 문제 |
|---|---|---|---|
| 실행 pipeline | Chain of Responsibility | typed stage handler + actual Spring execution decorator | stage 목록만 있고 실행되지 않는 문제 |
| Spring integration | Adapter | pure policy를 interceptor/instrumentation/wiring으로 변환 | framework-free core와 runtime 연결 분리 |
| transport | Strategy | MVC/WebFlux leaf별 transport strategy | 두 runtime classpath와 blocking policy 혼합 제거 |
| DataLoader | Decorator | loader에 chunk/deadline/auth/observation을 조합 | 병렬 custom framework와 정책 중복 제거 |
| error | Mapper + Adapter | pure wire-error mapper + Spring resolver | 두 resolver/category drift 제거 |
| cursor | Versioned Codec Strategy | v1 read/v2 write codec과 key-ring signer | framing migration과 rotation 분리 |
| persisted/admin/subscription | State | 허용 transition과 CAS version 명시 | blocked 재활성, drain race, 허위 audit 제거 |
| application 경계 | Anti-Corruption Mapper | GraphQL context/input → command/context | transport DTO/application leakage 방지 |
| configuration | Validated Plan/Builder | bind → aggregate validate → immutable runtime plan | resource 생성 뒤 validation과 inert setting 제거 |
### 6.2 피할 패턴
- `Factory`, `Generator`, `Interceptor`라는 이름만 붙이고 metadata/policy 객체만 반환하지 않는다.
- 28개 설계상 “모듈”을 근거 없이 28개 Gradle leaf로 기계 분해하지 않는다.
- controller/router와 Spring 기본 endpoint를 병렬로 유지하지 않는다.
- custom DataLoader, custom preparsed cache, custom transport를 framework가 제공하는 extension point와 경쟁시키지 않는다.
- architecture rule을 runtime reflection suffix 검사 하나로만 강제하지 않는다.
- Advanced라는 이유로 repository/use-case 경계를 완화하지 않는다.
## 7. 권장 Gradle·폴더 구조
### 7.1 대안 비교
| 대안 | 장점 | 단점 | 판정 |
|---|---|---|---|
| A. 현재 단일 leaf 유지 + 경계 test 복구 | 가장 빠름, registry 변경 최소 | public/classpath/runtime 결합 유지 | GQL-001 응급 복구용 |
| B. 6~8 capability leaf로 단계 분리 | 실제 runtime 책임과 dependency를 격리 | registry/settings/lock/CI 갱신 필요 | **권장** |
| C. 설계의 28 package를 28 leaf로 분리 | 가장 강한 compile boundary | Gradle/lock/CI 비용과 빈 facade 증가 | 현재 과도함 |
### 7.2 권장 target
```text
graphql-platform-core
src/main/java/.../graphql/core/api
src/main/java/.../graphql/core/policy
# pure Java, framework/transport/application type 없음
graphql-schema
src/main/java/.../graphql/schema
src/main/java/.../graphql/scalar
src/main/java/.../graphql/compat
# GraphQL Java AST/wiring, no web server
graphql-spring-execution
src/main/java/.../graphql/execution
src/main/java/.../graphql/security
src/main/java/.../graphql/error
src/main/java/.../graphql/dataloader
src/main/java/.../graphql/autoconfigure
# application-core bridge + Spring GraphQL extension points
graphql-transport-mvc
src/main/java/.../graphql/http/mvc
# starter-web only
graphql-transport-webflux
src/main/java/.../graphql/http/webflux
# starter-webflux only
graphql-advanced
src/main/java/.../graphql/advanced/{persisted,subscription,federation,...}
# 실제 wired capability만 opt-in; feature가 커지면 사용 단위별 추가 분리
graphql-testkit
src/testFixtures/java 또는 전용 leaf
graphql-release-verification
# Gradle/build logic와 evidence manifest, production runtime에 포함하지 않음
```
허용 방향의 기본안은 다음과 같다.
```text
transport-mvc/webflux → spring-execution → schema → platform-core
spring-execution → application-core → domain-core
advanced → spring-execution/schema/platform-core
testkit → 공개 api/spi만
release-verification → 각 leaf의 test/evidence artifact만
```
실제 edge와 runtime membership은 반드시 `src/config/architecture/modules.json`에 먼저 등록하고 같은 SSOT의
Gradle gate로 검증한다. `graphql-persisted-<database>`가 inbound contract에 역의존하는 구조는 만들지 않는다.
### 7.3 package visibility
- public 허용: 외부 resolver/adopter가 구현·호출해야 하는 `api`, `spi`, configuration properties.
- package-private/internal: calculator, parser walker, state transition, mapper implementation, factory implementation.
- test-only: fixture, fake/in-memory, fixed key/principal/context, contract assertion helper.
- public API snapshot에는 FQCN, constructor/method signature, stability level을 기록한다.
## 8. 구현 순서 — 그대로 issue/PR로 분리 가능한 단위
### Wave 0 — build와 증거 복구
1. **PR GQL-001A**: ignored package red test와 `moduleboundary` package 복구.
2. **PR GQL-001B**: module boundary negative fixtures와 required FQCN/lane 연결.
3. focused test, Stable/contract/Advanced lane을 실행한다. 여기서 발견되는 test failure는 다음 wave의
characterization backlog로 분리한다.
### Wave 1 — 실제 endpoint baseline
1. 현재 기본 `/graphql`에 health query를 보내는 full configuration test를 만든다.
2. cost/auth/DataLoader/custom adapter bean이 존재하지만 호출되지 않는 현재 상태를 failing test로 증명한다.
3. Spring-native endpoint를 canonical로 확정하고 custom transport dead path를 제거한다.
4. auto-configuration imports, binder defaults, framework property cross-check를 추가한다.
### Wave 2 — executable pipeline
1. request context interceptor.
2. parse/select/introspection/cost/auth instrumentation/decorator.
3. scalar/preparsed/DataLoader/error wiring.
4. actual observation과 cleanup/cancellation.
5. servlet random-port qualification을 platform adoption test로 교체한다.
### Wave 3 — correctness/security
서로 독립인 작은 PR로 다음을 처리한다.
- null-preserving request JSON + byte/nesting limit.
- Accept negotiation.
- fragment introspection/selected-operation analyzer.
- cursor v2/rotation/direction/tenant.
- mutation canonical fingerprint/tenant.
- schema kind/default/extensions/directives.
- scalar bounds.
- persisted admin/subscription state machines.
각 PR은 먼저 failing unit/property/integration test를 추가한다.
### Wave 4 — architecture와 모듈 분리
1. actual controller generic/bytecode gate와 application import gate.
2. repository exposure capability 제거.
3. testkit/fixed/in-memory API를 test fixtures로 이동.
4. `platform-core`, `schema`, `spring-execution` 추출.
5. MVC/WebFlux leaf 분리와 runtime classpath tests.
6. Advanced/release verification을 runtime jar에서 분리.
7. public API snapshot과 package-private 축소.
### Wave 5 — Advanced promotion
각 capability는 다음 네 증거가 모두 있을 때만 `wired` 이상으로 승격한다.
1. 실제 Spring handler/extension point가 존재한다.
2. real request 또는 protocol-level integration test가 해당 path를 호출한다.
3. disabled 상태에서 bean/resource/route가 0개다.
4. restart/concurrency/fault가 필요한 stateful capability는 durable evidence가 있다.
codegen/federation/subscription/persisted operation이 이 기준을 못 채우면 policy/catalog로 이름과 문서를
낮추고 production support claim을 하지 않는다. Spring GraphQL은 federation에 `@EntityMapping`을 포함한
공식 통합을 제공하므로 별도 facade보다 이를 우선 검토한다
([Spring GraphQL federation](https://docs.spring.io/spring-graphql/reference/federation.html)).
## 9. 테스트 전략과 Definition of Done
### 9.1 최소 테스트 피라미드
| 계층 | 테스트 | 핵심 assertion |
|---|---|---|
| pure policy | unit + property | canonicalization, bounds, transition, deterministic output |
| Spring composition | `ApplicationContextRunner` | enabled/disabled, bean exact set, unsafe config failure |
| schema/execution | `ExecutionGraphQlServiceTester` | scalar, parse, validation, error path, DataLoader |
| transport | random-port MVC/WebFlux | media type, auth, body cap, actual policy rejection |
| architecture | ArchUnit/bytecode + Gradle edge | generic DTO/entity/repository, package/leaf edge, public API |
| stateful Advanced | concurrency/restart/store integration | CAS/fencing/audit/replay/durable transition |
| release | same-SHA evidence manifest | 실행한 lane/version/scenario와 지원 문서 일치 |
### 9.2 전체 완료 조건
- `:adapter:inbound:graphql:compileJava`, focused `test`, Stable/contract/Advanced lane이 실행되고 green이다.
- performance lane이 필요한 support claim은 실제 tagged scenario/evidence 없이는 승격되지 않는다.
- real `/graphql` E2E에서 모든 mandatory policy가 최소 한 번 차단/허용 경로를 가진다.
- GraphQL DTO/context/framework type이 application/domain에 유출되지 않는다.
- controller/resolver가 repository, persistence entity, transaction을 직접 소유하지 않는다.
- MVC/WebFlux runtime dependency가 서로의 server stack을 끌어오지 않는다.
- production jar에 testkit/fixed secret/in-memory development facade가 없다.
- public API와 capability support status가 snapshot/manifest로 검증된다.
- docs의 property name/test count/support status는 generated metadata와 JUnit evidence에서 파생된다.
## 10. 이번 리뷰에서 실행한 검증
### 성공
```bash
cd src
./gradlew verifyCleanArchitectureDependencies --console=plain
```
- current HEAD fresh run은 `BUILD SUCCESSFUL in 769ms`, 1 actionable task executed였다.
- 이 결과는 registry에 선언된 project dependency edge가 맞다는 증거다.
- 누락된 내부 package boundary, runtime wiring, correctness를 승인하는 증거는 아니다.
### 실패
```bash
cd src
./gradlew :adapter:inbound:graphql:compileJava --console=plain
./gradlew :adapter:inbound:graphql:test --console=plain
```
- direct `compileJava`의 current HEAD fresh 재현은 `BUILD FAILED in 1s`, 7 errors였다
(직전 첫 재현도 같은 7 errors, 7s).
- focused `test`도 같은 `compileJava` 단계에서 실패했다.
- 누락 package: `dev.caskeleton.adapter.inbound.graphql.build`.
- 참조 파일: `GraphQlPlatformAutoConfiguration`, `GraphQlAdvancedDependencyRules`.
- test 75개는 실행 단계에 진입하지 못했다.
### 정적 재현
```bash
git check-ignore -v --no-index \
src/adapter/inbound/graphql/src/main/java/dev/caskeleton/adapter/inbound/graphql/build/GraphQlBuildModel.java
```
- `src/.gitignore:2:build/`이 반환되어 누락 source package와 ignore rule의 충돌을 확인했다.
### 미실행
- `graphqlStableTest`, `graphqlContractTest`, `graphqlAdvancedTest`: 동일 compile blocker 때문에 실행 불가.
- `graphqlPerformanceTest`: compile blocker에 더해 실제 tagged load/fault scenario가 없는 상태.
- repository 전체 `test`/`check`: review-only 범위이며 focused compile blocker가 먼저 존재한다.
- production adopter, actual feature schema, JPA/Mongo query-count, real WebSocket/SSE/RSocket, load/soak/fault.
## 11. 남은 위험과 판정 범위
- GraphQL leaf는 현재 두 composition root runtime에 포함되지 않으므로 발견 사항을 현 서비스의 즉시 runtime
장애로 확대하지 않는다.
- 반대로 빈 runtime membership은 adopter 안전성의 증거도 아니다. opt-in 직후 compile/auto-config/runtime
wiring 문제가 드러난다.
- build blocker가 해결되면 지금까지 실행되지 못한 524 test annotation에서 추가 failure가 나올 수 있다.
- Advanced 130개 production class의 모든 concurrent/protocol path를 실환경에서 검증하지 않았다.
- 공식 Spring GraphQL extension point 선택은 타당하지만 정확한 Boot 4.0.0/Spring GraphQL 2.0.0 API
signature와 auto-configuration ordering은 구현 시 lock 기준으로 확인해야 한다.
- 이 리뷰의 `FACT`는 명시한 source/command에 한정되고, target module split은 그 사실에서 도출한
`INFERENCE/권고`다. registry 변경 전에 별도 설계 문서와 실행 계획을 남겨야 한다.
최종 판정은 **CHANGES REQUIRED**다. 구현 순서는 `GQL-001 → GQL-002/003 → GQL-005~018 →
GQL-024~026 → Advanced promotion`을 권장한다.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -25,8 +25,12 @@ status: stub
### Step 1 — 확인
1. ERROR log에서 `OUTBOX_PUBLISH_FAILED` 라인 확인: `event_type`, `event_id`, `correlation_id`, `attempt_count` 추출
2. broker(기본 Kafka adapter) 상태 확인: `APP_MESSAGING_KAFKA_ENABLED`과 broker endpoint 가용성
- Kafka disabled(default) 상태에서 outbox 이벤트가 append 되고 있으면 publish 경로가 `AdapterDisabledException`으로 전부 실패하는 구성 오류 — 이 경우 producer use case 쪽 활성화/구성을 먼저 의심
2. broker 상태 확인: `APP_MESSAGING_BROKER` 값(공백이면 messaging 비활성)과 broker endpoint 가용성
- `APP_MESSAGING_BROKER`가 공백인 채로 relay가 켜져 있으면 **애플리케이션이 기동하지 않는다**
(`OutboxRelayBrokerRequirementValidator`, MSG-024). 이 조합에서는 publish가 전부
`AdapterDisabledException`으로 실패하며 PENDING row가 DEAD까지 소진되기 때문이다.
기동 실패를 보고 있다면 broker를 설정하거나 `ca-skeleton.outbox.relay-enabled=false`로 둔다.
- 기동은 했는데 실패가 쌓인다면 broker는 설정돼 있고 도달이 안 되는 것이다 — endpoint부터 본다.
3. `outbox.pending.size` status 분포 확인 (FAILED 누적 vs PENDING 누적)
### Step 2 — 임시 격리