Value Objects
A robust and simple implementation of the Value Object pattern for Dart and Flutter projects. This package helps you create type-safe, validated, and immutable value types for your domain models, reducing bugs and improving code clarity.
Value Objects are a fundamental concept in Domain-Driven Design (DDD). Instead of representing domain concepts with primitive types (like String for an email or double for a monetary value), you create a class that encapsulates the value, its validation rules, and its behavior.
Features
- β Type Safety: Avoid "Primitive Obsession". EmailAddress is clearer and safer than String.
- π Immutability: Once created, a Value Object's value cannot be changed.
- βοΈ Built-in Validation: Integrated validation logic that throws specific exceptions like InvalidValueException and RequiredValueException.
- π Easy Parsing: Safely parse values from strings (e.g., user input) with parse() and tryParse().
- π€ Form Integration: A validator method ready to be used directly with TextFormField.
- βοΈ Value Equality: Two Value Objects are equal if their underlying values are equal, not by reference.
- π Pre-built Implementations: A collection of common Value Objects ready to use out-of-the-box.
Getting Started
Add the package to your pubspec.yaml file:
dependencies:
value_objects: ^1.0.0 # Replace with the latest version
Then, run flutter pub get or dart pub get to install the package.
Usage
You can either use one of the pre-built Value Objects or create your own by extending the base ValueObject<T> class.
Creating a Custom Value Object
To create your own, extend ValueObject<T> and implement the doParse method, which contains your specific validation logic.
Let's create an EmailAddress Value Object:
import 'package:value_objects/value_objects.dart';
class EmailAddress extends ValueObject<String> {
EmailAddress({
required super.defaultValue,
required super.isRequired,
});
// The core parsing and validation logic for this value type.
@override
String doParse(String? parseValue) {
final email = parseValue?.trim() ?? '';
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
// The base \`validate\` method handles the \`isRequired\` check.
// Here, we only need to validate the format of the email itself.
if (email.isNotEmpty && \!emailRegex.hasMatch(email)) {
// Throw the specific exception for clear error handling.
throw InvalidValueException('Invalid email format');
}
return email;
}
}
Using the Value Object in Flutter
Now you can use EmailAddress in your application to ensure you always have a valid email.
Instantiation and Parsing
// Create an instance (usually in your domain entity or model)
final email = EmailAddress(defaultValue: '', isRequired: true);
// The `parse` method throws a `ValueException` on failure.
try {
email.parse('test@example.com');
print('Valid email: ${email.value}'); // Output: test@example.com
} on ValueException catch (e) {
print('Validation Error: $e');
}
// The `tryParse` method returns null on failure, which is useful
// when you don't want to handle exceptions.
final parsedValue = email.tryParse('invalid-email');
if (parsedValue == null) {
print('Parsing failed!');
}
Flutter Form Validation
The .validator() method is designed to integrate directly with a TextFormField.
import 'package:flutter/material.dart';
import 'package:value_objects/value_objects.dart';
class MyForm extends StatefulWidget {
@override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
final _formKey = GlobalKey<FormState>();
final email = EmailAddress(defaultValue: '', isRequired: true);
void _submit() {
// Validate the entire form.
final isValid = _formKey.currentState?.validate() ?? false;
if (!isValid) {
return;
}
// Save the form, which triggers the onSaved callback.
_formKey.currentState!.save();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Submitted Email: ${email.value}')),
);
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
// Use the validator method directly!
validator: email.validator,
onSaved: (value) {
// On save, parse the validated value into the Value Object.
// This is safe because `validate` has already passed.
email.parse(value);
},
),
ElevatedButton(
onPressed: _submit,
child: Text('Submit'),
),
],
),
);
}
}
Available Value Objects
Class | Description |
---|---|
ColorValue | Represents a Color from a hex string. |
CpfValue | Validates a Brazilian individual taxpayer registry number (CPF). |
DateTimeValue | Parses and validates a DateTime from a string. |
DecimalValue | Handles double values, useful for monetary amounts. |
EmailAddressValue | Validates a standard email address format. |
FullNameValue | Validates that a string contains at least a first and last name. |
GenericStringValue | A basic String with requirement and length checks. |
HtmlContentValue | Intended for storing sanitized HTML content. |
IntValue | Parses and validates an int from a string. |
MongoIdValue | Validates a MongoDB ObjectId format. |
PasswordValue | Validates common password strength requirements. |
PhoneNumberValue | Validates a general phone number format. |
UriValue | Parses and validates a Uri from a string. |
Additional Information
Filing Issues
Please file any issues, bugs, or feature requests on the official issue tracker. (<- Replace with your repo link)
Contributing
Contributions are welcome! If you'd like to contribute, please fork the repository and submit a pull request with a clear description of your changes.
License
This package is licensed under the MIT License. See the LICENSE file for more details.
Libraries
- domain/core/value_objects
- domain/exceptions/value_exceptions
- domain/value_objects/color_value
- domain/value_objects/cpf_value
- domain/value_objects/date_time_value
- domain/value_objects/decimal_value
- domain/value_objects/email_address_value
- domain/value_objects/full_name_value
- domain/value_objects/generic_string_value
- domain/value_objects/html_content_value
- domain/value_objects/int_value
- domain/value_objects/mongo_id_value
- domain/value_objects/password_value
- domain/value_objects/phone_number_value
- domain/value_objects/uri_value
- value_object