前回作成したソースコードにCacheを適用して、有効期限以内ならキャッシュデータを返し、そうでないなら新しい値を取得するようにした
参考
改良版
キャッシュされたかどうかは判別可能な気がする
有効期限がきれたかどうかは判別不可能な気がする
import { Cache, Duration, Effect, Layer } from "effect"
type type_employee_id = string
type type_department_id = string
// ---- db : employee ----
interface interface_employee {
employee_id: type_employee_id
employee_name: string
department_id: type_department_id
}
class database_employee_service extends Effect.Service<database_employee_service>()("database_employee_service", {
succeed: ({
get_one_employee: (employee_id: type_employee_id) =>
Effect.gen(function*() {
yield* Effect.log("get_one_employee start")
yield* Effect.sleep(Duration.seconds(5))
const data: interface_employee = {
employee_id,
employee_name: "test employee",
department_id: test_department_id
}
yield* Effect.log("get_one_employee end")
return data
})
})
}) {}
const employee_cache = Cache.make({
capacity: 100,
timeToLive: Duration.seconds(5),
lookup: (employee_id: type_employee_id) =>
Effect.gen(function*() {
const DatabaseEmployee = yield* database_employee_service
const employee = yield* DatabaseEmployee.get_one_employee(employee_id)
return employee
})
})
// ---- db : department ----
interface interface_department {
department_id: type_department_id
department_name: string
}
class database_department_service extends Effect.Service<database_department_service>()("database_department_service", {
succeed: ({
get_one_department: (department_id: type_department_id) =>
Effect.gen(function*() {
yield* Effect.log("get_one_department start")
yield* Effect.sleep(Duration.seconds(3))
const data: interface_department = {
department_id,
department_name: "test department"
}
yield* Effect.log("get_one_department end")
return data
})
})
}) {}
const department_cache = Cache.make({
capacity: 100,
timeToLive: Duration.seconds(5),
lookup: (department_id: type_department_id) =>
Effect.gen(function*() {
const DatabaseDepartment = yield* database_department_service
const department = yield* DatabaseDepartment.get_one_department(department_id)
return department
})
})
// ---- test data ----
const test_employee_id: type_employee_id = "e_00_test"
const test_department_id: type_department_id = "d_00_test"
// ---- layer -----------------
const service_live = Layer.mergeAll(database_employee_service.Default, database_department_service.Default)
interface employee_data {
employee: interface_employee
department: interface_department
}
class domain_employee_service extends Effect.Service<domain_employee_service>()("domain_employee_service", {
effect: Effect.gen(function*() {
const CacheEmployee = yield* employee_cache
const CacheDepartment = yield* department_cache
return {
get_one_employee_data: (
employee_id: type_employee_id
) =>
Effect.gen(function*() {
const isHitEmployee = yield* CacheEmployee.contains(employee_id)
if (isHitEmployee) yield* Effect.log("employee_data from cache")
const employee = yield* CacheEmployee.get(employee_id)
const isHitDepartment = yield* CacheDepartment.contains(employee.department_id)
if (isHitDepartment) yield* Effect.log("department_data from cache")
const department = yield* CacheDepartment.get(employee.department_id)
const result: employee_data = {
employee,
department
}
return result
})
}
}),
dependencies: [service_live]
}) {}
const get_data_from_domain = Effect.gen(function*() {
const DomainEmployee = yield* domain_employee_service
const result_employee = yield* DomainEmployee.get_one_employee_data(test_employee_id)
yield* Effect.log(result_employee)
const result_employee_2 = yield* DomainEmployee.get_one_employee_data(test_employee_id)
yield* Effect.log(result_employee_2)
yield* Effect.sleep(Duration.seconds(5))
const result_employee_3 = yield* DomainEmployee.get_one_employee_data(test_employee_id)
yield* Effect.log(result_employee_3)
})
const domain_live = Layer.mergeAll(
domain_employee_service.Default
)
const program_domain = Effect.provide(get_data_from_domain, domain_live)
// ---- Entry Point ----
Effect.runPromise(program_domain)
実行結果
timestamp=2025-09-14T15:24:50.368Z level=INFO fiber=#0 message="get_one_employee start"
timestamp=2025-09-14T15:24:55.378Z level=INFO fiber=#0 message="get_one_employee end"
timestamp=2025-09-14T15:24:55.387Z level=INFO fiber=#0 message="get_one_department start"
timestamp=2025-09-14T15:24:58.394Z level=INFO fiber=#0 message="get_one_department end"
timestamp=2025-09-14T15:24:58.397Z level=INFO fiber=#0 message="{
\"employee\": {
\"employee_id\": \"e_00_test\",
\"employee_name\": \"test employee\",
\"department_id\": \"d_00_test\"
},
\"department\": {
\"department_id\": \"d_00_test\",
\"department_name\": \"test department\"
}
}"
timestamp=2025-09-14T15:24:58.400Z level=INFO fiber=#0 message="employee_data from cache"
timestamp=2025-09-14T15:24:58.403Z level=INFO fiber=#0 message="department_data from cache"
timestamp=2025-09-14T15:24:58.406Z level=INFO fiber=#0 message="{
\"employee\": {
\"employee_id\": \"e_00_test\",
\"employee_name\": \"test employee\",
\"department_id\": \"d_00_test\"
},
\"department\": {
\"department_id\": \"d_00_test\",
\"department_name\": \"test department\"
}
}"
timestamp=2025-09-14T15:25:03.413Z level=INFO fiber=#0 message="employee_data from cache"
timestamp=2025-09-14T15:25:03.416Z level=INFO fiber=#0 message="get_one_employee start"
timestamp=2025-09-14T15:25:08.430Z level=INFO fiber=#0 message="get_one_employee end"
timestamp=2025-09-14T15:25:08.431Z level=INFO fiber=#0 message="department_data from cache"
timestamp=2025-09-14T15:25:08.432Z level=INFO fiber=#0 message="get_one_department start"
timestamp=2025-09-14T15:25:11.442Z level=INFO fiber=#0 message="get_one_department end"
timestamp=2025-09-14T15:25:11.445Z level=INFO fiber=#0 message="{
\"employee\": {
\"employee_id\": \"e_00_test\",
\"employee_name\": \"test employee\",
\"department_id\": \"d_00_test\"
},
\"department\": {
\"department_id\": \"d_00_test\",
\"department_name\": \"test department\"
}
}"