Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modern Java Web / SPA Development

Modern Java Web / SPA Development

JJUG CCC 2014 Spring の発表資料です。

Bf9540996d162846c723821a111c673d?s=128

monzou

May 18, 2014
Tweet

Transcript

  1. JAVA MODERN WEB DEVELOPMENT JJUG CCC 2014 SPRING Simplex Inc,

    Takuro Monji
  2. JAVA MODERN SPA DEVELOPMENT JJUG CCC 2014 SPRING Simplex Inc,

    Takuro Monji
  3. @monzou Takuro Monji 文字 拓郎 Simplex Inc. Financial Application Developer

  4. 6 MONTHS AGO … https://gist.github.com/monzou/e355a2c425cdad109537

  5. 700+ BOOKMARKS 6 MONTHS AGO … https://gist.github.com/monzou/e355a2c425cdad109537

  6. 700+ BOOKMARKS 60+ STARS 6 MONTHS AGO … https://gist.github.com/monzou/e355a2c425cdad109537

  7. 700+ BOOKMARKS 60+ STARS 40+ STOCKS 6 MONTHS AGO …

    https://gist.github.com/monzou/e355a2c425cdad109537
  8. WHY ?

  9. モダンな Java の 開発事情 Java + SPA の 開発事例 プロジェクトの

    全体感 公開情報 の 不足
  10. 実例を知りたい

  11. 目次 プロジェクトの概要 アーキテクチャ説明 アーキテクチャ概要 サーバーサイド詳細 クライアントサイド詳細 まとめ

  12. プロジェクトの概要 アーキテクチャ アーキテクチャ サーバーサイド クライアントサイド まとめ

  13. 金融機関向け Web アプリケーション プロジェクト条件 ! ・海外拠点向け (英語) ・そこそこ複雑な業務ロジック インフラ条件 ・Java

    7 ・WebLogic 12c ・Oracle 11g ・IE 9
  14. ウェブの案件は滅多に無い 開発チームも経験値低め 業務に関しては多少ナレッジあり はじめて の ウェブ案件

  15. 既存 Web システム の 問題点 “構造”の欠落 ! ・Ajax 化に伴うフラグメント化 ・業務レイヤの独立性が担保出来ていない

    クライアントの進歩についていけない ! ・クライアントとサーバーの密結合 ・独立して開発することが困難 ・将来的なネイティブクライアント対応が …
  16. SPA (Single Page Application) 未来のために ! ・B2C の Web サービスはリッチ化が進む一方……

    ・金融業界でもリッチなクライアントは増加傾向 ・今後のためにナレッジを蓄積したい クライアントとサーバーを分離 ! ・ステートフルな JavaScript Client + REST API ・ネイティブクライアントに近い
  17. 不安もあった…… ノウハウの不足 ! ・Servlet ? Struts ? war ? WebLogic

    ? ・JavaScript?jQuery ?
  18. 不安もあった…… 対策 2 週間ほど技術調査 ! ・jQuery の使い方を調べたり … ・Backbone.js のコードを読んでみたり

    … ・JS MVC FW を作ってみたり … ・WAF を作ってみたり … ノウハウの不足 ! ・Servlet ? Struts ? war ? WebLogic ? ・JavaScript?jQuery ?
  19. 不安もあった…… 対策 2 週間ほど技術調査 ! ・jQuery の使い方を調べたり … ・Backbone.js のコードを読んでみたり

    … ・JS MVC FW を作ってみたり … ・WAF を作ってみたり … ノウハウの不足 ! ・Servlet ? Struts ? war ? WebLogic ? ・JavaScript?jQuery ? 問題なさそう! … だけど他メンバーは大丈夫かな?
  20. プロジェクトの アーキテクチャ説明 アーキテクチャ概要 サーバーサイド クライアントサイド まとめ

  21. View Model Backbone.js Presentation Layer Resource JAX-RS Web Layer HTTP

    REST JSON Service Model Business Layer Infrastructure Database Persistence Layer JDBC IE 9 WebLogic 12c Oracle 11g
  22. View Model Backbone.js Presentation Layer Resource JAX-RS Web Layer HTTP

    REST JSON Service Model Business Layer Infrastructure Database Persistence Layer JDBC IE 9 WebLogic 12c Oracle 11g Web から切り離し 独立性を保つ
  23. View Model Backbone.js Presentation Layer Resource JAX-RS Web Layer HTTP

    REST JSON Service Model Business Layer Infrastructure Database Persistence Layer JDBC IE 9 WebLogic 12c Oracle 11g Web から切り離し 独立性を保つ API の 提供のみ
  24. application-base application-common application-web application-batch web REST service service-implementation REST-implementation persistence

    domain-model domain-model-meta 静的ファイル群 HTML, JS, CSS … コンパイル時に 実装に依存しないように モジュールを分割
  25. ポイント “ふつう” のレイヤリング ! ・業務レイヤが Web の都合を意識しない ・レイヤ構造を遵守する ・依存関係の破綻はコンパイル時に検知する

  26. ポイント “ふつう” のレイヤリング ! ・業務レイヤが Web の都合を意識しない ・レイヤ構造を遵守する ・依存関係の破綻はコンパイル時に検知する クライアントや

    API は容易に変化するが ドメインは簡単には変化しない
  27. プロジェクトの アーキテクチャ説明 アーキテクチャ サーバーサイド詳細 クライアントサイド まとめ

  28. 主な使用技術 ・Gradle - Build Runner ・Guice - DI Container ・Jersey

    - WAF (JAX-RS) ・Hibernate - ORM ・Guava - Utilities ・Jackson - JSON Converter その他, 開発時には Jetty の埋め込みサーバーを利用して スタンドアローンの Java アプリ化しています http://qiita.com/monzou/items/eb5f25ae9b9925d3c63e
  29. Gradle 知識ゼロでも使える敷居の低さ ! ・Maven と違って何も知らなくても使える ・自動的に良く出来たビルド環境が手に入る 全体は Gradle によるマルチプロジェクト構成 拡張性の高さ

    ! ・Groovy によるスクリプティング ・様々な自動化タスクを組み込んだり … ・動的に環境設定を読み込んでビルドタスクを作成したり …
  30. Guava モダン Java 開発の必須ライブラリ ! ・アプリケーションのあらゆるところで多用 ・関数型プログラミングの導入にも 頻繁に登場するクラス群 ! ・Function,

    Supplier, Predicate, Optional … ・ImmutableCollection, Iterables, Ordering … ・Cache, Joiner, Splitter …
  31. Function を多用するので, 自動生成すると良い @Grind public class Trade implements Serializable {

    private final ID id; private String tradeNo; private long version; private boolean deleted; public Trade(ID id) { this.id = id; } public String getTradeNo() { return tradeNo; } public void setTradeNo(String tradeNo) { this.tradeNo = tradeNo; } … } import static trade.TradeMeta.*; ! List<Trade> trades = dao.findAll(); ImmutableSet<String> activeTradeNos = FluentIterable.from(trades) .filter(Predicates.not(Predicates2.fromFunction(deleted))).transform(tradeNo).toSet(); 今回は Grinder という 自作の APT を使っています https://github.com/monzou/grinder
  32. application-base application-common application-web application-batch web REST service service-implementation REST-implementation persistence

    domain-model domain-model-meta DOMAIN MODEL
  33. DDD ? DCI ?

  34. 技術的な方法論以前に…… ・業務に興味を持つ ・As-Is だけでなく To-Be を考える ・進化を止めない その上で原理主義的にならず柔軟に考えれば良いのでは?

  35. 技術的な方法論以前に…… ・業務に興味を持つ ・As-Is だけでなく To-Be を考える ・進化を止めない その上で原理主義的にならず柔軟に考えれば良いのでは? ライブラリを利用してアプリを作るだけなら誰でも出来る ビジネス的な問題を解決するための「システム」をデザインする

    DDD 云々以前にドメインに踏み込んでいく姿勢が必要
  36. 興味大事 ・顧客の業務に興味を持つ ・競合製品を知る ・要件定義や基本設計から参加する

  37. 考える ・エンジニアが最も価値を出すべきところ ・ビジネス上の課題をエンジニア的に解釈する ・「何故その業務があるのか?」を考える ・「業務で達成したいことは何か?」を考える

  38. 考える ・エンジニアが最も価値を出すべきところ ・ビジネス上の課題をエンジニア的に解釈する ・「何故その業務があるのか?」を考える ・「業務で達成したいことは何か?」を考える 顧客に言われるままにシステムをデザインせず, 批判的に考える

  39. 進化を止めない ・ドメインへの理解は日々変化する ・突然あたらしい概念を見出すこともある ・完全な正解は無い

  40. 進化を止めない ・ドメインへの理解は日々変化する ・突然あたらしい概念を見出すこともある ・完全な正解は無い 基本設計時から大きく変化することもある 変化を恐れずより良いものを追求する アプリケーションへのフィードバックを繰り返す

  41. 実装方針 ・出来るだけドメインモデルに寄せる ・トランザクションスクリプトを避ける ・抽象化可能なドメイン知識は抽象化する 多少の妥協を許容して, バランスを取りつつ素直に実装する

  42. 実装方針 ・出来るだけドメインモデルに寄せる ・トランザクションスクリプトを避ける ・抽象化可能なドメイン知識は抽象化する 多少の妥協を許容して, バランスを取りつつ素直に実装する 具体的には …

  43. 実装方針 ・出来るだけドメインモデルに寄せる ・トランザクションスクリプトを避ける ・抽象化可能なドメイン知識は抽象化する 多少の妥協を許容して, バランスを取りつつ素直に実装する → 副作用の無い業務的な操作はドメインモデルに実装 具体的には …

  44. 実装方針 ・出来るだけドメインモデルに寄せる ・トランザクションスクリプトを避ける ・抽象化可能なドメイン知識は抽象化する 多少の妥協を許容して, バランスを取りつつ素直に実装する → 副作用の無い業務的な操作はドメインモデルに実装 → 副作用を伴う一連のトランザクションはサービスに実装

    具体的には …
  45. 副作用の無い業務ロジック public interface HolidayChecker { boolean isHoliday(LocalDate date, BusinessCenter ...

    businessCenters); } ! public class BusinessDateCalculator { ! private final HolidayChecker holidayChecker; private final BusinessDayConvention convention; private final BusinessCenter[] businessCenters; ! public BusinessDateCalculator(HolidayChecker holidayChecker, BusinessDayConvention convention, BusinessCenter ... businessCenters) { this.holidayChecker = holidayChecker; this.convention = convention; this.businessCenters = businessCenters; } ! public LocalDate calculateNextBusinessDate(LocalDate baseDate) { ... } ! } 例えば業務日付計算などの計算処理はドメインに寄せやすい
  46. ポイント 「カレンダー情報」のような依存は切り離す → HolidayChecker 「日付計算方式」のような情報は設定ファイルに切り出す → BusinessDayConvention 切り離した依存コンポーネントはアプリケーション側で実装する

  47. ポイント 「カレンダー情報」のような依存は切り離す → HolidayChecker 「日付計算方式」のような情報は設定ファイルに切り出す → BusinessDayConvention 切り離した依存コンポーネントはアプリケーション側で実装する ・依存を切り離すことで lazy

    な評価を可能にする ・依存の実装についての詳細を隠蔽する  (実装がキャッシュしてもしなくても良い)
  48. ポイント 「カレンダー情報」のような依存は切り離す → HolidayChecker 「日付計算方式」のような情報は設定ファイルに切り出す → BusinessDayConvention 切り離した依存コンポーネントはアプリケーション側で実装する ・依存を切り離すことで lazy

    な評価を可能にする ・依存の実装についての詳細を隠蔽する  (実装がキャッシュしてもしなくても良い) 依存を Push するのではなく Pull するように設計するのが大事
  49. 副作用を伴う業務ロジック ex) 約定の状態を遷移させて監査ログに記録 → 通知する ex) 約定の感応度を分散計算 → 永続化する

  50. 副作用を伴う業務ロジック ex) 約定の状態を遷移させて監査ログに記録 → 通知する ex) 約定の感応度を分散計算 → 永続化する 多少の妥協は許容する

    これらの一連のトランザクションは サービス層で実装した方が楽だし自然 実装コストや実行パフォーマンスを考えると, ドメインモデルに寄せすぎるのはツラいときも多い
  51. 業務知識を正確に反映する 例えば「通貨」や「ステータス」は単なるコード値ではない 業務的にはかなり重要な知識を含んでいる public final class Currency implements Serializable, Comparable<Currency>

    { ! public static final Currency BASIS_CURRENCY; public static final Currency DOMESTIC_CURRENCY; public static final Currency HEAD_OFFICE_CURRENCY; private static final Map<String, Currency> VALUES; static { // load from currency.yml } ! public int getOrdinal() {} public String getDisplayName() {} public RoundingMode getDefaultRoundingMode() {} public int getDefaultSignificantScale() {} ... ! protected Currency readResolve() {} ! }
  52. application-base application-common application-web application-batch web REST service service-implementation REST-implementation persistence

    domain-model domain-model-meta PERSISTENCE
  53. ORM の選択 Hibernate ! ・生 SQL / JPQL は避けたい ・ドメインが複雑なので

    ORM は必須 ・もともと Hibernate を利用していたので実績がある ・今のところ Hibernate (JPA) 以外の選択肢が無い http://tech.usopla.com/blog/2012/12/26/honeyant/ Type-safe extension ! ・自作のライブラリで型安全性と生産性を向上 ・Eclipse プラグインでメタクラスなどを自動生成
  54. エンティティメタクラス RepositoryMeta meta = RepositoryMeta.getInstance(); Criteria<Repository> criteria = meta.createCriteria() .add(meta.deleted.isFalse())

    .add(meta.issues.issueDate.gt(LocalDate.today()) .add(meta.issues.status.eq(Status.OPEN)); Hibernate に依存しないタイプセーフな クライテリアを生成 (QueryDSL より型安全) クライテリアの利用用途 ! ・タイプセーフ DAO (Hibernate Criteria に変換) ・継続クエリ (OQL に変換)
  55. タイプセーフ DAO Dao<Repository> dao = daoFactory.createDao(meta); ! // Find by

    PK Repository repository = dao.find(id); ! // Update, Save, Delete etc … Repository updated = dao.update(repository); ! // Entity Query (エンティティ単位) List<Repository> repositories = dao.createQuery().orderBy(meta.id, Order.ASC).list(criteria); ! // Projection Query (射影) List<Projection<Repository>> projections = dao.createProjectionQuery().listProjections(criteria, meta.id, meta.issues.subject); ! // Aggregation Query (集約) LocalDate maxIssueDate = dao.createAggregationQuery().uniqueValue(criteria, Aggregations.max(meta.issues.issueDate)); Hibernate を利用した型安全な DAO DAO レイヤは作らず, 必要に応じてサービスレイヤで作成
  56. 継続クエリ CriteriaFilter filter = CriteriaFilterFactory.create(criteria); boolean match = filter.apply(repository); ネイティブクライアントで利用

    ! Presentation Model Binding ! Cache Criteria Consumer Criteria Filter MQ Entity Update XA View RMI DAO フィルタを登録して イベントを選択 同じ Criteria から フィルタを作成出来る Subscribe
  57. 改善したいところ ・Eclipse プラグインは重いので APT ベースにしたい ・いい加減 JPA から脱却したいが選択肢が無い……

  58. 改善したいところ ・Eclipse プラグインは重いので APT ベースにしたい ・いい加減 JPA から脱却したいが選択肢が無い…… ちなみに !

    普段は WebLogic の TransactionManager で 分散トランザクションしようとしたら結構ハマりました……
  59. application-base application-common application-web application-batch web REST service service-implementation REST-implementation persistence

    domain-model domain-model-meta SERVICE
  60. 実装方針 ・業務的な要求に応じてつくる ・”ふつう” のサービスレイヤ ・Web のことを一切意識しないのが重要 他のコンポーネントを利用しつつ副作用のある トランザクションを実装するだけなので, 比較的薄くなる ・ドメインの各コンポーネントに委譲して計算

    ・DAO を使って永続化 ・必要に応じて他サービスと連携
  61. @Transactional public class TradeServiceImpl implements TradeService { ! @Inject public

    TradeServiceImpl(sessionProvider, businessContextProvider, daoFactory, notificationService) { this.sessionProvider = sessionProvider; this.businessContextProvider = businessContextProvider; this.dao = daoFactory.create(meta); this.notificationService = notificationService; } ! public Trade transit(Trade trade, TradeAction action) throws DataUpdateException { TradeTransition transition = TradeTransition.getTransitionFor(trade, action); checkOperationAllowed(transition); if (transition.needsBackup()) { dao.update(dao.clone(trade).invalidate()); } Trade updated = dao.save(transition.transit(trade).validate()); notifyTransition(updated, backup); return updated; } ! private void checkOperationAllowed(TradeTransition transition) throws OperationNotAllowedException { businessContextProvider.getContext().map(new TradeTransitionChecker(getSession(), transition)).orThrow(…); } private void notifyTransition(Trade updated, Trade backup) { notificationService.notify(I18N.translate("message.trade.transit", updated.getTradeNo())); } ! } ドメインロジックを 使って遷移させる 宣言的トランザクションは 自前で実装 セッション情報は Web とは関係ない
  62. application-base application-common application-web application-batch web REST service service-implementation REST-implementation persistence

    domain-model domain-model-meta REST
  63. 実装方針 ・業務ロジックは書かない ・Web の API (IN / OUT) のみに専念する 主に以下の機能を担うだけの薄いレイヤ

    ! ・JSON や XML などの変換 ・入力値のバリデーション ・業務例外を Web のレスポンスに変換 ! 内部的にはほぼサービスへの委譲のみを行う
  64. JAX-RS 基盤として JAX-RS (Jersey) を採用 ! ・Spring MVC : Spring

    は大き過ぎる … ・Dropwizard : J2EE サーバーは想定していない ・Play Framework : 無駄に複雑過ぎる … ・Ninja Framework : 悪くないけどわざわざ使わなくても……
  65. JAX-RS 基盤として JAX-RS (Jersey) を採用 ! ・Spring MVC : Spring

    は大き過ぎる … ・Dropwizard : J2EE サーバーは想定していない ・Play Framework : 無駄に複雑過ぎる … ・Ninja Framework : 悪くないけどわざわざ使わなくても…… API サーバーの実装をしようと考えた場合, いずれの WAF も大したことをしているわけではない JAX-RS の拡張性は非常に高いので, 必要に応じて自作すれば十分
  66. JAX-RS を用いた Resource 例 @Path("/branches") @AuthenticationRequired @AuthorityRequired(Authorities.BRANCH_REFERENCE) public interface BranchResource

    { ! @NoCache @GET @Produces(MediaType.APPLICATION_JSON) PageableCollectionDto<BranchDto> get(@InjectParam BranchSearchConditionDto condition); ! @NoCache @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) @Nullable BranchDto get(@PathParam("id") Long id); ! @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @AuthorityRequired(Authorities.BRANCH_REGISTRATION) BranchDto create(@ValidParam(BranchDtoValidator.Create.class) BranchDto branch); ! } 赤字は独自拡張した アノテーションなど
  67. Guice + JAX-RS public class WebAppModule extends JerseyServletModule { !

    @Override protected void configureServlets() { installModules(); Map<String, String> properties = ... serve(configuration.endpoint()).with(GuiceContainer.class, properties); } ! private void installModules() { install(new ApplicationDescriptorModule()); install(new SessionModule()); install(new PersistenceModule()); install(new TransactionModule()); install(new ServiceModule()); install(new ResourceModule()); ... } ! } jersey-servlet を使えば簡単 JAX-RS 自体が DI を持っているのはちょっと使いづらい面も……
  68. Jackson ・jackson-annotations ・jackson-databind ・jackson-jaxrs-json-provider ・jackson-dataformat-yaml ・jackson-module-afterburner JSON・YAML 変換 設定ファイルの読み込みにも使用

  69. Bean Validation (JSR-303) 使いづらい…… ! ・複雑なことをしようとすると Bean が汚れる ・コードの見通しが悪い ・生産性/再利用性が低い

    ・DI コンテナとの親和性も低い 皆どうしているのか知りたい……
  70. GuardMan import static meta.CredentialDtoMeta.*; ! public class CredentialDtoValidator implements BeanValidator<CredentialDto>

    { ! @Override public Violations apply(CredentialDto bean) { BeanValidationContext<CredentialDto> context = new BeanValidationContext<>(bean); context.property(userCode).required().validate( minLength(0), maxLength(10), alphaNumeric(false) ); context.property(password).required().validate(DomainValidators.PASSWORD); context.property(version).required().eq(ApplicationDescriptor.getVersion()); return context; } ! } ・Jackson / JAX-RS で JSON deserialize 時のバリデーション ・GuardMan で deserialize 後の Bean のバリデーション https://github.com/monzou/guardman
  71. 気にしたところ ① Enum や Value Object のエンコード/デコード ! ・永続化値や JSON

    値への変換コンポーネントを定義 ・クライアントはエンコードされた表現を元に記述する ・表示文字列に依存しないようにする Hibernate の UserType や Jackson の Module から 専用のコンポーネントを利用して変換するようにする
  72. 気にしたところ ② REST 原理主義的になり過ぎない ! ・あらゆる操作を全て HTTP メソッドに割り当てるのは無理 ・ある程度の妥協を許した上で, 統一感のある

    API にする 例えば強制削除 … ! ・DELETE /resources/1 ・PUT /resources/1/delete?force=true
  73. 気にしたところ ③ クライアントを意識した API ! ・バッチはアプリケーションモジュールが分かれているので,  今のところは Web クライアントしか使用しない ・出来るだけ汎用的に設計しつつ,

    場合によっては  クライアントのリクエスト数を減らすための妥協もする HATEOAS ではないけれど…… ! ・例えば「約定」を取得したら「遷移情報」も同時に返すなど ・可能な遷移情報はドメインモデルの複雑な業務ロジックの中にしかない
  74. 改善したいところ JSON DTO と Entity の変換 ! ・ModelMapper を使ってみたがイマイチ ・素直に実装した方が良さそう

    ! バリデーションの扱いが難しい ! ・「どこで」「何を」バリデーションすべきか ・「入力値の検証」と「業務的なバリデーション」は違う 遅い・重い 結局テストで検証用の コードを書くことになる
  75. application-base application-common application-web application-batch web REST service service-implementation REST-implementation persistence

    domain-model domain-model-meta APPLICATION
  76. アプリケーションの組み立て ・モジュール定義 ・ログ出力等の設定 ・ディスクリプタ等の実装 アプリケーションに応じて Guice モジュールを組み立てる セッションやトランザクションマネージャの実装の切り替えなど

  77. プロジェクトの アーキテクチャ説明 アーキテクチャ サーバーサイド詳細 クライアントサイド まとめ まとめ

  78. “ふつう” の延長線 Web レイヤ以外は “ふつう” ! ・ネイティブクライアントと変わらない ・業務レイヤを切り離せていれば SPA 化は難しくない

    JAX-RS で必要十分 ! ・REST API の実装だけなので大したことをするわけではない ・JAX-RS の拡張性は十分に高い ・無理して複雑な WAF を使わなくても……
  79. J2EE サーバーはツラいよ 重いので開発には使用出来ない ! ・開発環境との差異が生じてハマる サーバー固有の設定や拡張実装の存在 ! ・分散トランザクションするだけでも大変 謎の挙動あれこれ !

    ・クラスローダー ・デプロイし直したら変な挙動……
  80. J2EE サーバーはツラいよ 重いので開発には使用出来ない ! ・開発環境との差異が生じてハマる サーバー固有の設定や拡張実装の存在 ! ・分散トランザクションするだけでも大変 謎の挙動あれこれ !

    ・クラスローダー ・デプロイし直したら変な挙動…… 可能な限り J2EE サーバーの利用は避けた方が無難な印象 (※ 個人の印象です) Java App Servers are Dead !
  81. プロジェクトの アーキテクチャ説明 アーキテクチャ サーバーサイド クライアントサイド詳細 まとめ

  82. application-base application-common application-web application-batch web REST service service-implementation REST-implementation persistence

    domain-model domain-model-meta ココ
  83. 主な使用技術 ・SCSS - Alternative CSS ・CoffeeScript - Alternative JavaScript ・Backbone.js

    + Backbone.Marionette - MVC FW ・Handlebars - Template Engine ・Bower - Dependencies Manager ・Grunt - Build Runner サーバーサイドとは全く独立して Grunt で管理 ビルドの雛形は Yeoman で作成したものを拡張して利用
  84. Grunt ・各種アセットのコンパイル ・静的ファイルのビルド/圧縮 ・ビルド/環境毎の設定情報の埋め込み ・Livereload gulp とかあるけど ! 今のところ敢えて Grunt

    以外を使う必要性は感じていない SCSS → CSS CoffeeScript → JavaScript Handlebars → JavaScript i18n YAML → JSON ! etc …
  85. CoffeeScript 生 JavaScript は厳しい ! ・Bad Parts の存在 ・冗長で読みづらい !

    他の Alt JS 言語と比較して明確な利点がある ! ・Bad Parts の隠蔽 ・糖衣構文に過ぎない (学習コストの低さ) ・気持ち良いシンタックス ! 型は欲しいが … ! ・複雑なロジックはサーバーサイド ・GUI は結局ランタイムバインドの世界 経験の少ない JS プログラマが 多い場合は非常に有用
  86. Backbone.js 軽量 MVC “ライブラリ” ! ・最低限の “構造” の提供 ・黒魔術が無い ・軽量で自由に組み合わせられる

     ・デバッグが容易  ・学習コストがほぼ無い  ・既存ライブラリとの組み合わせが容易 経験値の低いチームはまずここから始めると良いと思う
  87. Angular ? Ember ? 重厚長大な FW が本当に必要か? ! ・内部で何をしているか分からないほど複雑 ・高い学習コスト

    ・デバッグも大変 ・アーキテクチャを強制される (侵略的) ・いずれの FW もまだまだ改善の余地が大きい
  88. Angular ? Ember ? 重厚長大な FW が本当に必要か? ! ・内部で何をしているか分からないほど複雑 ・高い学習コスト

    ・デバッグも大変 ・アーキテクチャを強制される (侵略的) ・いずれの FW もまだまだ改善の余地が大きい Backbone は薄くて黒魔術が無いので挙動が全て把握出来る 個人的には薄いライブラリを組み合わせる方が良いと思う ! ・Backbone.js + Backbone.stickit / Rivets.js ・Vue.js + Superagent + Page.js
  89. Backbone.Marionette 素の Backbone には足りないものを補完する ! ・モジュール機構 ・イベントバス ・ネストした View のライフサイクル管理

    ・ボイラープレートの排除
  90. Backbone.Marionette 素の Backbone には足りないものを補完する ! ・モジュール機構 ・イベントバス ・ネストした View のライフサイクル管理

    ・ボイラープレートの排除 Backbone だけだと結局これらを自作することになるので, Backbone を使うなら最初から導入した方が良い
  91. Marionette vs Chaplin Marionette は Backbone の “拡張ライブラリ” Chaplin は

    Backbone を使った “フレームワーク” Marionette は Backbone の部品を提供するだけだが, Chaplin はアプリケーションの作り方を規定する。 素の Backbone だけだと結局足りないものに気付くので, 経験値や好みによってどちらかを選択すると良い。
  92. Marionette vs Chaplin Marionette は Backbone の “拡張ライブラリ” Chaplin は

    Backbone を使った “フレームワーク” Marionette は Backbone の部品を提供するだけだが, Chaplin はアプリケーションの作り方を規定する。 素の Backbone だけだと結局足りないものに気付くので, 経験値や好みによってどちらかを選択すると良い。 → 自前で作り込みたい人は Marionette → フレームワークが欲しい人は Chaplin
  93. Backbone 実装方針 モジュールを分割する ! ・共通コンポーネントを纏める ・サブアプリケーション毎にモジュール化する ・バックエンドとの連携を疎結合にする 出来るだけ Promise 化する

    ! ・バックエンドとの同期 ・非同期なアプリケーションの初期化 古典的な MVC とは違う ! ・Backbone らしく ・Backbone.View は Controller
  94. Promise Show = Application.module "Branches.Show" Show.Controller = show: (id) ->

    $.when(Application.request "session:checkAuthorities", "BRANCH_REFERENCE") .then -> Application.request "branch", id .done (branch) -> view = new Show.View.FormView model: branch Application.mainRegion.show view Application.execute "set:layout", "application" ・Future みたいなもの ・Backbone.sync は Promise を返さないのでラップすると良い
  95. アプリケーションの初期化も class Application extends Marionette.Application ! constructor: (options) -> super

    options @_asyncInitCallbacks = [] ! addAsyncInitializer: (initializer) -> @_asyncInitCallbacks.push initializer ! start: (options) -> @triggerMethod "initialize:before", options @_initCallbacks.run options, @ asyncCallbackPromises = _.map @_asyncInitCallbacks, (callback) -> callback() $.when(asyncCallbackPromises...).done => @triggerMethod "initialize:after", options @triggerMethod "start", options ・起動時に必要なリソースを非同期に取り寄せる ・設定情報や translation など Marionette の Application を拡張して 非同期なコールバックも登録出来るようにする
  96. Backbone.js の問題点 バインディング ! ・それほど動的な画面で無ければ無理に導入しなくても良い ・とはいえあった方が良いと感じる場面は多い ・Backbone.stickit / Rivets.js など

    ! 貧弱なイベント管理 ! ・ネストしたモデルのイベント監視が出来ない ・backbone-deep-model や backbone-nested など ! ActiveRecord のように振る舞うモデル ! ・ViewModel とバックエンドへの Facade は分離したい ・既存ライブラリの取り扱いが悩ましい
  97. モジュール管理 Require JS (AMD) を廃止 ! ・やり過ぎ感が漂う……(結局最後は結合する) ・モジュール機構はあった方が良いが, 規模による ・ネームスペースなら

    Marionette.Module が提供してくれる ! Browserify ! ・プリプロセッサなので AMD より分かりやすい ・モジュール機構があった方がコードが構造化されやすい
  98. やってみた MVC の役割の明確化 ! ・Controller はアプリケーションのイベントを管理する ・View は DOM のイベントを管理する

    ・ViewModel は分離する ! バインディング ! ・Backbone.stickit を使ってみた ・Rivets.js は開発がアクティブではない印象 ・Ractive.js は大き過ぎる https://github.com/monzou/backbone.marionette.example
  99. ViewModel _ = require "underscore" ViewModel = require "app/common/view_model" !

    module.exports = class UserViewModel extends ViewModel ! view: fullName: observe: [ "firstName", "lastName" ] value: -> (_.filter [ @get("firstName"), @get("lastName") ], (name) -> not _.str.isBlank name).join (" ") ・View 用の Model を分離してみた ・computed プロパティを定義出来るようにしてみた ひっそりと Browsrify 化もしてみた …
  100. View Backbone = require "backbone" module.exports = class UserFormView extends

    Backbone.Marionette.ItemView ! template: "#user-form" triggers: "click .save-button": "user:form:save" "click .cancel-button": "user:form:cancel" behaviors: binding: {} bindings: "#title": "title" "#firstName": "firstName" "#lastName": "lastName" "#email": "email" "#remarks": "remarks" ! getEditingModel: -> @model.commit() ・Backbone.stickit でバインドしてみた (Behavior) ・DOM のイベントをアプリケーションのセマンティクスに DOM のイベントを アプリケーションのイベントに変換 stickit の Binding 定義 Marionette.Behavior を使って自動化
  101. View Backbone = require "backbone" module.exports = class UserFormView extends

    Backbone.Marionette.ItemView ! template: "#user-form" triggers: "click .save-button": "user:form:save" "click .cancel-button": "user:form:cancel" behaviors: stickit: {} ! getEditingModel: -> @model.commit() ・バインディングが明示的なのは良いが些か面倒 … ・標準で自動的にバインドするようにしてみた ココ もちろん追加で bindings の指定も出来る
  102. Controller module.exports = class EditController extends Backbone.Marionette.Controller ! constructor: (@region)

    -> _.bindAll @, "save", "goToIndex" ! show: (id) -> UserRepository.find(id).done (model) => @region.show @createView model ! createView: (model) -> viewModel = new ViewModel {}, model: model view = new View model: viewModel @listenTo view, "user:form:save", @save @listenTo view, "user:form:cancel", @goToIndex view ! save: (params) -> UserRepository.save(params.model.commit()).done @goToIndex ! goToIndex: -> CRUD.execute "action:users:list" ・アプリケーションのセマンティクスでイベント処理 ・DOM は意識しない アプリケーションのイベントを 処理して画面を駆動する
  103. IE 9 対応の思い出 モダンブラウザになりきれていない ! ・Ajax を利用したファイルアップロード ・セレクタ数上限問題 ・描画パフォーマンス !

    IE 9 のみをターゲットにして開発するのは悪手 ! ・殆どの機能で IE 9 はモダンブラウザ ・Chrome Dev Tools は大変優秀 ・IE 固有問題は FW レベルで隠蔽しましょう
  104. クライアントのテスト 自社のリソースとリリースサイクルを考える ! ・Web 業界ほどリリースサイクルが早くない ・テスト工程とテスト用のリソースが確保出来る

  105. クライアントのテスト 自社のリソースとリリースサイクルを考える ! ・Web 業界ほどリリースサイクルが早くない ・テスト工程とテスト用のリソースが確保出来る 本当に単体テストで吸収すべきか冷静に判断した方が良い 場合によっては GUI のテストはコストに見合わないかも?

  106. 今回やっていないこと リアルタイム化 ! ・永続化 → XA → MQ → WebSocket

    / SSE で push ・もしまた Web のプロジェクトがあったらやりたい ! オフライン対応 ! ・今回は考えなかった ・一部データは初期化時に localStorage に永続化したが … ・金融のプロ向けアプリだとあまり必要になることはなさそう
  107. プロジェクトの アーキテクチャ説明 アーキテクチャ サーバーサイド クライアントサイド詳細 まとめ まとめ

  108. SPA は採用すべき 明らかなユーザーエクスペリエンスの向上 ! ・ネイティブクライアントに近づく ・業務アプリの方がむしろ採用しやすい ! 開発面でも利点がある ! ・クライアントとサーバーの分離が促進される

    ・将来的なクライアントの拡張がやりやすくなる ・開発環境も整備されてきているので開発効率は悪くない
  109. SPA は採用すべき 明らかなユーザーエクスペリエンスの向上 ! ・ネイティブクライアントに近づく ・業務アプリの方がむしろ採用しやすい ! 開発面でも利点がある ! ・クライアントとサーバーの分離が促進される

    ・将来的なクライアントの拡張がやりやすくなる ・開発環境も整備されてきているので開発効率は悪くない クライアントアプリケーションに「構造」がもたらされるため, 中途半端にリッチ化したクライアントより保守性や開発効率が上がる印象
  110. 開発環境 SPA 開発用の FW は成熟していない ! ・当面は流動的だと思われる ・まだまだ理想的な FW は存在しない

    ・今のところは Backbone.js ぐらい薄い FW がオススメ ! SPA の開発環境は整備されてきている ! ・Grunt や gulp のようなビルドツール ・Browserify や Require JS ・Chrome Dev Tools
  111. ネイティブにはまだまだ勝てない ネイティブアプリなら出来ることが出来ない ! ・レンダリングに介入出来ない ・レイアウトマネージャが弄れない ・クリップボードが操作出来ない  etc … ! ネイティブアプリに比べると開発効率は劣る

    ! ・特に CSS が貧弱過ぎる・闇が深い … ・多人数での開発に向いていない
  112. UI の設計が難しい 業務アプリでの SPA 採用事例がまだまだ少ない ! ・B2C の Web サービスと比べると非常に画面が複雑

    ・どのような画面が実現出来るのか/出来ないのか ・コモンセンスの確立 ! ネイティブアプリとは違う ! ・似たようなエクスペリエンスを提供出来るが実はかなり違う ・CRUD ひとつとってもどんな UI が良いかは検討の余地がある
  113. UI の設計が難しい 業務アプリでの SPA 採用事例がまだまだ少ない ! ・B2C の Web サービスと比べると非常に画面が複雑

    ・どのような画面が実現出来るのか/出来ないのか ・コモンセンスの確立 ! ネイティブアプリとは違う ! ・似たようなエクスペリエンスを提供出来るが実はかなり違う ・CRUD ひとつとってもどんな UI が良いかは検討の余地がある SPA の UI 設計のガイドラインが欲しい … UI デザイナーが欲しい …
  114. プロジェクトの アーキテクチャ アーキテクチャ サーバーサイド クライアントサイド まとめ

  115. おさらい SPA 開発は “ふつう” の延長線上にある ! ・API とクライアントが違うだけ ・ドメイン設計やインフラ層の知見は流用出来る ・きちんとしたドメイン理解と開発を心がけましょう

    ! ! まだまだこれからの分野 ! ・開発環境は整備されてきているが今後ますます発展するはず ・ツールや UI 等を含めてまだまだ洗練される必要がある ! Keep it simple ! ! ・関連技術は多様化し複雑化する一方 … ・出来る限り複雑な仕組みを避けて軽量に作った方が良いのでは?
  116. まとめ SPA はこわくない ! ・これまでの延長線上にある ・きちんと設計出来れば経験値が少ないチームでも大丈夫 ! ! 継続的なキャッチアップは必要 !

    ・Java もモダンに生まれ変わりつつある ・Web 開発は進歩のスピードが物凄い ・半年経てば主流技術が変わることも … ・アーキテクトは多種多様な最新技術を知る必要がある ・ナレッジの蓄積が出来る体制づくりを
  117. Thank you