Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

외래키 제거 완료 #391

Merged
merged 37 commits into from
Jun 26, 2024
Merged

외래키 제거 완료 #391

merged 37 commits into from
Jun 26, 2024

Conversation

limehee
Copy link
Collaborator

@limehee limehee commented Jun 26, 2024

Summary

#388

외래키를 제거하고 엔티티 간의 직접 참조를 ID를 통한 간접 참조로 전환하였습니다. 이는 헥사고날 아키텍처의 외부-내부 의존성 역전을 준수함과 동시에 성능 및 운영적인 측면에서의 문제를 해결하기 위함입니다. 외래키 사용의 단점을 극복하고 애플리케이션의 확장성과 유지보수성을 높이기 위해 다음과 같은 작업을 수행하였습니다.

Tasks

  • Member 엔티티의 직접 참조를 제거하고, memberId를 통해 간접 참조하도록 변경
  • 엔티티의 업데이트 및 생성 시 MemberLookupService를 사용하여 memberId의 유효성을 검사
  • 외래키 제약 조건 제거 및 관련 스키마 변경
  • MemberDeletedEvent와 MemberUpdatedEvent를 사용하여 회원 삭제 및 업데이트 시 정합성을 유지하도록 이벤트 기반 처리 로직 추가
  • MemberEventDispatcher와 MemberEventProcessor 인터페이스를 사용하여 회원 이벤트 처리 로직을 중앙집중화 및 모듈화

ETC

활동 관련 테이블과 미사용 테이블은 작업에 포함하지 않았습니다.

Query

다음은 변경된 테이블 구조를 반영하는 쿼리문입니다.

  1. 제약 조건, 컬럼 이름 확인
SELECT
    conname,
    conrelid::regclass AS tablename,
    a.attname AS column_name
FROM
    pg_constraint AS c
    JOIN pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
WHERE
    c.contype = 'f'
    AND c.conrelid::regclass IN (
        'public.accuse', 'public.award', 'public.blog', 'public.board', 'public.book',
        'public.book_loan_record', 'public.comment', 'public.donation', 'public.account_lock_info',
        'public.membership_fee', 'public.notification', 'public.position', 'public.work_experience'
    );
  1. 동적 SQL을 사용한 제약 조건 삭제
DO $$
DECLARE
    r RECORD;
BEGIN
    FOR r IN (
        SELECT
            conname,
            conrelid::regclass AS tablename,
            a.attname AS column_name
        FROM
            pg_constraint AS c
            JOIN pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
        WHERE
            c.contype = 'f'
            AND c.conrelid::regclass IN (
                'public.accuse', 'public.award', 'public.blog', 'public.board', 'public.book',
                'public.book_loan_record', 'public.comment', 'public.donation', 'public.account_lock_info',
                'public.membership_fee', 'public.notification', 'public.position', 'public.work_experience'
            )
            AND a.attname = 'member_id'
    ) LOOP
        EXECUTE 'ALTER TABLE ' || r.tablename || ' DROP CONSTRAINT ' || r.conname;
    END LOOP;
END $$;

limehee added 30 commits June 26, 2024 00:32
@limehee limehee self-assigned this Jun 26, 2024
@limehee limehee linked an issue Jun 26, 2024 that may be closed by this pull request
@limehee limehee added the 🔨 Refactor 코드 수정 및 개선 label Jun 26, 2024
Copy link
Collaborator

@mingmingmon mingmingmon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저희 DB구조 변경이 있는 경우 새로 설정한 PR 규칙에 대해서 질문이 있습니다.
이번 작업이 DB구조 변경이 있던 경우라 예시를 들어서 궁금했던 점 질문드릴게요!

  1. "DB 구조에 변경이 있는 경우에는 기존 데이터 마이그레이션을 포함하도록 하고"에 대한 작업은 프로덕션 서버의 데이터를 덤프를 떴다가 어디로 마이그레이션을 하는건가요?
  2. "쿼리문 같이 올려주도록 한다"에 대한 것은 실제 쿼리문을 프로덕션 서버와 연결된 DB에 접속해서 실행하신건가요?

제가 로컬 작업과 배포작업을 연결지어 생각하는게 아직 미숙해서 질문 남깁니다!

@limehee
Copy link
Collaborator Author

limehee commented Jun 26, 2024

데이터를 다른 서버로 옮긴다는 의미가 아니라, 구조 변경이 생겼을 때 기존 데이터가 정상적으로 작동할 수 있도록 호환시키는 것을 의미합니다. 데이터베이스 변경은 직접 조작이 필요하기 때문에 쿼리문을 통해 수정을 해야 하며, 현재는 직접 접속해서 실행하는 방식을 사용할 계획입니다. 서버가 올라갈 때 즉각적으로 반영할 수 있는 방법에 대해서는 추가적인 고민이 필요하지만, 현재는 젠킨스 CD 과정에서 쿼리문이 실행되도록 하는 것도 고려 중입니다.

@mingmingmon
Copy link
Collaborator

그렇다면 pr에 적어주신 쿼리문은 머지가 되고 프로덕션DB에 접속하여 실행될 예정인건가요?

@SongJaeHoonn
Copy link
Collaborator

SongJaeHoonn commented Jun 26, 2024

참조된 member 객체로 넘어가는 것을 전부 memberId로 변경하셨네요! 코드를 전부 보는데도 한참 걸렸는데,
해당 부분들을 일일이 찾아가며 전부 수정하셨다니 정말 고생 많으셨습니다.
한 가지 궁금한 점이 있는데요, 제가 이벤트 부분이 이해가 잘 안가서 질문드립니다!
이벤트 핸들러를 통해 관련된 다른 도메인들에게 update 또는 delete를 알려야 하는 이유가 있나요?
db 상에서 Member를 외래 키로 사용하지 않아도 MemberId가 간접적으로 존재하기 때문에 참조 무결성이 지켜지지 않나요?
그리고, MemberEventProcessor를 implement한 클래스들이 있던데, 해당 이벤트 프로세서가 있는 도메인과 없는 도메인은 어떤 차이인건지 궁금합니다!
변경된 코드가 많아 헷갈리기도 하고, 제가 DB쪽이 많이 부족한 것 같아서 질문 드립니다..

@limehee
Copy link
Collaborator Author

limehee commented Jun 26, 2024

MemberUpdatedEvent에 대해 먼저 설명드리겠습니다. 현재 모든 테이블에서 외래키를 제거하고 memberId만을 저장하여 사용하고 있습니다. 그러나 필요에 따라 조인 성능을 고려해 memberId 외에도 다양한 컬럼을 저장하여 사용하는 방식을 채택할 수 있습니다. 이런 경우 Member 테이블의 데이터가 변경될 때 연결된 다른 테이블의 데이터도 업데이트하여 데이터의 일관성을 유지할 필요가 있습니다. 현재는 사용하지 않지만, 향후 사용 가능성을 고려해 추가해 두었습니다.

MemberDeletedEvent는 Member 테이블에서 삭제가 발생했을 때 발생하는 이벤트입니다. Member 테이블의 데이터가 삭제될 때, 함께 삭제해야 하는 데이터도 있지만, 그렇지 않고 조회 가능하게 유지해야 하는 데이터(게시글, 댓글 등)도 있습니다. 이를 반영하기 위한 이벤트이며, 코드를 보시면 domain.delete()를 통해 테이블에 따라 관련 데이터를 소프트 딜리트하는 것을 볼 수 있습니다.

이벤트 프로세서가 있는 도메인은 Member 테이블의 데이터가 변경됨에 따라 함께 변화해야 하는 도메인입니다. 필요에 따라 추가/수정/삭제할 수 있습니다.

답변이 도움 되셨길 바랍니다.

@limehee limehee merged commit c9ea0ee into develop Jun 26, 2024
1 check passed
@limehee limehee deleted the refactor/#388 branch June 26, 2024 15:36
@limehee
Copy link
Collaborator Author

limehee commented Jun 26, 2024

그렇다면 pr에 적어주신 쿼리문은 머지가 되고 프로덕션DB에 접속하여 실행될 예정인건가요?

뒤늦게 봤네요. 스테이징에 먼저 적용해서 테스트하고, 문제 없으면 프로덕션에 배포하면서 쿼리문으로 DB 변경 예정입니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 Refactor 코드 수정 및 개선
Projects
None yet
Development

Successfully merging this pull request may close these issues.

외래키 제거
3 participants