Class RequiredString
- Namespace
- FunctionalDdd
- Assembly
- FunctionalDdd.CommonValueObjects.dll
Base class for creating strongly-typed string value objects that cannot be null or empty. Provides a foundation for domain primitives like names, descriptions, codes, and other textual concepts.
public abstract class RequiredString : ScalarValueObject<string>, IComparable<ValueObject>, IEquatable<ValueObject>, IConvertible
- Inheritance
-
RequiredString
- Implements
- Inherited Members
- Extension Methods
Examples
Creating a strongly-typed name value object:
// Define the value object (partial keyword enables source generation)
public partial class FirstName : RequiredString
{
}
// The source generator automatically creates:
// - public static Result<FirstName> TryCreate(string? value)
// - private FirstName(string value) : base(value) { }
// Usage examples:
// Create with validation
var result1 = FirstName.TryCreate("John");
// Returns: Success(FirstName("John"))
var result2 = FirstName.TryCreate("");
// Returns: Failure(ValidationError("FirstName cannot be empty"))
var result3 = FirstName.TryCreate(null);
// Returns: Failure(ValidationError("FirstName cannot be empty"))
var result4 = FirstName.TryCreate(" John ");
// Returns: Success(FirstName("John")) - automatically trimmed
// Using in entity creation
public class Person : Entity<PersonId>
{
public FirstName FirstName { get; }
public LastName LastName { get; }
public static Result<Person> Create(string firstName, string lastName) =>
FirstName.TryCreate(firstName)
.Combine(LastName.TryCreate(lastName))
.Map((first, last) => new Person(PersonId.NewUnique(), first, last));
private Person(PersonId id, FirstName firstName, LastName lastName)
: base(id)
{
FirstName = firstName;
LastName = lastName;
}
}
Using in API validation:
// Request DTO
public record CreateUserRequest(string FirstName, string LastName, string Email);
// API endpoint with automatic validation
app.MapPost("/users", (CreateUserRequest request) =>
FirstName.TryCreate(request.FirstName)
.Combine(LastName.TryCreate(request.LastName))
.Combine(EmailAddress.TryCreate(request.Email))
.Bind((first, last, email) => User.Create(first, last, email))
.ToHttpResult());
// POST /users with empty FirstName:
// Response: 400 Bad Request
// {
// "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
// "title": "One or more validation errors occurred.",
// "status": 400,
// "errors": {
// "FirstName": ["FirstName cannot be empty"]
// }
// }
Multiple string-based value objects:
public partial class FirstName : RequiredString { }
public partial class LastName : RequiredString { }
public partial class CompanyName : RequiredString { }
public partial class ProductName : RequiredString { }
public partial class Description : RequiredString { }
public class Product : Entity<ProductId>
{
public ProductName Name { get; private set; }
public Description Description { get; private set; }
public Result<Product> UpdateName(ProductName newName) =>
newName.ToResult()
.Tap(name => Name = name)
.Map(_ => this);
// Compiler prevents mixing types:
// UpdateName(description); // Won't compile!
// UpdateName(firstName); // Won't compile!
}
Advanced: Adding custom validation to derived types:
// While RequiredString handles null/empty, you can add domain-specific rules
public partial class ProductSKU : RequiredString
{
// Additional validation can be done in factory methods
public static Result<ProductSKU> TryCreateSKU(string? value) =>
TryCreate(value) // Use base validation first
.Ensure(sku => sku.Value.Length <= 20,
Error.Validation("SKU must be 20 characters or less", "sku"))
.Ensure(sku => sku.Value.All(c => char.IsLetterOrDigit(c) || c == '-'),
Error.Validation("SKU can only contain letters, digits, and hyphens", "sku"));
}
// Usage
var result = ProductSKU.TryCreateSKU("PROD-12345");
// Success
var invalid = ProductSKU.TryCreateSKU("PROD@12345");
// Failure: "SKU can only contain letters, digits, and hyphens"
Remarks
This class extends ScalarValueObject<T> to provide a specialized base for string-based value objects
with automatic validation that prevents null or empty strings. When used with the partial keyword,
the CommonValueObjectGenerator source generator automatically creates:
- Static factory method (TryCreate) with null/empty validation
- String parsing that trims whitespace
- JSON serialization support
- Comparison and equality operations
- Implicit string conversion for easy usage
Common use cases:
- Person names (FirstName, LastName, FullName)
- Product attributes (ProductName, Description, SKU)
- Location data (City, State, Country, PostalCode)
- Business identifiers (CompanyName, TaxId, AccountNumber)
- Any domain concept represented by required text
Benefits over plain strings:
- Type safety: Cannot mix FirstName with LastName
- Validation: Prevents null/empty strings at creation time
- Domain clarity: Self-documenting code that expresses intent
- Consistency: Centralized trimming and normalization
- Testability: Easy to test validation rules in isolation
Constructors
RequiredString(string)
Initializes a new instance of the RequiredString class with the specified string value.
protected RequiredString(string value)
Parameters
valuestringThe string value. Must not be null or empty.
Remarks
This constructor is protected and should be called by derived classes.
When using the source generator (with partial keyword), a private constructor
is automatically generated that includes validation and trimming.
Direct instantiation should be avoided. Instead, use the generated factory method:
TryCreate(string?)- Create from string with validation and trimming
The generated TryCreate method automatically:
- Returns validation error for null values
- Returns validation error for empty strings
- Returns validation error for whitespace-only strings
- Trims leading and trailing whitespace from valid strings