hikoutei 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/sync/outbound/SheetsEffectDispatcher.d.ts +64 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.d.ts.map +1 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.js +440 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.js.map +1 -0
- package/dist/application/sync/service/SyncServiceBootstrap.d.ts +3 -4
- package/dist/application/sync/service/SyncServiceBootstrap.d.ts.map +1 -1
- package/dist/application/sync/service/SyncServiceBootstrap.js +7 -4
- package/dist/application/sync/service/SyncServiceBootstrap.js.map +1 -1
- package/dist/application/sync/telemetry/syncTiming.d.ts +20 -33
- package/dist/application/sync/telemetry/syncTiming.d.ts.map +1 -1
- package/dist/application/sync/telemetry/syncTiming.js +14 -12
- package/dist/application/sync/telemetry/syncTiming.js.map +1 -1
- package/dist/infrastructure/storage/errors.d.ts +10 -5
- package/dist/infrastructure/storage/errors.d.ts.map +1 -1
- package/dist/infrastructure/storage/errors.js +10 -7
- package/dist/infrastructure/storage/errors.js.map +1 -1
- package/dist/infrastructure/storage/index.d.ts +6 -4
- package/dist/infrastructure/storage/index.d.ts.map +1 -1
- package/dist/infrastructure/storage/index.js +3 -2
- package/dist/infrastructure/storage/index.js.map +1 -1
- package/dist/infrastructure/storage/sqlite/schema.d.ts +12 -11
- package/dist/infrastructure/storage/sqlite/schema.d.ts.map +1 -1
- package/dist/infrastructure/storage/sqlite/schema.js +17 -109
- package/dist/infrastructure/storage/sqlite/schema.js.map +1 -1
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts +2 -2
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.js +2 -2
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.js.map +1 -1
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.d.ts +1 -2
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.js +1 -2
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.js.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.js +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.js.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationTypes.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationTypes.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationValidation.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationValidation.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.js +1 -2
- package/dist/infrastructure/storage/state/observation/observationWriter.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts +1 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.js +1 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js +2 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js.map +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.d.ts +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.d.ts.map +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.js +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.js.map +1 -1
- package/dist/infrastructure/storage/sync/shared/syncRegistry.d.ts +1 -1
- package/dist/infrastructure/storage/sync/shared/syncRegistry.js +1 -1
- package/package.json +13 -7
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.d.ts +0 -66
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.js +0 -123
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts +0 -111
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js +0 -369
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts +0 -127
- package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorker.js +0 -552
- package/dist/application/sync/outbound/effects/SyncEffectWorker.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts +0 -85
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js +0 -74
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts +0 -31
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js +0 -222
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts +0 -14
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js +0 -25
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts +0 -61
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js +0 -296
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts +0 -17
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js +0 -80
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts +0 -13
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js +0 -248
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts +0 -141
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.js +0 -318
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts +0 -143
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js +0 -16
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts +0 -33
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js +0 -259
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts +0 -27
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js +0 -316
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js.map +0 -1
- package/dist/infrastructure/storage/sync/shared/writerLease.d.ts +0 -76
- package/dist/infrastructure/storage/sync/shared/writerLease.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/shared/writerLease.js +0 -189
- package/dist/infrastructure/storage/sync/shared/writerLease.js.map +0 -1
- package/docs/advanced-sheets-gateway-concurrency-problem.md +0 -434
- package/docs/architecture.md +0 -224
- package/docs/ci.md +0 -293
- package/docs/code-guidelines.md +0 -248
- package/docs/development.md +0 -74
- package/docs/gateway-removal-inventory.md +0 -147
- package/docs/git-workflow.md +0 -224
- package/docs/google-sheets-sync-scaling-strategy.md +0 -459
- package/docs/mikro-orm-adapter-spike.md +0 -89
- package/docs/quick-start.md +0 -137
- package/docs/sql-layer-plan.md +0 -59
- package/docs/sync-bulk-write-benchmark.md +0 -2117
- package/docs/sync-observability.md +0 -100
- package/docs/task-queue-write-model.md +0 -640
- package/docs/typed-sheets-mvp-scope-2026-06-29.md +0 -490
- package/docs/typed-sheets-plan.md +0 -417
- package/docs/write-and-synchronization-flow.md +0 -179
|
@@ -1,490 +0,0 @@
|
|
|
1
|
-
# typed-sheets MVP Scope - 2026-06-29
|
|
2
|
-
|
|
3
|
-
## MVP Goal
|
|
4
|
-
|
|
5
|
-
`typed-sheets`의 MVP는 Google Sheets를 범용 DB처럼 만드는 것이 아니다.
|
|
6
|
-
|
|
7
|
-
MVP의 목표는 다음 한 문장으로 제한한다.
|
|
8
|
-
|
|
9
|
-
> Google Sheets-backed MVP에서 schema drift, parse failure, duplicate key, stale write를 조용히 성공 처리하지 않는 typed repository layer를 만든다.
|
|
10
|
-
|
|
11
|
-
즉, 처음 버전은 기능이 많은 ORM이 아니라 "운영 중 깨질 수 있는 상태를 실패로 드러내는 최소 repository"여야 한다.
|
|
12
|
-
|
|
13
|
-
## MVP에 반드시 포함할 것
|
|
14
|
-
|
|
15
|
-
### 1. Domain / Application / Adapter 분리
|
|
16
|
-
|
|
17
|
-
MVP부터 domain, application, adapter를 분리한다.
|
|
18
|
-
|
|
19
|
-
Domain은 Google API와 SQLite 구현을 몰라야 한다. 테스트는 fake adapter로
|
|
20
|
-
먼저 작성한다.
|
|
21
|
-
|
|
22
|
-
초기 adapter port:
|
|
23
|
-
|
|
24
|
-
```ts
|
|
25
|
-
export type SheetCell = string | number | boolean | null;
|
|
26
|
-
|
|
27
|
-
export interface SheetSnapshot {
|
|
28
|
-
headers: string[];
|
|
29
|
-
rows: SheetCell[][];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface SheetAdapter {
|
|
33
|
-
readSheet(sheetName: string): Promise<SheetSnapshot>;
|
|
34
|
-
appendRow(sheetName: string, row: SheetCell[]): Promise<void>;
|
|
35
|
-
updateRow(sheetName: string, rowNumber: number, row: SheetCell[]): Promise<void>;
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### 2. Schema Definition API
|
|
40
|
-
|
|
41
|
-
포함:
|
|
42
|
-
|
|
43
|
-
- `text()`
|
|
44
|
-
- `number()`
|
|
45
|
-
- `boolean()`
|
|
46
|
-
- `.optional()`
|
|
47
|
-
|
|
48
|
-
MVP에서는 date, enum, array, object, custom parser는 제외한다.
|
|
49
|
-
|
|
50
|
-
예상 API:
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
const users = createSheetRepository({
|
|
54
|
-
adapter,
|
|
55
|
-
sheetName: "Users",
|
|
56
|
-
key: "id",
|
|
57
|
-
columns: {
|
|
58
|
-
id: text(),
|
|
59
|
-
email: text(),
|
|
60
|
-
age: number().optional(),
|
|
61
|
-
active: boolean(),
|
|
62
|
-
_version: number(),
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### 3. Schema Drift Detection
|
|
68
|
-
|
|
69
|
-
`assertSchema()`에서 반드시 검증한다.
|
|
70
|
-
|
|
71
|
-
- required column 누락
|
|
72
|
-
- key column 누락
|
|
73
|
-
- `_version` column 누락
|
|
74
|
-
- duplicate header
|
|
75
|
-
|
|
76
|
-
MVP에서는 extra column은 허용하는 쪽이 낫다. 비개발자가 sheet에 메모용 column을 추가할 수 있기 때문이다.
|
|
77
|
-
|
|
78
|
-
단, extra column을 무시한다는 정책은 README에 명확히 적는다.
|
|
79
|
-
|
|
80
|
-
### 4. Row Parsing
|
|
81
|
-
|
|
82
|
-
포함:
|
|
83
|
-
|
|
84
|
-
- text parsing
|
|
85
|
-
- number parsing
|
|
86
|
-
- boolean parsing
|
|
87
|
-
- optional empty value handling
|
|
88
|
-
- required empty value failure
|
|
89
|
-
|
|
90
|
-
실패 시 `ParseError`를 던진다.
|
|
91
|
-
|
|
92
|
-
MVP에서는 Google Sheets의 formatted value/raw value 차이는 adapter 책임으로 미룬다. core는 adapter가 준 cell value만 파싱한다.
|
|
93
|
-
|
|
94
|
-
### 5. Repository Read API
|
|
95
|
-
|
|
96
|
-
포함:
|
|
97
|
-
|
|
98
|
-
- `findAll()`
|
|
99
|
-
- `findById(id)`
|
|
100
|
-
|
|
101
|
-
검증:
|
|
102
|
-
|
|
103
|
-
- row를 typed object로 반환
|
|
104
|
-
- duplicate key 발견 시 실패
|
|
105
|
-
- parse 실패 시 실패
|
|
106
|
-
|
|
107
|
-
MVP에서는 filter, sort, pagination, query builder는 제외한다.
|
|
108
|
-
|
|
109
|
-
### 6. Insert API
|
|
110
|
-
|
|
111
|
-
포함:
|
|
112
|
-
|
|
113
|
-
- `insert(row)`
|
|
114
|
-
|
|
115
|
-
검증:
|
|
116
|
-
|
|
117
|
-
- schema에 맞는 row만 insert
|
|
118
|
-
- duplicate key면 실패
|
|
119
|
-
- header 순서대로 cell serialize
|
|
120
|
-
|
|
121
|
-
MVP에서 insert의 `_version` 정책은 단순하게 간다.
|
|
122
|
-
|
|
123
|
-
- 사용자가 `_version`을 명시하면 그대로 검증
|
|
124
|
-
- 문서에서는 최초 insert 시 `_version: 1` 사용을 권장
|
|
125
|
-
|
|
126
|
-
자동 `_version` 주입은 2차로 미룬다. MVP API를 작게 유지하기 위해서다.
|
|
127
|
-
|
|
128
|
-
### 7. Update API
|
|
129
|
-
|
|
130
|
-
포함:
|
|
131
|
-
|
|
132
|
-
- `update(id, updater)`
|
|
133
|
-
|
|
134
|
-
동작:
|
|
135
|
-
|
|
136
|
-
1. 현재 sheet를 읽는다.
|
|
137
|
-
2. key로 row를 찾는다.
|
|
138
|
-
3. 현재 row를 parse한다.
|
|
139
|
-
4. updater를 적용한다.
|
|
140
|
-
5. write 직전 sheet를 다시 읽어 `_version`이 그대로인지 확인한다.
|
|
141
|
-
6. `_version`이 바뀌었으면 `ConflictError`를 던진다.
|
|
142
|
-
7. 같으면 `_version + 1`로 update한다.
|
|
143
|
-
|
|
144
|
-
MVP에서 이 방식은 완전한 atomic compare-and-set은 아니다. 하지만 stale write를 조용히 성공 처리하지 않는 core 정책을 테스트로 증명할 수 있다.
|
|
145
|
-
|
|
146
|
-
완전한 직렬화는 Apps Script `LockService` gateway 단계에서 다룬다.
|
|
147
|
-
|
|
148
|
-
### 8. Error Types
|
|
149
|
-
|
|
150
|
-
포함:
|
|
151
|
-
|
|
152
|
-
- `SchemaDriftError`
|
|
153
|
-
- `ParseError`
|
|
154
|
-
- `ConflictError`
|
|
155
|
-
|
|
156
|
-
추가로 필요하면 내부적으로 `TypedSheetsError` base class를 둘 수 있다.
|
|
157
|
-
|
|
158
|
-
MVP에서는 error 종류를 더 늘리지 않는다.
|
|
159
|
-
|
|
160
|
-
## MVP에서 제외할 것
|
|
161
|
-
|
|
162
|
-
다음은 처음 구현하지 않는다.
|
|
163
|
-
|
|
164
|
-
- relation / join
|
|
165
|
-
- SQL-like query language
|
|
166
|
-
- migration engine
|
|
167
|
-
- transaction manager
|
|
168
|
-
- multi-row atomic transaction
|
|
169
|
-
- Apps Script 자동 설치
|
|
170
|
-
- Apps Script Web App gateway
|
|
171
|
-
- cache
|
|
172
|
-
- request collapse
|
|
173
|
-
- retry/backoff
|
|
174
|
-
- browser support
|
|
175
|
-
- dashboard UI
|
|
176
|
-
- date parser
|
|
177
|
-
- enum parser
|
|
178
|
-
- custom parser
|
|
179
|
-
- soft delete
|
|
180
|
-
- audit log
|
|
181
|
-
- `_createdAt`, `_updatedAt`
|
|
182
|
-
- Google Sheet template generator
|
|
183
|
-
- GitHub Action
|
|
184
|
-
|
|
185
|
-
## 애매하지만 MVP에서 빼는 것이 좋은 것
|
|
186
|
-
|
|
187
|
-
### Cache / Request Collapse
|
|
188
|
-
|
|
189
|
-
프로젝트 차별점과 연결되지만 MVP에서는 빼는 것이 낫다.
|
|
190
|
-
|
|
191
|
-
이유:
|
|
192
|
-
|
|
193
|
-
- schema drift와 stale write 검증이 먼저다.
|
|
194
|
-
- cache가 들어가면 테스트 surface가 커진다.
|
|
195
|
-
- stale data와 optimistic locking 설명이 복잡해진다.
|
|
196
|
-
|
|
197
|
-
문서에는 확장 방향으로만 둔다.
|
|
198
|
-
|
|
199
|
-
### Retry / Backoff
|
|
200
|
-
|
|
201
|
-
quota 관점에서 중요하지만 MVP에서는 빼는 것이 낫다.
|
|
202
|
-
|
|
203
|
-
이유:
|
|
204
|
-
|
|
205
|
-
- adapter concern이다.
|
|
206
|
-
- fake adapter 기반 core 테스트와 직접 관련이 없다.
|
|
207
|
-
- 실제 Google adapter를 만들 때 넣는 편이 자연스럽다.
|
|
208
|
-
|
|
209
|
-
### Apps Script Gateway
|
|
210
|
-
|
|
211
|
-
장기적으로 중요하지만 MVP에는 넣지 않는다.
|
|
212
|
-
|
|
213
|
-
이유:
|
|
214
|
-
|
|
215
|
-
- OAuth, Workspace 정책, Apps Script API 활성화가 필요하다.
|
|
216
|
-
- 설치/권한 문제가 core repository 검증보다 크다.
|
|
217
|
-
- MVP의 핵심 메시지를 흐린다.
|
|
218
|
-
|
|
219
|
-
## MVP Success Criteria
|
|
220
|
-
|
|
221
|
-
MVP가 성공했다고 볼 기준:
|
|
222
|
-
|
|
223
|
-
1. fake adapter만으로 전체 core 테스트가 통과한다.
|
|
224
|
-
2. header가 깨지면 `SchemaDriftError`가 난다.
|
|
225
|
-
3. row 값이 잘못되면 `ParseError`가 난다.
|
|
226
|
-
4. duplicate key가 있으면 실패한다.
|
|
227
|
-
5. update 중 version이 바뀌면 `ConflictError`가 난다.
|
|
228
|
-
6. `findAll`, `findById`, `insert`, `update`가 typed API로 동작한다.
|
|
229
|
-
7. README에서 Google Sheets의 한계를 명확히 말한다.
|
|
230
|
-
|
|
231
|
-
## Recommended MVP File Structure
|
|
232
|
-
|
|
233
|
-
```txt
|
|
234
|
-
src/
|
|
235
|
-
index.ts
|
|
236
|
-
adapter.ts
|
|
237
|
-
columns.ts
|
|
238
|
-
errors.ts
|
|
239
|
-
repository.ts
|
|
240
|
-
|
|
241
|
-
test/
|
|
242
|
-
fake-adapter.ts
|
|
243
|
-
schema.test.ts
|
|
244
|
-
parsing.test.ts
|
|
245
|
-
repository-read.test.ts
|
|
246
|
-
insert.test.ts
|
|
247
|
-
update.test.ts
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
## Recommended Package Split
|
|
251
|
-
|
|
252
|
-
처음부터 monorepo로 너무 잘게 나누는 것은 피한다. MVP에서는 core 안정성을 증명하는 것이 우선이므로, 패키지는 "지금 필요한 분리"와 "나중에 필요한 분리"를 나눠서 가져간다.
|
|
253
|
-
|
|
254
|
-
### MVP 패키지 구성
|
|
255
|
-
|
|
256
|
-
MVP에서는 단일 npm package로 시작한다.
|
|
257
|
-
|
|
258
|
-
```txt
|
|
259
|
-
typed-sheets/
|
|
260
|
-
src/
|
|
261
|
-
core/
|
|
262
|
-
adapter.ts
|
|
263
|
-
columns.ts
|
|
264
|
-
errors.ts
|
|
265
|
-
repository.ts
|
|
266
|
-
schema.ts
|
|
267
|
-
serialization.ts
|
|
268
|
-
testing/
|
|
269
|
-
fake-adapter.ts
|
|
270
|
-
index.ts
|
|
271
|
-
test/
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
배포 package는 하나다.
|
|
275
|
-
|
|
276
|
-
```txt
|
|
277
|
-
typed-sheets
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
외부 사용자는 하나의 entrypoint만 쓴다.
|
|
281
|
-
|
|
282
|
-
```ts
|
|
283
|
-
import {
|
|
284
|
-
createSheetRepository,
|
|
285
|
-
text,
|
|
286
|
-
number,
|
|
287
|
-
boolean,
|
|
288
|
-
SchemaDriftError,
|
|
289
|
-
ConflictError,
|
|
290
|
-
ParseError,
|
|
291
|
-
} from "typed-sheets";
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
이 단계에서 `@typed-sheets/core`, `@typed-sheets/google`, `@typed-sheets/testing`처럼 나누지 않는다.
|
|
295
|
-
|
|
296
|
-
이유:
|
|
297
|
-
|
|
298
|
-
- MVP는 아직 public API가 고정되지 않았다.
|
|
299
|
-
- adapter가 fake adapter뿐이면 package 분리가 오히려 비용이다.
|
|
300
|
-
- multi-package build, versioning, release 관리가 MVP 속도를 늦춘다.
|
|
301
|
-
- core와 adapter의 경계는 폴더와 interface로도 충분히 검증할 수 있다.
|
|
302
|
-
|
|
303
|
-
### MVP 내부 module 경계
|
|
304
|
-
|
|
305
|
-
단일 package 안에서 module 경계는 명확히 둔다.
|
|
306
|
-
|
|
307
|
-
```txt
|
|
308
|
-
src/domain/
|
|
309
|
-
src/application/
|
|
310
|
-
src/adapter/
|
|
311
|
-
src/infrastructure/
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
domain에는 순수 규칙을, application에는 ORM과 동기화 흐름을, adapter에는
|
|
315
|
-
외부 provider 계약과 구현을, infrastructure에는 SQLite 저장을 둔다.
|
|
316
|
-
|
|
317
|
-
- schema validation
|
|
318
|
-
- row parsing
|
|
319
|
-
- serialization
|
|
320
|
-
- duplicate key detection
|
|
321
|
-
- optimistic locking
|
|
322
|
-
- error types
|
|
323
|
-
|
|
324
|
-
테스트용 fake adapter는 `test/support/`에 둔다.
|
|
325
|
-
|
|
326
|
-
- in-memory sheet snapshot
|
|
327
|
-
- append row 기록
|
|
328
|
-
- update row 기록
|
|
329
|
-
- conflict simulation helper
|
|
330
|
-
|
|
331
|
-
fake adapter는 public package surface에 포함하지 않는다.
|
|
332
|
-
|
|
333
|
-
### 2차 패키지 구성
|
|
334
|
-
|
|
335
|
-
실제 Google adapter가 들어가는 시점에 package 분리를 검토한다.
|
|
336
|
-
|
|
337
|
-
권장 monorepo 구조:
|
|
338
|
-
|
|
339
|
-
```txt
|
|
340
|
-
packages/
|
|
341
|
-
core/
|
|
342
|
-
src/
|
|
343
|
-
googleapis-adapter/
|
|
344
|
-
src/
|
|
345
|
-
google-spreadsheet-adapter/
|
|
346
|
-
src/
|
|
347
|
-
testing/
|
|
348
|
-
src/
|
|
349
|
-
cli/
|
|
350
|
-
src/
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
각 패키지 역할:
|
|
354
|
-
|
|
355
|
-
```txt
|
|
356
|
-
@typed-sheets/core
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
순수 repository core.
|
|
360
|
-
|
|
361
|
-
- Google API dependency 없음
|
|
362
|
-
- runtime dependency 최소화
|
|
363
|
-
- fake adapter로 대부분 테스트 가능
|
|
364
|
-
|
|
365
|
-
```txt
|
|
366
|
-
@typed-sheets/googleapis-adapter
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
`@googleapis/sheets` 기반 adapter.
|
|
370
|
-
|
|
371
|
-
- service account / OAuth auth 처리
|
|
372
|
-
- range read/write
|
|
373
|
-
- batch API
|
|
374
|
-
- retry/backoff 후보
|
|
375
|
-
|
|
376
|
-
```txt
|
|
377
|
-
@typed-sheets/google-spreadsheet-adapter
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
`google-spreadsheet` 기반 adapter.
|
|
381
|
-
|
|
382
|
-
- 더 사용하기 쉬운 wrapper adapter
|
|
383
|
-
- 빠른 adoption에 유리
|
|
384
|
-
|
|
385
|
-
```txt
|
|
386
|
-
@typed-sheets/testing
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
사용자 adapter 검증용 test utilities.
|
|
390
|
-
|
|
391
|
-
- fake adapter
|
|
392
|
-
- adapter contract tests
|
|
393
|
-
- conflict simulation
|
|
394
|
-
|
|
395
|
-
```txt
|
|
396
|
-
@typed-sheets/cli
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
나중에 Apps Script 설치, template 생성, schema check CLI를 담당.
|
|
400
|
-
|
|
401
|
-
- `typed-sheets init`
|
|
402
|
-
- `typed-sheets check`
|
|
403
|
-
- `typed-sheets generate-template`
|
|
404
|
-
|
|
405
|
-
### 언제 패키지를 나눌지
|
|
406
|
-
|
|
407
|
-
패키지 분리 기준은 기능 기준이 아니라 dependency boundary 기준으로 잡는다.
|
|
408
|
-
|
|
409
|
-
나눌 시점:
|
|
410
|
-
|
|
411
|
-
- Google API dependency가 core에 들어오려고 할 때
|
|
412
|
-
- adapter별 dependency가 무거워질 때
|
|
413
|
-
- CLI가 OAuth, file system, Apps Script API dependency를 요구할 때
|
|
414
|
-
- 사용자가 core만 설치하고 싶다는 니즈가 생길 때
|
|
415
|
-
- adapter contract test를 외부에 제공해야 할 때
|
|
416
|
-
|
|
417
|
-
아직 나누지 말아야 할 시점:
|
|
418
|
-
|
|
419
|
-
- fake adapter만 있는 MVP
|
|
420
|
-
- repository API가 바뀔 가능성이 큰 단계
|
|
421
|
-
- README와 테스트로 project shape를 검증하는 단계
|
|
422
|
-
|
|
423
|
-
## Recommended Import Strategy
|
|
424
|
-
|
|
425
|
-
MVP에서는 top-level export를 작게 유지한다.
|
|
426
|
-
|
|
427
|
-
```ts
|
|
428
|
-
export {
|
|
429
|
-
createSheetRepository,
|
|
430
|
-
text,
|
|
431
|
-
number,
|
|
432
|
-
boolean,
|
|
433
|
-
SchemaDriftError,
|
|
434
|
-
ConflictError,
|
|
435
|
-
ParseError,
|
|
436
|
-
};
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
adapter type은 public으로 export한다.
|
|
440
|
-
|
|
441
|
-
```ts
|
|
442
|
-
export type {
|
|
443
|
-
SheetAdapter,
|
|
444
|
-
SheetSnapshot,
|
|
445
|
-
SheetCell,
|
|
446
|
-
};
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
단, internal helper는 export하지 않는다.
|
|
450
|
-
|
|
451
|
-
- header normalization helper
|
|
452
|
-
- row parser internals
|
|
453
|
-
- serialization internals
|
|
454
|
-
- duplicate key scanner
|
|
455
|
-
|
|
456
|
-
이렇게 해야 나중에 내부 구현을 바꿔도 public API compatibility를 지킬 수 있다.
|
|
457
|
-
|
|
458
|
-
## Implementation Order
|
|
459
|
-
|
|
460
|
-
1. package setup
|
|
461
|
-
2. adapter interface
|
|
462
|
-
3. error classes
|
|
463
|
-
4. column primitives
|
|
464
|
-
5. schema validation
|
|
465
|
-
6. row parser
|
|
466
|
-
7. `findAll`
|
|
467
|
-
8. `findById`
|
|
468
|
-
9. `insert`
|
|
469
|
-
10. `update`
|
|
470
|
-
11. fake adapter tests
|
|
471
|
-
12. README cleanup
|
|
472
|
-
|
|
473
|
-
## MVP Line Budget
|
|
474
|
-
|
|
475
|
-
목표:
|
|
476
|
-
|
|
477
|
-
- production TypeScript: 500~900 lines
|
|
478
|
-
- tests: 700~1,200 lines
|
|
479
|
-
- docs/config: 400~700 lines
|
|
480
|
-
- total: 1,500~2,800 lines
|
|
481
|
-
|
|
482
|
-
기능을 많이 넣는 것보다 실패 조건을 테스트로 증명하는 것이 우선이다.
|
|
483
|
-
|
|
484
|
-
## Final MVP Boundary
|
|
485
|
-
|
|
486
|
-
MVP는 여기까지다.
|
|
487
|
-
|
|
488
|
-
> Fake adapter 기반으로 schema drift, parse failure, duplicate key, stale update를 검출하는 typed repository core.
|
|
489
|
-
|
|
490
|
-
실제 Google Sheets adapter는 MVP core가 안정화된 다음 단계로 잡는다.
|