Simple Value Object Generator
A Dart code generator that simplifies the creation of Value Objects using Dart's extension type.
It allows you to define wrapper types for primitives (like int, String) with built-in validation logic, ensuring type safety and data integrity with minimal boilerplate.
Features
Extension Types
Utilizes Dart's extension type for zero-cost abstractions (at runtime, they are the underlying type).
Validation
Automatically generates validation logic in the constructor.
int,double:min,maxString / Iterable<T>, Map<K,V>:minLength,maxLengthallowEmpty
Simple Syntax
Define Value Objects using typedef and annotations.
Getting started
Add the dependencies to your pubspec.yaml:
dependencies:
simple_value_object_annotation: ^1.0.0
dev_dependencies:
simple_value_object_generator: ^1.0.0
Usage
1. Define your Value Object
Create a file (e.g., example.dart), add the part directive, and define your type using typedef and the @ValueObject annotation.
import 'package:simple_value_object_annotation/simple_value_object_annotation.dart';
part 'example.g.dart';
@ValueObject<int>(min: 0, max: 100)
typedef Id = _$Id;
@ValueObject<String>(allowEmpty: false, maxLength: 200)
typedef Email = _$Email;
Supported ValueObject<T> annotation values
-
Numbers (
int,double)min: Minimum value (inclusive).max: Maximum value (inclusive).
-
String, Collections (has
length,isEmptyproperty )allowEmpty: Whether to allow empty strings/collections (default:true).minLength: Minimum character/element count (inclusive)maxLength: Maximum character/element count (inclusive)
2. Run the Generator
Run build_runner to generate the implementation code.
dart run build_runner build
Following is genenerated code example:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'example.dart';
// **************************************************************************
// ValueObjectGenerator
// **************************************************************************
extension type const _$Id._(int value) {
// ignore: empty_constructor_bodies
_$Id(this.value) {
if (value < 0) {
throw ArgumentError.value(value, 'value', 'must be >= 0');
}
if (value > 100) {
throw ArgumentError.value(value, 'value', 'must be <= 100');
}
}
}
extension type const _$Email._(String value) {
// ignore: empty_constructor_bodies
_$Email(this.value) {
if (value.length > 200) {
throw ArgumentError.value(value, 'value', 'length must be <= 200');
}
if (value.isEmpty) {
throw ArgumentError.value(value, 'value', 'must not be empty');
}
}
}
3. Use it
void main() {
// Valid creation
final id = Id(42);
final email = Email('test@example.com');
print(id.value); // 42
// Runtime behavior
print(id is int); // true (Runtime type is the underlying type)
// Validation errors
try {
final invalidId = Id(-1); // Throws ArgumentError
} catch (e) {
print(e);
}
}