docs(TechLog): 도메인 규칙을 기록에 엮는다
§1.4 로 세운 도메인·비즈니스 규칙을 그것이 실제로 설명하는 기록에 넣었다. 프로젝트가 문서 게시 파이프라인을 안 타는 이유 → 화면 다섯이 비어 있던 Case 홈 focus 설정이 FK 없이 사는 설계 → 「열린 질문이 없습니다」 Case 결정이 자기 화면을 안 갖는 이유 → 목록이 문서 전체를 실어야 했던 Case 게시가 단계마다 다른 코드로 거절하는 설계 → 화면이 추측 셋을 출력한 Case (반대 사례) 종류마다 애그리거트와 테이블이 다르다 → 매퍼가 종류를 판정해야 하는 Case 축을 지우면 연결만 끊고 주제를 지우면 거절하는 이유 → 축 Concept 개념이 문서 테이블에 얹힌다 → 열세 곳 Case 화면 상태와 도메인 상태가 원래 갈려 있었다 → 이름을 두 번 바꾼 Case Case 본문 중앙값 675 → 1,342 자. 검사 넷 전부 통과한다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4769e52e48
commit
e9f6a93327
+16
@@ -68,6 +68,22 @@ topic (주제)
|
|||||||
|
|
||||||
대가는 데이터베이스가 참조 무결성을 지켜 주지 않는다는 것이다. 기록을 지울 때 그 쌍을 함께 지우는 것은 코드가 한다.
|
대가는 데이터베이스가 참조 무결성을 지켜 주지 않는다는 것이다. 기록을 지울 때 그 쌍을 함께 지우는 것은 코드가 한다.
|
||||||
|
|
||||||
|
## 축을 지우면 기록은 남는다
|
||||||
|
|
||||||
|
축을 지울 때 걸려 있던 기록은 지우지 않는다. 연결만 끊고 그 기록은 주제의 공통 기록이 된다.
|
||||||
|
|
||||||
|
주제를 지울 때와 반대다. 주제는 그것을 쓰는 기록이 있으면 아예 거절한다.
|
||||||
|
|
||||||
|
> 주제를 지웠다는 이유로 그 주제를 쓰던 문서의 분류가 조용히 사라지면 안 되기 때문이다 — 지우려면 먼저 그 문서들을 옮기라는 뜻이다.
|
||||||
|
|
||||||
|
주제는 그 기록이 무엇에 관한 것인지를 말하는 분류이고, 축은 같은 주제 안에서 무엇과 견주는지를 말하는 비교 축이다. 분류가 사라지면 기록의 뜻이 바뀌고, 비교 축이 사라지면 비교만 없어진다.
|
||||||
|
|
||||||
|
## slug 제약이 주제 안에서만 걸린다
|
||||||
|
|
||||||
|
축 slug 의 유일성 제약은 주제와 slug 의 쌍이다. 그래서 다른 주제에서 같은 이름을 쓸 수 있고, 여러 주제가 「공통」 같은 같은 이름을 각자 갖는 것이 정상이다.
|
||||||
|
|
||||||
|
축 화면을 조회할 때 주제까지 함께 맞춰야 하는 이유가 이것이다. 주제를 빼면 다른 주제의 같은 이름 축이 함께 걸린다.
|
||||||
|
|
||||||
## 사람이 쓰는 칸
|
## 사람이 쓰는 칸
|
||||||
|
|
||||||
주제의 논지, 축의 요약과 결론은 기록을 합쳐 자동으로 나오는 글이 아니다.
|
주제의 논지, 축의 요약과 결론은 기록을 합쳐 자동으로 나오는 글이 아니다.
|
||||||
|
|||||||
+8
@@ -78,6 +78,14 @@ tech-log-frontend : 계약에서 생성한 타입을 그대로 사용
|
|||||||
|
|
||||||
다섯이 같은 원인인데 숨은 깊이가 달랐다. 프로젝트 공개 여부와 관계 연결은 화면에 자리는 있고 값만 없으므로 「아직 안 채웠다」로 읽힌다. 홈 focus 는 그보다 깊다 — 세 슬롯이 다 비면 화면이 그 영역을 통째로 그리지 않으므로, 그런 영역이 있다는 사실조차 화면에서 알 수 없다. 그래서 운영에서 한 번도 나타난 적이 없다.
|
다섯이 같은 원인인데 숨은 깊이가 달랐다. 프로젝트 공개 여부와 관계 연결은 화면에 자리는 있고 값만 없으므로 「아직 안 채웠다」로 읽힌다. 홈 focus 는 그보다 깊다 — 세 슬롯이 다 비면 화면이 그 영역을 통째로 그리지 않으므로, 그런 영역이 있다는 사실조차 화면에서 알 수 없다. 그래서 운영에서 한 번도 나타난 적이 없다.
|
||||||
|
|
||||||
|
## 프로젝트는 문서가 아니다
|
||||||
|
|
||||||
|
다섯 중 프로젝트 공개 여부는 결이 조금 다르다. 프로젝트는 종류 다섯에 들어 있지 않고 본문도 검증 대상도 없으므로 문서 게시 파이프라인을 타지 않는다.
|
||||||
|
|
||||||
|
> 프로젝트는 Studio 문서가 아니다 — `RecordKind` 에 없고 본문도 검증 대상도 없다. 그래서 문서 게시 파이프라인 대신 여기서 직접 공개 상태를 세운다. 릴리스가 자체 경로를 갖는 것과 같은 이유다.
|
||||||
|
|
||||||
|
공개 화면들은 전부 공개 투영의 프로젝트 행을 가시성 관문으로 쓴다. 그 행을 세우는 경로가 없었으므로 프로젝트는 영원히 비공개였고, 화면에서는 「아직 공개 안 했나 보다」로 보였다.
|
||||||
|
|
||||||
## 같은 계약이 반대 방향으로도 깨졌다
|
## 같은 계약이 반대 방향으로도 깨졌다
|
||||||
|
|
||||||
빠진 구현이 화면을 비우는 것과 반대로, 있는 계약이 값을 거절하는 경우도 났다. `home_focus_config.default_focus_type` 은 마이그레이션 직후 NULL 인데 계약은 이 필드를 required 에 enum 세 값으로 선언한다.
|
빠진 구현이 화면을 비우는 것과 반대로, 있는 계약이 값을 거절하는 경우도 났다. `home_focus_config.default_focus_type` 은 마이그레이션 직후 NULL 인데 계약은 이 필드를 required 에 enum 세 값으로 선언한다.
|
||||||
|
|||||||
+8
@@ -73,6 +73,14 @@ tech-log-backend : 911e8ba — 없던 두 컨트롤러를 구현
|
|||||||
|
|
||||||
읽기 전용 화면이면 방문자가 「데이터가 없나 보다」로 넘어간다. 작성 도구에서는 그 오독이 곧바로 작업 판단이 된다 — 없다고 읽으면 다시 쓰게 된다.
|
읽기 전용 화면이면 방문자가 「데이터가 없나 보다」로 넘어간다. 작성 도구에서는 그 오독이 곧바로 작업 판단이 된다 — 없다고 읽으면 다시 쓰게 된다.
|
||||||
|
|
||||||
|
## 이 설정은 없는 대상을 가리킬 수 있다
|
||||||
|
|
||||||
|
홈이 무엇을 앞에 세울지 정하는 설정은 지목한 대상에 외래키를 걸지 않는다. 설정이 대상보다 오래 살아남는 것을 허용하는 설계다.
|
||||||
|
|
||||||
|
대신 저장할 때 그 대상이 실제로 있는지 확인한다. 확인하지 않으면 없는 id 가 그대로 저장되고, 공개 화면은 조용히 빈 focus 를 그린다 — 저장은 성공했는데 화면에는 아무것도 안 나오는, 이유를 알 수 없는 실패가 된다.
|
||||||
|
|
||||||
|
같은 화면의 두 자리가 반대로 처리돼 있었다. 저장 쪽은 없는 대상을 막고, 목록을 못 읽은 쪽은 없는 것으로 그렸다.
|
||||||
|
|
||||||
## 못 읽었다고 적는다
|
## 못 읽었다고 적는다
|
||||||
|
|
||||||
> 거짓말을 하느니 못 읽었다고 말한다.
|
> 거짓말을 하느니 못 읽었다고 말한다.
|
||||||
|
|||||||
+6
@@ -85,6 +85,12 @@ tech-log-design-package : 76a7ccb
|
|||||||
|
|
||||||
모양이 다른데 매퍼를 공유하면서 종류 판정까지 그쪽 것을 따랐다. 지식 목록의 매퍼는 CASE 와 REFERENCE 만 다루면 되는 화면을 위해 만들어졌으므로, 질문과 개념을 받으면 빈 값을 돌려준다.
|
모양이 다른데 매퍼를 공유하면서 종류 판정까지 그쪽 것을 따랐다. 지식 목록의 매퍼는 CASE 와 REFERENCE 만 다루면 되는 화면을 위해 만들어졌으므로, 질문과 개념을 받으면 빈 값을 돌려준다.
|
||||||
|
|
||||||
|
## 왜 매퍼가 종류를 판정해야 하나
|
||||||
|
|
||||||
|
편집 화면이 고르는 유형 다섯은 하나의 애그리거트가 아니다. 각 유형이 자기 애그리거트와 테이블을 갖고, 문서 세 종류(Case·Reference·Concept)만 한 테이블을 공유한다.
|
||||||
|
|
||||||
|
그래서 한 목록에 여러 종류가 섞이면 항목마다 어디서 온 것인지를 판정해야 한다. 그 판정을 다른 화면의 매퍼에서 빌려 오면 그 화면이 다루던 종류만 통과한다.
|
||||||
|
|
||||||
## 두 가지를 함께 고쳐야 했다
|
## 두 가지를 함께 고쳐야 했다
|
||||||
|
|
||||||
응답 모양에 맞는 매퍼를 쓰고 모든 종류를 싣게 했다. 그것만으로는 줄이 「제목만 있고 가운뎃점만 남은」 모양이었다.
|
응답 모양에 맞는 매퍼를 쓰고 모든 종류를 싣게 했다. 그것만으로는 줄이 「제목만 있고 가운뎃점만 남은」 모양이었다.
|
||||||
|
|||||||
+8
@@ -80,6 +80,14 @@ const path = kind === "CASE" ? "/cases/"
|
|||||||
|
|
||||||
CONCEPT 을 더해도 이 코드는 컴파일된다. 마지막 가지가 나머지를 받으므로 모든 값에 갈 곳이 있고, 각 가지가 내놓는 타입도 같기 때문이다. 실행하면 `/concepts/idp-brokering` 대신 프로젝트 경로가 만들어지고, 그 주소로 질문 조회가 나가 404 가 돌아온다.
|
CONCEPT 을 더해도 이 코드는 컴파일된다. 마지막 가지가 나머지를 받으므로 모든 값에 갈 곳이 있고, 각 가지가 내놓는 타입도 같기 때문이다. 실행하면 `/concepts/idp-brokering` 대신 프로젝트 경로가 만들어지고, 그 주소로 질문 조회가 나가 404 가 돌아온다.
|
||||||
|
|
||||||
|
## 개념은 새 테이블이 아니었다
|
||||||
|
|
||||||
|
종류를 하나 더한다는 것이 테이블 하나를 더하는 일은 아니다. 편집 화면이 고르는 유형 다섯은 하나의 애그리거트가 아니고, 각 유형이 자기 애그리거트와 테이블을 갖는다.
|
||||||
|
|
||||||
|
> `CONCEPT`은 `document`의 세 번째 유형이다 — Case/Reference와 테이블을 공유하고 `concept_detail`에 기준 버전만 따로 둔다.
|
||||||
|
|
||||||
|
저장 쪽은 그래서 작았다. 늘어난 것은 그 종류를 아는 코드였고, 그 코드가 손 목록으로 흩어져 있었다.
|
||||||
|
|
||||||
## 열세 곳
|
## 열세 곳
|
||||||
|
|
||||||
| # | 어디 | 증상 | 커밋 |
|
| # | 어디 | 증상 | 커밋 |
|
||||||
|
|||||||
+8
@@ -84,6 +84,14 @@ case TOPIC_IN_USE -> "이 주제를 쓰는 기록이 있어 삭제할 수 없습
|
|||||||
|
|
||||||
그래서 화면이 실어 나를 수 있는 것은 이 고정 문구다. 서버 안의 진단 메시지는 서버 로그에만 남는다.
|
그래서 화면이 실어 나를 수 있는 것은 이 고정 문구다. 서버 안의 진단 메시지는 서버 로그에만 남는다.
|
||||||
|
|
||||||
|
## 같은 저장소가 반대로 만든 경로
|
||||||
|
|
||||||
|
게시 경로는 정반대로 설계돼 있다. 단계마다 다른 계약 코드로 거절하고, 그 이유가 use case 의 javadoc 에 적혀 있다.
|
||||||
|
|
||||||
|
> 단계별 실패가 서로 다른 계약 코드로 나가는 것이 이 use case 의 핵심이다. `DOCUMENT_VALIDATION_FAILED`(지금 검증하면 실패)와 `VALIDATION_STALE`(통과했으나 전제가 바뀜)은 다른 사건이고, 작성자가 해야 할 일도 다르다 — 전자는 고치는 것이고 후자는 다시 검증하는 것이다.
|
||||||
|
|
||||||
|
미리보기도 만료된 것과 이 버전을 설명하지 않는 것이 다른 코드로 갈린다. 삭제 실패만 그 갈래를 화면에서 다시 뭉갰다.
|
||||||
|
|
||||||
## 무엇이 구분되지 않았나
|
## 무엇이 구분되지 않았나
|
||||||
|
|
||||||
세 원인은 해야 할 일이 다르다.
|
세 원인은 해야 할 일이 다르다.
|
||||||
|
|||||||
+14
-1
@@ -84,7 +84,7 @@ REFERENCE → 적용 기준 DECISION → 설계 결정
|
|||||||
QUESTION → 열린 질문
|
QUESTION → 열린 질문
|
||||||
```
|
```
|
||||||
|
|
||||||
두 안이 말하는 것은 같다. 「직접 해보니」와 「검증 기록」은 둘 다 그 글이 재현한 결과라고 말한다. 갈린 것은 이 사이트의 다른 글들이 쓰는 어조와 맞느냐다.
|
두 안이 말하는 것은 같다. 「직접 해보니」와 「검증 기록」은 둘 다 그 글이 재현한 결과라고 말한다. 갈린 것은 이 사이트의 다른 글들이 쓰는 어조와 맞느냐였다.
|
||||||
|
|
||||||
## 이름 표가 하나였기 때문에 두 번 바꿀 수 있었다
|
## 이름 표가 하나였기 때문에 두 번 바꿀 수 있었다
|
||||||
|
|
||||||
@@ -96,6 +96,19 @@ QUESTION → 열린 질문
|
|||||||
|
|
||||||
이름을 바꿔도 이 표는 그대로였다. 표시 이름과 지식 상태가 다른 축이라 따로 두었기 때문이다.
|
이름을 바꿔도 이 표는 그대로였다. 표시 이름과 지식 상태가 다른 축이라 따로 두었기 때문이다.
|
||||||
|
|
||||||
|
## 화면 이름과 도메인 상태는 원래 다르다
|
||||||
|
|
||||||
|
이 저장소는 화면에 보이는 값과 도메인이 들고 있는 값을 이미 갈라 두고 있었다.
|
||||||
|
|
||||||
|
| 계약이 화면에 주는 값 | 도메인이 들고 있는 값 |
|
||||||
|
|---|---|
|
||||||
|
| 질문 상태 : OPEN · RESOLVED | OPEN · INVESTIGATING · PAUSED · RESOLVED |
|
||||||
|
| 결정 상태 : PROPOSED · ADOPTED | PROPOSED · ACCEPTED |
|
||||||
|
|
||||||
|
질문 상태 셋이 화면에서 하나로 접히고, 그 역방향 변환은 도메인 상태를 덮어쓰지 않는다. 결정의 `ACCEPTED` 는 화면에서 `ADOPTED` 로 보인다.
|
||||||
|
|
||||||
|
종류 이름을 두 번 바꾸면서 계약을 건드리지 않은 것은 새로 정한 규칙이 아니라 이미 있던 규칙을 따랐다.
|
||||||
|
|
||||||
## 계약의 kind 는 그대로 뒀다
|
## 계약의 kind 는 그대로 뒀다
|
||||||
|
|
||||||
바꾼 것은 화면에 보이는 이름이다. 계약의 종류 값은 다섯 그대로이고 주소도 그대로다.
|
바꾼 것은 화면에 보이는 이름이다. 계약의 종류 값은 다섯 그대로이고 주소도 그대로다.
|
||||||
|
|||||||
@@ -3029,5 +3029,5 @@
|
|||||||
"unlisted": 0,
|
"unlisted": 0,
|
||||||
"candidates": 91
|
"candidates": 91
|
||||||
},
|
},
|
||||||
"ssotSha256": "75e2c6368a5f6866ad72956619d020c89d4442428a79093fec29b1dfc647490c"
|
"ssotSha256": "974abab805e33daa531fa23fde85309c0de17becf6c5e460ef5ff00d057e20c9"
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -65,6 +65,16 @@ tech-log-frontend : 31afb4d 이후
|
|||||||
|
|
||||||
앞선 종류들과 여기서 갈린다. Case 와 Reference 와 Question 은 상세 화면이 있어서 목록 항목이 가볍고 상세를 부르면 나머지가 온다. 결정에는 상세를 부를 곳이 없으므로 화면이 그리는 칸이 전부 목록 항목에 있어야 한다.
|
앞선 종류들과 여기서 갈린다. Case 와 Reference 와 Question 은 상세 화면이 있어서 목록 항목이 가볍고 상세를 부르면 나머지가 온다. 결정에는 상세를 부를 곳이 없으므로 화면이 그리는 칸이 전부 목록 항목에 있어야 한다.
|
||||||
|
|
||||||
|
## 종류마다 사는 곳이 다르다
|
||||||
|
|
||||||
|
편집 화면이 고르는 유형 다섯은 하나의 애그리거트가 아니다. 각 유형이 자기 애그리거트와 테이블을 갖고, 문서 세 종류만 한 테이블을 공유한다.
|
||||||
|
|
||||||
|
결정은 그중 문서 테이블에 살지 않는다. 프로젝트 안에서만 읽히므로 자기 목록도 자기 상세 화면도 없고, 공개 주소가 프로젝트 결정 목록 위의 앵커다.
|
||||||
|
|
||||||
|
종류별 목록 주소 표에서도 결정만 다른 규칙을 따른다. 그 표의 javadoc 이 그것을 적어 둔다.
|
||||||
|
|
||||||
|
> 결정은 프로젝트 안에서만 읽히므로 자기 목록이 없다. 그 자리를 `/projects` 로 두는 것은 빠뜨린 것이 아니라 그렇게 정한 것이고, 표에 적혀 있으니 다음 사람이 구분할 수 있다.
|
||||||
|
|
||||||
## 네 증상이 한 원인이었다
|
## 네 증상이 한 원인이었다
|
||||||
|
|
||||||
목록 항목에 `title`·`summary`·`consequences`·`evidence` 가 빠져 있었고, 화면 쪽이 없는 칸을 각자 메우고 있었다.
|
목록 항목에 `title`·`summary`·`consequences`·`evidence` 가 빠져 있었고, 화면 쪽이 없는 칸을 각자 메우고 있었다.
|
||||||
|
|||||||
Reference in New Issue
Block a user