Domain Concepts
This outline describes how Vogen represents domain concepts as value objects.
- Value objects are immutable objects whose equality is based on value, not identity. README.md
- They are created using a factory method named
From
, e.g.,Age.From(12)
. README.md - They are equatable, e.g.,
Age.From(12) == Age.From(12)
. README.md - They are validated with a static method named
Validate
that returns aValidation
result. README.md - Any validation that is not
Validation.Ok
results in aValueObjectValidationException
being thrown. README.md
Example:
[ValueObject]
public partial struct CustomerId;
[ValueObject]
public partial struct AccountId;
[ValueObject]
public partial struct PaymentAmount;
// Usage:
public void HandlePayment(CustomerId customerId, AccountId accountId, PaymentAmount paymentAmount)
{
// ...
}
Benefits of Using Value Objects:
- Strong typing: Value objects enforce better method signatures, making code more readable and preventing accidental misuse of parameters. README.md
- Validation in one place: Value objects ensure that all instances are valid, eliminating the need for repeated validation checks. README.md
- Domain-specific constraints: They allow you to represent and enforce constraints on domain concepts that cannot be represented using primitive types. README.md
Creating Value Objects:
- Use the
[ValueObject]
attribute to mark a struct as a value object. README.md - The value object is constructed via a factory method named
From
. README.md - Validation logic is encapsulated in a static method named
Validate
. README.md - If validation fails, a
ValueObjectValidationException
is thrown. README.md
Example:
[ValueObject]
public partial struct CustomerId
{
private static Validation Validate(int input) => input > 0
? Validation.Ok
: Validation.Invalid("Customer IDs must be greater than 0.");
}
Specifying Pre-Set Values:
- You can define pre-set instances of value objects using the
[Instance]
attribute. docs/site/Writerside/topics/tutorials/Specifying-pre-set-values.md - These pre-set instances can be used to represent special domain concepts like “unspecified” or “invalid.” docs/site/Writerside/topics/tutorials/Specifying-pre-set-values.md
Example:
[ValueObject]
[Instance("Unspecified", "0")]
public partial struct CustomerId
{
private static Validation Validate(int input) => input > 0
? Validation.Ok
: Validation.Invalid("Customer IDs must be greater than 0.");
}
// Usage:
public CustomerId TryGetOptionalCustomerId(string input)
{
if (string.IsNullOrEmpty(input))
{
return CustomerId.Unspecified;
}
return CustomerId.From(123);
}
Primitive Obsession:
- Primitive obsession refers to the overuse of primitive types like
int
,string
, anddouble
to represent domain concepts. README.md - This can lead to code that is harder to understand, maintain, and validate. README.md
Example:
int customerId = 42; // Primitive obsession
Why Value Objects are Better:
- They provide a way to represent domain concepts more accurately and enforce domain-specific constraints. README.md
- They improve code readability and maintainability by encapsulating validation logic and providing a consistent way to work with domain objects. README.md
- They make it easier to ensure data integrity and reduce the risk of errors caused by invalid data. README.md
## Top-Level Directory Explanations
<a class='local-link directory-link' data-ref="samples/" href="#samples/">samples/</a> - This directory contains example projects that demonstrate the usage of the Vogen library. Each subdirectory represents a different example, and contains the necessary files and configurations for that example.
<a class='local-link directory-link' data-ref="src/" href="#src/">src/</a> - This directory contains the source code for the project. It includes the Vogen library itself, as well as any shared types and code fixers.
<a class='local-link directory-link' data-ref="src/obj/" href="#src/obj/">src/obj/</a> - This directory contains object files generated during the compilation process.
<a class='local-link directory-link' data-ref="src/Vogen/" href="#src/Vogen/">src/Vogen/</a> - This subdirectory contains the core Vogen library code. It includes subdirectories for diagnostics, extensions, generators, properties, rules, suppressors, templates, and binaries and object files.
<a class='local-link directory-link' data-ref="tests/" href="#tests/">tests/</a> - This directory contains unit tests and benchmarks for the project. It includes subdirectories for analyzer tests, consumer tests, snapshot tests, and Vogen benchmarks.
<a class='local-link directory-link' data-ref="tests/AnalyzerTests/" href="#tests/AnalyzerTests/">tests/AnalyzerTests/</a> - This subdirectory contains unit tests for the analyzer component of the Vogen library.
<a class='local-link directory-link' data-ref="tests/ConsumerTests/" href="#tests/ConsumerTests/">tests/ConsumerTests/</a> - This subdirectory contains unit tests for the consumer-side components of the Vogen library.
<a class='local-link directory-link' data-ref="tests/SnapshotTests/" href="#tests/SnapshotTests/">tests/SnapshotTests/</a> - This subdirectory contains snapshot tests, which test the output of the code generation and serialization components of the Vogen library.