본문으로 건너뛰기

에러 시스템 사양

Functorium의 에러 시스템은 레이어별 sealed record 계층(DomainErrorType, ApplicationErrorType, AdapterErrorType)과 레이어별 팩토리(DomainError, ApplicationError, EventError, AdapterError)로 구성됩니다. 팩토리의 공통 생성 로직은 내부 LayerErrorCore에 집중되고, Expected 에러의 공통 override는 ErrorCodeExpectedBase에 통합됩니다. 이 사양서는 공개/내부 타입의 시그니처, 속성, 에러 코드 생성 규칙을 정의합니다.

타입네임스페이스설명
ErrorTypeFunctorium.Abstractions.Errors모든 레이어 에러 타입의 추상 기반 record
IHasErrorCodeFunctorium.Abstractions.Errors에러 코드 접근 인터페이스
ErrorCodeFactoryFunctorium.Abstractions.ErrorsExpected/Exceptional 에러 생성 팩토리
DomainErrorTypeFunctorium.Domains.Errors도메인 에러 타입 sealed record 계층 (10개 카테고리)
DomainErrorFunctorium.Domains.Errors도메인 에러 생성 팩토리
ApplicationErrorTypeFunctorium.Applications.Errors애플리케이션 에러 타입 sealed record 계층 (14개 타입)
ApplicationErrorFunctorium.Applications.Errors애플리케이션 에러 생성 팩토리
EventErrorTypeFunctorium.Applications.Errors이벤트 에러 타입 sealed record 계층 (4개 타입)
EventErrorFunctorium.Applications.Errors이벤트 에러 생성 팩토리
AdapterErrorTypeFunctorium.Adapters.Errors어댑터 에러 타입 sealed record 계층 (20개 타입)
AdapterErrorFunctorium.Adapters.Errors어댑터 에러 생성 팩토리
타입네임스페이스설명
ErrorCodeExpectedBaseFunctorium.Abstractions.ErrorsExpected 에러 4종의 공통 LanguageExt Error override 기반 클래스
ErrorCodeExpected (4종)Functorium.Abstractions.ErrorsExpected 에러 — 값 저장만 담당하며 나머지는 base에서 상속
ErrorCodeExceptionalFunctorium.Abstractions.ErrorsException을 에러 코드와 함께 래핑
LayerErrorCoreFunctorium.Abstractions.Errors4개 레이어 팩토리의 공통 에러 코드 생성 로직
ErrorAssertionCoreFunctorium.Testing.Assertions.Errors3개 레이어 Assertion의 공통 검증 로직

모든 에러 코드는 다음 패턴을 따릅니다:

{LayerPrefix}.{ContextName}.{ErrorName}
레이어접두사예시
DomainDomainErrorsDomainErrors.Email.Empty
ApplicationApplicationErrorsApplicationErrors.CreateProductCommand.AlreadyExists
AdapterAdapterErrorsAdapterErrors.ProductRepository.NotFound

namespace Functorium.Abstractions.Errors;
public abstract record ErrorType
{
public const string DomainErrorsPrefix = "DomainErrors";
public const string ApplicationErrorsPrefix = "ApplicationErrors";
public const string AdapterErrorsPrefix = "AdapterErrors";
public virtual string ErrorName => GetType().Name;
}

ErrorType 모든 레이어별 에러 타입(DomainErrorType, ApplicationErrorType, AdapterErrorType)의 공통 기반입니다.

멤버종류설명
DomainErrorsPrefixconst string도메인 에러 코드 접두사 "DomainErrors"
ApplicationErrorsPrefixconst string애플리케이션 에러 코드 접두사 "ApplicationErrors"
AdapterErrorsPrefixconst string어댑터 에러 코드 접두사 "AdapterErrors"
ErrorNamevirtual string에러 코드의 마지막 세그먼트. 기본값은 GetType().Name
namespace Functorium.Abstractions.Errors;
public interface IHasErrorCode
{
string ErrorCode { get; }
}

IHasErrorCode 리플렉션 없이 타입 안전하게 에러 코드에 접근하기 위한 인터페이스입니다. ErrorCodeExpectedErrorCodeExceptional이 이 인터페이스를 구현합니다.


internal abstract record ErrorCodeExpectedBase(
string ErrorCode,
string ErrorMessage,
int ErrorCodeId = -1000,
Option<Error> Inner = default) : Error, IHasErrorCode

ErrorCodeExpectedBase Expected 에러 4종(ErrorCodeExpected, <T>, <T1,T2>, <T1,T2,T3>)의 공통 기반 클래스입니다. LanguageExt Error의 13개 override를 한 곳에 정의하여 파생 타입의 중복을 제거합니다.

멤버종류설명
ErrorCodestring"{Prefix}.{Context}.{ErrorName}" 형식 에러 코드
Messageoverride string사람이 읽을 수 있는 에러 메시지
Codeoverride int정수 에러 코드 ID (기본값 -1000)
Inneroverride Option<Error>내부 에러 (기본값 None)
ToString()sealed overrideMessage 반환. sealed로 파생 record의 자동생성 방지
ToErrorException()overrideWrappedErrorExpectedException 반환
IsExpectedbool항상 true
IsExceptionalbool항상 false

sealed override ToString(): C# record는 파생 클래스에서 ToString()을 자동 재생성합니다. sealed로 이를 차단하여 모든 파생 타입이 일관되게 Message를 반환하도록 보장합니다.

internal record ErrorCodeExpected(
string ErrorCode,
string ErrorCurrentValue,
string ErrorMessage,
int ErrorCodeId = -1000,
Option<Error> Inner = default)
: ErrorCodeExpectedBase(ErrorCode, ErrorMessage, ErrorCodeId, Inner)

ErrorCodeExpected 비즈니스 규칙 위반 등 예상된(Expected) 에러를 표현합니다. ErrorCodeExpectedBase에서 ErrorCode, Message, Code, Inner, ToString(), IsExpected, IsExceptional 등 공통 멤버를 상속받고, 파생 타입은 에러 발생 시점의 값(ErrorCurrentValue)만 추가로 정의합니다.

오버로드추가 속성설명
ErrorCodeExpectedErrorCurrentValue: string문자열 값
ErrorCodeExpected<T>ErrorCurrentValue: T타입 값 (1개)
ErrorCodeExpected<T1, T2>ErrorCurrentValue1: T1, ErrorCurrentValue2: T2타입 값 (2개)
ErrorCodeExpected<T1, T2, T3>ErrorCurrentValue1: T1, ErrorCurrentValue2: T2, ErrorCurrentValue3: T3타입 값 (3개)
internal record ErrorCodeExceptional : Error, IHasErrorCode
{
public ErrorCodeExceptional(string errorCode, Exception exception);
}

ErrorCodeExceptional 시스템 예외(Exception)를 에러 코드와 함께 래핑합니다. IsExpected = false, IsExceptional = true입니다.

속성타입설명
ErrorCodestring"{Prefix}.{Context}.{ErrorName}" 형식 에러 코드
Messagestringexception.Message에서 추출
Codeintexception.HResult에서 추출
InnerOption<Error>InnerException이 있으면 재귀적으로 래핑
IsExpectedbool항상 false
IsExceptionalbool항상 true
namespace Functorium.Abstractions.Errors;
public static class ErrorCodeFactory
{
// Expected 에러 생성
public static Error Create(string errorCode, string errorCurrentValue, string errorMessage);
public static Error Create<T>(string errorCode, T errorCurrentValue, string errorMessage) where T : notnull;
public static Error Create<T1, T2>(string errorCode, T1 errorCurrentValue1, T2 errorCurrentValue2, string errorMessage)
where T1 : notnull where T2 : notnull;
public static Error Create<T1, T2, T3>(string errorCode, T1 errorCurrentValue1, T2 errorCurrentValue2, T3 errorCurrentValue3, string errorMessage)
where T1 : notnull where T2 : notnull where T3 : notnull;
// Exceptional 에러 생성
public static Error CreateFromException(string errorCode, Exception exception);
// 에러 코드 조합
public static string Format(params string[] parts);
}

ErrorCodeFactory ErrorCodeExpectedErrorCodeExceptional 인스턴스를 생성하는 정적 팩토리입니다. 레이어별 팩토리는 다음 흐름으로 에러를 생성합니다:

DomainError.For<T>(DomainErrorType, ...) ← 레이어 타입 안전성 (공개 API)
→ LayerErrorCore.Create<T>(prefix, ErrorType, ...) ← 공통 에러 코드 조립 (internal)
→ ErrorCodeFactory.Create(errorCode, ...) ← ErrorCodeExpected 인스턴스 생성 (internal)

LayerErrorCore가 에러 코드 문자열({Prefix}.{Context}.{ErrorName})을 조립하고, ErrorCodeFactory가 최종 Error 인스턴스를 생성합니다. 모든 메서드에 [AggressiveInlining]이 적용되어 JIT이 위임 호출을 제거하므로 성능 차이는 없습니다.

메서드반환설명
Create(...)ErrorExpected 에러 생성 (4개 오버로드)
CreateFromException(...)ErrorException을 Exceptional 에러로 래핑
Format(...)string문자열 배열을 '.'으로 결합하여 에러 코드 생성

namespace Functorium.Domains.Errors;
public abstract partial record DomainErrorType : ErrorType;

DomainErrorType 도메인 레이어 에러의 sealed record 계층 기반입니다. 10개 카테고리로 분류됩니다.

sealed record속성설명
NotFound(없음)값을 찾을 수 없음
AlreadyExists(없음)값이 이미 존재함
Duplicate(없음)중복된 값
Mismatch(없음)값이 일치하지 않음 (예: 비밀번호 확인)
public sealed record NotFound : DomainErrorType;
public sealed record AlreadyExists : DomainErrorType;
public sealed record Duplicate : DomainErrorType;
public sealed record Mismatch : DomainErrorType;
sealed record속성설명
Empty(없음)값이 비어있음 (null, empty string, empty collection 등)
Null(없음)값이 null임
public sealed record Empty : DomainErrorType;
public sealed record Null : DomainErrorType;
sealed record속성설명
InvalidFormatstring? Pattern값의 형식이 유효하지 않음. Pattern은 기대되는 형식 패턴
NotUpperCase(없음)값이 대문자가 아님
NotLowerCase(없음)값이 소문자가 아님
public sealed record InvalidFormat(string? Pattern = null) : DomainErrorType;
public sealed record NotUpperCase : DomainErrorType;
public sealed record NotLowerCase : DomainErrorType;
sealed record속성설명
TooShortint MinLength값이 최소 길이보다 짧음. 기본값 0 (미지정)
TooLongint MaxLength값이 최대 길이를 초과함. 기본값 int.MaxValue (미지정)
WrongLengthint Expected값의 길이가 기대와 불일치. 기본값 0 (미지정)
public sealed record TooShort(int MinLength = 0) : DomainErrorType;
public sealed record TooLong(int MaxLength = int.MaxValue) : DomainErrorType;
public sealed record WrongLength(int Expected = 0) : DomainErrorType;
sealed record속성설명
Zero(없음)값이 0임
Negative(없음)값이 음수임
NotPositive(없음)값이 양수가 아님 (0 또는 음수)
OutOfRangestring? Min, string? Max값이 허용 범위를 벗어남
BelowMinimumstring? Minimum값이 최소값보다 작음
AboveMaximumstring? Maximum값이 최대값을 초과함
public sealed record Zero : DomainErrorType;
public sealed record Negative : DomainErrorType;
public sealed record NotPositive : DomainErrorType;
public sealed record OutOfRange(string? Min = null, string? Max = null) : DomainErrorType;
public sealed record BelowMinimum(string? Minimum = null) : DomainErrorType;
public sealed record AboveMaximum(string? Maximum = null) : DomainErrorType;
sealed record속성설명
DefaultDate(없음)날짜가 기본값(DateTime.MinValue)임
NotInPast(없음)날짜가 과거여야 하는데 미래임
NotInFuture(없음)날짜가 미래여야 하는데 과거임
TooLatestring? Boundary날짜가 기준 날짜보다 이후임 (이전이어야 함)
TooEarlystring? Boundary날짜가 기준 날짜보다 이전임 (이후여야 함)
public sealed record DefaultDate : DomainErrorType;
public sealed record NotInPast : DomainErrorType;
public sealed record NotInFuture : DomainErrorType;
public sealed record TooLate(string? Boundary = null) : DomainErrorType;
public sealed record TooEarly(string? Boundary = null) : DomainErrorType;
sealed record속성설명
RangeInvertedstring? Min, string? Max범위가 역전됨 (최소값이 최대값보다 큼)
RangeEmptystring? Value범위가 비어있음 (최소값과 최대값이 같음)
public sealed record RangeInverted(string? Min = null, string? Max = null) : DomainErrorType;
public sealed record RangeEmpty(string? Value = null) : DomainErrorType;
sealed record속성설명
InvalidTransitionstring? FromState, string? ToState무효한 상태 전이 (예: Paid -> Active)
public sealed record InvalidTransition(string? FromState = null, string? ToState = null) : DomainErrorType;

FromStateToState로 전이 전후 상태를 기록합니다. 에러 메시지와 별개로 구조화된 데이터로 전이 정보를 보존하여, 로깅/모니터링에서 활용할 수 있습니다.

public abstract record Custom : DomainErrorType;

Custom 표준 에러 타입으로 표현할 수 없는 도메인 특화 에러의 기반 클래스입니다. 파생 sealed record로 정의하여 사용합니다.

// 엔티티 내부에 nested record로 정의
public sealed record InsufficientStock : DomainErrorType.Custom;
DomainError.For<Inventory>(new InsufficientStock(), currentStock, "재고가 부족합니다");
// 에러 코드: DomainErrors.Inventory.InsufficientStock

namespace Functorium.Applications.Errors;
public abstract record ApplicationErrorType : ErrorType;

ApplicationErrorType 애플리케이션 레이어 에러의 sealed record 계층 기반입니다.

sealed record속성설명
Empty(없음)값이 비어있음
Null(없음)값이 null임
NotFound(없음)값을 찾을 수 없음
AlreadyExists(없음)값이 이미 존재함
Duplicate(없음)중복된 값
InvalidState(없음)유효하지 않은 상태
Unauthorized(없음)인증되지 않음
Forbidden(없음)접근 금지
public sealed record Empty : ApplicationErrorType;
public sealed record Null : ApplicationErrorType;
public sealed record NotFound : ApplicationErrorType;
public sealed record AlreadyExists : ApplicationErrorType;
public sealed record Duplicate : ApplicationErrorType;
public sealed record InvalidState : ApplicationErrorType;
public sealed record Unauthorized : ApplicationErrorType;
public sealed record Forbidden : ApplicationErrorType;
sealed record속성설명
ValidationFailedstring? PropertyName검증 실패. PropertyName은 실패한 속성 이름
public sealed record ValidationFailed(string? PropertyName = null) : ApplicationErrorType;
sealed record속성설명
BusinessRuleViolatedstring? RuleName비즈니스 규칙 위반. RuleName은 위반된 규칙 이름
ConcurrencyConflict(없음)동시성 충돌
ResourceLockedstring? ResourceName리소스 잠금. ResourceName은 잠긴 리소스 이름
OperationCancelled(없음)작업 취소됨
InsufficientPermissionstring? Permission권한 부족. Permission은 필요한 권한
public sealed record BusinessRuleViolated(string? RuleName = null) : ApplicationErrorType;
public sealed record ConcurrencyConflict : ApplicationErrorType;
public sealed record ResourceLocked(string? ResourceName = null) : ApplicationErrorType;
public sealed record OperationCancelled : ApplicationErrorType;
public sealed record InsufficientPermission(string? Permission = null) : ApplicationErrorType;
public abstract record Custom : ApplicationErrorType;
// 사용 예시
public sealed record CannotProcess : ApplicationErrorType.Custom;
namespace Functorium.Applications.Errors;
public abstract record EventErrorType : ErrorType;

EventErrorType 도메인 이벤트 발행/처리 과정의 에러 타입입니다.

sealed record속성설명
PublishFailed(없음)이벤트 발행 실패
HandlerFailed(없음)이벤트 핸들러 실행 실패
InvalidEventType(없음)이벤트 타입이 유효하지 않음
PublishCancelled(없음)이벤트 발행 취소됨
public sealed record PublishFailed : EventErrorType;
public sealed record HandlerFailed : EventErrorType;
public sealed record InvalidEventType : EventErrorType;
public sealed record PublishCancelled : EventErrorType;

커스텀 확장:

public abstract record Custom : EventErrorType;
// 사용 예시
public sealed record RetryExhausted : EventErrorType.Custom;

namespace Functorium.Adapters.Errors;
public abstract record AdapterErrorType : ErrorType;

AdapterErrorType 어댑터 레이어 에러의 sealed record 계층 기반입니다.

sealed record속성설명
Empty(없음)값이 비어있음
Null(없음)값이 null임
NotFound(없음)값을 찾을 수 없음
PartialNotFound(없음)요청한 ID 중 일부를 찾을 수 없음
AlreadyExists(없음)값이 이미 존재함
Duplicate(없음)중복된 값
InvalidState(없음)유효하지 않은 상태
NotConfigured(없음)필수 설정이 누락됨
NotSupported(없음)지원되지 않는 연산
Unauthorized(없음)인증되지 않음
Forbidden(없음)접근 금지
public sealed record Empty : AdapterErrorType;
public sealed record Null : AdapterErrorType;
public sealed record NotFound : AdapterErrorType;
public sealed record PartialNotFound : AdapterErrorType;
public sealed record AlreadyExists : AdapterErrorType;
public sealed record Duplicate : AdapterErrorType;
public sealed record InvalidState : AdapterErrorType;
public sealed record NotConfigured : AdapterErrorType;
public sealed record NotSupported : AdapterErrorType;
public sealed record Unauthorized : AdapterErrorType;
public sealed record Forbidden : AdapterErrorType;
sealed record속성설명
PipelineValidationstring? PropertyName파이프라인 검증 실패. PropertyName은 실패한 속성 이름
PipelineException(없음)파이프라인 예외 발생
public sealed record PipelineValidation(string? PropertyName = null) : AdapterErrorType;
public sealed record PipelineException : AdapterErrorType;
sealed record속성설명
ExternalServiceUnavailablestring? ServiceName외부 서비스 사용 불가. ServiceName은 서비스 이름
ConnectionFailedstring? Target연결 실패. Target은 연결 대상
TimeoutTimeSpan? Duration타임아웃. Duration은 타임아웃 시간
public sealed record ExternalServiceUnavailable(string? ServiceName = null) : AdapterErrorType;
public sealed record ConnectionFailed(string? Target = null) : AdapterErrorType;
public sealed record Timeout(TimeSpan? Duration = null) : AdapterErrorType;
sealed record속성설명
Serializationstring? Format직렬화 실패. Format은 직렬화 형식
Deserializationstring? Format역직렬화 실패. Format은 역직렬화 형식
DataCorruption(없음)데이터 손상
public sealed record Serialization(string? Format = null) : AdapterErrorType;
public sealed record Deserialization(string? Format = null) : AdapterErrorType;
public sealed record DataCorruption : AdapterErrorType;
public abstract record Custom : AdapterErrorType;
// 사용 예시
public sealed record RateLimited : AdapterErrorType.Custom;

namespace Functorium.Abstractions.Errors;
internal static class LayerErrorCore
{
internal static Error Create<TContext>(string prefix, ErrorType errorType, string currentValue, string message);
internal static Error Create<TContext, TValue>(string prefix, ErrorType errorType, TValue currentValue, string message)
where TValue : notnull;
internal static Error Create<TContext, T1, T2>(...) where T1 : notnull where T2 : notnull;
internal static Error Create<TContext, T1, T2, T3>(...) where T1 : notnull where T2 : notnull where T3 : notnull;
internal static Error Create(string prefix, Type contextType, ErrorType errorType, string currentValue, string message);
internal static Error ForContext(string prefix, string contextName, ErrorType errorType, string currentValue, string message);
internal static Error ForContext<TValue>(string prefix, string contextName, ErrorType errorType, TValue currentValue, string message)
where TValue : notnull;
internal static Error FromException<TContext>(string prefix, ErrorType errorType, Exception exception);
}

LayerErrorCore 4개 레이어 팩토리(DomainError, ApplicationError, EventError, AdapterError)의 공통 구현입니다. 에러 코드 문자열 {prefix}.{typeof(TContext).Name}.{errorType.ErrorName}을 조립하고 ErrorCodeFactory에 위임합니다.

설계 원리: 공개 팩토리는 레이어별 타입 파라미터(DomainErrorType, ApplicationErrorType 등)를 유지하여 컴파일 타임 안전성을 보장합니다. LayerErrorCore는 기반 타입 ErrorType으로 수신하여 구현 중복을 제거합니다. 모든 메서드에 [AggressiveInlining]이 적용되어 JIT이 위임 호출을 인라인 처리합니다.

// 컴파일 타임 안전성 보장 예시
DomainError.For<Email>(new DomainErrorType.Empty(), ...) // ✅ 컴파일 OK
DomainError.For<Email>(new AdapterErrorType.Timeout(), ...) // ❌ CS1503
namespace Functorium.Testing.Assertions.Errors;
internal static class ErrorAssertionCore
{
// Error — ErrorCode 검증, 값 검증 (1~3개), Exceptional 검증
internal static void ShouldBeError<TContext>(Error error, string prefix, string errorName);
internal static void ShouldBeError<TContext, TValue>(Error error, string prefix, string errorName, TValue expectedValue);
internal static void ShouldBeExceptionalError<TContext>(Error error, string prefix, string errorName);
// Fin<T> — 실패 상태 + ErrorCode 검증
internal static void ShouldBeFinError<TContext, T>(Fin<T> fin, string prefix, string errorName);
// Validation<Error, T> — 에러 포함/유일/복수 검증
internal static void ShouldHaveError<TContext, T>(Validation<Error, T> validation, string prefix, string errorName);
internal static void ShouldHaveOnlyError<TContext, T>(Validation<Error, T> validation, string prefix, string errorName);
internal static void ShouldHaveErrors<TContext, T>(Validation<Error, T> validation, string prefix, params string[] errorNames);
}

ErrorAssertionCore 3개 레이어 Assertion(DomainErrorAssertions, ApplicationErrorAssertions, AdapterErrorAssertions)의 공통 검증 로직입니다. 에러 코드 조립({prefix}.{typeof(TContext).Name}.{errorName})과 ErrorCodeExpected<T> 타입 캐스팅, 값 비교를 제공합니다. 레이어별 Assertion은 prefix와 에러 타입만 바인딩하는 thin wrapper입니다.


namespace Functorium.Domains.Errors;
public static class DomainError
{
public static Error For<TDomain>(DomainErrorType errorType, string currentValue, string message);
public static Error For<TDomain, TValue>(DomainErrorType errorType, TValue currentValue, string message)
where TValue : notnull;
public static Error For<TDomain, T1, T2>(DomainErrorType errorType, T1 value1, T2 value2, string message)
where T1 : notnull where T2 : notnull;
public static Error For<TDomain, T1, T2, T3>(DomainErrorType errorType, T1 value1, T2 value2, T3 value3, string message)
where T1 : notnull where T2 : notnull where T3 : notnull;
}

에러 코드 형식: DomainErrors.{typeof(TDomain).Name}.{errorType.ErrorName}

오버로드값 파라미터설명
For<TDomain>(...)string currentValue기본 문자열 값
For<TDomain, TValue>(...)TValue currentValue제네릭 단일 값
For<TDomain, T1, T2>(...)T1 value1, T2 value2제네릭 2개 값
For<TDomain, T1, T2, T3>(...)T1 value1, T2 value2, T3 value3제네릭 3개 값

사용 예시:

using static Functorium.Domains.Errors.DomainErrorType;
// 기본 사용
DomainError.For<Email>(new Empty(), "", "이메일은 비어있을 수 없습니다");
// 에러 코드: DomainErrors.Email.Empty
// 속성이 있는 에러 타입
DomainError.For<Password>(new TooShort(MinLength: 8), value, "비밀번호가 너무 짧습니다");
// 에러 코드: DomainErrors.Password.TooShort
// 상태 전이 에러
DomainError.For<Order>(new InvalidTransition(FromState: "Paid", ToState: "Active"), orderId, "유효하지 않은 상태 전이");
// 에러 코드: DomainErrors.Order.InvalidTransition
// 커스텀 에러
DomainError.For<Currency>(new Unsupported(), value, "지원되지 않는 통화입니다");
// 에러 코드: DomainErrors.Currency.Unsupported
namespace Functorium.Applications.Errors;
public static class ApplicationError
{
public static Error For<TUsecase>(ApplicationErrorType errorType, string currentValue, string message);
public static Error For<TUsecase, TValue>(ApplicationErrorType errorType, TValue currentValue, string message)
where TValue : notnull;
public static Error For<TUsecase, T1, T2>(ApplicationErrorType errorType, T1 value1, T2 value2, string message)
where T1 : notnull where T2 : notnull;
public static Error For<TUsecase, T1, T2, T3>(ApplicationErrorType errorType, T1 value1, T2 value2, T3 value3, string message)
where T1 : notnull where T2 : notnull where T3 : notnull;
}

에러 코드 형식: ApplicationErrors.{typeof(TUsecase).Name}.{errorType.ErrorName}

오버로드값 파라미터설명
For<TUsecase>(...)string currentValue기본 문자열 값
For<TUsecase, TValue>(...)TValue currentValue제네릭 단일 값
For<TUsecase, T1, T2>(...)T1 value1, T2 value2제네릭 2개 값
For<TUsecase, T1, T2, T3>(...)T1 value1, T2 value2, T3 value3제네릭 3개 값

사용 예시:

ApplicationErrors.CreateProductCommand.AlreadyExists
using static Functorium.Applications.Errors.ApplicationErrorType;
ApplicationError.For<CreateProductCommand>(new AlreadyExists(), productId, "이미 존재합니다");
ApplicationError.For<UpdateOrderCommand>(new ValidationFailed("Quantity"), value, "수량은 양수여야 합니다");
// 에러 코드: ApplicationErrors.UpdateOrderCommand.ValidationFailed
namespace Functorium.Applications.Errors;
public static class EventError
{
public static Error For<TPublisher>(EventErrorType errorType, string currentValue, string message);
public static Error For<TPublisher, TValue>(EventErrorType errorType, TValue currentValue, string message)
where TValue : notnull;
public static Error FromException<TPublisher>(Exception exception);
public static Error FromException<TPublisher>(EventErrorType errorType, Exception exception);
}

에러 코드 형식: ApplicationErrors.{typeof(TPublisher).Name}.{errorType.ErrorName}

EventError ApplicationErrors 접두사를 공유하며, 이벤트 발행/처리 실패를 표현합니다.

메서드설명
For<TPublisher>(...)Expected 에러 생성
For<TPublisher, TValue>(...)제네릭 값의 Expected 에러 생성
FromException<TPublisher>(exception)예외를 PublishFailed 타입의 Exceptional 에러로 래핑
FromException<TPublisher>(errorType, exception)예외를 지정한 타입의 Exceptional 에러로 래핑

사용 예시:

ApplicationErrors.DomainEventPublisher.PublishFailed
using static Functorium.Applications.Errors.EventErrorType;
EventError.For<DomainEventPublisher>(new PublishFailed(), eventType, "이벤트 발행에 실패했습니다");
EventError.FromException<DomainEventPublisher>(exception);
// 에러 코드: ApplicationErrors.DomainEventPublisher.PublishFailed (Exceptional)
namespace Functorium.Adapters.Errors;
public static class AdapterError
{
public static Error For<TAdapter>(AdapterErrorType errorType, string currentValue, string message);
public static Error For(Type adapterType, AdapterErrorType errorType, string currentValue, string message);
public static Error For<TAdapter, TValue>(AdapterErrorType errorType, TValue currentValue, string message)
where TValue : notnull;
public static Error For<TAdapter, T1, T2>(AdapterErrorType errorType, T1 value1, T2 value2, string message)
where T1 : notnull where T2 : notnull;
public static Error For<TAdapter, T1, T2, T3>(AdapterErrorType errorType, T1 value1, T2 value2, T3 value3, string message)
where T1 : notnull where T2 : notnull where T3 : notnull;
public static Error FromException<TAdapter>(AdapterErrorType errorType, Exception exception);
}

에러 코드 형식: AdapterErrors.{typeof(TAdapter).Name}.{errorType.ErrorName}

오버로드값 파라미터설명
For<TAdapter>(...)string currentValue기본 문자열 값
For(Type, ...)string currentValue런타임 Type으로 어댑터 지정 (베이스 클래스에서 GetType() 사용 시)
For<TAdapter, TValue>(...)TValue currentValue제네릭 단일 값
For<TAdapter, T1, T2>(...)T1 value1, T2 value2제네릭 2개 값
For<TAdapter, T1, T2, T3>(...)T1 value1, T2 value2, T3 value3제네릭 3개 값
FromException<TAdapter>(...)Exception exceptionException을 Exceptional 에러로 래핑

사용 예시:

using static Functorium.Adapters.Errors.AdapterErrorType;
// Expected 에러
AdapterError.For<ProductRepository>(new NotFound(), id, "제품을 찾을 수 없습니다");
// 에러 코드: AdapterErrors.ProductRepository.NotFound
// Pipeline 에러
AdapterError.For<UsecaseValidationPipeline>(new PipelineValidation("PropertyName"), value, "검증에 실패했습니다");
// 에러 코드: AdapterErrors.UsecaseValidationPipeline.PipelineValidation
// Exception 래핑
AdapterError.FromException<UsecaseExceptionPipeline>(new PipelineException(), exception);
// 에러 코드: AdapterErrors.UsecaseExceptionPipeline.PipelineException (Exceptional)
// 런타임 Type 사용
AdapterError.For(GetType(), new ConnectionFailed("DB"), connectionString, "연결에 실패했습니다");
// 에러 코드: AdapterErrors.{실제타입이름}.ConnectionFailed

문서설명
에러 시스템: 기초와 네이밍에러 처리 원칙, Fin 패턴, 네이밍 규칙 R1~R8
에러 시스템: Domain/Application/EventDomain, Application, Event 에러 상세 가이드
에러 시스템: Adapter와 테스트Adapter 에러와 테스트 패턴 가이드
검증 시스템 사양TypedValidation, ContextualValidation 사양