feat: jpa, messaging, notification, mongo, graphql 어댑터터 구현체 추가
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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 이하여야 한다
|
||||
|
||||
@@ -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.
|
||||
@@ -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이 지울 때까지 쌓이기 때문이다.
|
||||
|
||||
|
||||
@@ -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 없음(플랫폼이 대행), 기본 비활성 |
|
||||
|
||||
@@ -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.
|
||||
@@ -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은 제거 계획이 생길 때 붙인다.
|
||||
@@ -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
|
||||
@@ -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).
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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` 한 곳에만 둔다.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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` →
|
||||
|
||||
@@ -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
@@ -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 — 임시 격리
|
||||
|
||||
Reference in New Issue
Block a user