#DataStoreとは?
- DBやExternal InterfacesへのGateway(の様子)
- DBやExternal interfacesとはつまり、データの入手先・保存先のこと
- Gateway? どういうことだってばよ?
- gateway.q(from:"勤務実績")と唱えるだけで、必要な結果が得られる
- 勤務実績がどのDBにあってもよい
- DBMS、WebSite、ファイル、、、全てOK!
- データへのアクセス手段をドライバとして提供する必要がありますな
複数のデータソースを一個に
- 役割名はGateway(入出力どちらもあるから)
- DBの足りない値を補完(表示順序値とか)できる
- RDBのview張るようなもん?
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張るのできるな。
使うとき
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の違い
- 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でクリーンアーキテクチャはどうすればいいのか(前編)
- JavaScriptでクリーンアーキテクチャはどうすればいいのか(Usecase編)
- JavaScriptでクリーンアーキテクチャはどうすればいいのか(Presenter編)
- JavaScriptでクリーンアーキテクチャはどうすればいいのか(initializer編)
- JavaScriptでクリーンアーキテクチャはどうすればいいのか(Repository編)
- JavaScriptでクリーンアーキテクチャはどうすればいいのか(DataStore編)
- JavaScriptでクリーンアーキテクチャはどうすればいいのか(DomainModel編)