3 visitas

Potenciá tus modelos Eloquent en Laravel con Value Objects y Arquitectura Limpia

En proyectos Laravel, es común que los modelos Eloquent tengan atributos como simples strings, números o fechas. Pero… ¿qué pasa cuando queremos que esos valores tengan reglas de negocio y validaciones propias? Ahí es donde entran los Value Objects, una técnica muy utilizada en arquitectura limpia y Domain-Driven Design (DDD). En este post te voy a mostrar cómo utilizar Value Objects en modelos Eloquent para enriquecer tu dominio, mejorar la calidad del código y evitar errores silenciosos.

📌 ¿Qué es un Value Object?

Un Value Object es un objeto inmutable que representa un valor del dominio con sus propias reglas y comportamientos.
No se identifica por un ID, sino por su valor. Ejemplos típicos:

  • Email
  • Precio
  • DNI
  • Teléfono
  • Coordenadas geográficas

En resumen: un Value Object encapsula un valor y sus reglas, de modo que no haya forma de crear un valor inválido.


🚀 ¿Por qué usar Value Objects en Eloquent?

Laravel es muy flexible y nos permite integrarlos de forma simple pero poderosa.
Con Custom Casts podemos lograr que un atributo de base de datos se convierta automáticamente a un objeto de dominio cuando lo obtenemos, y viceversa cuando lo guardamos.

Beneficios clave

Beneficio Descripción
Validaciones centralizadas Las reglas de negocio viven en el Value Object, no en varios lugares del código.
Inmutabilidad Una vez creado, el valor no se puede modificar de forma insegura.
Código más expresivo El tipo de dato transmite su intención y reglas de uso.
Menos errores No podés guardar valores inválidos accidentalmente.

🛠 Ejemplo práctico: Email como Value Object

Supongamos que tenemos usuarios y queremos que el campo email siempre sea válido.

1️⃣ Creamos el Value Object

<?php

namespace App\ValueObjects;

final class Email
{
    public function __construct(private string $address)
    {
        if (!filter_var($address, FILTER_VALIDATE_EMAIL)) {
            throw new \InvalidArgumentException("El email '{$address}' no es válido");
        }
    }

    public function value(): string
    {
        return $this->address;
    }

    public function domain(): string
    {
        return substr(strrchr($this->address, "@"), 1);
    }

    public function __toString(): string
    {
        return $this->address;
    }
}

📌 Claves:

  • Constructor con validación.
  • Método adicional domain() para enriquecer el valor.
  • Inmutable: no hay setters.

2️⃣ Creamos el Custom Cast

<?php

namespace App\Casts;

use App\ValueObjects\Email;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

final class EmailCast implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes): Email
    {
        return new Email($value);
    }

    public function set($model, string $key, $value, array $attributes): string
    {
        if ($value instanceof Email) {
            return $value->value();
        }

        return (string) $value;
    }
}

📌 Claves:

  • El método get devuelve un Email al leer de la DB.
  • El método set guarda un string al escribir.

3️⃣ Integramos en el modelo Eloquent

<?php

namespace App\Models;

use App\Casts\EmailCast;
use Illuminate\Database\Eloquent\Model;

final class User extends Model
{
    protected $casts = [
        'email' => EmailCast::class,
    ];
}

4️⃣ Uso en el código

use App\Models\User;
use App\ValueObjects\Email;

// Crear usuario
$user = new User();
$user->name = 'Juan Pérez';
$user->email = new Email('juan@example.com');
$user->save();

// Obtener usuario y usar métodos del Value Object
$user = User::first();
echo "Dominio del email: " . $user->email->domain();

💡 Nivel avanzado: múltiples Value Objects en un mismo modelo

Podemos tener varios atributos mapeados a Value Objects:

<?php

namespace App\Models;

use App\Casts\EmailCast;
use App\Casts\PriceCast;
use Illuminate\Database\Eloquent\Model;

final class Product extends Model
{
    protected $casts = [
        'email_contacto' => EmailCast::class,
        'precio' => PriceCast::class,
    ];
}

Esto te permite que cada valor importante en tu dominio tenga su propia lógica encapsulada.


Conclusiones

  • Value Objects + Eloquent = modelos más ricos, seguros y expresivos.
  • Laravel facilita su integración gracias a los Custom Casts.
  • Aplicar esta técnica te acerca a principios de arquitectura limpia y DDD.
  • Evitás errores silenciosos y repetición de lógica de validación.
  • Es escalable: podés empezar con uno o dos Value Objects y luego ir creciendo.

Glosario

  • Value Object: Objeto inmutable que encapsula un valor y sus reglas de negocio.
  • Custom Cast: Funcionalidad de Laravel para transformar atributos al leer/escribir.
  • Arquitectura limpia: Enfoque que separa reglas de negocio de frameworks e infraestructura.
  • DDD (Domain-Driven Design): Metodología de desarrollo centrada en el dominio del negocio.
  • Inmutabilidad: Propiedad que evita modificar un objeto después de crearlo.
Potenciá tus modelos Eloquent en Laravel con Value Objects y Arquitectura Limpia
14 August 2025

Me dedico a crear soluciones web eficientes y a compartir mi conocimiento con la comunidad de desarrolladores.

Alejandro Leone
Backend Developer