ADR-010: Code Organization Conventions
Status
Accepted
Context
An audit of the 12 domain skills revealed inconsistent conventions for where code lives in the codebase:
Service directories don't match their domains:
- LocationService lives in
services/resources/(notservices/locations/) - WaitlistService lives in
services/user/(notservices/waitlist/) - CreditTransferService is separated from
services/credits/intoservices/transfers/ - LockoutManagementService is separated from
services/scheduling/intoservices/lockouts/
Test file locations follow three competing conventions:
- Most domains: centralized under
tests/directory - Physical-access: co-located with source files (e.g.
SecurityService.test.tsnext toSecurityService.ts) - Credits: duplicate test paths (
tests/unit/services/credits/ANDtests/services/credits/) - Some domains use
tests/api/routes/{domain}.*.test.ts, others usetests/api/{domain}/
Webhook handler placement is inconsistent:
- Stripe and Acuity webhooks have dedicated services (StripeWebhookService, AcuityWebhookService)
- Auth-sync and Twilio webhooks have logic directly in route files
Decision
Service directory organization
Services live in a directory named after their domain:
services/locations/(notservices/resources/for LocationService)services/waitlist/(notservices/user/for WaitlistService)services/credits/includes CreditTransferService (not separateservices/transfers/)services/reservations/includes LockoutManagementService (not separateservices/lockouts/)
Test file location conventions
- All tests live under the centralized
tests/directory, not co-located with source. - API/integration tests:
tests/api/{domain}/(one directory per domain). - Unit tests:
tests/unit/services/{domain}/(single nesting level). - Permission tests:
tests/api/{domain}/alongside other API tests for that domain, not in a separatetests/api/routes/directory.
Webhook handler convention
Webhook handlers that dispatch to multiple sub-handlers or contain non-trivial business logic get a dedicated service (following the StripeWebhookService and AcuityWebhookService pattern). Simple single-purpose webhooks (auth-sync, twilio incoming) may keep logic in the route file.
Alternatives considered
Co-locate tests with source. Some teams prefer __tests__/ directories next to source files. Rejected because the codebase overwhelmingly uses the centralized tests/ directory — co-located tests are the exception (only physical-access), and agents would produce inconsistent output.
Group services by layer instead of domain (e.g. services/crud/, services/webhooks/). Rejected because domain grouping matches how agents discover code — they're tasked with domain work, not layer work.
Consequences
Benefits:
- Service directories match their domain names — agents find services where they expect them
- Consistent test locations make it easy to find and add tests
- Duplicate test paths eliminated
Tradeoffs:
- Moving service files changes every import site. Each move should be its own commit.
- Moving test files may require updating test runner configuration or glob patterns.
Risks:
- Service directory renames must be coordinated — if
services/resources/still has other services (ResourceService, ResourceGroupService, etc.), only LocationService moves out. Don't leave an empty directory.