Service Class Pattern
A complete example of using the @cached decorator with a service class.
Full Example
import { cached } from '@humanspeak/memory-cache'
interface Product {
id: string
name: string
price: number
category: string
}
class ProductService {
private db: Database
constructor(db: Database) {
this.db = db
}
@cached<Product | null>({ ttl: 300000 }) // 5 minutes
async getProduct(id: string): Promise<Product | null> {
return await this.db.products.findUnique({ where: { id } })
}
@cached<Product[]>({ ttl: 60000, maxSize: 100 }) // 1 minute
async getProductsByCategory(category: string): Promise<Product[]> {
return await this.db.products.findMany({
where: { category },
orderBy: { name: 'asc' }
})
}
@cached<Product[]>({ ttl: 30000, maxSize: 50 })
async searchProducts(query: string, limit: number): Promise<Product[]> {
return await this.db.products.findMany({
where: {
name: { contains: query, mode: 'insensitive' }
},
take: limit
})
}
// No caching for write operations
async createProduct(data: Omit<Product, 'id'>): Promise<Product> {
return await this.db.products.create({ data })
}
}
// Usage
const productService = new ProductService(db)
// These are cached
const product = await productService.getProduct('prod-123')
const electronics = await productService.getProductsByCategory('electronics')
const results = await productService.searchProducts('laptop', 10)import { cached } from '@humanspeak/memory-cache'
interface Product {
id: string
name: string
price: number
category: string
}
class ProductService {
private db: Database
constructor(db: Database) {
this.db = db
}
@cached<Product | null>({ ttl: 300000 }) // 5 minutes
async getProduct(id: string): Promise<Product | null> {
return await this.db.products.findUnique({ where: { id } })
}
@cached<Product[]>({ ttl: 60000, maxSize: 100 }) // 1 minute
async getProductsByCategory(category: string): Promise<Product[]> {
return await this.db.products.findMany({
where: { category },
orderBy: { name: 'asc' }
})
}
@cached<Product[]>({ ttl: 30000, maxSize: 50 })
async searchProducts(query: string, limit: number): Promise<Product[]> {
return await this.db.products.findMany({
where: {
name: { contains: query, mode: 'insensitive' }
},
take: limit
})
}
// No caching for write operations
async createProduct(data: Omit<Product, 'id'>): Promise<Product> {
return await this.db.products.create({ data })
}
}
// Usage
const productService = new ProductService(db)
// These are cached
const product = await productService.getProduct('prod-123')
const electronics = await productService.getProductsByCategory('electronics')
const results = await productService.searchProducts('laptop', 10)With Hooks for Monitoring
import { cached } from '@humanspeak/memory-cache'
class ProductService {
@cached<Product | null>({
ttl: 300000,
hooks: {
onHit: ({ key }) => {
metrics.increment('product_service.cache.hit', { method: 'getProduct' })
},
onMiss: ({ key }) => {
metrics.increment('product_service.cache.miss', { method: 'getProduct' })
}
}
})
async getProduct(id: string): Promise<Product | null> {
metrics.increment('product_service.db.query', { method: 'getProduct' })
return await this.db.products.findUnique({ where: { id } })
}
@cached<Product[]>({
ttl: 60000,
maxSize: 100,
hooks: {
onHit: () => metrics.increment('product_service.cache.hit', { method: 'getByCategory' }),
onMiss: () => metrics.increment('product_service.cache.miss', { method: 'getByCategory' }),
onEvict: () => metrics.increment('product_service.cache.eviction', { method: 'getByCategory' })
}
})
async getProductsByCategory(category: string): Promise<Product[]> {
return await this.db.products.findMany({
where: { category },
orderBy: { name: 'asc' }
})
}
}import { cached } from '@humanspeak/memory-cache'
class ProductService {
@cached<Product | null>({
ttl: 300000,
hooks: {
onHit: ({ key }) => {
metrics.increment('product_service.cache.hit', { method: 'getProduct' })
},
onMiss: ({ key }) => {
metrics.increment('product_service.cache.miss', { method: 'getProduct' })
}
}
})
async getProduct(id: string): Promise<Product | null> {
metrics.increment('product_service.db.query', { method: 'getProduct' })
return await this.db.products.findUnique({ where: { id } })
}
@cached<Product[]>({
ttl: 60000,
maxSize: 100,
hooks: {
onHit: () => metrics.increment('product_service.cache.hit', { method: 'getByCategory' }),
onMiss: () => metrics.increment('product_service.cache.miss', { method: 'getByCategory' }),
onEvict: () => metrics.increment('product_service.cache.eviction', { method: 'getByCategory' })
}
})
async getProductsByCategory(category: string): Promise<Product[]> {
return await this.db.products.findMany({
where: { category },
orderBy: { name: 'asc' }
})
}
}Key Considerations
- TTL by Method: Different methods may need different TTLs
- Max Size: Limit based on expected unique argument combinations
- Write Operations: Don’t cache write operations
- Cache Invalidation: Consider how updates affect cached data