ORM: Data Mapper vs Active Record

Dec 06 2022
Quale è meglio e perchè?
ORM sta per Object-Relational Mapping, ovvero la tecnica che trasforma gli Oggetti da OOP in righe nel database e viceversa. Esistono due modelli: Active Record Come accennato in precedenza, l'entità stessa può essere creata, aggiornata o eliminata.

ORM è l'acronimo di Object-Relational Mapping , ovvero la tecnica che trasforma gli Oggetti da OOP in righe nel database e viceversa.

Ci sono due modelli:

  • Record attivo : il record (entità) stesso può modificare attivamente il DB
  • Data Mapper : esiste un oggetto esterno che persiste le entità

Registro attivo

Come accennato in precedenza, l'entità stessa può essere creata, aggiornata o eliminata. In PHP, ad esempio, Eloquent è un Active Record ORM sviluppato dalla comunità 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
    }
}

Nel data mapper, abbiamo bisogno di una classe esterna che aggiornerà il DB. In PHP, ad esempio, Doctrine è di fatto l'ORM Data Mapper per 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();
    }
}

Sebbene, se dai un'occhiata a entrambi i frammenti, puoi vedere che il Data Mapper sta già usando una dipendenza esterna ( Entity Manageroggetto) che è strano e chiaramente, è più difficile se decidi di introdurre alcuni test alla UserCreatorclasse, nel frattempo nella Active Recordclasse, le entità si stanno salvando, quindi non abbiamo alcuna dipendenza a colpo d'occhio, ma non è completamente vero, l'entità stessa conosce il DB e sarà difficile scrivere alcuni test se usiamo l'entità direttamente User.

Modello di deposito

Il modello di repository è un modello di progettazione che nasconde la logica del database in una nuova classe, esponendo solo i metodi che vogliamo fornire all'utente.

Per fare ciò, dobbiamo creare un'interfaccia e una classe che implementerà l'interfaccia in cui elimineremo tutta la logica.

# 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);
    }
}