SlideShare a Scribd company logo
サービス開発における
フロントエンド・ドメイン駆動設計の実践
DeNA TechCon 2018 BLUE Stage 2018.02.07
吉井 健文
システムデザイン本部デザイン戦略部
デザインエンジニアリンググループ
  @Takepepe
  @takefumi-yoshii
自己紹介
担当のKenCoMについて
健康リコメンデーションメディア。
健康データの一元管理、利用者の健康度に応じた情報提供。
健康への行動変容を促すサービスへと成長中。
担当のKenCoMが先日リニューアルリリース
担当のKenCoMが先日リニューアルリリース
アプリから取得した、歩数・体重・血圧
などのバイタルデータを自動連携。
PCブラウザでも閲覧でき、
手動入力による機能を導入。
React + Redux という構成は以前から存在したが、
部分的な機能に閉じており、機能追加で Store 乱立が懸念された。
各エンドポイントで、Single Store を維持しつつ
分割統合を柔軟に行いたい。
リニューアル課題
バイタルデータの表示・編集機能など、
複雑な機能は OOPをもって乗り切る必要性を感じた。
モデルも抽象化も無いRedux に、
OOPデザインパターンを統合できるのか?
リニューアル課題
小粒度の戦略・アイデアを柔軟に反映可能な、
PDCAサイクルを加速させる
フロントエンドの基礎を築きたい。
リニューアル課題
リニューアル課題まとめ
1. エンドポイント毎に異なる、Storeの分割統合
2. 複雑な機能に備え、Redux に OOP を持ち込む
3. PDCAサイクルを加速するための基礎構築
技術選定の背景
- Hexagonal Redux -
課題領域(ドメイン)を疎結合にし、再利用・分割統合が容易な
最適解を求めDDDに辿り着く。
エリック・エヴァンスのレイヤードアーキテクチャ(※) 導入を試みるも、
Store と モデルが分断され、状態の2重管理が発生してしまった。
(※) エリック・エヴァンスのドメイン駆動設計
アーキテクチャ選定背景
そこから、ヴァーン・ヴァーノンの「実践ドメイン駆動設計」(※) で
紹介されているヘキサゴナルアーキテクチャに出会う。(※) 実践ドメイン駆動設計
イベント駆動で表現されたヘキサゴナルアーキテクチャは、
Redux と相性が良く、多くのコンセプトが一致。
全エンドポイントで Hexagonal Redux を 導入するに至った。
アーキテクチャ選定背景
React + Redux + redux-saga + immutable.js
flowtype + jest
Hexagonal Redux モジュール構成
◼ DomainModel
◼ ReduxAction
◼ Adapter (redux-saga)
◼ Adapter (react-redux)
Hexagonal Redux 概念図
Hexagonal Redux 概要
1. ヘキサゴナルアーキテクチャを模範とした構成
2. Storeの内側に複数のドメインモデルを構える
3. 「イベント・サービス・モデル」で単体ドメイン扱い
`domains` 配下に「イベント・サービス・モデル」==
「Redux + redux-saga + immutable.js」
`views` には React コード
`applications` には エントリーポイント
Hexagonal Redux パッケージ構成
./front/javascripts/
├── applications
├── constants
├── domains
│ ├── achievementQueue
│ ├── articles
│ ├── healthRecords
│ │ ├── stepCounts
│ │ ├── weights
│ │ └── ...
│ ├── recommendedArticles
│ │ ├── model.js
│ │ └── redux.js
│ ├── renderWidget
│ │ ├── model.js
│ │ ├── redux.js
│ │ └── saga.js
│ └── ...
├── helpers
├── lib
└── views
抽象化の文脈
- 抽象化へのターニングポイント -
バイタルデータ表示・編集機能 - OOP基底クラス -
項目毎に、表示期間に
応じたグラフリソースの捻
出・不足レコードの fetch
バイタルデータ表示・編集機能 - OOP基底クラス -
表示期間に応じた
平均値の算出
バイタルデータ表示・編集機能 - OOP基底クラス -
該当日のレコードの
選択・更新
バイタルデータ表示・編集機能 - OOPファクトリー -
食前/食後 など、
ドメイン固有の測定値
バイタルデータ表示・編集機能 - OOPファクトリー -
異なるレコードの扱い
(1日に複数の測定値)
バイタルデータ表示・編集機能 - OOPファクトリー -
1日に複数の測定値を
保持するレコードの
取り扱い
基底クラス・ファクトリーのほか、
表示における関心事の分離など、(値の丸め処理・単位ラベル etc)
OOPのデザインパターンが活きる場面が多数見受けられた。
また、今後取り扱うバイタルデータが増えることが想定できた。
抽象化の文脈 - 抽象化へのターニングポイント -
抽象化の文脈
- Redux における抽象化とは? -
ReduxAction を抽象化することで、構成要素の
全てを同一レベルで抽象化することが可能。
ReduxAction 抽象化の立て役となった、
ボイラープレートジェネレーターを紹介。
抽象化の文脈 - Reduxにおける抽象化とは? -
ReduxAction をモデルに委譲する higher-order-reducer を生成。
export function createReducer (commands: string[], namespace: string): Function {
return (initialModel: immutable.Record) => {
return (model = initialModel, action: ActionCreator) => {
const fn = action.type.replace(namespace, '')
if (model[fn] !== undefined) return model[fn](action.payload)
return model
}
}
}
抽象化の文脈 - Reduxにおける抽象化とは? -
ActionCreators と ActionTypes を生成。
export function createActions (commands: string[], namespace: string) {
const types: ActionTypes = {}
const creators: ActionCreators = {}
commands.map((row: string) => {
const type: ActionType = `${namespace}${row}`
types[row] = type
creators[row] = payload => { return { type, payload } }
})
return { types, creators }
}
抽象化の文脈 - Reduxにおける抽象化とは? -
ボイラープレートジェネレーターを集約。
このジェネレーターは、状態を変更する Action名を宣言することで、
3種のボイラープレートが一度に出来上がる。(Action名配列の宣言・名前空間の宣言 )
export function createReduxBoilerplate (commands: string[], namespace: string) {
const { types, creators } = createActions(commands, namespace)
const reducer = createReducer(commands, namespace)
return { types, creators, reducer }
}
抽象化の文脈 - Reduxにおける抽象化とは? -
普段の3種のボイラープレート実装は
状態を変化する「コマンド」の StringArray 宣言のみ、
という明快なものとなった。 (要別途 payload 型定義)
const { types, creators, reducer } = createReduxBoilerplate([
'registerAchievement',
'registerAchievements',
'deleteQueueItemByIndex'
], '/domains/achievementQueue/')
export { types, creators, reducer }
抽象化の文脈 - Reduxにおける抽象化とは? -
名前空間
export const abstractCommands = [
'registerHealthRecordsSrc',
'updateHealthRecordsSrc',
'shiftShowRange',
'shiftCurrentDate'
]
const { types, creators, reducer } = createReduxBoilerplate([
...abstractCommands
], '/domains/healthRecords/stepCounts/')
const { types, creators, reducer } = createReduxBoilerplate([
...abstractCommands,
'setEditorCurrentIndex',
'toggleTimingActive'
], '/domains/healthRecords/bloodSugars/')
ReduxAction の抽象コード実体は StringArray。
export const abstractCommands = [
'registerHealthRecordsSrc',
'updateHealthRecordsSrc',
'shiftShowRange',
'shiftCurrentDate'
]
const { types, creators, reducer } = createReduxBoilerplate([
...abstractCommands
], '/domains/healthRecords/stepCounts/')
const { types, creators, reducer } = createReduxBoilerplate([
...abstractCommands,
'setEditorCurrentIndex',
'toggleTimingActive'
], '/domains/healthRecords/bloodSugars/')
ReduxAction の抽象コード実体は StringArray。
New
New
レコードを登録する
レコードを更新する
表示期間を変更する
表示日を変更する
ReduxAction の抽象コード実体は StringArray。(コード要約)
歩数のレコードを登録する
歩数のレコードを更新する
歩数の表示期間を変更する
歩数の表示日を変更する
血糖値のレコードを登録する
血糖値のレコードを更新する
血糖値の表示期間を変更する
血糖値の表示日を変更する
血糖値の編集測定点を切り替える
血糖値の測定値表示を切り替える
バイタルドメインイベント
歩数ドメインイベント
血糖値ドメインイベント
「歩数の」名前空間
「血糖値の」名前空間
New
New
状態を変化する「コマンド = ReduxAction」
のリファクタが容易である点も、
運用において大きな利点であると言える。
抽象化の文脈 - Reduxにおける抽象化とは? -
抽象化の文脈
- Redux におけるモデルとは? -
通常 ReduxStore の状態管理は
データソースの保持のみであり、
モデルとしての振る舞いは持ち合わせていない。
ヘルパーモジュールか、view上での処理に
頼らざるを得なかった。
抽象化の文脈 - Reduxにおけるモデルとは? -
この課題を immutable.Record をモデルとして扱う手法で解決。
先ほど宣言した Action が Dispatch されると、
Reducer から委譲された setter/updater が実行され状態が変化する。
export class BloodSugarsModel extends MultiItemModel(props) {
setEditorCurrentIndex (value: number): BloodSugarsModel {
return this.set('editor_current_index', value)
}
toggleTimingActive (index: number): BloodSugarsModel {
return this.update('timings', timings => {
return timings.update(index, timing => timing.toggleActive())
})
}
}
抽象化の文脈 - Reduxにおけるモデルとは? -
Immutable.Record は継承可能。
この抽象レイヤーで、先ほど宣言した Action と
同一抽象レベルのメソッドを定義。
export const MultiItemModel = (opt: Props) => class extends ItemQueryModel(props(opt)) {
updateHealthRecordsSrc (src: RecordProps): MultiItemModel {
const { base_date, end_date, records } = src
return this._updateRecords(base_date, end_date, fromJS(records))
}
}
抽象化の文脈 - Reduxにおけるモデルとは? -
「業務処理・ビジネスロジック・表示上の関心事」を継承階層で分離
抽象化の文脈 - Reduxにおけるモデルとは? -
XHRレスポンスに応じてレコード生成。
全てのレコードが欲しい。
今表示しているレコードの平均は?
次の表示期間に十分なレコードはあるか?
丸め処理済みのラベルが欲しい。
今表示している期間のラベルが欲しい。
継承
継承
バイタル
ドメインモデル
(抽象モデル)
プレゼンテーション層
ビジネスロジック層
業務処理層
「基底クラスメソッド・ファクトリーメソッド」のオーバーライド
抽象化の文脈 - Reduxにおけるモデルとは? -
XHRレスポンスに応じて 血糖値レコード生成。
全てのレコードが欲しい。
今表示しているレコードの 各測定値平均は?
次の表示期間に十分なレコードはあるか?
丸め処理済みのラベルが欲しい。
今表示している期間のラベルが欲しい。
継承
継承
血糖値
ドメインモデル
バイタル
ドメインモデル
(抽象モデル)
継承
override
override
override
プレゼンテーション層
ビジネスロジック層
業務処理層
OOP デザインパターンを FRP パラダイムに
持ち込むことが可能となった。
課題領域(ドメイン)を分離し、責務に応じて
ドメインをスケールさせる、DDD の基礎が出来上がった。
抽象化の文脈 - Reduxにおけるモデルとは? -
コンテキストマップの文脈
- ドメインモデルの粒度 -
ファットモデルを回避するため、
ドメインモデルの課題粒度を細かくした。
粒度が細かいことで、機能同士が疎結合に。
コンテキストマップの文脈 - ドメインモデルの粒度 -
コンテキストマップの文脈 - ドメインモデルの粒度 -
バイタルドメイン集約 行動目標記録ドメイン集約
共有ドメイン集約
コンテキストマップの文脈 - ドメインモデルの粒度 -
バイタルドメイン集約 行動目標記録ドメイン集約
共有ドメイン集約
ドメインモデルの粒度が細かくなることで、
「横断的関心事」をどの様に参照するべきか、
という点が課題になる。
コンテキストマップの文脈 - ドメインモデルの粒度 -
コンテキストマップの文脈
- 横断的関心事 -
モデル同士が直接参照しあうことは避け、
ドメイン同士が pub/sub する機構上で結合。
モデル外側に位置する「サービス層」に結合点を置くことで、
モデル同士の依存関係は無くなる。
コンテキストマップの文脈 - 横断的関心事 -
コンテキストマップの文脈 - 横断的関心事 -
バイタルドメイン集約 行動目標記録ドメイン集約
共有ドメイン集約
コンテキストマップの文脈 - 横断的関心事 -
バイタルドメイン集約 行動目標記録ドメイン集約
共有ドメイン集約
サービス層 = redux-saga
非同期処理の middleware として認知されている redux-saga。
ここで実装する継続的コルーチンをサービス層として機能させた。
コンテキストマップの文脈 - 横断的関心事 -
export function * mapRequestStateToUI ({ creators }: { creators: ActionCreators }) {
// 無限ループをもって継続的コルーチンを起動
while (true) {
// requestQueueドメインを購読する
yield take(action => {
return action.type === RequestQueueTypes.requestSend ||
action.type === RequestQueueTypes.receivedSuccess ||
action.type === RequestQueueTypes.receivedError
})
const { requestQueue } = yield select()
const isProcessing = requestQueue.isProcessing()
// 横断的関心事である XHR処理中という状態を、遷移ボタンを押せなくする状態に変換
yield put(creators.setDisabledUI(isProcessing))
}
}
【横断的関心事を結合するサービス】
【横断的関心事を結合するサービス】(コード要約)
1. XHRQueueドメインイベントを購読
 (イベントが発生するまで loop処理は停止)
2. XHRQueueドメインの状態を参照
3.「UIが操作可能か?」という状態に変換
4. 付随ドメインに書き込み
5. 1.に戻る
4.
1.
ドメインサービスはドメインモデルの一部であるといえる
2.
XHRQueue
ドメインモデル
ドメインモデル
状態変換
サービス
外部
コンテキストマップの文脈
- サービスの抽象化 -
redux-saga は async/await にデザインが似ており、標準に近い。
分割統合・手続きの差し込み・変更なども容易。
将来的に async/await を利用する様になっても、
設計は変わらないことが期待出来る。
コンテキストマップの文脈 - サービスの抽象化 -
ReduxAction の抽象化はサービス層にも伝播。
継承元が同じドメインは、同名の ActionType を継承。
名前空間が切り分けられているため、
競合することなく手続きを継承する。
コンテキストマップの文脈 - サービスの抽象化 -
例えば、レコードを fetch するなどの非同期処理。
継続的コルーチン関数起動時に、
ActionTypes / ActionCreators / modelName を注入する。
コンテキストマップの文脈 - サービスの抽象化 -
export function activityGoalLogsSagas () {
return [
activityGoalLogsSaga({
types: Daily.types,
creators: Daily.creators,
modelName: 'activityGoalLogsDaily'
}),
activityGoalLogsSaga({
types: Weekly.types,
creators: Weekly.creators,
modelName: 'activityGoalLogsWeekly'
}),
activityGoalLogsSettingsSaga()
]
}
【抽象サービスのコンテキストマップ】
【抽象サービスのコンテキストマップ】(コード要約)
〜の時、状態を取得する
〜の時、記録を変更する
〜の時、記録を取得する
〜の時、日別行動目標の状態を取得する
〜の時、日別行動目標の記録を変更する
〜の時、日別行動目標の記録を取得する
〜の時、週間行動目標の状態を取得する
〜の時、週間行動目標の記録を変更する
〜の時、週間行動目標の記録を取得する
行動目標記録サービス
日別行動目標記録サービス
週間行動目標記録サービス
「日別行動目標の」名前空間
「週間行動目標の」名前空間
コンテキストマップの文脈
- ドメインクライアント -
ドメインクライアント = View = React
ReactComponent の抽象化パターンはこれまで通り。
mapStateToProps・mapDispatchToProps で、
文脈にあった State・ActionCreator を抽象名でマッピングする。
コンテキストマップの文脈 - ドメインクライアント -
Hexagonal Redux では、StateObject の
代わりにドメインモデルをマッピング。
モデル表層(プレゼンテーション層)
の存在で、細かな View の出し分けが
不要になる。
課題領域分割で得られる React.render 最適化
【日別】 【週間】 【月間】
export function HealthRecordPanel ({ model }: { model: HealthRecordQueryModel }) {
const ctx = 'c-indexHealthRecordPanel'
// model の getter で得られる値は、期間分岐・ラベル・丸め処理・添字付与済み
return (
<div className={`${ctx}`}>
<div className={`${ctx}__upper`}>
<p className={`${ctx}__itemLabel`}>{model.getUpperPanelItemLabel()}</p>
<p className={`${ctx}__dateRangeLabel`}>{model.getUpperPanelDateRangeLabel()}</p>
<p className={`${ctx}__value`} dangerouslySetInnerHTML={model.getUpperPanelValueLabel()} />
</div>
<div className={`${ctx}__lower`}>
<p className={`${ctx}__itemLabel`}>{model.getLowerPanelItemLabel()}</p>
<p className={`${ctx}__dateRangeLabel`}>{model.getLowerPanelDateRangeLabel()}</p>
<p className={`${ctx}__value`} dangerouslySetInnerHTML={model.getLowerPanelValueLabel()} />
</div>
</div>
)
}
【ビジネスロジックが引き剥がされたコンポーネント】
【ビジネスロジックが引き剥がされたコンポーネント】(コード要約)
パネル上部に表示する項目ラベルを取得
パネル上部に表示する日付ラベルを取得
パネル上部に表示する値ラベルを取得
パネル下部に表示する項目ラベルを取得
パネル下部に表示する日付ラベルを取得
パネル下部に表示する値ラベルを取得
モデルに表現力があるため、ReactComponentの仕事は単純
分岐・フィルタリング・ソートなど、
従来 ReactComponent で行われていた処理は、モデルの責務。
View からビジネスロジックを引き剥がすべきという、
普遍的な最適解が得られた。
課題領域分割で得られる React.render 最適化
ドメインモデルを細かく分割する利点は、
課題領域をシンプルにするだけでなく、
ReactComponent の render 最適化にも繋がる。
render は状態の変化に反応。
各Model が抱える schema が少ない程良い。
課題領域分割で得られる React.render 最適化
分割統合の文脈
- Queue -
各種キューイングを課題としたドメインをそれぞれ設けた。
ReduxActionを通じてキュータスクが積まれるため、各々独立している。
【例】Modal / Achievement / Notification / XHR
分割統合の文脈 - 各種キューを司るドメイン -
表示要素毎にキューを構える。
各種表示キューを取りまとめる親キューの存在により、
要素が被るトラブルを回避できた。
ここでも redux-saga による継続的コルーチンが活きる。
分割統合の文脈 - 表示キューを司るドメイン -
export function * renderAchievementQueue (store: Store) {
while (true) {
// achievementQueue ドメインモデルを取得
const { achievementQueue } = yield select()
// achievementQueue に登録されたの先頭要素を取得
const achievementItem = achievementQueue.getFirstQueueItem()
// achievementQueue の要素が無くなったら loop を抜ける
if (achievementItem === undefined) break
// achievementItem を render
yield call(renderAchievementWidget, achievementItem, store)
// achievementQueue から 先頭要素を削除
const index = achievementQueue.getQueueItemIndex(achievementItem)
yield put(creators.deleteQueueItemByIndex(index))
}
// マウント先コンポーネントを空にする
disposeReact('[data-react-widget-achievements]')
}
【表示キューの継続的コルーチン】
export function renderAchievementWidget (achievementItem: ItemModel, store: Store) {
// Promise に紐づけられた React.render。役目を終えると resolve する
return new Promise(resolve => {
const selector = '[data-react-widget-achievements]'
renderReact(selector, AchievementWidget, store, { resolve, achievementItem })
})
}
【Promise で wrap された表示キューアイテム・レンダラー】
【Promise で wrap された表示キューアイテム・レンダラー】(コード要約)
1. 先頭のキューアイテムを取得
2. キューアイテムがなければ終了
3. キューアイテムの表示
4. 表示が終わるまで待つ( Promise)
5. キューアイテムを削除する
6. 1.に戻る
3.
2.DONE
1.
4.
React
表示キュー
サービス
AchievementQueue
ドメインモデル
外部
Promise.resolve()
分割統合の文脈
- ドメインパッケージ -
「カラダの記録」や
「行動目標記録」などの機能は
複数エントリーポイントで利用。
ドメインをパッケージ単位で集約。
分割統合の文脈 - ドメインパッケージ -
エントリーポイント毎にモデル層とサービス層は異なるため、
そこで必要なパッケージを統合する。
ヘキサゴナルアーキテクチャ構成要素の内側から
生成・起動・実行する様式は、どのエントリーポイントでも一律。
フレームワークらしき形が表出する。
分割統合の文脈 - ドメインパッケージ -
export const commonReducers = {
requestQueue: RequestQueueReducer(new RequestQueueModel()),
modalQueue: ModalQueueReducer(new ModalQueueModel()),
notificationQueue: NotificationQueueReducer(new NotificationQueueModel()),
achievementQueue: AchievementQueueReducer(new AchievementQueueModel()),
renderWidget: RenderWidgetReducer(new RenderWidgetModel()),
pointAccount: PointAccountReducer(new PointAccountModel()),
insurancePointAccount: InsurancePointAccountReducer(new InsurancePointAccountModel())
}
【全エントリーポイントに存在するドメインモデル集約】
export const healthRecordsReducers = {
healthRecordsCalendar: CalendarReducer(new CalendarModel()),
healthRecordsEditor: EditorReducer(new EditorModel()),
healthRecordsSettings: SettingsReducer(new SettingsModel()),
healthRecordsStepCounts: StepCountsReducer(new StepCountsModel()),
healthRecordsWeights: WeightsReducer(new WeightsModel()),
healthRecordsBloodPressures: BloodPressuresReducer(new BloodPressuresModel()),
healthRecordsBloodSugars: BloodSugarsReducer(new BloodSugarsModel())
}
export const activityGoalLogsReducers = {
activityGoalLogsDaily: DailyReducer(new DailyModel()),
activityGoalLogsWeekly: WeeklyReducer(new WeeklyModel()),
activityGoalLogsSettings: SettingsReducer(new SettingsModel())
}
【機能パッケージ単位のドメインモデル集約】
// create Store
const aggregateRoot = extendReducers(
commonReducers,
healthRecordsReducers,
activityGoalLogsReducers
)
const store = createReduxStore(aggregateRoot)
// run services
runRootSaga(commonSagas(store), [
...healthRecordsSagas(),
...activityGoalLogsSagas()
])
// view scripts
applyCommonViewScripts(store)
renderAppReactViews(store)
【集約ルートでStore生成、Service層・View層に注入】
集約ルート生成
Store生成
サービス起動
View起動
全エントリーポイントが
この様式に
【集約ルートでStore生成、Service層・View層に注入】(コード要約)
/index.js
/articles.js
/vitals.js
/points.js
エントリーポイント毎に、必要なドメイン・サービスを採用
共有
ドメイン集約
バイタル
ドメイン集約
行動目標記録
ドメイン集約
記事
ドメイン
ポイント
ドメイン
【集約ルートでStore生成、Service層・View層に注入】(コード要約)
/index.js
/articles.js
/vitals.js
/points.js
エントリーポイント毎に、必要なドメイン・サービスを採用
共有
ドメイン集約
バイタル
ドメイン集約
行動目標記録
ドメイン集約
記事
ドメイン
ポイント
ドメイン
【集約ルートでStore生成、Service層・View層に注入】(コード要約)
/index.js
/articles.js
/vitals.js
/points.js
エントリーポイント毎に、必要なドメイン・サービスを採用
共有
ドメイン集約
バイタル
ドメイン集約
行動目標記録
ドメイン集約
記事
ドメイン
ポイント
ドメイン
Store の分割統合は容易になったが、
ユーザーステータスなど、初期状態の注入を行う層が必要に。
分割統合の文脈 - ドメインパッケージ -
分割統合の文脈
- DI of HTML to Store -
分割統合された Store は初期状態で zero value の状態。
rails に載せるうえで、erb から初期値を取得したい。
この課題を jQueryプラグインライクな vanilla plugin で解決。
plugin 適用時に Store を引数に与えることで、
ReduxAction を Dispatch するDOMに変化する。
分割統合の文脈 - DI of HTML to Store -
export function LoadActionDispatcher (selector, store) {
return document.querySelectorAll(selector).forEach(element => {
const dataset = JSON.parse(element.dataset.domLoadActionDispatcher)
function dispatch (data) {
const { type, payload } = data
store.dispatch({ type, payload })
}
if (Array.isArray(dataset)) {
dataset.map(data => dispatch(data))
} else {
dispatch(dataset)
}
})
}
【HTMLにレンダリングされた json を ActionDispatch するプラグイン】
<%= content_tag :div, nil, class: ctx, data: {
'dom-load-action-dispatcher': [
{
type: '/healthRecords/stepCounts/registerHealthRecordsSrc',
payload: {
base_date: @health_record[:base_date],
end_date: @health_record[:end_date],
src: @health_record['step_counts']
}
}
]
} %>
【プラグインが適用されるHTML】
【DI of HTML to Store】(コード要約)
/index.js
共有
ドメイン集約
バイタル
ドメイン集約
行動目標記録
ドメイン集約
記事
ドメイン
ポイント
ドメイン
/articles.js
/vitals.js
/points.js
サーバーから得られるインスタンスを Load時に Dispatch
/vitals/index.html.erb /index.html.erb
/articles/index.html.erb /points/index.html.erb
成果と今後の展望
ReduxAction を抽象化することで、構成要素全てに抽象化が伝搬。
技術選定時の狙いどおり、OOPデザインパターンの恩恵を多く受けた。
ビジネスロジックの再利用で、複雑なユースケースも難なく解を得た。
類似ドメイン間の差異が表面化したため、
新規類似ドメインの受け入れ・機能拡張が容易になった。
KenCoMリニューアルにおける成果 - 抽象化 -
横断的関心事を結合するサービス層を構えることで、
ドメインモデルが純粋になり、 各種関心事が分離された。
組み替えが容易なサービス層の存在で、
機能の追加変更が容易になった。
KenCoMリニューアルにおける成果 - 分割統合 -
1. チーム横断でDDDアプローチを発展(脱軽量DDD)
2. 定量的な効果測定を仕込み、PDCAサイクルを加速
3. UI/UX を通じて 事業KPI に貢献(デザイン戦略部メンバー全員の課題)
今後の展望
ご静聴ありがとうございました
Reduxにドメイン層を導入する@Qiita
実装 - Hexagonal Redux - @Qiita
MobXは複雑さに耐えられるのか?@Qiita
async/await で Modal の Queueing @Qiita
redux-ddd-example @github.com
immutablejs-record-oop-example @github.com
関連資料

More Related Content

What's hot

TDD のこころ
TDD のこころTDD のこころ
TDD のこころ
Takuto Wada
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
Yoshitaka Kawashima
 
プロトタイピングとユーザビリティテストで「UXデザイン」を練りあげよう! | UXデザイン基礎セミナー 第4回
プロトタイピングとユーザビリティテストで「UXデザイン」を練りあげよう! | UXデザイン基礎セミナー 第4回プロトタイピングとユーザビリティテストで「UXデザイン」を練りあげよう! | UXデザイン基礎セミナー 第4回
プロトタイピングとユーザビリティテストで「UXデザイン」を練りあげよう! | UXデザイン基礎セミナー 第4回
Yoshiki Hayama
 
データモデリング・テクニック
データモデリング・テクニックデータモデリング・テクニック
データモデリング・テクニック
Hidekatsu Izuno
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう
増田 亨
 
3週連続DDDその1 ドメイン駆動設計の基本を理解する
3週連続DDDその1  ドメイン駆動設計の基本を理解する3週連続DDDその1  ドメイン駆動設計の基本を理解する
3週連続DDDその1 ドメイン駆動設計の基本を理解する
増田 亨
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?
Yoshitaka Kawashima
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)
Yoshitaka Kawashima
 
超実践 Cloud Spanner 設計講座
超実践 Cloud Spanner 設計講座超実践 Cloud Spanner 設計講座
超実践 Cloud Spanner 設計講座
Samir Hammoudi
 
フロー効率性とリソース効率性、再入門 #devlove #devkan
フロー効率性とリソース効率性、再入門 #devlove #devkanフロー効率性とリソース効率性、再入門 #devlove #devkan
フロー効率性とリソース効率性、再入門 #devlove #devkan
Itsuki Kuroda
 
アジャイル開発の現在・過去・未来~今を知り、源流を訪ね、先を見据える~
アジャイル開発の現在・過去・未来~今を知り、源流を訪ね、先を見据える~アジャイル開発の現在・過去・未来~今を知り、源流を訪ね、先を見据える~
アジャイル開発の現在・過去・未来~今を知り、源流を訪ね、先を見据える~
Kenji Hiranabe
 
シリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのかシリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのか
Atsushi Nakada
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門
増田 亨
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
Kentaro Matsui
 
Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版
Masahito Zembutsu
 
LEANSTARTUPアンチパターン #devlove #leanstartup
LEANSTARTUPアンチパターン #devlove #leanstartupLEANSTARTUPアンチパターン #devlove #leanstartup
LEANSTARTUPアンチパターン #devlove #leanstartup
Itsuki Kuroda
 
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
Yoshiki Hayama
 
私にとってのテスト
私にとってのテスト私にとってのテスト
私にとってのテスト
Takuto Wada
 
xOps: エンジニアがスタートアップの成長の原動力となる日
xOps: エンジニアがスタートアップの成長の原動力となる日xOps: エンジニアがスタートアップの成長の原動力となる日
xOps: エンジニアがスタートアップの成長の原動力となる日
Takaaki Umada
 
フロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugフロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjug
Itsuki Kuroda
 

What's hot (20)

TDD のこころ
TDD のこころTDD のこころ
TDD のこころ
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
プロトタイピングとユーザビリティテストで「UXデザイン」を練りあげよう! | UXデザイン基礎セミナー 第4回
プロトタイピングとユーザビリティテストで「UXデザイン」を練りあげよう! | UXデザイン基礎セミナー 第4回プロトタイピングとユーザビリティテストで「UXデザイン」を練りあげよう! | UXデザイン基礎セミナー 第4回
プロトタイピングとユーザビリティテストで「UXデザイン」を練りあげよう! | UXデザイン基礎セミナー 第4回
 
データモデリング・テクニック
データモデリング・テクニックデータモデリング・テクニック
データモデリング・テクニック
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう
 
3週連続DDDその1 ドメイン駆動設計の基本を理解する
3週連続DDDその1  ドメイン駆動設計の基本を理解する3週連続DDDその1  ドメイン駆動設計の基本を理解する
3週連続DDDその1 ドメイン駆動設計の基本を理解する
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)
 
超実践 Cloud Spanner 設計講座
超実践 Cloud Spanner 設計講座超実践 Cloud Spanner 設計講座
超実践 Cloud Spanner 設計講座
 
フロー効率性とリソース効率性、再入門 #devlove #devkan
フロー効率性とリソース効率性、再入門 #devlove #devkanフロー効率性とリソース効率性、再入門 #devlove #devkan
フロー効率性とリソース効率性、再入門 #devlove #devkan
 
アジャイル開発の現在・過去・未来~今を知り、源流を訪ね、先を見据える~
アジャイル開発の現在・過去・未来~今を知り、源流を訪ね、先を見据える~アジャイル開発の現在・過去・未来~今を知り、源流を訪ね、先を見据える~
アジャイル開発の現在・過去・未来~今を知り、源流を訪ね、先を見据える~
 
シリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのかシリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのか
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版
 
LEANSTARTUPアンチパターン #devlove #leanstartup
LEANSTARTUPアンチパターン #devlove #leanstartupLEANSTARTUPアンチパターン #devlove #leanstartup
LEANSTARTUPアンチパターン #devlove #leanstartup
 
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
 
私にとってのテスト
私にとってのテスト私にとってのテスト
私にとってのテスト
 
xOps: エンジニアがスタートアップの成長の原動力となる日
xOps: エンジニアがスタートアップの成長の原動力となる日xOps: エンジニアがスタートアップの成長の原動力となる日
xOps: エンジニアがスタートアップの成長の原動力となる日
 
フロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugフロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjug
 

Similar to サービス開発における フロントエンド・ドメイン駆動設計の実践

Ajax basic
Ajax basicAjax basic
Ajax basic
Katsuyuki Seino
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回Naoyuki Yamada
 
ドメイン駆動設計という仕事の流儀
ドメイン駆動設計という仕事の流儀ドメイン駆動設計という仕事の流儀
ドメイン駆動設計という仕事の流儀
増田 亨
 
DSL駆動によるクラウド・アプリケーション開発
DSL駆動によるクラウド・アプリケーション開発DSL駆動によるクラウド・アプリケーション開発
DSL駆動によるクラウド・アプリケーション開発Tomoharu ASAMI
 
Linux 対応だけじゃない!! sql server 2017 こんな機能が追加されています。
Linux 対応だけじゃない!! sql server 2017 こんな機能が追加されています。Linux 対応だけじゃない!! sql server 2017 こんな機能が追加されています。
Linux 対応だけじゃない!! sql server 2017 こんな機能が追加されています。
Masayuki Ozawa
 
Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標
Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標
Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標
Tomoharu ASAMI
 
ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計Tadayoshi Sato
 
Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用
stomita
 
XPages 開発 Tips 百連発
XPages 開発 Tips 百連発XPages 開発 Tips 百連発
XPages 開発 Tips 百連発
Mitsuru Katoh
 
Net advantage 2012 volume2 最新情報 xaml プラットフォーム編
Net advantage 2012 volume2 最新情報 xaml プラットフォーム編Net advantage 2012 volume2 最新情報 xaml プラットフォーム編
Net advantage 2012 volume2 最新情報 xaml プラットフォーム編
Daizen Ikehara
 
Symfony2でより良いソフトウェアを作るために
Symfony2でより良いソフトウェアを作るためにSymfony2でより良いソフトウェアを作るために
Symfony2でより良いソフトウェアを作るためにAtsuhiro Kubo
 
SQL Server 2016 R Services + Microsoft R Server 技術資料
SQL Server 2016 R Services + Microsoft R Server 技術資料SQL Server 2016 R Services + Microsoft R Server 技術資料
SQL Server 2016 R Services + Microsoft R Server 技術資料
Koichiro Sasaki
 
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Yoshifumi Kawai
 
Seasarプロジェクト徹底攻略
Seasarプロジェクト徹底攻略Seasarプロジェクト徹底攻略
Seasarプロジェクト徹底攻略
takezoe
 
ADO.NET Entity Framework
ADO.NET Entity Framework ADO.NET Entity Framework
ADO.NET Entity Framework
Microsoft
 
React+redux+saga 01
React+redux+saga 01React+redux+saga 01
React+redux+saga 01
TIS Inc
 
ドメイン駆動設計 の 実践 Part3 DDD
ドメイン駆動設計 の 実践 Part3 DDDドメイン駆動設計 の 実践 Part3 DDD
ドメイン駆動設計 の 実践 Part3 DDD
増田 亨
 
Windows PowerShell 2.0 の基礎知識
Windows PowerShell 2.0 の基礎知識Windows PowerShell 2.0 の基礎知識
Windows PowerShell 2.0 の基礎知識
shigeya
 
20160121 データサイエンティスト協会 木曜セミナー #5
20160121 データサイエンティスト協会 木曜セミナー #520160121 データサイエンティスト協会 木曜セミナー #5
20160121 データサイエンティスト協会 木曜セミナー #5
Koichiro Sasaki
 

Similar to サービス開発における フロントエンド・ドメイン駆動設計の実践 (20)

BPStudy20121221
BPStudy20121221BPStudy20121221
BPStudy20121221
 
Ajax basic
Ajax basicAjax basic
Ajax basic
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
 
ドメイン駆動設計という仕事の流儀
ドメイン駆動設計という仕事の流儀ドメイン駆動設計という仕事の流儀
ドメイン駆動設計という仕事の流儀
 
DSL駆動によるクラウド・アプリケーション開発
DSL駆動によるクラウド・アプリケーション開発DSL駆動によるクラウド・アプリケーション開発
DSL駆動によるクラウド・アプリケーション開発
 
Linux 対応だけじゃない!! sql server 2017 こんな機能が追加されています。
Linux 対応だけじゃない!! sql server 2017 こんな機能が追加されています。Linux 対応だけじゃない!! sql server 2017 こんな機能が追加されています。
Linux 対応だけじゃない!! sql server 2017 こんな機能が追加されています。
 
Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標
Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標
Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標
 
ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計
 
Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用
 
XPages 開発 Tips 百連発
XPages 開発 Tips 百連発XPages 開発 Tips 百連発
XPages 開発 Tips 百連発
 
Net advantage 2012 volume2 最新情報 xaml プラットフォーム編
Net advantage 2012 volume2 最新情報 xaml プラットフォーム編Net advantage 2012 volume2 最新情報 xaml プラットフォーム編
Net advantage 2012 volume2 最新情報 xaml プラットフォーム編
 
Symfony2でより良いソフトウェアを作るために
Symfony2でより良いソフトウェアを作るためにSymfony2でより良いソフトウェアを作るために
Symfony2でより良いソフトウェアを作るために
 
SQL Server 2016 R Services + Microsoft R Server 技術資料
SQL Server 2016 R Services + Microsoft R Server 技術資料SQL Server 2016 R Services + Microsoft R Server 技術資料
SQL Server 2016 R Services + Microsoft R Server 技術資料
 
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
 
Seasarプロジェクト徹底攻略
Seasarプロジェクト徹底攻略Seasarプロジェクト徹底攻略
Seasarプロジェクト徹底攻略
 
ADO.NET Entity Framework
ADO.NET Entity Framework ADO.NET Entity Framework
ADO.NET Entity Framework
 
React+redux+saga 01
React+redux+saga 01React+redux+saga 01
React+redux+saga 01
 
ドメイン駆動設計 の 実践 Part3 DDD
ドメイン駆動設計 の 実践 Part3 DDDドメイン駆動設計 の 実践 Part3 DDD
ドメイン駆動設計 の 実践 Part3 DDD
 
Windows PowerShell 2.0 の基礎知識
Windows PowerShell 2.0 の基礎知識Windows PowerShell 2.0 の基礎知識
Windows PowerShell 2.0 の基礎知識
 
20160121 データサイエンティスト協会 木曜セミナー #5
20160121 データサイエンティスト協会 木曜セミナー #520160121 データサイエンティスト協会 木曜セミナー #5
20160121 データサイエンティスト協会 木曜セミナー #5
 

サービス開発における フロントエンド・ドメイン駆動設計の実践