# Vogen for guarded C# value objects

> Vogen is a .NET source generator and analyzer that turns primitives such as int, decimal, and string into named C# value objects. It is suited to domain code where invalid states and accidental primitive substitution need compiler-enforced boundaries, but its generated constructors and analyzer restrictions add constraints.

**SteveDunn/Vogen** — A semi-opinionated library which is a source generator and a code analyser. It Source generates Value Objects

- Repository: https://github.com/SteveDunn/Vogen
- Stars: 1,506 · Forks: 93
- Language: C#
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/stevedunn-vogen

## Why Vogen replaces ambiguous C# primitives

Vogen addresses primitive obsession in C# domain models. An `int` can represent a customer identifier, an account number, a quantity, or a status code without carrying that meaning in the type itself. Passing the wrong integer to a method is therefore accepted by the compiler. Vogen replaces such values with named types such as `CustomerId`, `AccountBalance`, and `LegalEntityName`, making the domain concept visible in signatures and local declarations.

The library is aimed at developers who want domain constraints enforced closer to compile time. Its central mechanism is not naming alone. Vogen adds generated behavior and analyzer diagnostics intended to prevent creation of uninitialized or otherwise invalid instances. A method that accepts `CustomerId` receives a different contract from one accepting a bare `int`.

The README also positions Vogen for semi-opinionated use. That matters because the generator does not merely wrap a primitive and leave construction policy open. It generates a controlled creation path and introduces rules that developers must understand before adopting it across a solution.

## How Vogen generates and guards a value object

The initial declaration supplies an attribute and, optionally, the underlying primitive type. Vogen generates the remaining partial struct or class, including storage, equality members, conversion operators, text output, and a factory. For `int`, the README describes a generated `CustomerId` with a private `int` field, a `Value` property, a private constructor, and `From(int value)`.

The generated `From` method is the intended construction path. The public default constructor exists to support the value type shape, but its body throws `Vogen.ValueObjectValidationException` when called. The README says the analyzer prevents normal application code from using that constructor. This combines generation with compiler enforcement: generated code establishes the API, while the analyzer reports prohibited source-level construction patterns.

For a struct, `default` is a special risk because it can bypass the generated private constructor. The documentation consequently assigns diagnostics to `default`, `default(CustomerId)`, `new CustomerId()`, target-typed `new()`, lambdas, return expressions, optional parameters, and calls to `Activator.CreateInstance`. The examples identify diagnostics including `VOG009`, `VOG010`, and `VOG025`. The boundary is explicit: direct constructors and several reflective or language-level creation paths are intended to produce compiler errors.

## A first Vogen value object and domain call

Package installation commands are not shown here. It directs readers to the project Wiki for getting started, tutorials, and how-to material, and links the NuGet package as `Vogen`. Since no install command or project-file configuration is documented here, this tutorial starts with the smallest declaration explicitly shown by the README.

Declare a partial struct and apply `[ValueObject<int>]`. The type argument makes the stored primitive explicit, which the README recommends even though `int` is the default.

```csharp
[ValueObject<int>]
public partial struct CustomerId;
```

Create the identifier through the generated factory rather than `new` or `default`:

```csharp
CustomerId customerId = CustomerId.From(123);
SendInvoice(customerId);
```

The generated type exposes `Value`, equality against both `CustomerId` and the primitive, comparison members, `GetHashCode`, and `ToString`. Some generated members in the example end with ellipses.

Other domain concepts follow the same declaration model. A monetary concept uses `decimal`, while a class-based text concept uses `string`:

```csharp
[ValueObject<decimal>]
public partial struct AccountBalance;
```

```csharp
[ValueObject<string>]
public partial class LegalEntityName;
```

These examples show that the wrapper type is a project decision. Struct and class are both supported in the declarations shown, but the excerpt does not document serialization details, nullable behavior, or the precise default primitive for every wrapper form.

## What Vogen's construction rules cost developers

The strict construction model is Vogen's strongest feature and its most visible constraint. User-defined constructors are rejected because they could fail to initialize the backing value. The documentation reports `VOG008` for a user-defined constructor and points developers to `From`. Generated default-constructor use, `default`, `new`, target-typed construction, and reflection are also subject to analyzer rules.

This design can prevent a common value-object defect, but it may require changes in code that relies on serializers, object mappers, frameworks, or generic code expecting an accessible constructor. The README does not explain how Vogen handles those integrations, nor does it document a bypass or suppression policy. Those cases should be checked against the Wiki before choosing the library for a framework-heavy application.

The generated surface can also affect equality decisions. The example supports equality between the wrapper and its primitive and provides equality operators. That convenience means developers must decide whether comparison with the raw value is desirable at their domain boundary. Serialization contracts, persistence mappings, culture-sensitive text conversion, and custom equality policy are not documented here.

## Vogen compared with hand-written value objects

The direct alternative is to write value objects by hand. A team can define a `CustomerId` struct, keep its value private, add a factory, define equality and hashing, and use constructor validation. That approach provides direct control over the public API and framework integration without requiring generated code.

Vogen differs by deriving a substantial portion of that boilerplate from a partial type and by adding an analyzer that treats prohibited construction as compilation errors. The trade-off is less freedom. Developers cannot add user-defined constructors, and several language or reflection paths are blocked by design. Vogen is therefore more suitable for codebases that want a consistent generated contract than for projects that need a custom wrapper lifecycle.

It also differs from a simple record or type alias. A record can provide value equality, while a type alias does not create a distinct runtime type, but neither example documented here supplies Vogen's factory-centered validation model or analyzer diagnostics.

## Vogen release status and adoption boundaries

The repository is not archived, and the last push was on 2026-10-14. The listed releases include `9.0.0-beta.2` on 2026-07-21, `8.0.7` with the displayed package label `8.0.6` on the same date, and `9.0.0-beta.1` on 2026-07-10. The presence of a beta release means adoption planning should distinguish the stable `8.0.7` entry from the experimental `9.0.0-beta.2` entry. The excerpt does not provide a migration guide or compatibility matrix for moving between them.

Vogen is licensed under Apache-2.0. The repository includes `LICENSE`, `CONTRIBUTING.md`, and a `Dockerfile`. The Dockerfile starts from `mcr.microsoft.com/dotnet/sdk:7.0` and runs `Build.ps1`, but no consumer package command or complete setup walkthrough is shown.

For a team, the initial adoption boundary is the domain layer. Wrapping every primitive may create excessive types where no invariant exists. Vogen is more defensible when a primitive carries a distinct meaning, has validation, and should not be exchanged accidentally with another primitive.

## Conclusion

Adopt Vogen when C# domain types need named primitives, centralized validation, and compiler diagnostics around invalid construction. Do not choose it if generated APIs or strict construction restrictions conflict with an existing model. Verify first that your supported underlying types, required validation behavior, generator package version, and solution build diagnostics match your project, especially when evaluating the 9.0.0 beta line.

## FAQ

### What is Vogen used for in C#?

Vogen turns primitives such as `int`, `decimal`, and `string` into named value objects such as `CustomerId` and `AccountBalance`. It is intended for domain code where the meaning and validity of a primitive need to be represented in the type.

### How does Vogen create a value object?

The README shows applying `[ValueObject<int>]` to a partial struct and then calling `CustomerId.From(123)`. Vogen generates the backing value, factory, equality members, and related methods, while its analyzer blocks several prohibited construction paths.

### Can Vogen value objects use new constructors or default values?

The documented design says user-defined constructors are prohibited and points developers to the `From` method. The analyzer reports diagnostics for `default`, `new`, target-typed `new()`, and `Activator.CreateInstance` examples shown in the README.

## Sources

- [Issues](https://github.com/SteveDunn/Vogen/issues)
- [License: Apache-2.0](https://github.com/SteveDunn/Vogen/blob/main/LICENSE)
- [README](https://github.com/SteveDunn/Vogen/blob/main/README.md)
- [Releases](https://github.com/SteveDunn/Vogen/releases)
- [SteveDunn/Vogen on GitHub](https://github.com/SteveDunn/Vogen)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/stevedunn-vogen
