You might not need… the always-valid entity
One of the classic OOP principles is the ‘always-valid domain entity’. The idea is that entities and value objects don’t need to be validated, as invalid construction is actively guarded against.

The always-valid entity pops up in DDD quite often, such as in Vaughn Vernon’s Implementing Domain-Driven Design, which frames it as defensive programming.
I’ve always liked the idea, but found it hard to implement in practice. For starters, what does ‘valid’ actually mean?
It’s parsing, not validating
Validation checks a value against some kind of invariant: you’ve got a string, you ask whether it looks like an email, you proceed if it does. The always-valid approach refuses to let the bad value exist in the first place.
Alexis King summarizes this in parse, don’t validate: push the check to the boundary, and once it passes, encode the result in a type that can’t be anything else. For primitives, that means a new-type wrapper instead of a bare string or number:
// You probably want something like:
// https://github.com/sindresorhus/type-fest/blob/main/source/tagged.d.ts
type EmailAddress = string & { readonly __brand: 'EmailAddress' };
function parseEmailAddress(input: string): EmailAddress {
// Very robust email parsing:
if (!input.includes('@')) {
throw new Error(`Not an email address: ${input}`);
}
return input as EmailAddress;
}TypeScript
Once a function takes an EmailAddress rather than a string, it never has to ask whether the email is valid. The check happens once, at the edge.
Classes/entities can guard in their constructor:
class DateRange {
constructor(
readonly start: Date,
readonly end: Date,
) {
if (start > end) {
throw new Error('A date range cannot end before it starts');
}
}
}TypeScript
There’s no way to hold a DateRange whose end precedes its start, because there’s no way to make one without intentionally going via Object.create() or similar. The invariant doesn’t need to be checked in a service, since it’s structurally impossible to violate.
The same pattern can be used with a private constructor and a static factory method, if you need (for instance) async/await.
Validation is contextual
The problem with validation is that it’s often contextual. An email address might always be an email address (or not), but a User might be valid or invalid depending on the operation being performed.
Martin Fowler made this point twenty years ago: an isValid method is almost always the wrong shape. Validity is bound to a context. The useful question isn’t ‘is this order valid?’ but ‘is this order valid to be filled?’. So you reach for isValidForDelivery() rather than a context-free isValid().
Optionality behaves the same way, which is the thread Rich Hickey pulls on in Maybe Not: whether a field is required isn’t an intrinsic property of the field. A User might need an email to be stored, but not at the instant it’s first created in a sign-up flow.
So the always-valid principle is best restricted to the handful of rules that must hold for the thing to be the thing at all. A DateRange with its start after its end isn’t a date range. Everything else probably belongs outside the entity. The classic home for it is a specification:
interface Specification<T> {
isSatisfiedBy(candidate: T): boolean;
}
interface Article {
title: string;
body: string;
featuredImage?: string;
}
class CanBePublished implements Specification<Article> {
isSatisfiedBy(article: Article): boolean {
return (
article.title.length > 0 &&
article.body.length > 0 &&
article.featuredImage !== undefined
);
}
}TypeScript
The Article is always valid in the core sense: it exists, it has a title that’s a real string, it has a body. Whether it can be published is a separate, contextual question, and it lives in a CanBePublished specification rather than leaking into the entity.
Traditional specifications have fallen out of fashion (a plain predicate usually does the trick), but the core idea is sound: keep contextual validation out of the entity.
Not every language plays along
All of this assumes your language will actually let you forbid the invalid instance.
Go is the obvious offender. You can un-export a struct’s fields and offer a constructor, but you can’t stop the zero value:
type Money struct {
amount int64
currency string
}
func NewMoney(amount int64, currency string) (Money, error) {
if currency == "" {
return Money{}, errors.New("currency is required")
}
return Money{amount, currency}, nil
}
// Nothing stops this:
var m Money // {0, ""}Go
var m Money is a completely invalid Money. The best you can do is enforce correctness by convention.
Invalid states are important
Invalid states are frequently information the user needs to see and act on. A spreadsheet with a circular reference is in an invalid state, but any usable Excel clone lets you create one anyway. You can’t fix both ends of the cycle at once. A Git merge conflict is an invalid state, and it’s Git’s job to keep working so that you can resolve it. If you can’t represent the invalid state, users can’t fix it.
Steven Wittens makes this case in I is for Intent. Wittens’ argument is that the thing a user actually edits is intent, which is often incomplete or self-contradictory. You parse and validate that intent into a clean State for display, but you hold on to the messy intent, because that’s what the user is working on. Keep the un-parsed intent as the real source of truth, and re-derive the valid state from it on every change.
Fowler, channeling Alan Cooper’s About Face, makes the same point about persistence: you should almost always be able to save an object, even one with errors in it:
people tend to prevent saving more than they ought.
— Martin Fowler
The parse-don’t-validate rebuttal here is that the messy intent should simply be its own type: a draft with missing fields and unresolved references is perfectly valid as a draft. This is Scott Wlaschin’s take in make illegal states unrepresentable: if your domain genuinely contains the state, model it.
I broadly agree, but before you know it the invalid state hasn’t been forbidden, it’s been promoted: DraftArticle, UnresolvedReference, ConflictedMerge. And since validity is contextual, every operation implies another type (an order that’s valid to be filled is a different type from one that’s valid to be placed).
Follow the pattern to its conclusion and the types multiply until each one is doing exactly the job a specification/validation function would have done anyway.
So should entities always be valid?
Yes, but ‘valid’ means far less than the name suggests. Nearly every rule you’re tempted to enforce in a constructor turns out to be contextual. What’s left is the handful of invariants without which the thing isn’t the thing at all (e.g. a Money with no currency). Guard those, and hold even them loosely. We have a tendency to over-validate.
In my experience, everything else belongs outside the entity, where it can vary by context without your types multiplying.