Functorium의 Usecase Pipeline 시스템은 횡단 관심사(cross-cutting concerns)를 Mediator IPipelineBehavior<TRequest, TResponse> 체인으로 분리합니다. 이 문서는 8개 기본 Pipeline 동작, 커스텀 확장 포인트, PipelineConfigurator API, OpenTelemetry 설정 타입을 정의합니다.
타입 네임스페이스 설명 UsecasePipelineBase<TRequest>Functorium.Adapters.Pipelines모든 Pipeline의 공통 베이스 클래스 UsecaseMetricsPipeline<TRequest, TResponse>동일 메트릭 자동 수집 Pipeline UsecaseTracingPipeline<TRequest, TResponse>동일 분산 추적 Pipeline UsecaseLoggingPipeline<TRequest, TResponse>동일 구조화 로깅 Pipeline UsecaseValidationPipeline<TRequest, TResponse>동일 FluentValidation 검증 Pipeline UsecaseCachingPipeline<TRequest, TResponse>동일 Query 캐싱 Pipeline UsecaseExceptionPipeline<TRequest, TResponse>동일 예외 → FinResponse.Fail 변환 Pipeline UsecaseTransactionPipeline<TRequest, TResponse>동일 트랜잭션 + UoW + 도메인 이벤트 Pipeline ICustomUsecasePipeline동일 커스텀 Pipeline 마커 인터페이스 UsecaseMetricCustomPipelineBase<TRequest>동일 커스텀 메트릭 Pipeline 베이스 UsecaseTracingCustomPipelineBase<TRequest>동일 커스텀 트레이싱 Pipeline 베이스 PipelineConfiguratorFunctorium.Adapters.Observabilities.Builders.ConfiguratorsPipeline 활성화/비활성화 Fluent API OpenTelemetryBuilderFunctorium.Adapters.Observabilities.BuildersOpenTelemetry 설정 메인 Builder LoggingConfigurator동일(Configurators) Serilog 확장 설정 MetricsConfigurator동일(Configurators) Metrics 확장 설정 TracingConfigurator동일(Configurators) Tracing 확장 설정 OpenTelemetryOptionsFunctorium.Adapters.ObservabilitiesOTLP 엔드포인트/프로토콜 설정 ObservabilityNamingFunctorium.Adapters.Observabilities.Naming관측 가능성 네이밍 상수
Pipeline은 DI 등록 순서에 따라 바깥(Request 쪽)에서 안쪽(Handler 쪽)으로 실행됩니다. Command와 Query는 적용되는 Pipeline이 다릅니다.
Command 실행 순서:
Request → Metrics → Tracing → Logging → Validation → Exception → Transaction → Custom → Handler
Query 실행 순서:
Request → Metrics → Tracing → Logging → Validation → Caching → Exception → Custom → Handler
순서 Pipeline Command Query 설명 1 Metrics O O 요청/응답 수, 처리 시간 수집 2 Tracing O O Activity Span 생성 및 태그 기록 3 Logging O O 요청/응답 구조화 로깅 4 Validation O O FluentValidation 검증 5 Caching - O ICacheable 구현 시 IMemoryCache 캐싱6 Exception O O 예외 → FinResponse.Fail 변환 7 Transaction O - UoW.SaveChanges + 도메인 이벤트 발행 8 Custom O O 사용자 정의 Pipeline 9 Handler O O 실제 Usecase Handler
Transaction Pipeline은 where TRequest : ICommand<TResponse> 제약으로 Command에만 적용됩니다.
Caching Pipeline은 where TRequest : IQuery<TResponse> 제약으로 Query에만 적용됩니다.
모든 Pipeline이 상속하는 추상 베이스 클래스입니다. 요청 타입 분석, 핸들러 이름 추출, 에러 정보 추출 등 공통 유틸리티를 제공합니다.
public abstract partial class UsecasePipelineBase <TRequest>
메서드 반환 타입 설명 GetRequestCategoryType<T>(T request)string요청 인스턴스에서 CQRS 타입 식별 (command, query, unknown) GetRequestCategoryType(Type requestType)string요청 Type에서 CQRS 타입 식별 GetRequestHandlerPath()stringTRequest의 FullName 반환 (네임스페이스 포함 전체 경로)GetRequestHandler()stringTRequest의 FullName에서 핸들러 클래스 이름 추출GetRequestHandlerLower()stringGetRequestHandler()의 소문자 변환 (메트릭 네이밍용)GetErrorInfo(Error error)(string ErrorType, string ErrorCode)에러에서 타입/코드 정보 추출
GetRequestCategoryType은 ICommandRequest<> / IQueryRequest<> 인터페이스 구현 여부로 판별합니다.
GetRequestHandler()는 typeof(TRequest).FullName에서 중첩 타입(+)과 네임스페이스(.)를 파싱하여 클래스 이름만 추출합니다.
예외를 FinResponse.Fail로 변환하여 예외가 Pipeline 바깥으로 전파되지 않도록 합니다.
internal sealed class UsecaseExceptionPipeline <TRequest, TResponse>
: UsecasePipelineBase<TRequest>, IPipelineBehavior<TRequest, TResponse>
where TRequest : IMessage
where TResponse : IFinResponseFactory<TResponse>
항목 설명 제약 조건 TResponse : IFinResponseFactory<TResponse>동작 try-catch로 예외 포착 후 TResponse.CreateFail(AdapterError.FromException(...)) 반환에러 타입 AdapterErrorType.PipelineException
FluentValidation IValidator<TRequest>를 실행하여 검증 실패 시 FinResponse.Fail을 반환합니다.
internal sealed class UsecaseValidationPipeline <TRequest, TResponse>
: UsecasePipelineBase<TRequest>, IPipelineBehavior<TRequest, TResponse>
where TRequest : IMessage
where TResponse : IFinResponseFactory<TResponse>
항목 설명 DI 의존성 IEnumerable<IValidator<TRequest>>동작 Validator가 없으면 next() 통과, 있으면 모든 Validator 실행 에러 타입 AdapterErrorType.PipelineValidation(PropertyName)다중 에러 검증 실패가 2개 이상이면 Error.Many(errors) 반환
요청/응답 정보를 구조화 로깅으로 기록합니다. IUsecaseCtxEnricher가 DI에 등록되어 있으면 커스텀 속성을 자동 Push합니다.
internal sealed class UsecaseLoggingPipeline <TRequest, TResponse>
: UsecasePipelineBase<TRequest>, IPipelineBehavior<TRequest, TResponse>
where TRequest : IMessage
where TResponse : IFinResponse, IFinResponseFactory<TResponse>
항목 설명 DI 의존성 ILogger<UsecaseLoggingPipeline<TRequest, TResponse>>, IUsecaseCtxEnricher<TRequest, TResponse>? (선택)요청 로그 Information 레벨, {Layer} {Category} {CategoryType} {Handler} {Method} requesting 응답 로그 (성공) Information 레벨, responded success in {Elapsed:0.0000} ms 응답 로그 (Expected 에러) Warning 레벨, responded failure ... with {@Error} 응답 로그 (Exceptional 에러) Error 레벨, responded failure ... with {@Error}
OpenTelemetry ActivitySource를 사용하여 분산 추적 Span을 생성합니다.
internal sealed class UsecaseTracingPipeline <TRequest, TResponse>
: UsecasePipelineBase<TRequest>, IPipelineBehavior<TRequest, TResponse>
where TRequest : IMessage
where TResponse : IFinResponse, IFinResponseFactory<TResponse>
항목 설명 DI 의존성 ActivitySourceSpan 이름 {layer} {category}.{categoryType} {handler}.{method}ActivityKind Internal요청 태그 request.layer, request.category.name, request.category.type, request.handler.name, request.handler.method응답 태그 (성공) response.status = success, ActivityStatusCode.Ok응답 태그 (실패) response.status = failure, error.type, error.code, ActivityStatusCode.Error시간 태그 response.elapsed (초 단위)
요청 수, 응답 수, 처리 시간을 OpenTelemetry Meter로 수집합니다.
internal sealed class UsecaseMetricsPipeline <TRequest, TResponse>
: UsecasePipelineBase<TRequest>, IPipelineBehavior<TRequest, TResponse>, IDisposable
where TRequest : IMessage
where TResponse : IFinResponse, IFinResponseFactory<TResponse>
항목 설명 DI 의존성 IOptions<OpenTelemetryOptions>, IMeterFactoryMeter 이름 {ServiceNamespace}.applicationCounter (요청) application.usecase.{categoryType}.requests (단위: {request})Counter (응답) application.usecase.{categoryType}.responses (단위: {response})Histogram (시간) application.usecase.{categoryType}.duration (단위: s)요청 태그 request.layer, request.category.name, request.category.type, request.handler.name, request.handler.method응답 태그 (성공) 요청 태그 + response.status = success 응답 태그 (실패) 요청 태그 + response.status = failure + error.type + error.code
Command Usecase에 대해 명시적 트랜잭션, UoW.SaveChanges, 도메인 이벤트 발행을 자동 처리합니다.
internal sealed class UsecaseTransactionPipeline <TRequest, TResponse>
: UsecasePipelineBase<TRequest>, IPipelineBehavior<TRequest, TResponse>
where TRequest : ICommand<TResponse>
where TResponse : IFinResponse, IFinResponseFactory<TResponse>
항목 설명 제약 조건 TRequest : ICommand<TResponse> (Command 전용)DI 의존성 IUnitOfWork, IDomainEventPublisher, ILogger실행 순서 1) 트랜잭션 시작 → 2) Handler 실행 → 3) 실패 시 롤백 → 4) SaveChanges → 5) 커밋 → 6) 도메인 이벤트 발행 실패 처리 Handler 실패 또는 SaveChanges 실패 시 TResponse.CreateFail(error) 반환, 트랜잭션 자동 롤백
ICacheable을 구현한 Query 요청에 대해 IMemoryCache 기반 캐싱을 수행합니다.
internal sealed class UsecaseCachingPipeline <TRequest, TResponse>
: UsecasePipelineBase<TRequest>, IPipelineBehavior<TRequest, TResponse>
where TRequest : IQuery<TResponse>
where TResponse : IFinResponse, IFinResponseFactory<TResponse>
항목 설명 제약 조건 TRequest : IQuery<TResponse> (Query 전용)DI 의존성 IMemoryCache (services.AddMemoryCache() 필요)캐시 키 ICacheable.CacheKey캐시 기간 ICacheable.Duration (null이면 기본 5분)동작 캐시 히트 시 즉시 반환, 미스 시 Handler 실행 후 성공 응답만 캐시 저장
ICacheable 인터페이스:
public interface ICacheable
TimeSpan? Duration { get ; }
사용자가 정의하는 커스텀 Pipeline입니다. 기본 Pipeline(Exception, Validation 등) 이후, Handler 직전에 실행됩니다. ICustomUsecasePipeline 마커 인터페이스를 구현하면 어셈블리 자동 스캔으로 등록할 수 있습니다.
Scrutor 자동 검색 등록을 위한 마커 인터페이스입니다.
public interface ICustomUsecasePipeline { }
AddCustomPipeline<T>()를 사용하면 이 인터페이스를 구현한 타입을 명시적으로 DI에 등록합니다.
Usecase별 개별 Metric을 생성하기 위한 베이스 클래스입니다. TRequest 타입으로부터 CQRS 타입을 자동 식별합니다.
public abstract class UsecaseMetricCustomPipelineBase <TRequest>
: UsecasePipelineBase<TRequest>, ICustomUsecasePipeline
멤버 설명 protected readonly Meter _meter{ServiceNamespace}.application Meter 인스턴스protected const string DurationUnit"s"protected const string CountUnit"requests"GetMetricName(string metricName)application.usecase.{cqrs}.{handler}.{metricName} 형식 반환GetMetricNameWithoutHandler(string metricName)application.usecase.{cqrs}.{metricName} 형식 반환
생성자:
protected UsecaseMetricCustomPipelineBase ( string serviceNamespace, IMeterFactory meterFactory)
RequestDuration 헬퍼:
public class RequestDuration : IDisposable
public RequestDuration (Histogram< double > histogram);
public void Dispose (); // 경과 시간을 histogram에 자동 기록
using 구문과 함께 사용하여 자동으로 시간 측정 및 Histogram 기록을 수행합니다.
Usecase별 커스텀 Tracing을 생성하기 위한 베이스 클래스입니다. ActivitySource를 통해 커스텀 Activity(Span)를 생성하고 표준 태그를 설정합니다.
public abstract class UsecaseTracingCustomPipelineBase <TRequest>
: UsecasePipelineBase<TRequest>, ICustomUsecasePipeline
멤버 설명 protected readonly ActivitySource _activitySourceActivity 생성에 사용하는 ActivitySource StartCustomActivity(string operationName, ActivityKind kind){prefix}.{operationName} 형식의 커스텀 Activity 생성GetActivityName(string operationName)Activity 이름 조회 ({prefix}.{operationName}) SetStandardRequestTags(Activity activity, string method)표준 요청 태그 5종 설정
생성자:
protected UsecaseTracingCustomPipelineBase (ActivitySource activitySource)
Activity 이름 접두사: {layer} {category}.{categoryType} {handler}
부모 Activity.Current가 존재하면 자식 Span으로 생성됩니다.
PipelineConfigurator는 Fluent API로 개별 Pipeline을 활성화/비활성화하고 커스텀 Pipeline을 추가합니다.
public class PipelineConfigurator
메서드 반환 타입 설명 UseObservability()PipelineConfigurator관측성 4종 일괄 활성화 (CtxEnricher, Metrics, Tracing, Logging) UseMetrics()PipelineConfiguratorMetrics Pipeline 활성화 (CtxEnricher 자동 포함) UseTracing()PipelineConfiguratorTracing Pipeline 활성화 (CtxEnricher 자동 포함) UseLogging()PipelineConfiguratorLogging Pipeline 활성화 (CtxEnricher 자동 포함) UseValidation()PipelineConfiguratorValidation Pipeline 활성화 UseCaching()PipelineConfiguratorCaching Pipeline 활성화 (IMemoryCache DI 등록 필요) UseException()PipelineConfiguratorException Pipeline 활성화 UseTransaction()PipelineConfiguratorTransaction Pipeline 활성화 (IUnitOfWork, IDomainEventPublisher, IDomainEventCollector DI 등록 필요)
메서드 반환 타입 설명 WithLifetime(ServiceLifetime lifetime)PipelineConfiguratorPipeline 서비스 Lifetime 설정 (기본값: Scoped) AddCustomPipeline<TPipeline>()PipelineConfigurator커스텀 Pipeline을 타입으로 명시적 등록 (파이프라인 실행 순서의 결정론적 보장)
// 관측성 + 유효성검증 + 예외처리 활성화
. RegisterOpenTelemetry (configuration, Assembly . GetExecutingAssembly ())
. ConfigurePipelines (pipelines => pipelines
. UseObservability () // CtxEnricher, Metrics, Tracing, Logging 일괄 활성화
// 선택적 활성화 + 커스텀 Pipeline 등록
. RegisterOpenTelemetry (configuration, Assembly . GetExecutingAssembly ())
. ConfigurePipelines (pipelines => pipelines
. AddCustomPipeline <PlaceOrderCommandMetricPipeline>()
. WithLifetime ( ServiceLifetime . Scoped ))
Apply() 내부에서 Pipeline은 다음 순서로 IPipelineBehavior<,>에 등록됩니다.
Metrics
Tracing
Logging
Validation
Caching
Exception
Transaction
Custom (AddCustomPipeline<T>() 호출 순서대로 등록)
Handler
Transaction Pipeline 자동 감지: UseTransaction()을 호출해도 IUnitOfWork, IDomainEventPublisher, IDomainEventCollector가 DI에 모두 등록되어 있지 않으면 등록을 건너뜁니다.
OpenTelemetry 설정을 위한 메인 Builder 클래스입니다. Serilog, Metrics, Tracing, Pipeline 설정을 체이닝으로 구성합니다.
public partial class OpenTelemetryBuilder
// IServiceCollection 확장 메서드
public static OpenTelemetryBuilder RegisterOpenTelemetry (
this IServiceCollection services,
IConfiguration configuration,
Assembly projectAssembly)
메서드 반환 타입 설명 ConfigureLogging(Action<LoggingConfigurator> configure)OpenTelemetryBuilderSerilog 확장 설정 ConfigureMetrics(Action<MetricsConfigurator> configure)OpenTelemetryBuilderOpenTelemetry Metrics 확장 설정 ConfigureTracing(Action<TracingConfigurator> configure)OpenTelemetryBuilderOpenTelemetry Tracing 확장 설정 ConfigurePipelines(Action<PipelineConfigurator> configure)OpenTelemetryBuilderPipeline 구성 (명시적 opt-in 필수) ConfigureStartupLogger(Action<ILogger> configure)OpenTelemetryBuilder시작 시 추가 로깅 설정 WithAdapterObservability(bool enable = true)OpenTelemetryBuilderAdapter 관측 가능성 활성화/비활성화 (기본값: true) Build()IServiceCollection모든 설정 적용 후 IServiceCollection 반환
OpenTelemetryOptions 읽기 (IOptions<OpenTelemetryOptions>)
Resource Attributes 생성
Serilog 설정 (ReadFrom.Configuration + WriteTo.OpenTelemetry + ErrorsDestructuringPolicy)
CtxEnricherContext PushProperty 팩토리 설정
OpenTelemetry 설정 (Metrics + Tracing + OTLP Exporter)
Adapter Observability 등록 (ActivitySource, IMeterFactory)
Usecase Pipeline 등록
StartupLogger IHostedService 등록
Serilog 확장 설정을 위한 Builder 클래스입니다.
public class LoggingConfigurator
멤버 설명 OptionsOpenTelemetryOptions 접근 프로퍼티AddDestructuringPolicy<TPolicy>()IDestructuringPolicy 구현 타입 등록AddEnricher(ILogEventEnricher enricher)Enricher 인스턴스 등록 AddEnricher<TEnricher>()Enricher 타입 등록 Configure(Action<LoggerConfiguration> configure)LoggerConfiguration 직접 접근
OpenTelemetry Metrics 확장 설정을 위한 Builder 클래스입니다.
public class MetricsConfigurator
멤버 설명 OptionsOpenTelemetryOptions 접근 프로퍼티AddMeter(string meterName)추가 Meter 등록 (와일드카드 지원: "MyApp.*") AddInstrumentation(Action<MeterProviderBuilder> configure)추가 Instrumentation 등록 Configure(Action<MeterProviderBuilder> configure)MeterProviderBuilder 직접 접근
OpenTelemetry Tracing 확장 설정을 위한 Builder 클래스입니다.
public class TracingConfigurator
멤버 설명 OptionsOpenTelemetryOptions 접근 프로퍼티AddSource(string sourceName)추가 ActivitySource 등록 (와일드카드 지원: "MyApp.*") AddProcessor(BaseProcessor<Activity> processor)추가 Processor 등록 Configure(Action<TracerProviderBuilder> configure)TracerProviderBuilder 직접 접근
appsettings.json의 "OpenTelemetry" 섹션에 바인딩되는 설정 클래스입니다.
public sealed class OpenTelemetryOptions : IStartupOptionsLogger, IOpenTelemetryOptions
프로퍼티 타입 기본값 설명 ServiceNamespacestring""서비스 네임스페이스(그룹) ServiceNamestring""서비스 이름 ServiceVersionstring(어셈블리 버전) 서비스 버전 (자동 설정) ServiceInstanceIdstring(호스트네임) 서비스 인스턴스 ID (자동 설정) CollectorEndpointstring""통합 OTLP Collector 엔드포인트 TracingEndpointstring?nullTracing 전용 엔드포인트 (null이면 CollectorEndpoint 사용) MetricsEndpointstring?nullMetrics 전용 엔드포인트 (null이면 CollectorEndpoint 사용) LoggingEndpointstring?nullLogging 전용 엔드포인트 (null이면 CollectorEndpoint 사용) CollectorProtocolstring"Grpc"통합 OTLP Protocol TracingProtocolstring?nullTracing 전용 Protocol MetricsProtocolstring?nullMetrics 전용 Protocol LoggingProtocolstring?nullLogging 전용 Protocol SamplingRatedouble1.0Tracing 샘플링 비율 (0.0 ~ 1.0) EnablePrometheusExporterboolfalsePrometheus Exporter 활성화
개별 엔드포인트(TracingEndpoint, MetricsEndpoint, LoggingEndpoint)의 해석 규칙은 다음과 같습니다.
값 동작 nullCollectorEndpoint 사용 (기본 동작)"" (빈 문자열)해당 신호 비활성화 "http://..."해당 엔드포인트 사용
메서드 반환 타입 설명 GetTracingProtocol()OtlpCollectorProtocolTracing Protocol (개별 설정 우선) GetMetricsProtocol()OtlpCollectorProtocolMetrics Protocol (개별 설정 우선) GetLoggingProtocol()OtlpCollectorProtocolLogging Protocol (개별 설정 우선) GetTracingEndpoint()stringTracing 엔드포인트 (해석 규칙 적용) GetMetricsEndpoint()stringMetrics 엔드포인트 (해석 규칙 적용) GetLoggingEndpoint()stringLogging 엔드포인트 (해석 규칙 적용)
public sealed class OtlpCollectorProtocol : SmartEnum<OtlpCollectorProtocol>
상수 값 설명 Grpc1 gRPC 프로토콜 (기본값) HttpProtobuf2 HTTP/Protobuf 프로토콜
FluentValidation 기반 옵션 검증기입니다.
규칙 설명 ServiceNamespace필수 (NotEmpty) ServiceName필수 (NotEmpty) 엔드포인트 CollectorEndpoint 또는 개별 엔드포인트 중 하나 이상 필수SamplingRate0.0 ~ 1.0 범위 Protocol SmartEnum 유효값 검증
"ServiceNamespace" : " mycompany.production " ,
"ServiceName" : " orderservice " ,
"CollectorEndpoint" : " http://localhost:4317 " ,
"CollectorProtocol" : " Grpc " ,
"EnablePrometheusExporter" : false
관측 가능성 관련 통합 네이밍 규칙을 정의합니다. 메트릭 이름, 태그 키, Span 이름 등의 단일 진실 공급원(Single Source of Truth)입니다.
public static partial class ObservabilityNaming
상수 값 설명 Application"application"Application Layer Adapter"adapter"Adapter Layer
상수 값 설명 Usecase"usecase"Usecase 카테고리 Repository"repository"Repository 카테고리 Event"event"Event 카테고리 Unknown"unknown"미식별 카테고리
상수 값 설명 Command"command"Command 타입 Query"query"Query 타입 Event"event"Event 타입 Unknown"unknown"미식별 타입
상수 값 설명 Success"success"성공 Failure"failure"실패
상수 값 설명 Expected"expected"예상된 비즈니스 에러 (IsExpected = true) Exceptional"exceptional"예외적 시스템 에러 (IsExceptional = true) Aggregate"aggregate"복합 에러 (ManyErrors)
상수 값 설명 Handle"Handle"Usecase Handler 메서드 Publish"Publish"이벤트 발행 메서드 PublishTrackedEvents"PublishTrackedEvents"추적 이벤트 발행 메서드
상수 값 ErrorType"error.type"ServiceNamespace"service.namespace"ServiceName"service.name"ServiceVersion"service.version"ServiceInstanceId"service.instance.id"DeploymentEnvironment"deployment.environment"
상수 값 용도 RequestMessage"request.message"요청 메시지 RequestParams"request.params"요청 파라미터 RequestLayer"request.layer"요청 레이어 RequestCategoryName"request.category.name"요청 카테고리 이름 RequestCategoryType"request.category.type"요청 카테고리 타입 RequestHandlerName"request.handler.name"요청 핸들러 이름 RequestHandlerMethod"request.handler.method"요청 핸들러 메서드 ResponseMessage"response.message"응답 메시지 ResponseStatus"response.status"응답 상태 ResponseElapsed"response.elapsed"응답 경과 시간 ErrorCode"error.code"에러 코드
메서드 예시 Metrics.UsecaseRequest("command")"application.usecase.command.requests"Metrics.UsecaseResponse("query")"application.usecase.query.responses"Metrics.UsecaseDuration("command")"application.usecase.command.duration"Metrics.AdapterRequest("repository")"adapter.repository.requests"Metrics.AdapterResponse("repository")"adapter.repository.responses"Metrics.AdapterDuration("repository")"adapter.repository.duration"
메서드 예시 Spans.OperationName("adapter", "repository", "OrderRepository", "GetById")"adapter repository OrderRepository.GetById"
범위 ID 이름 Application Request 1001 application.requestApplication Response (Success) 1002 application.response.successApplication Response (Warning) 1003 application.response.warningApplication Response (Error) 1004 application.response.errorAdapter Request 2001 adapter.requestAdapter Response (Success) 2002 adapter.response.successAdapter Response (Warning) 2003 adapter.response.warningAdapter Response (Error) 2004 adapter.response.error