NashTech Blog

Beyond Enums: Implementing the Smart Enum Pattern in .NET 10

Table of Contents

Beyond Enums: Implementing the Smart Enum Pattern in .NET 10

Traditional enums work well for fixed values, but they cannot hold business rules or metadata. This article uses a Subscription Plan example to demonstrate how the Smart Enum Pattern creates a richer domain model.

Table of Contents

  1. The Problem
  2. Traditional Enum Limitations
  3. Introducing Smart Enums
  4. Adding Behavior
  5. Rich Metadata
  6. EF Core
  7. JSON
  8. Library vs Custom
  9. When to Use
  10. Common Mistakes
  11. Conclusion

The Problem with Traditional Enums

public enum SubscriptionPlan
{
    Free,
    Professional,
    Enterprise
}

Business rules quickly spread across the application.

if(plan == SubscriptionPlan.Free)
{
    maxUsers = 1;
    storage = 1;
}
else if(plan == SubscriptionPlan.Professional)
{
    maxUsers = 50;
    storage = 500;
}
Code smell: When every new plan requires updating multiple if or switch statements, the behavior belongs with the value itself.

Traditional Enum Limitations

  • No metadata
  • No behavior
  • Duplicated business logic
  • Hard to extend

Introducing the Smart Enum Pattern

public sealed class SubscriptionPlan
{
    public static readonly SubscriptionPlan Free =
        new(1,"Free",1,1,false);

    public static readonly SubscriptionPlan Professional =
        new(2,"Professional",50,500,true);

    public static readonly SubscriptionPlan Enterprise =
        new(3,"Enterprise",9999,5000,true);

    public int Id { get; }
    public string Name { get; }
    public int MaxUsers { get; }
    public int StorageGB { get; }
    public bool PrioritySupport { get; }

    private SubscriptionPlan(
        int id,
        string name,
        int maxUsers,
        int storageGB,
        bool prioritySupport)
    {
        Id=id;
        Name=name;
        MaxUsers=maxUsers;
        StorageGB=storageGB;
        PrioritySupport=prioritySupport;
    }
}

Adding Behavior

public bool CanCreateProject(int currentUsers)
    => currentUsers < MaxUsers;

public bool HasPrioritySupport()
    => PrioritySupport;
var plan = SubscriptionPlan.Professional;

if(plan.CanCreateProject(25))
{
    Console.WriteLine("Project created.");
}

Subscription Plan Example

PlanUsersStoragePriority Support
Free11 GBNo
Professional50500 GBYes
EnterpriseUnlimited5 TBYes
Console.WriteLine(plan.MaxUsers);
Console.WriteLine(plan.StorageGB);
Console.WriteLine(plan.HasPrioritySupport());

EF Core Mapping

builder.Entity<Customer>()
    .Property(x => x.Plan)
    .HasConversion(
        p => p.Id,
        id => SubscriptionPlan.FromId(id));

JSON Serialization

Expose only the plan identifier or name in your API.

public record CustomerDto(
    int Id,
    string Plan);

Build Your Own or Use a Library?

Libraries such as Ardalis.SmartEnum provide parsing, equality, EF Core support, and serialization. Building one yourself is useful for learning or when custom behavior is required.

When to Use

  • Subscription Plans
  • Membership Levels
  • Shipping Methods
  • Order Status
  • Payment Status

Common Mistakes

  • Replacing every enum with a Smart Enum.
  • Returning Smart Enums directly from APIs.
  • Putting database or service calls inside a Smart Enum.

Conclusion

The Subscription Plan example demonstrates why Smart Enums are more expressive than traditional enums. Each plan encapsulates its own configuration and behavior, reducing duplicated business logic and making your domain model easier to understand, test, and extend.

 Demo

Picture of Huỳnh Minh Tú

Huỳnh Minh Tú

Senior Software Engineer

Leave a Comment

Suggested Article

Scroll to Top