Files
accounted/.claude/skills/create-extension/references/services-patterns.md
T
2026-02-27 11:08:53 +01:00

64 lines
2.3 KiB
Markdown

# Service Integration Patterns
Two patterns for extensions to provide services to core. Both ensure core compiles without the extension.
## Pattern A: Interface Registration
Best for single-implementation services (e.g., email). Core defines interface + noop default; extension registers at load time.
```typescript
// Core: lib/email/service.ts
let emailService: EmailService = new NoopEmailService()
export function getEmailService(): EmailService { return emailService }
export function registerEmailService(svc: EmailService): void { emailService = svc }
// Extension: extensions/general/email/index.ts
import { registerEmailService } from '@/lib/email/service'
registerEmailService(new ResendEmailService()) // Registers at module load
export const emailExtension: Extension = { id: 'email', name: 'Email', version: '1.0.0' }
// Core consumption:
const svc = getEmailService()
if (svc.isConfigured()) await svc.sendEmail({ to, subject, html })
```
## Pattern B: Services Record
Best for multiple named functions (e.g., AI categorization). Extension exposes via `services`; core discovers via registry.
```typescript
// Extension:
services: {
findSimilarTemplates: async (...args: unknown[]) => {
const { findSimilarTemplates } = await import('./lib/template-embeddings')
return findSimilarTemplates(args[0] as Transaction, args[1] as EntityType)
},
},
// Core facade (lib/bookkeeping/template-embeddings.ts):
const aiExt = extensionRegistry.get('ai-categorization')
if (aiExt?.services?.findSimilarTemplates) {
return aiExt.services.findSimilarTemplates(transaction, entityType)
}
return findMatchingTemplates(transaction, entityType) // Fallback
```
## Pattern C: Core Services for Extensions
Extensions consume core services via `ctx.services`:
```typescript
await ctx?.services.ingestTransactions(ctx.supabase, ctx.userId, rawTransactions)
```
## Comparison
| Aspect | Interface Registration | Services Record |
|--------|----------------------|-----------------|
| Defined in | Core (`lib/`) | Extension (`index.ts`) |
| Discovery | `get*()` getter | `extensionRegistry.get().services` |
| Functions | Single interface | Multiple named |
| Lazy imports | No | Yes (inside service fns) |
| Fallback | Noop default | Core provides fallback |
| Example | Email | AI categorization |