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 a Validation result. README.md
  • Any validation that is not Validation.Ok results in a ValueObjectValidationException 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:

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, and double 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.