最重要的部分是让您的域不依赖于实施细节。数据库是一个实现细节。
因此,将其包装在一个函数中以赋予它另一个名称并不重要。关键是让您的域不依赖于此。
你是怎么做到的?
通过定义一个接口(这是您的抽象),它说“我需要存储/获取数据,无论数据库后面是什么”。然后,您在 prod 中注入该接口的 BigQuery 实现......并且很容易在测试甚至开发模式下注入内存中的实现。
现在,在 JavaScript 中,没有显式接口。但是抽象的想法仍然存在。这只是隐含的。界面将是您实际使用的东西(鸭子打字)。
使用你的具体例子
假设你有:
async function doSomething() {
// Some other domain stuff…
await bigquery
.dataset(datasetId)
.table(tableId)
.insert(rows);
}
这没有多大帮助:
function insertInDb(rows) {
return bigquery
.dataset(datasetId)
.table(tableId)
.insert(rows);
}
async function doSomething() {
// Some other domain stuff…
await insertInDb(rows);
}
但是,这会有所帮助:
function insertInDb(rows) {
return bigquery
.dataset(datasetId)
.table(tableId)
.insert(rows);
}
async function doSomething(insertInDb) {
// Some other domain stuff…
await insertInDb(rows);
}
区别很细微,但实际的insertInDb函数是在运行时注入的,这会反转依赖关系。
更进一步:数据库上的 Repository 抽象
现在,这个概念通常被命名为存储库。
如果您花一些时间更好地表达领域概念,您的最终代码可能如下所示:
class ScoreRepositoryBigQuery {
save(newScore) {
// Some logic to convert `newScore` into BigQuery compatible `rows`…
return this.bigquery
.dataset(this.datasetId)
.table(this.tableId)
.insert(rows);
}
}
async function answerQuestion(scoreRepository) {
// Some other domain stuff…
await scoreRepository.save(newScore);
}
使用不同的存储机制(例如 MongoDB、第三方服务、内存实现等)创建新的 ScoreRepository 会很容易。
您只需要实现隐式接口(例如,它应该有一个异步 save() 方法,该方法接受 newScore 并存储它)。
无需触及其余代码,因为它不关心实际的实现。
所以 this 将是一个有用的抽象层。