Skip to content

흐름맵 BPMN 서브셋 · 온톨로지 참조 설계 (0단계) ​

대상 모듈: client/src/features/workflow-map/ 범위: 교환(D-교환) 대비 정규 모델까지. 실행 엔진(D-실행)은 이 문서의 범위가 아니다. 상태: 확정(권고 기본값으로 결정, 근거는 §7)

1. 배경·목표·비목표 ​

TaskFlowMap은 증상 기반으로 규칙이 누적되어 흐름(flow) 과 자사 규칙(레벨·권한·외부유입) 이 한 덩어리로 섞였다. 확장성·표준화·온톨로지 연계를 위해 흐름을 BPMN 서브셋으로 정규화하고, 의미는 온톨로지 참조로 분리한다.

목표

  1. 흐름은 표준 BPMN 요소로만 표현한다(커스텀 노드 종류를 늘리지 않는다).
  2. 자사 규칙은 표준 확장 슬롯(extensionElements)으로만 분리한다.
  3. BPMN Task가 온톨로지 개념을 참조한다(링크만, 전문은 온톨로지가 소유).
  4. .bpmn + 온톨로지 참조로 export/import 가능한 정규 모델을 갖는다.

비목표

  • 실행 엔진(Camunda/Zeebe) 도입 — 별도 프로젝트.
  • 편집기를 bpmn-js Modeler로 교체 — 후속 결정.
  • 온톨로지 전체 큐레이션 — 참조 대상으로만 사용.
  • 이 단계에서 화면(UI) 변경 — 없다.

2. 계층 구조 ​

온톨로지(의미)      개념·용어·관계        ← 참조 대상(termId/uri)
BPMN 서브셋(흐름)   Task·Gateway·Flow     ← 정규 모델(canonical)
렌더러(ui/)         자체 SVG / bpmn-js    ← 교체 가능
(미래) 실행기        엔진 어댑터           ← domain 경계에 이음새만

3. 정규 모델 ​

domain/bpmn.ts에 정의한다. UI·DB 타입에 의존하지 않는다.

ts
type BpmnSubsetModel = {
  processId: string;
  level?: string | null;              // L4/L5 (extensionElements)
  lanes: Lane[];
  nodes: BpmnNode[];
  flows: BpmnFlow[];
  participants: ParticipantRef[];     // 다른 L5(외부유입) 참조 대상
  extension?: ModelExtension;
};

type BpmnNode =
  | { kind: "startEvent"; id: string; laneId: string; extension?: NodeExtension }
  | { kind: "endEvent"; id: string; laneId: string; extension?: NodeExtension }
  | { kind: "task"; id: string; laneId: string; performers: RoleRef[];
      ontologyTerm?: OntologyTermRef; extension?: NodeExtension }
  | { kind: "exclusiveGateway"; id: string; laneId: string;
      documentation?: LocalizedText; defaultFlowId?: string; extension?: NodeExtension };

type BpmnFlow = {
  id: string;
  kind: "sequence" | "message";        // message = 외부유입
  sourceId: string | null;             // message + 외부 출발이면 null
  targetId: string;
  externalSource?: ExternalSourceRef;  // message 전용
  condition?: FlowCondition;           // sequence 출구 조건
  label?: LocalizedText | null;
  extension?: FlowExtension;
};

식별자 규칙(왕복 변환을 단순하게 유지):

대상규칙예
노드step:<stepId>step:62
내부 흐름transition:<transitionId>transition:9
외부유입 흐름inbound:<inboundId>inbound:41
lanelane:<l5Id>lane:50
participantparticipant:l5:<l5Id>participant:l5:70

각 요소의 extension에 원본 식별자를 남겨 역변환이 추측 없이 가능하게 한다(§5).

4. 현재 모델 → BPMN 서브셋 매핑 ​

현재BPMN 서브셋비고
step.kind: "start"startEvent
step.kind: "end"endEvent
step.kind: "process"taskperformers ← 배정 역할
step.kind: "gateway"exclusiveGatewaydocumentation ← question
step.kind: "condition"exclusiveGateway도메인 구분은 extension.sourceKind에 보존
transition.outcome: "YES"|"NO"sequence + condition{ type: "outcome", value }
transition.outcome: "DEFAULT"sequence + 게이트웨이 defaultFlowId출발이 게이트웨이가 아니면 조건 없이 둔다
transition.conditionExpressioncondition { type: "expression" }표준 conditionExpression으로 직렬화
inboundTransitionmessage + externalSource출발 L5는 participant(collapsed pool)
배정 역할(roleMasterId/code)performers[].roleCode표준 performer 계열
l5Id(단일 L5)lane:1개L4 통합맵은 L5별 lane

5. 확장(extensionElements) 계약 ​

표준에 없는 자사 규칙은 여기에만 둔다. 네임스페이스는 고정 상수로 관리한다.

ts
export const PC_EXTENSION_NAMESPACE = "urn:process-center:bpmn:extension:1";
확장붙는 곳내용
sourceStepIdnode/fLow extension원본 workflow_steps.id / workflow_transitions.id (역변환용)
sourceKindgateway extension"gateway" | "condition" 도메인 구분
levelmodel extension"L4" | "L5"
externalSourcemessage flow{ l5Id, l5Code?, stepId, stepKind, isReturn? }
ontologyTermtask{ termId, uri? }

.bpmn 직렬화 예:

xml
<bpmn:task id="step:62" name="승인">
  <bpmn:documentation>승인되었습니까?</bpmn:documentation>
  <bpmn:extensionElements>
    <pc:sourceStepId>62</pc:sourceStepId>
    <pc:ontologyTerm termId="activity.approval" uri="https://terms.example/activity.approval" />
  </bpmn:extensionElements>
</bpmn:task>

6. 교환 포맷 ​

  • 흐름: .bpmn(BPMN 2.0 XML). sequenceFlow·conditionExpression·exclusiveGateway·performer·participant는 표준 요소.
  • 의미: 온톨로지는 별도 직렬화(후속: OWL/JSON-LD)로 교환하고, .bpmn 안에는 ontologyTerm 참조만 넣는다.
  • export: 정규 모델 → .bpmn. 렌더러와 무관하게 동작해야 한다.
  • import: .bpmn 파싱 → 정규 모델. 확장이 없는 외부 BPMN도 허용하고(레벨·외부유입은 기본값), 알 수 없는 확장은 보존한다.

7. 확정된 결정 (권고 기본값) ​

#질문결정근거
1온톨로지 식별자termId(슬러그) 필수, uri 선택내부 용어집은 슬러그로 관리되고, 외부 온톨로지는 URI가 있을 수 있어 둘 다 수용
2의사결정 질문정규 모델에 구조화 필드 + .bpmn에는 표준 documentation표준 요소로 사람이 읽을 수 있게 하고, 기계 판독이 필요해지면 확장 추가
3확장 네임스페이스urn:process-center:bpmn:extension:1실제 도메인을 약속하지 않는 URN. 값은 상수 1곳에서 관리
4정규 모델 위치클라이언트 domain/ (의존성 0)서버 개입이 필요해지면 그대로 shared/로 이동 가능

8. 마이그레이션(정규화) ​

domain/normalize.ts — 기존 UI 데이터를 정규 모델로 바꾸는 순수 함수. DB·trpc에 의존하지 않는다.

ts
normalizeWorkflowMap({
  processId, level?, steps, transitions, inboundTransitions?, roleAssignments?, lanes?
}): BpmnSubsetModel

규칙:

  • 노드/흐름 id는 §3의 접두 규칙으로 만들고, 원본 id를 extension에 남긴다.
  • outcome: "DEFAULT"는 출발이 게이트웨이일 때만 defaultFlowId로 승격한다(그 외에는 조건 없는 흐름).
  • outcome: "YES"/"NO"는 condition { type: "outcome" }으로 둔다.
  • inboundTransition은 message 흐름으로 만들고 출발 L5를 participants에 등록한다(중복 제거).
  • 배정 역할은 processNodeId로 Task에 연결한다(배정이 없으면 performers: []).
  • 입력 순서에 의존하지 않는다(정렬은 id 기준).

9. 파일 구조 ​

client/src/features/workflow-map/
  domain/
    bpmn.ts           # 정규 모델 타입 + 확장 네임스페이스
    normalize.ts      # 기존 데이터 → 정규 모델
    normalize.test.ts # 단위 테스트
    index.ts          # domain 공개 API
  ui/                 # (후속) 캔버스·카드·패널
  index.ts            # 모듈 공개 API

10. 검증 ​

  • pnpm check(tsc) 통과.
  • domain/normalize.test.ts 통과(신규).
  • TaskFlowMap.test.tsx·IntegratedWorkflowMap.test.tsx 그대로 통과(회귀 없음).

11. 후속 단계(이 문서 범위 밖) ​

  1. ui/ 분리 — 표현 컴포넌트 추출(로직 변경 없이).
  2. TaskFlowMap.tsx(1,572줄) 분해 — 캔버스/카드/패널/연결.
  3. .bpmn import(XML → 정규 모델) + export와 왕복 테스트. (export는 domain/bpmnSerialize.ts로 구현됨)
  4. bpmn-js Viewer PoC(렌더·PDF만) → 필요 시 Modeler 교체 결정.
  5. 엔진 어댑터(domain/engine.ts) — 스텁만 유지, 도입은 별도 프로젝트.

12. 구현 상태 ​

항목상태위치
정규 모델 타입완료domain/bpmn.ts
정규화(기존 데이터 → 모델)완료 + 단위 테스트 10개domain/normalize.ts
.bpmn export완료 + 단위 테스트 8개domain/bpmnSerialize.ts
.bpmn import완료 + 왕복 단위 테스트 6개domain/bpmnParse.ts
엔진 어댑터스텁domain/engine.ts
화면 적용(ui)미착수(화면은 기존 경로 유지)—

| L4/L5 레벨, isReturn | extension | 비표준 |