Value objects with Zod
A value object is a small piece of data that carries its own rules. A Quantity is not just a number. It is a positive integer with a maximum of 100. Zod lets you encode that rule once, and it becomes impossible for invalid quantities to reach the rest of your code.
import { z } from 'zod';
// Value object: a quantity is a positive integer with a max
export const QuantitySchema = z
.number()
.int()
.positive()
.max(100, 'Cannot order more than 100 items at once');
// Validated input shape for creating an order
export const CreateOrderInputSchema = z.object({
userId: z.number().int().positive(),
bookId: z.number().int().positive(),
quantity: QuantitySchema,
});
export type CreateOrderInput = z.infer<typeof CreateOrderInputSchema>;
// Entity shape: the shape used inside the domain
export interface OrderEntity {
id?: number;
userId: number;
bookId: number;
quantity: number;
status: 'pending' | 'completed' | 'cancelled';
}Business rules expressed as Zod schemas. The types are inferred for free.
This is sometimes called the anti-corruption layer. Data coming in from HTTP, a CLI, or a message queue all pass through the same Zod schemas before they touch your domain. If it makes it past the schema, the rest of your code can trust it absolutely.
No. z.infer<typeof CreateOrderInputSchema> gives you the type for free. One schema, one source of truth, one set of rules. If the rule changes, the type updates automatically. This alone is a huge argument for reaching for Zod in the domain layer.
Quiz: Quiz
Loading practice…