추상 클래스 적용을 위해 미리 결정한 체크리스트는 다음과 같습니다.
temporaryDatabase
를 사용합니다. 이를 통해 서비스 간 데이터의 일관성을 유지하고, 필요한 데이터를 효율적으로 관리하도록 합니다.temporaryDatabase
를 조회하고, 마지막으로 실제 데이터베이스를 조회합니다. 이를 통해 가장 빠른 응답 속도를 제공하면서도 데이터의 일관성을 유지합니다.export abstract class BaseService<T extends HasUuid> {
protected cache: LRUCache;
protected className: string;
protected field: string;
protected prisma: PrismaServiceMySQL | PrismaServiceMongoDB;
protected temporaryDatabaseService: TemporaryDatabaseService;
constructor(options: BaseServiceOptions) {
this.cache = new LRUCache(options.cacheSize);
this.className = options.className;
this.field = options.field;
this.prisma = options.prisma;
this.temporaryDatabaseService = options.temporaryDatabaseService;
}
abstract generateKey(data: T): string;
create()
findOne()
update()
remove()
...
}
async findOne(key: string) {
const data = await this.getDataFromCacheOrDB(key);
const deleteCommand = this.temporaryDatabaseService.get(
this.className,
key,
'delete',
);
if (deleteCommand) {
throw new HttpException('Not Found', HttpStatus.NOT_FOUND);
}
if (data) {
const mergedData = this.mergeWithUpdateCommand(data, key);
this.cache.put(key, mergedData);
return ResponseUtils.createResponse(HttpStatus.OK, mergedData);
} else {
throw new HttpException('Not Found', HttpStatus.NOT_FOUND);
}
}
async getDataFromCacheOrDB(key: string): Promise<T | null> {
if (!key) throw new HttpException('Bad Request', HttpStatus.BAD_REQUEST);
const cacheData = this.cache.get(key);
if (cacheData) return cacheData;
const temporaryDatabaseData = this.temporaryDatabaseService.get(
this.className,
key,
'insert',
);
if (temporaryDatabaseData) return temporaryDatabaseData;
const databaseData = await this.prisma[this.className].findUnique({
where: {
[this.field]: key.includes('+') ? this.stringToObject(key) : key,
},
});
return databaseData;
}
private mergeWithUpdateCommand(data: T, key: string): T {
const updateCommand = this.temporaryDatabaseService.get(
this.className,
key,
'update',
);
if (updateCommand) return { ...data, ...updateCommand.value };
return data;
}
mergeWithUpdateCommand()
함수를 호출하여 업데이트 내역이 있는지 확인 후에 merge
한 뒤에 cache에 등록을 하고 데이터를 리턴합니다.@Injectable()
export class UsersService extends BaseService<UpdateUserDto> {
constructor(
protected prisma: PrismaServiceMySQL,
protected temporaryDatabaseService: TemporaryDatabaseService,
) {
super({
prisma,
temporaryDatabaseService,
cacheSize: USER_CACHE_SIZE,
className: 'USER_TB',
field: 'email_provider',
});
}
generateKey(data: UpdateUserDto) {
return `email:${data.email}+provider:${data.provider}`;
}
}