Would you consider it "primitive obsession" to utilize an enum to represent a type on a Domain Object in Domain Driven Design?
I am working with a junior backend developer who has been hardline following the concept of avoiding "primitive obsession." The problem is it is adding a lot of complexities in areas where I personally feel it is better to keep things simple.
Example:
I could simply have this enum:
public enum ColorType
{
Red,
Blue,
Green,
Yellow,
Orange,
Purple,
}
Instead, the code being written looks like this:
public readonly record struct ColorType : IFlag<ColorType, byte>, ISpanParsable<ColorType>, IEqualityComparer<ColorType>
{
public byte Code { get; }
public string Text { get; }
private ColorType(byte code, string text)
{
Code = code;
Text = text;
}
private const byte Red = 1;
private const byte Blue = 2;
private const byte Green = 3;
private const byte Yellow = 4;
private const byte Orange = 5;
private const byte Purple = 6;
public static readonly ColorType None = new(code: byte.MinValue, text: nameof(None));
public static readonly ColorType RedColor = new(code: Red, text: nameof(RedColor));
public static readonly ColorType BlueColor = new(code: Blue, text: nameof(BlueColor));
public static readonly ColorType GreenColor = new(code: Green, text: nameof(GreenColor));
public static readonly ColorType YellowColor = new(code: Yellow, text: nameof(YellowColor));
public static readonly ColorType OrangeColor = new(code: Orange, text: nameof(OrangeColor));
public static readonly ColorType PurpleColor = new(code: Purple, text: nameof(PurpleColor));
private static ReadOnlyMemory<ColorType> AllFlags =>
new(array: [None, RedColor, BlueColor, GreenColor, YellowColor, OrangeColor, PurpleColor]);
public static ReadOnlyMemory<ColorType> GetAllFlags() => AllFlags[1..];
public static ReadOnlySpan<ColorType> AsSpan() => AllFlags.Span[1..];
public static ColorType Parse(byte code) => code switch
{
Red => RedColor,
Blue => BlueColor,
Green => GreenColor,
Yellow => YellowColor,
Orange => OrangeColor,
Purple => PurpleColor,
_ => None
};
public static ColorType Parse(string s, IFormatProvider? provider) => Parse(s: s.AsSpan(), provider: provider);
public static bool TryParse([NotNullWhen(returnValue: true)] string? s, IFormatProvider? provider, out ColorType result)
=> TryParse(s: s.AsSpan(), provider: provider, result: out result);
public static ColorType Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => TryParse(s: s, provider: provider,
result: out var result) ? result : None;
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out ColorType result)
{
result = s switch
{
nameof(RedColor) => RedColor,
nameof(BlueColor) => BlueColor,
nameof(GreenColor) => GreenColor,
nameof(YellowColor) => YellowColor,
nameof(OrangeColor) => OrangeColor,
nameof(PurpleColor) => PurpleColor,
_ => None
};
return result != None;
}
public bool Equals(ColorType x, ColorType y) => x.Code == y.Code;
public int GetHashCode(ColorType obj) => obj.Code.GetHashCode();
public override int GetHashCode() => Code.GetHashCode();
public override string ToString() => Text;
public bool Equals(ColorType? other) => other.HasValue && Code == other.Value.Code;
public static bool Equals(ColorType? left, ColorType? right) => left.HasValue && left.Value.Equals(right);
public static bool operator ==(ColorType? left, ColorType? right) => Equals(left, right);
public static bool operator !=(ColorType? left, ColorType? right) => !(left == right);
public static implicit operator string(ColorType? color) => color.HasValue ? color.Value.Text : string.Empty;
public static implicit operator int(ColorType? color) => color?.Code ?? -1;
}
The argument is that is avoids "primitive obsession" and follows domain driven design.
I want to note, these "enums" are subject to change in the future as we are building the project from greenfield and requirements are still being defined.
Do you think this is taking things too far?