ORM: Data Mapper เทียบกับ Active Record

Dec 06 2022
อันไหนดีกว่าและทำไม?
ORM ย่อมาจาก Object-Relational Mapping นั่นคือเทคนิคที่แปลง Objects จาก OOP เป็นแถวในฐานข้อมูลและในทางกลับกัน มีสองรูปแบบ: Active Record ดังที่ฉันได้กล่าวไว้ก่อนหน้านี้ เอนทิตีสามารถสร้าง ปรับปรุง หรือลบได้

ORM ย่อมาจากObject-Relational Mappingนั่นคือเทคนิคที่แปลง Object จาก OOP เป็นแถวในฐานข้อมูลและในทางกลับกัน

มีสองรูปแบบ:

  • บันทึกที่ใช้งานอยู่ : บันทึก (เอนทิตี) เองสามารถเปลี่ยนฐานข้อมูลได้
  • Data Mapper : มีวัตถุภายนอกที่คงอยู่ในเอนทิตี

บันทึกที่ใช้งานอยู่

ดังที่ฉันได้กล่าวไว้ก่อนหน้านี้ เอนทิตีนั้นสามารถสร้าง อัปเดต หรือลบได้ ตัวอย่างเช่น ใน PHP Eloquent เป็น Active Record ORM ที่พัฒนาโดยชุมชน 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
    }
}

ใน data mapper เราต้องการคลาสภายนอกที่จะอัพเดต DB ใน PHP ตัวอย่างเช่น Doctrine คือ Data Mapper ORM สำหรับ 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();
    }
}

แม้ว่าหากคุณดูที่ส่วนย่อยทั้งสอง คุณจะเห็นว่า Data Mapper กำลังใช้การพึ่งพาภายนอก ( Entity Managerวัตถุ ) ซึ่งแปลกและชัดเจน แต่ยากขึ้นหากคุณตัดสินใจที่จะแนะนำการทดสอบบางอย่างในชั้นUserCreatorเรียน ในขณะเดียวกันในActive Recordชั้นเรียน เอนทิตีกำลังช่วยตัวเอง ดังนั้นเราจึงไม่มีการพึ่งพาใด ๆ ในทันที แต่นั่นไม่เป็นความจริงทั้งหมด เอนทิตีเองก็มีความรู้เกี่ยวกับฐานข้อมูล และจะเป็นการยากที่จะเขียนการทดสอบบางอย่างหากเราใช้เอนทิตีUserโดยตรง

รูปแบบที่เก็บข้อมูล

รูปแบบพื้นที่เก็บข้อมูลเป็นรูปแบบการออกแบบที่ซ่อนตรรกะของฐานข้อมูลไว้ในคลาสใหม่ โดยเปิดเผยเฉพาะวิธีการที่เราต้องการให้กับผู้ใช้

ในการทำเช่นนี้ เราจำเป็นต้องสร้างอินเทอร์เฟซและคลาสที่จะใช้อินเทอร์เฟซซึ่งเราจะทิ้งลอจิกทั้งหมด

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