Skip to main content

Core

Namespace: RA.Utilities.Core.Results

The the projects within the Core solution folder (RA.Utilities.Core.Results, RA.Utilities.Core.Constants, RA.Utilities.Core.Exceptions, and RA.Utilities.Core.ValueObjects) serve as the foundational, innermost layer of your Clean Architecture. They are designed to have zero dependencies on any other layer in your solution, which is a critical principle for creating a decoupled and maintainable system.

Here’s how they support the rest of the solution:

1. RA.Utilities.Core.Results - The Heart of Business Logic Communication​

This package contains the fundamental building blocks for your application's logic.

  • Purpose: It provides a way to handle expected operational outcomes (both success and failure) without resorting to throwing exceptions. This is a powerful pattern for distinguishing between predictable failures (e.g., "user not found," "invalid input") and true, unexpected errors.
  • How it Supports Other Layers:
    • Application Layer (RA.Utilities.Feature): Your business logic (CQRS handlers) will create and return Result<T> objects. This allows a handler to clearly communicate "The operation succeeded and here is the data" or "The operation failed for this specific business reason."
    • API Layer (RA.Utilities.Api): The API endpoints receive the Result<T> object from the application layer. As shown The endpoint can then use the .Match() method to transform the result into the appropriate HTTP response (200 OK on success, or a 400 Bad Request/404 Not Found on a predictable failure). This cleanly separates business logic from API concerns.

2. RA.Utilities.Core.Exceptions - Standardizing Unexpected Errors​

This package defines a set of custom, semantic exceptions that represent exceptional, unrecoverable application states.

  • Purpose: To create a standardized vocabulary for exceptional events. When your application logic encounters a situation it cannot recover from (e.g., trying to retrieve an entity that must exist but doesn't), it can throw a specific exception like NotFoundException or ConflictException.
  • How it Supports Other Layers:
    • Application & Data Layers: These layers can throw these exceptions without needing any knowledge of HTTP status codes. For example, a repository can throw NotFoundException("Product", 123) if a product with that ID is not found.
    • API Layer (RA.Utilities.Api): The GlobalExceptionHandler middleware in this layer is specifically designed to catch these custom exceptions. It acts as a translator, converting a NotFoundException into an HTTP 404 Not Found response, and a ConflictException into an HTTP 409 Conflict. This is a perfect example of the API layer adapting core business exceptions into web-specific responses.

3. RA.Utilities.Core.Constants - Eliminating Magic Strings​

This is the simplest but arguably one of the most important packages for maintaining consistency.

  • Purpose: It centralizes shared, read-only values: HTTP status codes (BaseResponseCode), standard response messages (BaseResponseMessages), validation error codes and messages (BaseErrorCode, BaseErrorMessage), HTTP header names (HeaderParameters), and the semantic ResponseType enum.
  • How it Supports Other Layers: Any project that needs to reference a common value can depend on this package. For example:
    • The DefaultHeadersMiddleware in RA.Utilities.Api needs the exact name of the request-id header. Instead of hardcoding "x-request-id", it references HeaderParameters.XRequestId from this package.
    • Exceptions in RA.Utilities.Core.Exceptions use BaseResponseCode, BaseResponseMessages, and ResponseType to build their default HTTP mapping, so every semantic exception reports the same code and wording as the rest of the ecosystem.

4. RA.Utilities.Core.ValueObjects - Strongly-Typed Domain Primitives​

This package provides validated, normalized value objects like Email, Currency, SSN, Money, and IBAN.

  • Purpose: To eliminate primitive obsession by wrapping simple concepts in types that guarantee validity. An Email that exists is always a normalized, valid email — invalid states are unrepresentable.
  • How it Supports Other Layers:
    • Application Layer (RA.Utilities.Feature): Business logic and CQRS handlers accept value objects instead of raw strings, so validation happens once at the boundary and never needs re-checking in the domain.
    • API Layer (RA.Utilities.Api): IParsable<T> implementations enable minimal API route, query, and header binding with automatic 400 Bad Request on invalid input, while the built-in System.Text.Json converters keep serialization contracts clean.

Summary​

In essence, the Core packages form the stable, central foundation of your entire ecosystem. They enforce the Dependency Rule of Clean Architecture: all dependencies flow inwards.

  • Api, Data, and Infrastructure all depend on Core.
  • Core depends on nothing.

This structure makes your solution incredibly robust, testable, and easy to reason about. Your business logic remains pure and independent of external details like databases, web frameworks, or logging providers.