diff --git a/ECMJobRunner.Application/Common/Exceptions/DEXJobException.cs b/ECMJobRunner.Application/Common/Exceptions/DEXJobException.cs
deleted file mode 100644
index 5cb974f..0000000
--- a/ECMJobRunner.Application/Common/Exceptions/DEXJobException.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System;
-
-namespace ECMJobRunner.Application.Common.Exceptions
-{
- ///
- /// Exception thrown when a DEX job operation fails
- ///
- public class DEXJobException : Exception
- {
- ///
- /// Initializes a new instance with an inner exception
- ///
- /// Name of the query that failed
- /// Batch ID associated with the operation
- /// SQL query that was executed (nullable)
- /// The inner exception that caused the failure
- public DEXJobException(string queryName, string batchId, string? sqlQuery, Exception innerException)
- : base(
- $"[SQL Execution Failure] Query '{queryName}' could not be completed for Batch '{batchId}'." + Environment.NewLine +
- "─────────────────────────────────────────" + Environment.NewLine +
- " Query:" + (sqlQuery is null ? string.Empty : Environment.NewLine + $" {sqlQuery}") + Environment.NewLine + Environment.NewLine +
- " Root Cause:" + Environment.NewLine +
- $" {innerException?.Message}" + Environment.NewLine +
- "─────────────────────────────────────────",
- innerException)
- {
- SqlQuery = sqlQuery;
- BatchId = batchId;
- QueryName = queryName;
- }
-
- ///
- /// Initializes a new instance with a reason message
- ///
- /// Name of the query that failed
- /// Batch ID associated with the operation
- /// SQL query that was executed (nullable)
- /// Reason for the failure
- public DEXJobException(string queryName, string batchId, string? sqlQuery, string reason)
- : base(
- $"[SQL Execution Failure] Query '{queryName}' could not be completed for Batch '{batchId}'." + Environment.NewLine +
- "─────────────────────────────────────────" + Environment.NewLine +
- " Query:" + (sqlQuery is null ? string.Empty : Environment.NewLine + $" {sqlQuery}") + Environment.NewLine + Environment.NewLine +
- " Reason:" + Environment.NewLine +
- $" {reason}" + Environment.NewLine +
- "─────────────────────────────────────────")
- {
- SqlQuery = sqlQuery;
- BatchId = batchId;
- QueryName = queryName;
- }
-
- ///
- /// Gets the SQL query that failed (if available)
- ///
- public string? SqlQuery { get; }
-
- ///
- /// Gets the batch ID associated with the operation
- ///
- public string BatchId { get; }
-
- ///
- /// Gets the name of the query that failed
- ///
- public string QueryName { get; }
- }
-}
diff --git a/ECMJobRunner.Application/Common/Exceptions/JobException.cs b/ECMJobRunner.Application/Common/Exceptions/JobException.cs
new file mode 100644
index 0000000..0c89a86
--- /dev/null
+++ b/ECMJobRunner.Application/Common/Exceptions/JobException.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+
+namespace ECMJobRunner.Application.Common.Exceptions
+{
+ ///
+ /// Base exception class for job execution failures
+ /// Provides a flexible structure for capturing job context and detailed error information
+ ///
+ public class JobException : Exception
+ {
+ ///
+ /// Initializes a new instance of JobException with detailed context information
+ ///
+ /// Name of the job that failed (e.g., "SQL Main Query", "ReC Request")
+ /// Name of the process/stage being executed (e.g., "MainQueryExecution", "CheckQueryValidation")
+ /// Unique batch identifier for tracking the execution
+ /// Human-readable reason for the failure (nullable)
+ /// The underlying exception that caused the failure (nullable)
+ /// Additional contextual details as name-value pairs with optional null-handling
+ ///
+ /// The details parameter accepts tuples with:
+ /// - Name: Display name of the detail
+ /// - Value: String value of the detail (nullable)
+ /// - IgnoreIfNull: If true, the detail is omitted from the message when value is null
+ ///
+ public JobException(string jobName, string processName, string batchId, string? reason, Exception? innerException, params (string Name, string? Value, bool IgnoreIfNull)[] details)
+ : base(
+ Message(jobName,
+ [
+ ("Process Name", processName, false),
+ ("Batch Id", batchId, false),
+ ("Reason", reason, true),
+ ..details
+ ]),
+ innerException)
+ {
+ JobName = jobName;
+ ProcessName = processName;
+ BatchId = batchId;
+ }
+
+ ///
+ /// Gets the name of the job that failed
+ ///
+ public string JobName { get; }
+
+ ///
+ /// Gets the name of the process/stage that was being executed when the failure occurred
+ ///
+ public string ProcessName { get; }
+
+ ///
+ /// Gets the unique batch identifier for tracking the execution
+ ///
+ public string BatchId { get; }
+
+ ///
+ /// Generates a formatted error message with job context and details
+ ///
+ /// Name of the job that failed
+ /// Collection of name-value pairs with optional null-handling
+ /// Formatted multi-line error message with visual separators
+ ///
+ /// Message format:
+ ///
+ /// {jobName} could not be completed.
+ /// ─────────────────────────────────────────
+ /// Process Name: {processName}
+ /// Batch Id: {batchId}
+ /// {additional details...}
+ /// ─────────────────────────────────────────
+ ///
+ /// Details with IgnoreIfNull=true are omitted when their value is null.
+ ///
+ internal static string Message(string jobName, IEnumerable<(string Name, string? Value, bool IgnoreIfNull)> details)
+ {
+ var message = new System.Text.StringBuilder();
+ message.AppendLine($"{jobName} could not be completed.");
+ message.AppendLine("─────────────────────────────────────────");
+ foreach (var (name, value, ignoreNullValue) in details)
+ {
+ if (ignoreNullValue && value is null)
+ continue;
+ message.AppendLine($" {name}: {value}");
+ }
+ message.AppendLine("─────────────────────────────────────────");
+ return message.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ECMJobRunner.Application/Common/Exceptions/JobHttpException.cs b/ECMJobRunner.Application/Common/Exceptions/JobHttpException.cs
new file mode 100644
index 0000000..8243aff
--- /dev/null
+++ b/ECMJobRunner.Application/Common/Exceptions/JobHttpException.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace ECMJobRunner.Application.Common.Exceptions
+{
+ ///
+ /// Exception for HTTP client-related job failures (e.g., ReC API calls, REST requests)
+ /// Extends JobException with client library and method context
+ ///
+ public class JobHttpException : JobException
+ {
+ ///
+ /// Initializes a new instance of JobHttpException with HTTP client context
+ ///
+ /// Name of the job that failed (e.g., "ReC Request", "API Call")
+ /// Name of the process/stage being executed
+ /// Unique batch identifier for tracking
+ /// Human-readable reason for the failure (nullable)
+ /// Name of the HTTP client library used (e.g., "ReC.Client", "HttpClient") (nullable)
+ /// Name of the client method that failed (e.g., "ExecuteAsync", "PostAsync") (nullable)
+ /// The underlying exception that caused the failure (nullable)
+ ///
+ /// Use this exception for HTTP-related failures such as:
+ /// - ReC API request failures
+ /// - REST API communication errors
+ /// - HTTP client timeout/network issues
+ /// - Authentication/authorization failures
+ ///
+ public JobHttpException(string jobName, string processName, string batchId, string? reason, string? clientLibrary, string? clientMethod, Exception? innerException) : base(jobName, processName, batchId, reason, innerException,
+ ("Client Library", clientLibrary, true),
+ ("Client Method", clientMethod, true))
+ {
+ ClientLibrary = clientLibrary;
+ ClientMethod = clientMethod;
+ }
+
+ ///
+ /// Gets the name of the HTTP client library that was used (e.g., "ReC.Client", "HttpClient")
+ ///
+ public string? ClientLibrary { get; }
+
+ ///
+ /// Gets the name of the client method that failed (e.g., "ExecuteAsync", "PostAsync")
+ ///
+ public string? ClientMethod { get; }
+ }
+}
diff --git a/ECMJobRunner.Application/Common/Exceptions/JobSqlException.cs b/ECMJobRunner.Application/Common/Exceptions/JobSqlException.cs
new file mode 100644
index 0000000..cde9537
--- /dev/null
+++ b/ECMJobRunner.Application/Common/Exceptions/JobSqlException.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace ECMJobRunner.Application.Common.Exceptions
+{
+ ///
+ /// Exception for SQL query execution failures
+ /// Extends JobException with SQL query context for debugging
+ ///
+ public class JobSqlException : JobException
+ {
+ ///
+ /// Initializes a new instance of JobSqlException with SQL query context
+ ///
+ /// Name of the job that failed (e.g., "Main Query Execution", "Check Query")
+ /// Name of the process/stage being executed
+ /// Unique batch identifier for tracking
+ /// Human-readable reason for the failure (nullable)
+ /// The SQL query that failed (nullable, for debugging purposes)
+ /// The underlying SQL exception (nullable)
+ ///
+ /// Use this exception for SQL-related failures such as:
+ /// - Main query execution errors
+ /// - Check query validation failures
+ /// - Database connection issues
+ /// - SQL syntax errors
+ /// - Query timeout exceptions
+ ///
+ /// The Query property can be logged for debugging but should be handled carefully
+ /// to avoid exposing sensitive data in production logs.
+ ///
+ public JobSqlException(string jobName, string processName, string batchId, string? reason, string? query, Exception? innerException) : base(jobName, processName, batchId, reason, innerException,
+ ("Query", query, true))
+ {
+ Query = query;
+ }
+
+ ///
+ /// Gets the SQL query that failed (nullable)
+ ///
+ ///
+ /// This property is marked as virtual to allow derived classes to customize query handling
+ /// (e.g., sanitizing sensitive data, truncating long queries)
+ ///
+ public virtual string? Query { get; }
+ }
+}