JavaScriptでクリーンアーキテクチャはどうすればいいのか(DataStore編)

#DataStoreとは?

* DBやExternal Interfacesへの**Gateway**(の様子)
    * DBやExternal interfacesとはつまり、データの入手先・保存先のこと
    * Gateway? どういうことだってばよ?
* gateway.q(from:"勤務実績")と唱えるだけで、必要な結果が得られる
    * 勤務実績が**どのDB**にあってもよい
    * DBMS、WebSite、ファイル、、、全てOK! 
* データへのアクセス手段をドライバとして提供する必要がありますな

---

# 複数のデータソースを一個に

* 役割名はGateway(入出力どちらもあるから)
* DBの足りない値を補完(表示順序値とか)できる
* RDBのview張るようなもん?

```javascript
COMPANY.tables = {
  "勤務実績": "labor_cost",
  "所属部門": "departmentList",
  "従業員": "users"
}

COMPANY.config = {
  tableRowType: Array,
  datasources: [
    {
      name: "JSO_DB",   // 同じ識別子で同一DBとみなす                                                                       
      type: JSOStore,   // window[TABLENAME]に実体が格納されているDB                                                        
      tableType: Array, // TableのRow構成が配列の場合                                                                       
      tableList: {      // createするときにちょっと余計なことができる
        "labor_cost" : function(tablenames){
          const sheetnames = tablenames.map((tablename)=>{return tablename.replace("labor_cost","")})
          return new Table({name: "labor_cost"})
            ._assign("thead", window[tablenames[0]].thead)
            ._assign("tbody", [])
            ._assign("sheetnames", sheetnames)
            ._assign("sheets", sheetnames.inject({}, (prev, sheetname, i)=>{
              return prev._assign(sheetname, window[tablenames[i]].tbody)
            }))
        },
        "labor_unit": null,
        "projects": null,
        "category0": null,
        "category00": null,
        "jobs": null
      }
    },{
      name: "JSO_DB",    // 既出なら、既出DataStoreオブジェクトがthisにセットされる                                         
      type: InlineStore, // config中に実体が格納されているDB                                                                
      tableType: Object, // TableのRow構成が連想配列の場合                                                                  
      tableList : {
        "departmentList": [{"所属":"A課","表示順":0},
                           {"所属":"B課","表示順":1},
                           {"所属":"C部","表示順":2}],
        "laborTypeList": [{"勤務形態":"社員", "表示順":0},
                          {"勤務形態":"パート","表示順":1}
      }
    }
  ]
}

```
---
# DataStoreの実装は?使うときは?

## 実装
* DataStore#constructor(config)
    * config: DB接続に必要な情報
* DataStore#q(jsQUERY): Table
    * jsQUERY: JavaScriptで記述したSQL
    * Table: Clean ArchitechtureとしてのEntityデータクラス
        * 当初DomainModelとしていたがEntityへ
* サブクラスとしてJSOStoreとかInlineStoreとか
* DataStores#create(COMPANY): Object(valueはDataStore)
    * COMPANY.config情報を使って、DataStoreの具象クラスを作るファクトリメソッド
    * 戻り値: COMPANY.tablesのキーをキーとして、DataStoreサブクラスのインスタンスを値としてもつ
    * DataStores.q({from:"勤務実績"})としたら、JSOStore.q({from: "勤務実績"})を呼び出してくれる
* 確かに、view張るのできるな。

## 使うとき
```javascript

  const repository = new LaborRepository(COMPANY);

class LaborRepository{
  constructor(company){
    this.COMPANY = company
    this.stores = DataStores.create(company)
  }
}

class DataStores {
  constructor(stores){
    this.stores = stores
  }
  static create(company){
    return new DataStores(company.config.datasources.map(function(config){
      return new config.type(config)
    }))
  }
  list(tabname){
    if (this._list == null){
      this._list = this.stores.inject({}, (prev, db)=>{
        db.list().forEach((tname)=>{
          prev._assign(tname, db)
        })
        return prev;
      })
    }
    return tabname == null ? this._list.keys() : this._list[tabname]; //ここ気持ち悪い
  }
  q(jsQUERY){
    return this.list(jsQUERY.from).q(jsQUERY);
  }
}

class DataStore {
  constructor(config){
    this.config  = config
  }
  list(){
    return this.tables.keys();
  }
  from(tabname){
    return this.tables[tabname]
  }
}

class JSOStore extends DataStore {
  constructor(config){
    super(config)
    // ここでwindow[tabname]に格納されたテーブルをプログラム中で参照できるようにする                                        
    this.tables = this.config.tableList.fold({}, (prev, tabname, cb)=>{
      if (cb == null)
        return prev._assign(tabname, new Table(window[tabname]))
      return prev._assign(tabname, cb.call(this, window.keys().filter((key)=>{return key.match(tabname)})));
    })
  }
}
class InlineStore extends DataStore {
  constructor(config){
    super(config)
    this.tables = this.config.tableList
  }
}
```

---
# DataStoreとRepository

* RepositoryがDataStoreを所有し、Usecaseから隠蔽
    * DataSourceの実体をUsecaseは知らなくてOK
* config与えたらそれでおしまいにできる
    * test用のconfigとか差し替えればいいよね
* COMPANY.configも良い
    * COMPANYは業務上の情報があるが、COMPANY.configは外部のドライバ情報
    * DBやExternal Interfacesには業務情報は不要
* 綺麗に役割分担できててすごくね？

![DataStoreとRepository](https://qiita-image-store.s3.amazonaws.com/0/192607/acd0a17c-b3f7-ffca-7c95-3e27d663eaaf.png)

---
# DataStoreとRepositoryの違い

* DataStoreは抽象化されたデータの塊=Entity
    * CRUID程度できればそれで良い
* Repositoryは人間にわかる意味を持ったデータの塊=DomainModel
    * EntityとDomainModelの違いは「意味」の違い
    * 同じTable構造を取っていても、入ってる値は全然違う
    * データ構造を変換するのがRepositoryの役割と言うよりも、
    * データに意味を付与するために、データ構造を変換するのでは？
* Usecaseは中身のデータ構造とか知りたくないからね
    * 知る必要がないともいう
* O/Rマッパで、DataStoreから直接DomainModelがえられれば
    * なるほどもっと話が簡単になるわけだ

---
#まとめ

* DataStoreは抽象化されたデータの塊
    * 役割: 複数のデータソースを単一に見せる    
    * 効果: 
        * データがメモリにあるかネットの先か
        * 本番かテスト環境か
        * 与えるconfig次第で容易に交換可能
* Repositoryの役割も明確になる
    * 役割: データの器Entityに人間がわかる意味を付与する
    * Usecaseで「今年一年勤務した従業員」と書かれてあれば、
        * 「」が**業務上の関心**つまりDomainModel
    * Repositoryは「勤務実績一覧から、今年一年ぶんを抜き出し、さらに氏名を重複なく列挙したもの」を提供する
        * 「」が**ビジネスロジック**つまりRepositoryが提供するAPI
        * ビジネスロジックはデータに意味を与えるデータ構造変換のことだったのか!
* 戻り値がインターフェースっていうのは、DataStoreとして抽象化したという話なのかしら
    * そういうこととして理解しよう

---

# DomainModel編へ続く

## 全記事
*  [JavaScriptでクリーンアーキテクチャはどうすればいいのか(前編)](https://qiita.com/Gatz/items/d6b0250bfab6e1e74d69)
*  [JavaScriptでクリーンアーキテクチャはどうすればいいのか(Usecase編)](https://qiita.com/Gatz/items/6a03b60a2bd903e44202)
*  [JavaScriptでクリーンアーキテクチャはどうすればいいのか(Presenter編)](https://qiita.com/Gatz/items/e70d6e854b3bbfef105e)
* [JavaScriptでクリーンアーキテクチャはどうすればいいのか(initializer編)](https://qiita.com/Gatz/items/c224000a510bf0f74298)
* [JavaScriptでクリーンアーキテクチャはどうすればいいのか(Repository編)](https://qiita.com/Gatz/items/ca0c5b7db7321d99451c)
* JavaScriptでクリーンアーキテクチャはどうすればいいのか(DataStore編)
* [JavaScriptでクリーンアーキテクチャはどうすればいいのか(DomainModel編)](https://qiita.com/Gatz/items/d88124c9db1facb6f67a)