send-grid-test / elysia-server

시퀀스 이메일 발송 파이프라인

3-layer (routes → services → db) 현 구조를 헥사고날 아키텍처로 전환했을 때의 이득과 비용 분석

2026-05-30 · alpha branch

분석 대상 선정 이유

이 프로젝트에서 도메인 무게중심이 가장 큰 기능을 골랐습니다. 시퀀스(이메일 캠페인) 파이프라인은 (a) 외부 provider 다중성(SendGrid·Gmail·SES), (b) 영속/휘발 SSOT 혼재(Postgres + Redis + BullMQ), (c) HTTP 와 워커 두 진입점이 같은 use-case 를 호출, (d) 인시던트 빈도 최상위 — 헥사고날 도입의 ROI 가 가장 명확한 영역입니다.

현 구조 스냅샷

영역위치규모
HTTP 진입routes/sequences.routes.ts1272 LOC, 단일 파일
서비스(분할)services/sequence-*.service.ts20+ 파일 — read/write/query/lifecycle/enrollment/execution/metrics/preview/timezone/copy/naming…
Barrelservices/sequence.service.ts114줄 re-export only
발송 워커workers/bullmq/sequence-email-worker/processor + 6 step (pre-check → validate → resolve-lead → verify → resolve-content → send)

강결합 핫스팟

헥사고날 전환 시 이득

#이득구체 효과
1Port/Adapter 분리EmailSenderPort, SequenceRepository, EnrollmentRepository, ThrottlePort 인터페이스화 → send-email.ts 의 drizzle/SendGrid 직접 호출 제거
2Use-case 단일화활성화·발송·재개 use-case 1 곳 → 라우트와 워커가 동일 application service 호출
3도메인 순수성Sequence, Enrollment, StepExecution 을 framework-free 객체로 → 인시던트(#7980 status preserve, #7501 chain break) 류 invariant 를 도메인 테스트로 사전 차단
4Provider swap 비용 0SendGrid ↔ Gmail ↔ SES 가 step 코드 무수정 — 현재는 send-email.ts 의 provider 분기 200+ 줄
5테스트성processor 6 step 을 in-memory adapter 로 결정론적 단위테스트 가능 → 현재 ad-hoc mocking 제거
6CI 게이트 확장check:routes 처럼 dependency-cruiser 로 "domain → infrastructure import 금지" lint 강제 → drift 자동 차단
7SSOT 정합sequence-{read,write,query,lifecycle} 의 중복 query 를 1 repository 로 수렴 → CLAUDE.md "같은 데이터 1곳" 원칙과 일치
8워커 안정성DelayedError / permanent / transient 분기를 도메인 result type 으로 → processor 의 흩어진 try/catch 정리

트레이드오프

비용설명
추상화 오버헤드Elysia·BullMQ 가 얇은 layer 라 단순 CRUD 라우트는 over-engineering 위험 → 발송/활성화 같은 복잡 도메인에만 선택 적용 권장
Drizzle 타입 손실row type 을 repository 인터페이스로 감싸면 type 추론이 약해짐 → DTO 별도 정의 필요
점진 마이그레이션 부담1272 LOC 라우트 + 20 services 동시 전환 불가 → strangler 패턴으로 send-email step 부터 권장
팀 학습 비용신규 팀원이 routes → services → db 가 아닌 ports/adapters 멘탈모델 학습 필요

권장 시작점

workers/bullmq/sequence-email-worker/ 만 먼저 hex 화 권장. 도메인 무게중심 가장 큼 + 외부 provider(SendGrid/Gmail/SES) 와 DB 가 동시에 묶여 있어 ROI 최대. 라우트는 마지막.

  1. Phase 1EmailSenderPort 추출, SendGrid/Gmail adapter 분리. send-email.ts 의 provider 분기 제거.
  2. Phase 2SequenceRepository, EnrollmentRepository 추출. read/write/query/lifecycle 의 query 중복 수렴.
  3. Phase 3SendSequenceEmail use-case 정의. 워커 processor 가 use-case 호출. 라우트는 점진 이행.
  4. Phase 4dependency-cruiser 룰 추가 → domain → infrastructure import 차단. CI 게이트화.