ORM: Data Mapper vs Active Record

Dec 06 2022
Qual deles é melhor e por quê?
ORM significa Object-Relational Mapping, ou seja, a técnica que transforma Objetos de OOP em linhas no banco de dados e vice-versa. Existem dois padrões: Active Record Como mencionei anteriormente, a própria entidade pode ser criada, atualizada ou excluída.

ORM significa Object-Relational Mapping , ou seja, a técnica que transforma Objetos de OOP em linhas no banco de dados e vice-versa.

Existem dois padrões:

  • Active Record : O próprio registro (entidade) pode alterar ativamente o banco de dados
  • Mapeador de dados : existe um objeto externo que persiste nas entidades

registro ativo

Como mencionei anteriormente, a própria entidade pode ser criada, atualizada ou excluída. Em PHP, por exemplo, o Eloquent é um Active Record ORM desenvolvido pela comunidade Laravel.

final class UserCreator
{
    public function create(UserInformation $userInfo): void
    {
        $address = Address::createFrom($userInfo->address());
        $address->save() # Address is stored in the database

        $user = User::createFrom($userInfo->user());
        $user->address = $address; // Set relationship
        $user->save(); # User is stored in the database
    }
}

No mapeador de dados, precisamos de uma classe externa que atualize o banco de dados. Em PHP, por exemplo, o Doctrine é o Data Mapper ORM de fato para Symfony.

final class UserCreator
{
    public function __construct(
        private EntityManager $entityManager,
    ) {}

    public function create(UserInformation $userInfo): void
    {
        $address = Address::createFrom($userInfo->address());
        $user = User::createFrom($userInfo->user());
        $user->address = $address; # Set relationship

        # We persist all objects we want to update
        $this->entityManager->persist($address);
        $this->entityManager->persist($user);

        # Finally, flushing the entity manager will execute the SQLs
        $this->entityManager->flush();
    }
}

Embora, se você der uma olhada em ambos os snippets, poderá ver que o Data Mapper já está usando uma dependência externa ( Entity Managerobjeto) que é estranho e claro, é mais difícil se você decidir introduzir alguns testes na UserCreatorclasse, enquanto isso na Active Recordclasse, as entidades estão se salvando, então não temos nenhuma dependência à primeira vista, mas isso não é totalmente verdade, a própria entidade tem conhecimento do banco de dados e será difícil escrever alguns testes se usarmos a entidade diretamente User.

Padrão de repositório

O padrão de repositório é um padrão de design que oculta a lógica do banco de dados em uma nova classe, expondo apenas os métodos que queremos fornecer ao usuário.

Para isso, precisamos criar uma interface e uma classe que irá implementar a interface onde iremos descartar toda a lógica.

# App/User/Domain
interface UserRepository
{
    public function save(UserInformation $userInfo): void;
}

~~~~

# App/User/Infrastructure
final class ActiveRecordUserRepository implements UserRepository
{
    public function save(UserInformation $userInfo): void
    {
        $address = Address::createFrom($userInfo->address());
        $address->save()

        $user = User::createFrom($userInfo->user());
        $user->address = $address;
        $user->save();
    }
}

final class DataMapperUserRepository implements UserRepository
{
    public function __construct(
        private EntityManager $entityManager,
    ) {}

    public function create(UserInformation $userInfo): void
    {
        $address = Address::createFrom($userInfo->address());
        $user = User::createFrom($userInfo->user());
        $user->address = $address;

        $this->entityManager->persist($address);
        $this->entityManager->persist($user);
        $this->entityManager->flush();
    }
}

final class UserCreator
{
    public function __construct(
        private UserRepository $userRepository,
    ) {}

    public function create(UserInformation $userInfo): void
    {
        $this->userRepository->save($userInfo);
    }
}