~/brunosong
← 프로젝트 목록

Architecture

구인구직 시스템

보험설계사 구인구직 플랫폼입니다. 개발자 2명이 포탈과 어드민을 처음부터 설계했습니다. 바운디드 컨텍스트 15개를 헥사고날 레이어 모듈로 갈라 의존 방향을 Maven 이 컴파일 타임에 막게 만들었습니다. 프로세스는 일부러 나누지 않았습니다.

15

바운디드 컨텍스트

호스트 2개 위에 라이브러리로 올라감

2

배포 단위

portal-service, admin-service

12

기술적 의사결정

기각한 선택지까지 기록했음

Java 17(런타임 JRE 21) · Spring Boot + eGovFrame 부트스타터 · Maven 멀티모듈 · Spring Data JPA + Hibernate 6 · QueryDSL · MapStruct + Lombok · PostgreSQL 17 · Flyway · Spring Security + JWT · Jasypt · Caffeine · Local/FTP/NCP Object Storage · Apache PDFBox·POI · Jakarta Mail + Thymeleaf · Toss Payments · SpringDoc OpenAPI · Jenkins + Jib + Docker Swarm · JUnit5 + AssertJ + H2

Spring Boot · 헥사고날 · 모듈러 모놀리스 · MSA · PostgreSQL

이 플랫폼의 핵심 복잡성

이 플랫폼이 일반 구인, 구직 플랫폼과 다른 점은 두 가지입니다. 뒤의 모델링이 전부 여기서 나옵니다.

1. 한 회원이 구직자와 구인자를 겸합니다

구직자로서

talent

Resume · JobSeekerPosting(인재노출, OPEN/CLOSE) · JobApplication(공고에 지원)

구인자로서

recruiting

Agency(대리점) · JobPosting(대리점이 소유)

보험업 특성상 구직자(설계사)가 경력을 쌓은 후 대리점을 개설해 구인자로 전환되는 경우가 빈번합니다. 동일한 회원이 이력서를 등록하면서 동시에 채용 공고를 올릴 수 있어야 합니다. 대리점을 등록하고 인증이 승인되면 그때 구인자 역할이 붙고, 공고의 소유자는 회원이 아니라 대리점입니다.

2. 지원 이후에도 서류 교환이 계속됩니다

일반 구인, 구직 플랫폼의 채용 흐름은 대체로 단방향입니다. 이 플랫폼은 시작점이 양쪽이고(구직자의 지원, 대리점의 면접 제안), 보험업 특성상 위촉 과정에서 요청과 제출과 보완이 여러 번 오갑니다.

일반 플랫폼

구직자 이력서 제출 → 구인자 검토 → 합격/불합격 (종료)

이 플랫폼

구직자 이력서 제출 → 구인자 검토 → 추가 서류 요청 → 구직자 서류 제출 → 구인자 검토 → 보완 요청 → 구직자 보완 제출 → 구인자 최종 승인 (종료)

왕복하는 것이 핵심이라 요건 상태에 누구 차례인지를 같이 담았습니다. 제출과 보완은 후보자 차례, 심사와 승인과 반려는 대리점 차례입니다. 화면이 상태 이름을 훑어 분기하지 않고 이 값 하나만 보면 됩니다.

바운디드 컨텍스트 15개

경계를 가르는 기준은 하나가 아닙니다. 액터(누가 하는가), 생명주기(언제까지 사는가), 재사용 주체(누가 공유하는가) 세 축을 섞어 씁니다.

기준적용
액터누가 이 행위를 하는가구직자의 행위는 talent, 구인자의 행위는 recruiting
생명주기언제 만들어져서 언제까지 사는가커머스를 product / order / point / subscription 으로
재사용 주체누가 이것을 공유하는가auth, file, code, notification 처럼 모든 BC 가 쓰는 것

액터 하나로 다 가르려고 하면 커머스가 안 갈립니다. 주문도 구독도 결국 회원이 산다라서 액터가 같습니다. 반대로 생명주기 하나로 가르면 이력서와 공고가 같은 칸에 들어갑니다. 둘 다 만들고 고치고 닫는 같은 주기입니다. 그래서 축을 섞어 쓰되, 어떤 축으로 갈랐는지를 BC 마다 기록해 뒀습니다.

액터 축은 사람이 아니라 역할을 가릅니다

  • 회원 레코드는 하나입니다. 액터로 가른다는 것은 회원을 구직자용과 구인자용으로 나누는 것이 아니라 행위를 나누는 것입니다. 같은 사람이 이력서를 쓰면 talent 의 일이고, 대리점을 열어 공고를 올리면 recruiting 의 일입니다.
  • 그래서 두 BC 는 서로의 데이터를 갖지 않고 회원 UUID 라는 같은 값을 각자 참조합니다. 한쪽이 상대의 역할을 확인해야 하는 일도 없습니다. 대리점이 있으면 구인자이고, 없으면 아닙니다.
  • 이 축으로 갈린 것은 talent 와 recruiting 둘뿐입니다. 나머지 열셋은 생명주기나 재사용 주체로 갈렸습니다. 액터는 1차 기준이지 유일한 기준이 아닙니다.
  • 행위자와 판정자가 어긋나는 자리에서는 판정자를 따릅니다. 위촉 요건을 수행하는 것은 후보자지만 승인과 반려는 대리점이 하고, 그래서 그 절차는 recruiting 이 소유합니다.
BC서브도메인소유 데이터
액터로 가른 것액터
구인핵심
recruiting
agency, jobposting, recruitment, appointment, interviewoffer, matching, banner, gacompany, attachment, support대리점, 공고, 채용 진행과 판정, 위촉 요건, 면접제안, 배너
인재핵심
talent
resume, jobseeker, jobapplication이력서 애그리거트, 인재노출, 지원(생명주기까지)
신원재사용 주체
인증
auth
authorization, identityPrincipal, PasswordAccount, EmailAccount, SocialAccount, Role, Permission, UrlAccess
회원
customer
(단일)고객 프로필, PII, 동의, 활동로그
직원
employee
(단일)직원
생명주기로 가른 것생명주기
주문·결제
order
order, payment, refund주문, 결제, 환불, PG 연동
상품
product
(단일)상품 카탈로그, 환불정책
포인트
point
(단일)포인트 지갑, 거래내역
구독
subscription
subscriber, quota구독 계약, 사용량 쿼터
모든 BC 가 쓰는 것재사용 주체
커뮤니티
community
board, chat, serviceCenter게시판, 채팅, 문의
파일
file
(단일)파일 메타, 접근 인가, 다운로드 로그
알림
notification
(단일)알림, 발송 큐
메시지
message
(단일)메시지 발송
공통코드
code
(단일)공통코드, 분류코드, 화면코드
통계
statistics
customer, jobseeker, recruiter, commission, pageview, collector집계 결과

데이터베이스는 아직 하나지만 테이블의 주인은 나눠 뒀습니다.

도메인 간 관계

아래 세 절이 한 덩어리입니다. 구직자가 지원해서 시작하는 흐름, 그 역방향인 대리점 제안 흐름, 그리고 둘을 갈라 둔 근거입니다. 여기서 반복해서 쓴 판단은 셋입니다.

지원과 채용처리를 다른 BC 가 소유합니다

talent 의 JobApplication 과 recruiting 의 Recruitment 는 서로 참조하지 않고 자연 상관키(공고·회원·이력서 UUID)로만 대응합니다. 소유자가 둘이라 한 테이블에 담으면 지원자의 취소와 대리점의 판정을 독립적으로 표현할 수 없었습니다.

지원과 제안 두 경로가 Recruitment 로 수렴합니다

initiated_from 과 출처 컬럼(둘 중 하나만, DB CHECK 로 강제)으로 어디서 왔는지가 아니라 어느 것에서 왔는지까지 남깁니다.

BC 를 넘는 참조는 전부 하위가 UUID 값으로 보유합니다

JPA 연관관계 매핑을 쓰지 않습니다. 대신 조회를 두 번 하는 비용을 냅니다. 기각한 대안과 그 비용은 아키텍처 장의 단방향 값참조 절에 있습니다.

구직자 지원 흐름

회원에서 이력서, 지원으로 이어지고 대리점의 공고와 만나 채용 진행과 위촉 요건까지 가는 경로입니다. 실선은 참조를 보유한 쪽에서 참조 대상 방향이고, 점선은 참조가 없이 자연 상관키로만 대응하는 관계입니다.

회원에서 이력서와 지원으로 이어지고, 대리점이 소유한 공고와 만나 채용 진행과 위촉 요건까지 가는 흐름도. 지원과 채용 진행 사이만 점선으로, 참조 없이 자연 상관키로 대응한다는 것을 나타낸다.

대리점 제안 흐름

대리점이 공개된 인재를 보고 먼저 면접을 제안하는 흐름입니다. 지원 흐름의 역방향이고, 두 흐름이 같은 채용 진행으로 수렴합니다.

이력서에서 인재노출로, 대리점의 공고와 만나 면접제안이 되고, 인재가 수락하면 채용 진행과 위촉 요건으로 이어지는 흐름도. 지원 흐름의 역방향이다.

지원과 채용 진행

두 흐름이 만나는 자리가 채용 진행입니다. 그런데 지원과 채용 진행은 한 테이블이 아닙니다. 소유자가 둘이라 지원자가 정하는 값과 대리점이 정하는 값을 한 레코드에 얹을 수 없었습니다.

지원 (구직)채용 진행 (구인)
소유구직자(회원 UUID)대리점(대리점 UUID)
담는 것지원했다는 사실, 그 시점 이력서 스냅샷절차와 판정이 매달리는 그릇
상태생명주기(유효 / 취소). 지원자가 정함판정(판정전 / 위촉 / 탈락). 대리점이 정함
없어도 되나면접제의로 시작하면 없음아무도 처리 안 하면 없음
서빙지원 목록. 공고별 지원자 목록에는 이 값을 실어 보냄채용 진행 목록. 공고별 지원자 목록도 여기가 서빙