###### tags: `node.js` # node.js mongoose circular dependency 每次在使用[burni FHIR Server](https://github.com/Chinlinlee/Burni)時,都會跳出以下Warnings。 ![](https://i.imgur.com/ZA1RLPq.png) 原本都不以為意,因為程式都可以正常運作,不過每次跳這些 warnings 心都很躁,所以這次來正視此問題。 # Circular Dependency 會出現 Circular Dependency 的問題,是因為 module 之間互相 require。 這裡舉一項例子: ```javascript= //Identifier.js const mongoose = require('mongoose'); const Reference = require('./Reference'); module.exports = new mongoose.Schema({ assigner: { type: Reference, default: void 0 } }); ``` ```javascript= //Reference.js const mongoose = require('mongoose'); const Identifier = require('./Identifier'); module.exports = new mongoose.Schema({ identifier: { type: Identifier, default: void 0 } }); ``` 從以上程式碼可以發現: `Identifier.js` require `Reference.js` 同時`Reference.js` require `Identifier.js` 在讀取時會呈現以下狀況: `Identifier`->`Reference`->`Identifier` 使用 `madge` : ![](https://i.imgur.com/ehV58R4.png) 這樣的情況就叫做 **Circular Dependency** 而當 `Reference` 讀取 `Identifier` 時,為了避免無窮迴圈,會複製**未完成**的 `Identifier` 到 `Reference` 讓 `Reference` 讀取完畢。 # Circular Dependency所造成的問題 開頭說到程式碼都正常運作,不過真實上問題算蠻大的。 因為 Circular 讀取的問題,讓 mongoose 定義的資料表瞬間變得不夠不嚴謹。 使用 `mongoose-schema-jsonschema` 把 `Identifier` dump出來,可以發現 `assigner`變成了空物件,此情況在新增資料時讓 `assigner.identifier` 允許了各種欄位名稱,相對的嚴謹度就降低。 ![](https://i.imgur.com/tuapFvn.png) # 解決方案 參考 [How to deal with cyclic dependencies in Node.js](https://stackoverflow.com/questions/10869276/how-to-deal-with-cyclic-dependencies-in-node-js) 彙整出以下方法: - `module.exports` 建議使用 `module.exporst.name` - 多一個module `topDef` 初始化所有會circular的module - 所有type的引入都改由 `topDef` 引入 - 多一個module來 merge 兩個circular的module ```javascript= //初始化會circular的module //topDef.js const mongoose = require('mongoose'); module.exports.Identifier = new mongoose.Schema({}); module.exports.Reference = new mongoose.Schema({}); ``` ```javascript= //Identifier.js const { Reference } = require('./topDef');//改由topDef引入 const { Identifier } = require('./topDef');//自己也要由topDef引入 Identifier.add({ assigner: { type: Reference, default: void 0 } }) module.exports.Identifier = Identifier; ``` ```javascript= //Reference.js const { Identifier } = require('./topDef'); const { Reference } = require('./topDef'); Reference.add({ identifier: { type: Identifier, default: void 0 } }) module.exports.Reference = Reference; ``` ```javascript= //merge.js const { Identifier } = require('./Identifier'); const { Reference } = require('./Reference'); module.exports = { Identifier : Identifier, Reference: Reference } ``` 最後使用 `mongoose-schema-jsonschema` 把 `Identifier` dump出來 發現 `assigner.identifier` 變成了 `$ref` 參照到自己。 ![](https://i.imgur.com/Fnys58I.png) 完整範例: [Github](https://github.com/chinlin-example/node-js-mongoose-circular-fix-example) # 參考資料 - [Circular Dependency in Node.js modules!](https://www.linkedin.com/pulse/circular-dependency-nodejs-modules-nihal-kaul) - [How to deal with cyclic dependencies in Node.js](https://stackoverflow.com/questions/10869276/how-to-deal-with-cyclic-dependencies-in-node-js)