SlideShare ist ein Scribd-Unternehmen logo
1 von 93
Downloaden Sie, um offline zu lesen
サービス開発における
フロントエンド・ドメイン駆動設計の実践
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
関連資料

Weitere ähnliche Inhalte

Was ist angesagt?

アジャイルにモデリングは必要か
アジャイルにモデリングは必要かアジャイルにモデリングは必要か
アジャイルにモデリングは必要かHiromasa Oka
 
45分間で「ユーザー中心のものづくり」ができるまで詰め込む
45分間で「ユーザー中心のものづくり」ができるまで詰め込む45分間で「ユーザー中心のものづくり」ができるまで詰め込む
45分間で「ユーザー中心のものづくり」ができるまで詰め込むYoshiki Hayama
 
フロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugフロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugItsuki Kuroda
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなKentaro Matsui
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!mosa siru
 
ドメイン駆動設計に15年取り組んでわかったこと
ドメイン駆動設計に15年取り組んでわかったことドメイン駆動設計に15年取り組んでわかったこと
ドメイン駆動設計に15年取り組んでわかったこと増田 亨
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」Takuto Wada
 
インフラCICDの勘所
インフラCICDの勘所インフラCICDの勘所
インフラCICDの勘所Toru Makabe
 
モデリングもしないでアジャイルとは何事だ
モデリングもしないでアジャイルとは何事だモデリングもしないでアジャイルとは何事だ
モデリングもしないでアジャイルとは何事だIwao Harada
 
初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!Tetsutaro Watanabe
 
なぜ「マイクロサービス“化”」が必要なのか
なぜ「マイクロサービス“化”」が必要なのかなぜ「マイクロサービス“化”」が必要なのか
なぜ「マイクロサービス“化”」が必要なのかYusuke Suzuki
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?Yoshitaka Kawashima
 
正しいものを正しく作る塾-設計コース
正しいものを正しく作る塾-設計コース正しいものを正しく作る塾-設計コース
正しいものを正しく作る塾-設計コース増田 亨
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織Takafumi ONAKA
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法についてYuji Otani
 
心理的安全性と、Veinの紹介 Psychological safety and introduction of Vein
心理的安全性と、Veinの紹介 Psychological safety and introduction of Vein心理的安全性と、Veinの紹介 Psychological safety and introduction of Vein
心理的安全性と、Veinの紹介 Psychological safety and introduction of VeinTokoroten Nakayama
 
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門増田 亨
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)Yoshitaka Kawashima
 
チャットコミュニケーションの問題と心理的安全性の課題 #EOF2019
チャットコミュニケーションの問題と心理的安全性の課題 #EOF2019チャットコミュニケーションの問題と心理的安全性の課題 #EOF2019
チャットコミュニケーションの問題と心理的安全性の課題 #EOF2019Tokoroten Nakayama
 
ドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解するドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解する増田 亨
 

Was ist angesagt? (20)

アジャイルにモデリングは必要か
アジャイルにモデリングは必要かアジャイルにモデリングは必要か
アジャイルにモデリングは必要か
 
45分間で「ユーザー中心のものづくり」ができるまで詰め込む
45分間で「ユーザー中心のものづくり」ができるまで詰め込む45分間で「ユーザー中心のものづくり」ができるまで詰め込む
45分間で「ユーザー中心のものづくり」ができるまで詰め込む
 
フロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugフロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjug
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
ドメイン駆動設計に15年取り組んでわかったこと
ドメイン駆動設計に15年取り組んでわかったことドメイン駆動設計に15年取り組んでわかったこと
ドメイン駆動設計に15年取り組んでわかったこと
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
インフラCICDの勘所
インフラCICDの勘所インフラCICDの勘所
インフラCICDの勘所
 
モデリングもしないでアジャイルとは何事だ
モデリングもしないでアジャイルとは何事だモデリングもしないでアジャイルとは何事だ
モデリングもしないでアジャイルとは何事だ
 
初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!
 
なぜ「マイクロサービス“化”」が必要なのか
なぜ「マイクロサービス“化”」が必要なのかなぜ「マイクロサービス“化”」が必要なのか
なぜ「マイクロサービス“化”」が必要なのか
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?
 
正しいものを正しく作る塾-設計コース
正しいものを正しく作る塾-設計コース正しいものを正しく作る塾-設計コース
正しいものを正しく作る塾-設計コース
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
心理的安全性と、Veinの紹介 Psychological safety and introduction of Vein
心理的安全性と、Veinの紹介 Psychological safety and introduction of Vein心理的安全性と、Veinの紹介 Psychological safety and introduction of Vein
心理的安全性と、Veinの紹介 Psychological safety and introduction of Vein
 
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
チャットコミュニケーションの問題と心理的安全性の課題 #EOF2019
チャットコミュニケーションの問題と心理的安全性の課題 #EOF2019チャットコミュニケーションの問題と心理的安全性の課題 #EOF2019
チャットコミュニケーションの問題と心理的安全性の課題 #EOF2019
 
ドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解するドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解する
 

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

データマイニング+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
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう増田 亨
 
ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計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 01TIS 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
 

Ähnlich wie サービス開発における フロントエンド・ドメイン駆動設計の実践 (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 : 次世代モデリングパラダイムへの道標
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう
 
ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計
 
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 の基礎知識
 

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