Robust Design through Value Objects in C#
Tags: csharp, fsharp, architecture, ddd
Published: 2023-09-18 16:24:36

In our [previous discussion](https://dev.to/maxarshinov/type-based-design-386l), we delved into the creation of the `UserContact` class, honoring idiomatic C# patterns with the utilization of `DataAnnotations` attributes and the `TryParse` method. Despite its functionality, the reliance on `DataAnnotations` attributes has been [criticized as a design smell](https://blog.ploeh.dk/2011/05/27/DesignSmellRedundantRequiredAttribute/), a perspective that holds merit. How can we ensure the robustness of our `UserContact` class while avoiding potential design pitfalls?

## **Enriching C# with F# Insights**

An ideal solution seems to be embracing the F# approach of "narrowing" existing types to create self-validating, robust value types:

```fsharp
type EmailAddress = private EmailAddress of string

let createEmailAddress (s:string) =
    if System.Text.RegularExpressions.Regex.IsMatch(s,@"^\S+@\S+\.\S+$")
        then Some (EmailAddress s)
        else None
        
let emailAddress = createEmailAddress "foo@bar.com"
```

This F# snippet gives us a glimpse of a design where types guarantee the correctness of the values they hold, reducing runtime errors and simplifying the code.

## **Bridging the Gap in C#**

While C# currently lacks direct support for this kind of functionality, there's a glimmer of hope with an [active proposal](https://github.com/dotnet/csharplang/discussions/5496) under discussion that aims to bring this feature to the language. This potential addition promises a future where C# can natively offer similar robust type narrowing.


As we anticipate advancements in C#, we can still forge ahead with crafting resilient applications by utilizing the "[ValueOf](https://github.com/mcintyre321/ValueOf/tree/master)" package as a provisional solution. Below, we create a validated EmailAddress class leveraging the package:

```csharp
public class EmailAddress : ValueOf<string, EmailAddress> 
{ 
    protected override bool TryValidate()
    {
        var index = Value.IndexOf('@');

        var isValid =
            index > 0 &&
            index != Value.Length - 1 &&
            index == Value.LastIndexOf('@');

        return isValid;
    }

    protected override void Validate()
    {
        if (!TryValidate())
        {
            throw new ValidationException($"{Value} is not a valid email address");
        }
    }
}

// ...

EmailAddress emailAddress = EmailAddress.From("foo@bar.com");
```

Not only does this approach centralize validation logic, but it also paves the way for cleaner and more maintainable code.

## Revamping the `UserContact` Class

Let's refactor the `UserContact` class from the previous article with `ValueOf`:

```csharp
public class UserContact
{
    public EmailAddress Email { get; protected set; } // protected for EF
    
    public Phone Phone { get; protected set; } // protected for EF

    protected UserContact(){} // protected for EF

    public static UserContact Create(EmailAddress email) => 
        new() { Email = email }; 
    
    public static UserContact Create(Phone phone) =>
        new() { Phone = phone };
    
    public static UserContact Create(
        EmailAddress email, Phone phone) => 
        new() { Email = email, Phone = phone };
}
```

In this renovated version, we introduce dedicated types for `Email` and `Phone`, ensuring type safety and facilitating various creation methods for different scenarios, be it with an email, a phone number, or both. Also, thanks to the `protected` access modifier, fields, and the empty constructor are accessible for Entity Framework but not for developers without using reflection.

## Revamping the `User` Class

Let's go further and a `SignUp` type representing a command from the frontend:

```csharp
public class User
{
   public int Id { get; protected set; } // protected for EF
    
   public Contact Contact { get; protected set; } // protected for EF
  
   public Profile? Profile { get; protected set; } // protected for EF

   protected User() {} // protected for EF

   public User (SignUp command)
   {
       Contact = command.Contact;
       Profile = command.Profile;
   }
}
```

We can even add a specialized constructor per user creation use case:

```csharp
public class User
{
   //...
    
   public User (SignUpByInvite command): this(command)
   {
       Invitee = command.Invitee;
   }    
}
```

Now it has become clear that a user can register independently (`SignUp`) or register through a friend's invitation (`SignUpByInvite`). The invitation mechanism might lead someone reading the code to think that there is a referral program in the system. This change has another unexpected side effect. Imagine that there are two different error messages in the logs:

1. Something went wrong during user registration
2. Something went wrong when Alice tried to register through Bob's invitation

The second message is much more informative. It specifies Alice and Bob, and the specific registration process, rather than an abstract registration error. Thus, maintaining the product will be somewhat easier for new developers, who are not familiar with all the requirements.

#### **Conclusion**

As we've traversed the nuances of enhancing type safety in our `UserContact` class, we see how leaning towards a type-centric design, inspired by F# practices and facilitated by the "ValueOf" package, crafts a framework that is both robust and resistant to typical design smells.

Yet, it's pivotal to note a significant consideration when working with the "ValueOf" package in a .NET environment, particularly in context with the ASP.NET Core model binding. "ValueOf" is not supported by default. You have to implement a custom model binder if you want to use these kinds of value objects in your DTOs. 

As we wrap up, it would be remiss not to encourage you to experiment with creating a `Phone` type, akin to the `EmailAddress` type.