Domain Model¶
Why This Layer Exists¶
domain_agent.domain.models는 이 템플릿의 핵심 비즈니스 의미를 담습니다.
이 레이어가 직접 표현하는 것은 다음입니다.
- request lifecycle
- agent가 준비한 plan과 proposal
- human review 결과
- 실행 가능 여부를 결정하는 규칙
즉, LangGraph가 "어떤 순서로 실행할지"를 맡는다면, domain은 "무엇이 유효한 상태인지"를 맡습니다.
Modeling Style¶
현재 domain은 Python 표준 라이브러리만 사용합니다.
dataclassStrEnumdatetime as dt
의도:
- framework-agnostic 유지
- transport validation과 domain validation 분리
- 단순한 값 객체와 aggregate를 가볍게 표현
위치:
- src/domain_agent/domain/models.py
Aggregate Boundary¶
Request¶
Request가 aggregate root입니다.
이 객체가 소유하는 것은:
- 식별자와 요약 정보
- 현재 lifecycle status
- 연결된
Proposal - 최종
ReviewDecision - action별
ActionReviewDecision - execution notes
외부 코드는 Request의 메서드를 통해서만 상태를 바꿔야 합니다.
핵심 메서드:
start_review()attach_proposal()request_review()record_review_result()execute()
이 구조 덕분에 상태 전이와 review 제약이 aggregate 내부에 모입니다.
Enums¶
RequestStatus¶
request lifecycle을 정의합니다.
DRAFTUNDER_REVIEWPROPOSEDAWAITING_APPROVALAPPROVEDREJECTEDEXECUTED
전이 규칙:
stateDiagram-v2
[*] --> DRAFT
DRAFT --> UNDER_REVIEW
UNDER_REVIEW --> PROPOSED
PROPOSED --> AWAITING_APPROVAL
AWAITING_APPROVAL --> APPROVED
AWAITING_APPROVAL --> REJECTED
APPROVED --> EXECUTED
이 규칙은 RequestStatus.can_transition_to(...)와 Request._transition_to(...)가 함께 강제합니다.
RiskLevel¶
generic risk classification입니다.
LOWMEDIUMHIGHCRITICAL
이 값은 request, proposal, agent plan 전반에서 공유됩니다.
Value Objects And Entities¶
PlannedAction¶
agent가 선택한 단일 실행 단위입니다.
필드:
action_idtool_namedescriptionarguments
invariants:
action_id는 비어 있으면 안 됨tool_name은 비어 있으면 안 됨description은 비어 있으면 안 됨arguments는 defensive copy로 보관
AgentPlan¶
여러 PlannedAction을 묶은 구조화된 실행 계획입니다.
필드:
summaryrationaleactionsexpected_outcomerisk_level
invariants:
summary는 비어 있으면 안 됨rationale은 비어 있으면 안 됨expected_outcome은 비어 있으면 안 됨actions는 최소 1개 이상이어야 함
Proposal¶
review 대상이 되는 human-readable proposal입니다.
필드:
summaryrationaleexecution_stepsrisk_levelprepared_by- optional
agent_plan
의미:
AgentPlan은 machine-oriented execution planProposal은 review-oriented summary
invariants:
summary,rationale,prepared_by는 비어 있으면 안 됨execution_steps는 최소 1개 이상이어야 함- step마다 빈 문자열이 있으면 안 됨
ReviewDecision¶
plan 전체에 대한 review outcome입니다.
필드:
approveddecided_bydecided_atcomment
invariants:
decided_by는 비어 있으면 안 됨decided_at는 timezone-aware여야 함- reject일 때는
comment가 반드시 있어야 함
ActionReviewDecision¶
개별 action에 대한 review outcome입니다.
필드:
action_idapprovedcomment
invariants:
action_id는 비어 있으면 안 됨- reject일 때는
comment가 반드시 있어야 함
PlanReviewResult¶
plan 전체 review 결과를 하나로 묶습니다.
필드:
decisionaction_decisions
invariants:
- action decision이 최소 1개 이상 있어야 함
- overall approve라면 approved action이 최소 1개 이상 있어야 함
- overall reject라면 approved action이 있으면 안 됨
즉 전체 결과와 action-level 결과가 서로 모순되면 안 됩니다.
Request Invariants¶
Request.__post_init__()는 생성 시점의 일관성을 검사합니다.
대표 규칙:
request_id,requester_id,summary는 비어 있으면 안 됨PROPOSED이후 상태는 반드시proposal이 있어야 함APPROVED,REJECTED,EXECUTED는 반드시review_decision이 있어야 함REJECTED는 approving decision을 가질 수 없음APPROVED,EXECUTED는 rejecting decision을 가질 수 없음- action review가 있으면 proposal의 planned action 전체를 정확히 한 번씩 덮어야 함
- proposal이 있는데 approved/rejected/executed 상태라면 action review도 반드시 있어야 함
이 검증 덕분에 repository나 workflow가 inconsistent aggregate를 저장하기 어렵습니다.
Lifecycle Methods¶
start_review()¶
- 허용 상태:
DRAFT - 결과 상태:
UNDER_REVIEW
attach_proposal(proposal)¶
- 허용 상태:
UNDER_REVIEW - 결과 상태:
PROPOSED - proposal을 aggregate에 연결
request_review()¶
- 허용 상태:
PROPOSED - 결과 상태:
AWAITING_APPROVAL - proposal이 없으면 실패
record_review_result(review_result)¶
- 허용 상태:
AWAITING_APPROVAL - review 결과가 모든 planned action을 덮는지 검사
review_decision과action_review_decisions를 저장- overall decision에 따라
APPROVED또는REJECTED로 전이
execute(execution_notes=None)¶
- 허용 상태:
APPROVED - positive review decision이 반드시 있어야 함
REJECTED상태에서는 항상 실패- 결과 상태:
EXECUTED
Execution Semantics¶
Request.approved_action_ids()는 실행 가능한 action id만 돌려줍니다.
이 값은 executor가 참고하는 핵심 계약입니다.
의미:
- partial review가 가능함
- 승인된 action만 실행할 수 있음
- review semantics는 executor 바깥이 아니라 domain에서 결정됨
즉 executor는 "무엇을 실행할지"를 계산하지 않고, domain이 계산한 결과를 따른다고 보는 편이 맞습니다.
Error Types¶
DomainError: 기본 domain 오류InvalidStatusTransitionError: lifecycle 전이 위반ReviewRequiredError: review 없이 실행 시도
이 예외들은 transport layer 예외가 아니라 domain rule violation을 나타냅니다.
How This Fits DDD¶
DDD 관점에서 이 모델의 장점은 다음입니다.
- lifecycle 규칙이 aggregate에 모임
- review semantics가 workflow나 UI에서 새지 않음
- framework 객체가 domain으로 들어오지 않음
- proposal, plan, review result가 명시적인 ubiquitous language를 가짐
즉 workflow나 LangChain adapter는 domain 규칙을 "실행"할 뿐, 규칙 자체를 정의하지 않습니다.
Extension Guidance¶
새 도메인에서 주로 확장하는 부분:
- domain-specific request model을 만들고
Request로 변환 ContextProvider구현AgentEngine구현ActionExecutor구현
generic core를 바꾸기 전에 먼저 검토할 질문:
- 이 규칙이 모든 도메인에서 공통인가?
- review semantics를 generic으로 유지할 수 있는가?
- workflow가 아니라 domain rule인가?
Related Docs¶
Code References¶
src/domain_agent/domain/models.pysrc/domain_agent/application/dto.pysrc/domain_agent/application/ports.pysrc/domain_agent/orchestration/langgraph/nodes/deterministic.py