dzentota/domain-primitives

A collection of reusable domain primitives for PHP applications

dev-main 2025-07-09 16:04 UTC

This package is auto-updated.

Last update: 2025-07-09 21:13:47 UTC


README

A comprehensive collection of reusable domain primitives for PHP applications, built on the robust dzentota/typedvalue library. This package provides secure, validated, and immutable value objects that follow the "Parse, Don't Validate" principle from the AppSec Manifesto.

Features

  • ๐ŸŽฏ Type-safe value objects with automatic validation
  • ๐Ÿ”’ Immutable by design - values cannot be changed after creation
  • โœ… Comprehensive validation with detailed error reporting
  • ๐Ÿ”„ TryParse pattern - safe parsing without exceptions
  • ๐Ÿ” Domain-specific validation - tailored rules for each primitive type
  • ๐ŸŒ Rich domain models - extensive utility methods for each primitive
  • ๐Ÿงช Production ready - battle-tested validation logic

Installation

composer require dzentota/domain-primitives

Requirements

  • PHP 8.1 or higher
  • dzentota/typedvalue ^1.0

Available Domain Primitives

Network

  • Port - Network port validation (1-65535) with type detection
  • ServiceUrl - HTTP/HTTPS URL validation with parsing utilities

Common

  • Email - Email address validation with normalization and obfuscation

Configuration

  • FeatureFlag - Boolean flag parsing with multiple format support

Database

  • DatabaseDsn - Database connection string validation and parsing

Identity

  • UuidId - UUID validation with version detection and generation

Quick Start

Basic Usage

use DomainPrimitives\Network\Port;
use DomainPrimitives\Common\Email;
use DomainPrimitives\Configuration\FeatureFlag;

// Create from valid input
$port = Port::fromNative(3000);
echo $port->toInt(); // 3000
echo $port->isWellKnown() ? 'Well-known' : 'Not well-known'; // Not well-known

// Safe parsing without exceptions
if (Email::tryParse('user@example.com', $email, $validationResult)) {
    echo "Valid email: " . $email->getDomain(); // example.com
} else {
    echo "Invalid: " . $validationResult->getFirstError()->getMessage();
}

// Feature flag parsing
$flag = FeatureFlag::fromNative('enabled');
echo $flag->isEnabled() ? 'Feature is on' : 'Feature is off'; // Feature is on

Domain Primitives Reference

Network\Port

Validates network ports (1-65535) with additional port type detection:

use DomainPrimitives\Network\Port;

$port = Port::fromNative(443);

echo $port->toInt();          // 443
echo $port->isWellKnown();    // false (443 > 1023)
echo $port->isRegistered();   // true (1024-49151)
echo $port->isDynamic();      // false (49152-65535)
echo $port->isSecure();       // true (HTTPS port)

Network\ServiceUrl

HTTP/HTTPS URL validation with rich parsing capabilities:

use DomainPrimitives\Network\ServiceUrl;

$url = ServiceUrl::fromNative('https://api.example.com:8080/v1/users');

echo $url->getScheme();       // https
echo $url->getHost();         // api.example.com
echo $url->getPort();         // 8080
echo $url->getPath();         // /v1/users
echo $url->isSecure();        // true
echo $url->getEffectivePort(); // 8080

// Immutable transformations
$newUrl = $url->withPath('/v2/users');
$queryUrl = $url->withQuery('limit=10&offset=0');

Common\Email

Email validation with normalization and utility features:

use DomainPrimitives\Common\Email;

$email = Email::fromNative('John.Doe+newsletter@gmail.com');

echo $email->getLocalPart();  // John.Doe+newsletter
echo $email->getDomain();     // gmail.com
echo $email->isPersonal();    // true (gmail.com is personal)
echo $email->isBusiness();    // false

// Gmail normalization (removes dots, plus addressing)
$normalized = $email->normalize();
echo $normalized->toString();  // johndoe@gmail.com

// Privacy protection
echo $email->obfuscate();     // J*******************m@gmail.com

Configuration\FeatureFlag

Smart boolean parsing supporting multiple formats:

use DomainPrimitives\Configuration\FeatureFlag;

// Supports: true/false, 1/0, yes/no, on/off, enabled/disabled
$flag1 = FeatureFlag::fromNative('enabled');
$flag2 = FeatureFlag::fromNative('1');
$flag3 = FeatureFlag::fromNative(true);

echo $flag1->isEnabled();     // true
echo $flag1->toBool();        // true
echo $flag2->isDisabled();    // false

Database\DatabaseDsn

Database connection string validation and parsing:

use DomainPrimitives\Database\DatabaseDsn;

$dsn = DatabaseDsn::fromNative('mysql:host=localhost;dbname=myapp;port=3306');

echo $dsn->getDriver();       // mysql
echo $dsn->getHost();         // localhost
echo $dsn->getPort();         // 3306
echo $dsn->getDatabaseName(); // myapp
echo $dsn->getDefaultPort();  // 3306
echo $dsn->isFileDatabase();  // false

// Special case handling
$sqlite = DatabaseDsn::fromNative('sqlite::memory:');
echo $sqlite->isInMemory();   // true

Identity\UuidId

UUID validation with version detection and utilities:

use DomainPrimitives\Identity\UuidId;

// Generate new UUID
$uuid = UuidId::generate();
echo $uuid->toNative();       // e.g., 550e8400-e29b-41d4-a716-446655440000

// Parse existing UUID
$existing = UuidId::fromNative('550e8400-e29b-41d4-a716-446655440000');

echo $existing->getVersion();  // 4 (random)
echo $existing->isNil();       // false

// Format transformations
echo $existing->toShort();     // 550e8400e29b41d4a716446655440000
$binary = $existing->toBinary(); // Binary representation

// UUID v1 timestamp extraction (if applicable)
$timestamp = $existing->getTimestamp(); // DateTimeImmutable or null

Error Handling

All domain primitives provide comprehensive error handling:

use DomainPrimitives\Network\Port;

// Exception-based (for known-good input)
try {
    $port = Port::fromNative(99999); // Invalid port
} catch (ValidationException $e) {
    echo $e->getMessage();
    print_r($e->getValidationErrors());
}

// TryParse pattern (for user input)
if (Port::tryParse($userInput, $port, $validationResult)) {
    echo "Valid port: " . $port->toInt();
} else {
    foreach ($validationResult->getErrors() as $error) {
        echo "Error: " . $error->getMessage() . "\n";
    }
}

Best Practices

1. Use TryParse for User Input

Always use tryParse() when dealing with untrusted input:

// Good: Safe parsing
if (Email::tryParse($_POST['email'], $email, $validationResult)) {
    // Process valid email
} else {
    // Handle validation errors gracefully
}

// Avoid: Direct fromNative with user input
try {
    $email = Email::fromNative($_POST['email']); // May throw
} catch (ValidationException $e) {
    // Exception handling is less elegant
}

2. Leverage Immutability

Domain primitives are immutable - transformations create new instances:

$originalUrl = ServiceUrl::fromNative('https://api.example.com/v1');
$newUrl = $originalUrl->withPath('/v2'); // Creates new instance
// $originalUrl is unchanged

3. Use Rich Domain Methods

Take advantage of the rich domain-specific methods:

$email = Email::fromNative('user@gmail.com');

// Rich domain logic
if ($email->isPersonal()) {
    $businessEmail = $email->normalize(); // Apply Gmail rules
    $safe = $email->obfuscate(); // For logging
}

4. Validate Early and Consistently

Always validate data at the boundaries of your application:

// Validate configuration at startup
$dsn = DatabaseDsn::fromNative($config['database_url']);
$port = Port::fromNative($config['server_port']);

// Use domain-specific validation
if ($email->isPersonal()) {
    // Handle personal email logic
}

Architecture

This library is built on the dzentota/typedvalue foundation and follows these principles:

  • Parse, Don't Validate - Values are parsed into valid objects immediately
  • Immutability - All values are read-only after creation
  • Type Safety - Rich type system prevents many classes of bugs
  • Security First - Secure validation with detailed error reporting
  • Domain-Rich - Each primitive includes relevant domain methods

Contributing

Contributions are welcome! Please ensure:

  1. All tests pass: composer test
  2. Code follows PSR-12 standards
  3. New primitives include comprehensive tests
  4. Documentation is updated

License

MIT License. See LICENSE file for details.

Related Libraries