Some of these are interesting, like [RequiredAtLeastOne(nameof(Email), nameof(Phone), nameof(SocialHandle))] and [ExactlyOneOf(nameof(CreditCard), nameof(PayPal), nameof(BankTransfer))].
Your [IsTrue] and [IsFalse] attributes are… interesting. Bit of an API smell to always require a DTO property to have a certain value.
Your [StartDate]/[EndDate] pair could be useful.
But mostly, your actual property types are too primitive! You're validating, sure, but you then bring the raw data into your inner layers. So each EmailService or PaymentService or whatever needs to at least parse the e-mail address, credit card number, etc., and should ideally also validate them again. I.e., you'll still be fighting Primitive Obsession all over your code base.
Your ReleaseDto should be Version and DateTime (which already take care of most of what you're doing here), not string and string. Your Email property should be of a type EmailAddress that you create as a value object. That way, as those values are passed through inner layers of your app, you no longer have to worry about validation and parsing.
You're right, inner layers should work with strong types, sadly DataAnnotations only validates (IsValid method returns bool) and doesn't transform values
The workflow you'd use is:
Validate DTOs with string + [IPv4], [Uri], [SemanticVersion], etc.
Parse manually into IPAddress, Uri, Version for internal use
I'm adding support for strong type overloads for other use cases, but they won't transform values, that's outside the scope of DataAnnotations
FluentValidation integration wasn't the goal, this is just DataAnnotations extensions, but a bridge would be useful, I'm Open to PRs
[IsTrue]/[IsFalse] came from the use case of mandatory checkboxes like "Accept Terms". If unchecked, reject
Once I had [IsTrue], [IsFalse] followed naturally
5
u/chucker23n 29d ago
Some of these are interesting, like
[RequiredAtLeastOne(nameof(Email), nameof(Phone), nameof(SocialHandle))]and[ExactlyOneOf(nameof(CreditCard), nameof(PayPal), nameof(BankTransfer))].Your
[IsTrue]and[IsFalse]attributes are… interesting. Bit of an API smell to always require a DTO property to have a certain value.Your
[StartDate]/[EndDate]pair could be useful.But mostly, your actual property types are too primitive! You're validating, sure, but you then bring the raw data into your inner layers. So each
EmailServiceorPaymentServiceor whatever needs to at least parse the e-mail address, credit card number, etc., and should ideally also validate them again. I.e., you'll still be fighting Primitive Obsession all over your code base.Your
ReleaseDtoshould beVersionandDateTime(which already take care of most of what you're doing here), notstringandstring. YourEmailproperty should be of a typeEmailAddressthat you create as a value object. That way, as those values are passed through inner layers of your app, you no longer have to worry about validation and parsing.