Skip to main content

Added deployment

· 2 min read

After this ADR


TITLE: ADR 007 - Exception Handling

  • Status: accepted

  • Deciders: ["Andrei Ignat"]

  • Date: [2023-11-05]

Context and Problem Statement

In every program we need to handle the unexpected situations. One of those are configuration errors. Others are network errors, database errors, etc. However, there are many situations where the errors are of our own - like a bug in the code, errors in the database and so on...

Decision Drivers

In order to make a difference between the errors that are of our own and the errors that are not of our own, we need to have a way to handle them differently. That's why a custom exception is needed.

Decision Outcome

Chosen option: "Use a custom exception" every time when the error is of our own. Also, use a custom exception when the error is not of our own, but we want to handle it differently.

Positive Consequences

Internal errors are handled differently than external errors. And some corrective measures can be applied.

  1. Domain-Specific Errors: When you have errors that are specific to your business logic or domain, custom exceptions can be used to represent these errors.

  2. Complex Error Handling: If your application has complex error handling requirements, custom exceptions can be used to differentiate between different types of errors.

  3. Enhanced Error Information: If you need to include additional information in your exceptions (like error codes or additional context), custom exceptions can be used.

  4. Control Flow: In some cases, custom exceptions can be used as a form of control flow. This is generally discouraged, but there are some scenarios where it can be useful.

  5. Third-Party Library Errors: If you're using a third-party library that throws generic exceptions, you might want to catch those and throw your own custom exceptions instead. This can make your error handling code more consistent and easier to understand.

Negative Consequences

  1. Loss of Inner Exception: If not handled properly, the inner exception that caused the custom exception might be lost, making it harder to debug the issue - or should be added to help the developer to understand the error.

  2. Increased Development Time: Creating and maintaining custom exceptions can increase development time.

  3. Potential for Misuse: Custom exceptions can be misused or overused, leading to unnecessary complexity.

  4. Learning Curve: Other developers unfamiliar with the custom exceptions might have a learning curve to understand them.

  5. Dependency: If the custom exceptions are part of a library that is used across multiple projects, changes to the exceptions can impact all these projects.

Pros and Cons of the Options

Pros of Using Custom Exceptions

  1. Specificity: Custom exceptions provide more specific error information, making it easier to understand and handle the error.
  2. Control: They allow for more control over error handling, as you can create different exceptions for different error conditions.
  3. Clarity: They can make the code more readable and maintainable, as the exception names can reflect the error conditions they represent.

Cons of Using Custom Exceptions

  1. Complexity: Creating custom exceptions can add complexity to the code, especially if overused.
  2. Maintenance: They require more maintenance. If the error conditions change, the custom exceptions also need to be updated.
  3. Standardization: Using standard exceptions can sometimes be more straightforward and less error-prone, as they are widely recognized and understood by most developers.

ADR based on

https://github.com/joelparkerhenderson/architecture-decision-record/tree/main/locales/en/templates/decision-record-template-of-the-madr-project

I have created the custom exception base class

namespace syncPowershellException;
public abstract class BaseAppException: Exception
{
public BaseAppException(string message): base(message)
{

}
public BaseAppException(string message, Exception inner)
: base(message, inner)
{
}

}

I resisted to the temptation to create a custom exception for every error. Example :

ArgumentNullException.ThrowIfNull(data);

should be left as it is.

Instead ,look at this example:

public async Task<DataToBeSent> GetData()
{
var powershell7Profile =environment.GetFolderPath( Environment.SpecialFolder.MyDocuments );
powershell7Profile = path.Combine(powershell7Profile, "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
DataToBeSent dataToBeSent = new ();
dataToBeSent.UserName=environment.UserName;
dataToBeSent.PC = environment.MachineName;
if (file.Exists(powershell7Profile))
{
dataToBeSent.Powershell = await file.ReadAllTextAsync(powershell7Profile, CancellationToken.None);
dataToBeSent.PowershellNumber = 7;
}
return dataToBeSent;
}

If the file does not exists , then an error must be thrown.

public class PowershellProfileNotExistsException : BaseAppException
{
public readonly string fileName;

public PowershellProfileNotExistsException(string fileName) : base($"file {fileName} does not exists")
{
this.fileName = fileName;
}

}

And the code will be modified like this:

public async Task<DataToBeSent> GetData()
{
var powershell7Profile =environment.GetFolderPath( Environment.SpecialFolder.MyDocuments );
powershell7Profile = path.Combine(powershell7Profile, "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
DataToBeSent dataToBeSent = new ();
dataToBeSent.UserName=environment.UserName;
dataToBeSent.PC = environment.MachineName;
if (!file.Exists(powershell7Profile))
throw new PowershellProfileNotExistsException(powershell7Profile);

dataToBeSent.Powershell = await file.ReadAllTextAsync(powershell7Profile, CancellationToken.None);
dataToBeSent.PowershellNumber = 7;


return dataToBeSent;
}

Also the test will be modified from

DataGatherer gatherer = new(en.Instance(), file.Instance(),path.Instance(), logger);
data = await gatherer.GetData();
data.Should().NotBeNull();

to

DataGatherer gatherer = new(en.Instance(), file.Instance(),path.Instance(), logger);
var X=async ()=> (await gatherer.GetData());
await X.Should().ThrowAsync<PowershellProfileNotExistsException>();