Skip to content

Commit 34a3230

Browse files
authored
chore: group src/ by clients, domain, and runtime (#5)
The flat src/ had clients, business logic, and process plumbing sitting side by side, with a types.ts grab-bag that held unrelated shapes for unrelated consumers. Grouping by role gives each folder a single reason to exist: clients/ is everything that talks to the outside world, domain/ is what a migration means (no HTTP, no config, no process plumbing), and runtime/ is how the two get wired to RabbitMQ and env vars. types.ts splits along the same lines. Config moves next to loadConfig in runtime/config.ts, the client response shapes (NextcloudEntry, CozyFile, DiskUsage) move next to StackClient in clients/stack-client.ts, and the tracking/migration data contract stays in domain/types.ts. File history is preserved through git mv. No behavior change; typecheck and all 47 tests pass.
1 parent 355bbce commit 34a3230

17 files changed

Lines changed: 94 additions & 89 deletions

docs/development.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,29 @@ cp .env.example .env
2525

2626
## Project structure
2727

28+
The `src/` tree is grouped by role so each folder has a single reason to exist.
29+
2830
```
2931
src/
30-
index.ts Entry point — connects RabbitMQ, subscribes, handles shutdown
31-
consumer.ts Message handler — validation, idempotency, quota check, early ACK
32-
migration.ts Core logic — lazy directory traversal, file transfers, progress tracking
33-
stack-client.ts HTTP client for the Cozy Stack API (token refresh on 401)
34-
cloudery-client.ts HTTP client for the Cloudery token endpoint
35-
tracking.ts Tracking document helpers with CouchDB 409 conflict retry
36-
config.ts Environment variable parsing
37-
types.ts Type definitions and message validation
32+
index.ts Entry point — connects RabbitMQ, subscribes, handles shutdown
33+
34+
clients/ External integrations. Everything that talks to something outside the process.
35+
stack-client.ts Cozy Stack API (token refresh on 401)
36+
cloudery-client.ts Cloudery token endpoint
37+
cozy-stack-client.d.ts Type shim for the cozy-stack-client library
38+
39+
domain/ What a migration is. No HTTP, no config, no process plumbing.
40+
migration.ts Core logic — lazy directory traversal, file transfers, progress tracking
41+
tracking.ts Tracking document helpers with CouchDB 409 conflict retry
42+
doctypes.ts Cozy doctype identifiers used across the codebase
43+
types.ts Migration command + tracking document schema
44+
45+
runtime/ Process wiring. How domain + clients get started and fed.
46+
consumer.ts Message handler — validation, idempotency, quota check, early ACK
47+
config.ts Environment variable parsing + Config type
3848
3949
test/
40-
*.test.ts Unit tests for each module (mocked HTTP, no real services needed)
50+
*.test.ts Unit tests for each module (mocked HTTP, no real services needed)
4151
```
4252

4353
## Testing
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Logger } from 'pino'
2-
import { MIGRATION_TOKEN_SCOPE } from './doctypes.js'
2+
import { MIGRATION_TOKEN_SCOPE } from '../domain/doctypes.js'
33

44
export interface ClouderyClient {
55
getToken(workplaceFqdn: string): Promise<string>
Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,30 @@ import type { Logger } from 'pino'
22
import cozyStackClientPkg from 'cozy-stack-client'
33
import type { CozyStackClient as CozyStackClientType } from 'cozy-stack-client'
44
import type { ClouderyClient } from './cloudery-client.js'
5-
import type {
6-
NextcloudEntry,
7-
CozyFile,
8-
DiskUsage,
9-
TrackingDoc,
10-
} from './types.js'
11-
import { DOCTYPES } from './doctypes.js'
5+
import type { TrackingDoc } from '../domain/types.js'
6+
import { DOCTYPES } from '../domain/doctypes.js'
7+
8+
export interface NextcloudEntry {
9+
type: 'file' | 'directory'
10+
name: string
11+
path: string
12+
size: number
13+
mime: string
14+
}
15+
16+
/** Unwrapped from the Stack's JSON-API response. Size is parsed from string. */
17+
export interface CozyFile {
18+
id: string
19+
name: string
20+
dir_id: string
21+
size: number
22+
}
23+
24+
export interface DiskUsage {
25+
used: number
26+
/** 0 means unlimited in Cozy Stack. */
27+
quota: number
28+
}
1229

1330
// cozy-stack-client is published as CommonJS with `__esModule: true`, which
1431
// means Node's ESM→CJS interop surfaces module.exports under the default
File renamed without changes.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Logger } from 'pino'
2-
import type { StackClient } from './stack-client.js'
2+
import type { StackClient } from '../clients/stack-client.js'
33
import type { MigrationCommand } from './types.js'
44
import {
55
setRunning,

src/tracking.ts renamed to src/domain/tracking.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { StackClient } from './stack-client.js'
1+
import type { StackClient } from '../clients/stack-client.js'
22
import type { TrackingDoc, TrackingError, TrackingSkipped } from './types.js'
33

44
const MAX_CONFLICT_RETRIES = 5

src/types.ts renamed to src/domain/types.ts

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,6 @@ export interface TrackingSkipped {
3838
size: number
3939
}
4040

41-
export interface NextcloudEntry {
42-
type: 'file' | 'directory'
43-
name: string
44-
path: string
45-
size: number
46-
mime: string
47-
}
48-
49-
/** Unwrapped from the Stack's JSON-API response. Size is parsed from string. */
50-
export interface CozyFile {
51-
id: string
52-
name: string
53-
dir_id: string
54-
size: number
55-
}
56-
57-
export interface DiskUsage {
58-
used: number
59-
/** 0 means unlimited in Cozy Stack. */
60-
quota: number
61-
}
62-
6341
/**
6442
* Validates and extracts a MigrationCommand from a raw RabbitMQ message.
6543
* @param msg - Raw message payload from RabbitMQ
@@ -85,15 +63,3 @@ export function parseMigrationCommand(msg: Record<string, unknown>): MigrationCo
8563
timestamp: typeof timestamp === 'number' ? timestamp : Date.now(),
8664
}
8765
}
88-
89-
export interface Config {
90-
rabbitmqUrl: string
91-
clouderyUrl: string
92-
clouderyToken: string
93-
logLevel: string
94-
flushInterval: number
95-
/** URL scheme used when addressing the target Cozy Stack. Defaults to
96-
* `https`; set `STACK_URL_SCHEME=http` for local development against a
97-
* non-TLS Stack. */
98-
stackUrlScheme: 'http' | 'https'
99-
}

src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import pino from 'pino'
22
import { RabbitMQClient, type RabbitMQMessage } from '@linagora/rabbitmq-client'
3-
import { loadConfig } from './config.js'
4-
import { createClouderyClient } from './cloudery-client.js'
5-
import { handleMigrationMessage } from './consumer.js'
6-
import { parseMigrationCommand } from './types.js'
3+
import { loadConfig } from './runtime/config.js'
4+
import { createClouderyClient } from './clients/cloudery-client.js'
5+
import { handleMigrationMessage } from './runtime/consumer.js'
6+
import { parseMigrationCommand } from './domain/types.js'
77

88
const EXCHANGE = 'migration'
99
const ROUTING_KEY = 'nextcloud.migration.requested'

src/config.ts renamed to src/runtime/config.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import type { Config } from './types.js'
1+
export interface Config {
2+
rabbitmqUrl: string
3+
clouderyUrl: string
4+
clouderyToken: string
5+
logLevel: string
6+
flushInterval: number
7+
/** URL scheme used when addressing the target Cozy Stack. Defaults to
8+
* `https`; set `STACK_URL_SCHEME=http` for local development against a
9+
* non-TLS Stack. */
10+
stackUrlScheme: 'http' | 'https'
11+
}
212

313
function requireEnv(name: string): string {
414
const value = process.env[name]

0 commit comments

Comments
 (0)