1. 시스템 목표"하루에 한 번"과 같이 정해진 알림을 누락 없이 전송한다. (At-least-once Delivery)API 서버 장애, DB 장애, 애플리케이션 장애 등 어떤 상황에서도 작업 유실을 방지한다.장애 복구 시 발생할 수 있는 중복 발송을 최소화하고, 제어(멱등성)할 수 있도록 설계한다.2. 아키텍처 설계[1단계: 생산자 (Scheduler) 영역]별도의 스케줄러 스레드가 알림 발송 시간 10분 전에 User 테이블에서 알람 설정 한 유저 정보를 조회한다.유저 정보를 조회한 스케줄러는 오늘 알림 설정을 on 한 사용자들에 대해서만 quiz_notification_log 테이블을 생성하고 저장한다. (초기 status='PENDING')발송 시간이 되면 메인 스케줄러 스레드는 quiz_not..
0. 들어가며 – “퀴즈 보기, 어디까지 쪼갤 건데?”내 서비스의 하루 학습 경험은 이렇게 생겼다.“키워드 설명을 읽고, 관련 뉴스를 보고, 마지막에 퀴즈 1문제를 푼다.그 퀴즈에는 여러 개의 보기(option) 가 달려 있다.”여기서 자연스럽게 나오는 질문이 있다.“이 보기(option) 를 DB에서 어디까지 테이블로 쪼갤까?”선택지는 크게 둘이었다.quiz_options 라는 테이블을 만들고,quiz_id 기준으로 1:N 로우를 두는 방식보기 전체를 하나의 값 객체(QuizOptions)로 보고,JSON 컬럼에 통째로 박아 넣는 방식나는 2번, 즉QuizOptions 값 객체내부에 ListJPA에서는 AttributeConverter + JSON TEXT 컬럼이 조합을 선택했다.이 글에서는왜 별도 테..
0. 이 글에서 이야기할 것oneco 콘텐츠의 도메인은 이렇게 생겼다.하루 단위 묶음: DailyContent (애그리거트 루트)그날 보여줄 뉴스들: NewsItem (엔티티 리스트)그날 풀게 될 퀴즈들: Quiz (엔티티 리스트)도메인 구조이번 글에서 이야기할 주제는 다음과 같다.“왜 DailyContent → News/Quiz 단방향만 만들고,NewsItem/Quiz → DailyContent 역방향은 안 만들었을까?”이 글에서는:지금 코드에서 DailyContent가 자식들을 어떻게 보호하고 검증하는지“만약 양방향으로 짰다면 코드가 어떻게 달라졌을지”JPA + DDD 관점에서 단방향 / 양방향 각각의 현실적인 장단점나중에 단방향이 막혀서 역방향이 필요해졌을 때 어떻게 확장할 계획인지까지, 실제 코드..
이번 글은 ONECO 프로젝트에서 DailyContent 애그리거트를 설계하면서 부딪힌 고민들을 바탕으로,“값 객체(Value Object)는 어디서 만들고, 엔티티(Entity)는 누가 만들게 할 것인가?” 를 정리해보는 글이다.실제로 프로젝트를 진행하며 작성한 코드들로 정리해보았다.0. 개념 정리 : 엔티티 vs 값 객체엔티티(Entity)ID(정체성) 으로 구분된다.값이 조금 바뀌어도 “그 객체”로 계속 취급돼야 한다.애그리거트 루트 아래에 매달려 있는 내부 엔티티(예: OrderLine, Comment 등)도 포함.값 객체(Value Object)ID가 없다.값이 같으면 같은 것이다.가능하면 불변(immutable)에 가깝게 두고, 생성 시점에 유효성 검증을 끝낸다.“값 객체는 서비스에서 만들어도..
Part 1에서는 “왜 DailyContent를 애그리거트 루트로 두었는가”를 개념적으로 정리했다면,이번 Part 2는 실제 코드 한 파일(DailyContent.java)(애그리거트)을 기준으로 설계 의도와 동작 방식을 해부하는 글이다.0. 도메인 구조 & 전체 코드 원문먼저 기준이 되는 DailyContent 애그리거트 전체 코드이다.(프로젝트를 진행하며 수정되거나 추가될 수도 있다. 수정된 코드는 깃허브에서 확인할 수 있다.)@Entity@NoArgsConstructor(access = AccessLevel.PROTECTED)@Table(name = "daily_contents", uniqueConstraints = { @UniqueConstraint( name..