Value objectsThe unconstructable-invalid idiom
No narration yet
Module 3 · Lesson 113 min

The unconstructable-invalid idiom

A value object is a small immutable thing defined entirely by its values: a flight number, a percentage, a note. It has no identity of its own. The DDD move that matters most in Fusion is this: a value object cannot exist in an invalid state. You make that true by hiding the constructor and forcing every caller through a validating factory.

Concretely: the constructor is private, and the only way in is public static Result<T> Create(...). The factory validates and either hands back a valid instance or a failure. Because there's no public new, an invalid value object simply cannot be built anywhere in the codebase. The type system carries the invariant.

A single-value VO (domain/ValueObjects/FlightNumberVo.cs)
public sealed class FlightNumberVo : SingleValueObject<FlightNumberVo, string>
{
    private FlightNumberVo(string value) : base(value) { }

    public static Result<FlightNumberVo> Create(string? value) =>
        Result<FlightNumberVo>
            .FailIf(string.IsNullOrWhiteSpace(value),
                new EjError(nameof(Value), "Flight number is required."))
            .OrSuccess(() => new FlightNumberVo(value!.Trim()));
}

Compare this to the Nest/Next instinct: a class-validator DTO with public fields, validated by decorators that a caller has to remember to run, or a plain object you new and hope was checked upstream. In that world, an invalid object exists the moment you construct it and validity is a runtime hope. Here, validity is a construction-time guarantee.

Practice

Try it yourself

Do

Make an invalid VO impossible

Feel the guarantee in your hands. Sketch a VO and try to break it.

Tick every step to confirm you did it.

Recall

Why the private constructor

State the guarantee it buys.

Why does a Fusion value object make its constructor private and expose only a static Create that returns a Result?