###### tags: `node.js`
# node.js mongoose circular dependency
每次在使用[burni FHIR Server](https://github.com/Chinlinlee/Burni)時,都會跳出以下Warnings。

原本都不以為意,因為程式都可以正常運作,不過每次跳這些 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` :

這樣的情況就叫做 **Circular Dependency**
而當 `Reference` 讀取 `Identifier` 時,為了避免無窮迴圈,會複製**未完成**的 `Identifier` 到 `Reference` 讓 `Reference` 讀取完畢。
# Circular Dependency所造成的問題
開頭說到程式碼都正常運作,不過真實上問題算蠻大的。
因為 Circular 讀取的問題,讓 mongoose 定義的資料表瞬間變得不夠不嚴謹。
使用 `mongoose-schema-jsonschema` 把 `Identifier` dump出來,可以發現 `assigner`變成了空物件,此情況在新增資料時讓 `assigner.identifier` 允許了各種欄位名稱,相對的嚴謹度就降低。

# 解決方案
參考 [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` 參照到自己。

完整範例: [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)