콘텐츠로 이동

Product Domain Modeling

분류: Layer 13 - Product Engineering & Growth Systems

Product Engineer는 화면을 만드는 동시에 그 화면 뒤의 제품 상태를 모델링한다. workspace, member, role, invite, subscription, entitlement, project 같은 객체가 흐릿하면 UX Flow, feature flag, analytics, billing, support tooling이 모두 흔들린다.

Product Domain Modeling은 사용자 경험 뒤에 있는 제품 객체, 관계, 상태 전이, 불변식을 정의하고 이를 API·DB·권한·analytics 계약으로 연결하는 역량이다.

Product Engineer가 자주 만나는 기능은 단순 CRUD처럼 보인다.

  • 팀 생성
  • 멤버 초대
  • role 변경
  • 유료 기능 접근
  • trial 시작과 종료
  • project archive
  • workspace 삭제

하지만 실제로는 권한, 결제, lifecycle, audit log, analytics가 붙어 있다. 예를 들어 멤버 초대는 Invite 하나를 만들고 끝나는 기능이 아니다. 초대 가능한 role, seat limit, email delivery, expired token, accepted membership, audit log, activation event가 모두 연결된다.

제품 도메인 모델이 약하면 화면마다 다른 상태 해석이 생긴다. 한 화면은 user.role을 보고, 다른 API는 membership.role을 보고, billing은 seat count를 따로 계산한다. 이런 시스템은 실험과 출시가 아니라 예외 처리의 늪이 된다.

2.5. 선행 방식의 한계 — 왜 Product Domain Modeling이 필요한가

섹션 제목: “2.5. 선행 방식의 한계 — 왜 Product Domain Modeling이 필요한가”

일반 DB 모델링은 테이블과 관계를 잘 잡는 데 초점이 있다. DDD는 도메인 언어와 aggregate, invariant를 강조한다. 하지만 Product Engineer에게는 여기에 제품 성장과 운영의 관점이 더 붙는다. 같은 User라도 개인 계정인지, workspace member인지, billing customer인지, experiment subject인지에 따라 의미가 달라진다.

CRUD 중심 모델링은 “저장할 수 있는가”에는 답하지만 “이 상태에서 어떤 사용자가 어떤 기능을 볼 수 있는가”, “이 상태 전이가 제품 지표에 어떤 이벤트로 남는가”, “결제 실패 후 entitlement를 언제 회수할 것인가”에는 약하다.

Product Domain Modeling은 이 한계를 메운다. DB 테이블은 결과물이고, 먼저 정의해야 하는 것은 제품 객체의 언어, 상태 전이, 불변식, 권한 경계, 분석 이벤트다.

많은 B2B/SaaS 제품은 다음 객체를 중심으로 돌아간다.

객체의미흔한 관계
User로그인 가능한 개인여러 workspace의 member가 될 수 있음
Account/Workspace협업과 결제의 기본 단위member, project, subscription을 가짐
Membershipuser와 workspace의 관계role, status, joined_at을 가짐
Role/Permission행동 가능 범위owner/admin/member/viewer
Invite아직 수락되지 않은 초대token, role, expiry, inviter
Project/Resource제품의 핵심 작업 단위owner workspace, visibility, lifecycle
Plan/Subscription결제 상태trialing, active, past_due, canceled
Entitlement기능 접근 권한feature key, limit, enabled state

Product Engineer는 이 객체를 UI 상태로 흩뿌리지 않고 서버 도메인 모델로 둔다. 그래야 feature flag, analytics, billing, support console이 같은 언어를 쓴다.

가장 흔한 실수는 user 하나에 모든 상태를 붙이는 것이다.

User 중심 모델과 Workspace 중심 모델

User 중심

role, plan, setting이 user에 붙어 있어 개인 제품에는 단순하다.

문제: 한 사용자가 여러 팀에 속하면 권한과 결제가 꼬인다.

Workspace 중심

협업, 결제, 권한의 기본 단위를 workspace로 둔다.

장점: B2B SaaS, 팀 초대, seat billing, audit log에 적합하다.

Membership 중심

user와 workspace 사이의 관계 객체가 role과 상태를 가진다.

장점: 같은 user가 workspace마다 다른 role을 가질 수 있다.

Entitlement 중심

plan 이름이 아니라 기능 접근 권한을 별도 객체로 둔다.

장점: billing 변경과 feature exposure를 느슨하게 연결한다.

협업 제품에서는 user.role = admin보다 membership.role = admin이 안전하다. 사용자는 여러 workspace에 속할 수 있고, 권한은 관계마다 달라진다.

제품 객체에는 lifecycle이 있다.

Invite:
pending -> accepted
pending -> expired
pending -> revoked
Workspace:
active -> suspended
active -> deleted_pending
deleted_pending -> deleted
Subscription:
trialing -> active
active -> past_due
past_due -> canceled

상태 전이는 API endpoint보다 먼저 정의해야 한다. 그리고 각 전이에 불변식을 붙인다.

  • expired invite는 accepted로 갈 수 없다.
  • owner가 한 명뿐인 workspace에서는 마지막 owner를 member로 낮출 수 없다.
  • suspended workspace에서는 새 project를 만들 수 없다.
  • past_due subscription은 일정 유예 기간 뒤 entitlement를 제한한다.
  • deleted_pending workspace는 audit log와 export window를 보존한다.

퀴즈

role을 user 테이블의 컬럼으로 두면 B2B workspace 제품에서 어떤 문제가 생기는가?

힌트: 권한은 사용자 자체가 아니라 사용자와 workspace의 관계일 수 있다.

정답 보기

한 사용자가 여러 workspace에서 서로 다른 role을 가져야 할 때 표현할 수 없다. 권한 변경, 초대, audit log, billing seat 계산도 관계 단위로 다뤄야 하므로 Membership 같은 관계 객체가 필요하다.

6. API와 Analytics에 같은 언어를 쓴다

섹션 제목: “6. API와 Analytics에 같은 언어를 쓴다”

제품 도메인 모델은 DB에만 머물지 않는다.

영역도메인 모델이 주는 효과
APIworkspace_id, membership_id, entitlement_key가 명확해진다
UIempty/error/success 상태가 도메인 상태와 맞아진다
Analyticsevent property가 같은 집계 단위를 쓴다
Feature flagtargeting rule이 role, plan, workspace 상태를 참조한다
Billingsubscription과 entitlement 변경이 기능 접근으로 이어진다
Support고객 문의를 같은 객체 언어로 추적한다

이 언어가 흔들리면 분석 이벤트도 흔들린다. 예를 들어 account_id, workspace_id, team_id가 같은 뜻인지 다른 뜻인지 불명확하면 cohort 분석도 신뢰하기 어렵다.

Product Engineer가 만든 제품 상태는 사용자와 support team이 나중에 설명할 수 있어야 한다.

초대 기능이라면 audit log에는 다음 정보가 필요할 수 있다.

  • 누가 초대했는가
  • 어떤 role로 초대했는가
  • 언제 만료되었는가
  • 누가 수락했는가
  • role이 바뀌었다면 누가 바꿨는가
  • billing seat count에 어떤 영향을 줬는가

Audit log는 보안 기능이기도 하지만 제품 운영 기능이기도 하다. 상태 전이를 설명할 수 없는 제품은 고객 지원과 incident 대응이 어렵다.

시나리오

팀 초대와 유료 seat 제한을 구현한다

무료 plan은 member 3명까지 가능하고, 유료 plan은 seat 수만큼 초대할 수 있다. 초대받은 사용자는 링크로 가입하거나 기존 계정으로 수락할 수 있다.

User, Workspace, Membership, Invite, Subscription, Entitlement 중 어떤 객체와 상태 전이를 먼저 정의해야 하는가?

권장 루틴은 다음과 같다.

  1. 제품 객체를 명명한다. 같은 개념에 여러 이름을 쓰지 않는다.
  2. 객체 간 관계를 그린다. user와 workspace 사이에는 membership이 있는지 확인한다.
  3. lifecycle state를 적는다. pending, active, suspended, expired 같은 상태를 나열한다.
  4. 상태 전이별 invariant를 적는다.
  5. 권한과 entitlement가 어떤 객체를 참조하는지 정한다.
  6. analytics event property가 이 객체 언어를 따르는지 확인한다.
  7. support와 audit log에서 설명 가능한지 확인한다.

Product Domain Modeling 체크

  • User, Workspace, Membership, Resource, Subscription, Entitlement의 의미가 분리되어 있다
  • role과 permission이 user 자체가 아니라 관계 또는 scope에 붙어 있다
  • 각 핵심 객체의 lifecycle state와 가능한 전이가 정의되어 있다
  • 상태 전이에 지켜야 할 invariant가 있다
  • API, UI, analytics, billing이 같은 객체 이름을 쓴다
  • feature flag targeting에 필요한 속성이 도메인 모델에 있다
  • audit log와 support console에서 상태 변화를 설명할 수 있다
  • plan 이름이 아니라 entitlement로 기능 접근을 판단할 수 있다

Product Domain Modeling은 이어지는 billing/entitlement 문서의 기반이다. subscription 상태와 entitlement 변경은 제품 객체와 상태 전이 없이는 안전하게 구현하기 어렵다.

  • Billing, Subscription & Entitlement: 결제 lifecycle과 기능 접근 권한을 모델링한다.
  • Privacy, Consent & Product Data Governance: 도메인 객체에 붙은 개인정보와 보존 정책을 다룬다.
  • Experimentation & Feature Flags: role, plan, workspace 상태를 flag targeting에 활용한다.
  • Account model
  • Workspace model
  • Membership
  • Role and permission
  • Product lifecycle state
  • Domain invariant
  • Entitlement
  • Audit log
  • Supportability