Spring RooによるDDDの 実 践 ~ 第 2 回 Spring Rooのアーキテクチャ~ 2011 年 4 月 7 日 笠 原 規 男
Spring Rooのキーメカニズム ITD ApectJのITD(インタータイプ 宣 言 )を 利 用 クラスにメソッドや 属 性 をウィービング ユーザーコード(.java)と 自 動 生 成 コード(.aj)を 分 離.ajはユーザーは 編 集 不 可 Ruby/GroovyのMix-in C#のパーシャルクラスに 似 た 方 式 スカフォルディング Web 層 のモジュールを 自 動 生 成 RESTfullなController CRUD 機 能 を 備 えたJSPX 見 た 目 をカスタマイズ 可 能 (CSS アイコン 等 の 差 し 替 え) ラウンドトリップ 可 能 Entityへの 属 性 追 加 がJSPXに 反 映 される EntityへのFinderの 追 加 がControllerおよびJSPXに 反 映 される 2
Rooのアーキテクチャ 要 素 Web Webリソース(HTML/CSS/ 画 像 )など) JSPX TAGX JS Controller Entity Entity Embeddable enum 3
EntityとController FooBar FooBarController FooBar.java FooBarController.java FooBar_Roo_Entity.aj FooBarController_Roo_Controller.aj FooBar_Roo_JavaBean.aj FooBar_Roo_Finder.aj FooBar_Roo_toString.aj FooBar_Roo_Configurable.aj 4
スカフォルドの 処 理 フロー : list.jspx 1: updateform() 一 覧 表 示 一 件 選 択 更 新 の 流 れ HttpMethodFilter 2: 更 新 フォーム() 3: update() 6: merge() : update.jspx : Controller : Entity 4: エラー 表 示 () 5: update() 7: show() 8: 更 新 後 データの 表 示 () : show.jspx 5
RESTfulなリソースマッピング アプリケーション:clinic リソース:petに 対 するURL リソース GET PUT POST DELETE コレクションのURI http://hoge.com/clinic/pet/ 一 覧 取 得 未 使 用 新 しいリ ソースの 作 成 (ID 採 番 ) 未 使 用 メンバーのURI http://hoge.com/clinic/pet/5 一 件 取 得 (ID=5) リソース の 更 新 (ID=5) 未 使 用 リソースの 削 除 (ID=5) コレクションのURI+formパラメタ http://hoge.com/clinic/pet?form メンバーのURI+formパラメタ http://hoge.com/clinic/pet/5? form 入 力 用 フォーム の 取 得 更 新 用 フォーム の 取 得 未 使 用 未 使 用 未 使 用 未 使 用 未 使 用 未 使 用 HTMLではGET/POSTしか 使 用 できないが HttpMethodFilterが methodパラメータをもとにpost PUT/DELETE 変 換 を 行 う 6
コントローラの 呼 び 出 し JSPXへのフォワード list.jspxが 生 成 したHTML <a title="update Pet" alt="update Pet" href="/petclinic/pets/5?form"> PetController_Roo_Controller.aj @RequestMapping(value = "/{id}", params = "form", method = RequestMethod.GET) public String PetController.updateForm( } @PathVariable("id") Long id, Model uimodel) { uimodel.addattribute("pet", Pet.findPet(id)); return "pets/update"; WEB-INF/views/views.xml <definition extends="default" name="pets/update"> <put-attribute name="body" value="/web-inf/views/pets/update.jspx"/> </definition> 7
更 新 フォームの 表 示 1 WEB-INF/views/pets/update.jspx <div xmlns:field="urn:jsptagdir:/web-inf/tags/form/fields" xmlns:form="urn:jsptagdir:/web-inf/tags/form" xmlns:jsp="http://java.sun.com/jsp/page" version="2.0"> <form:update id="fu_com_springsource_petclinic_domain_pet" modelattribute="pet" path="/pets" versionfield="version" z="gxoakvqt12xvqcdpw3hqmnvadve="> <field:input field="name" id="c_com_springsource_petclinic_domain_pet_name" min="1" required="true" z="zy+k75jeso9rmejyzrfnivs2abg="/> <field:select field="owner" id="c_com_springsource_petclinic_domain_pet_owner" itemvalue="id" items="${owners}" path="/owners" z="fgzswap4xxvhphowjksrvve929c="/> <!-- fieldの 定 義 が 続 く... --> </form:update> </div> 8
更 新 フォームの 表 示 2 update.jspxによって 生 成 されたHTML <form id="pet" action="/petclinic/pets" method="post"> <input type="hidden" name="_method" value="put"/> <label for="_name_id">name :</label> <input id="_name_id" name="name" type="text" value="ポチ"/> <label for="_owner_id">owner :</label> <a href="/petclinic/owners?form"> <img title="create new Owner" src="/petclinic/resources/images/add.png"/> </a> <label for="_weight_id">weight :</label> <input id="_weight_id" name="weight" type="text" value="10.0"/> <!-- fieldの 定 義 が 続 く... --> <input value="save" type="submit" id="proceed"/> </form> 9
コントローラの 呼 び 出 し PetController_Roo_Controller.aj @RequestMapping(method = RequestMethod.PUT) public String PetController.update(@Valid Pet pet, BindingResult bindingresult, Model uimodel, HttpServletRequest httpservletrequest) { } if (bindingresult.haserrors()) { } uimodel.addattribute("pet", pet); return "pets/update"; uimodel.asmap().clear(); pet.merge(); return "redirect:/pets/" + encodeurlpathsegment(pet.getid().tostring(), httpservletrequest); 10
エンティティの 呼 び 出 し( 永 続 化 ) Pet_Roo_Entity.aj @Transactional public Pet Pet.merge() { if (this.entitymanager == null) this.entitymanager = entitymanager(); Pet merged = this.entitymanager.merge(this); this.entitymanager.flush(); return merged; } 11
トランザクションスクリプトからドメインモデルへ トランザクションスクリプト ビジネスロジックを 一 連 の 手 続 きで 構 築 して その 各 手 順 でプレゼン テーションからの1つの 要 求 を 処 理 する シンプルなアーキテクチャ J2EE 時 代 は Stateless Session Beans + JDBCによるDAO で 実 装 Spring 時 代 は POJO + HibernateによるDAO で 実 装 シンプルな 業 務 ロジックの 実 装 に 向 く ドメインモデル 振 る 舞 いとデータの 両 方 を 一 体 化 させたドメインのオブジェクトモデル アーキテクチャは 相 対 的 に 複 雑 になる ユーザーインターフェイスおよびデータ 永 続 化 処 理 の 分 離 アクティブレコードもしくはデータマッパーを 利 用 してO-Rマッピングを 行 う 複 雑 な 業 務 ロジックの 実 装 に 向 く 複 数 のUI(ブラウザ/ 携 帯 /リッチクライアント...)に 対 応 しやすい 12
トランザクションスクリプトとドメインモデルの 対 比 トランザクションスクリプト 機 能 追 加 の 労 力 ドメインモデル PofEAAより ドメインロジックの 複 雑 性 13
レイヤアーキテクチャ ドメインモデルパターンのレイヤ 構 成 DDD ユーザーインター フェイス 層 PofEAA プレゼンテーション 層 Spring Roo Web 層 アプリケーション 層 ドメイン 層 ドメイン 層 サービス 層 ドメインモデル エンティティ 層 インフラ ストラクチャ 層 データソース 層 UI 層 からドメインモデルを 直 接 呼 び 出 すのを 原 則 とし 必 要 な 業 務 だけサー ビスを 作 成 するのがファウラー 流 14
Rooのレイヤアーキテクチャの 課 題 サービス 層 の 分 離 が 必 要 サービス 層 が 分 離 されていないため プロセスフローを 分 離 できない エンティティのメソッドがトランザクション 境 界 UIの 変 更 をエンティティが 受 けやすい 画 面 に 表 示 すべきデータが 増 えたときに そのデータを 取 ってくる 処 理 をエン ティティに 追 加 する? データアクセスとドメインロジックが 混 在 DDDでのファクトリ リポジトリ 相 当 のロジックがエンティティに ドメインロジックの 実 装 方 法 が 示 されていない 生 成 されたエンティティにはCRUDしか 存 在 しない ビジネスロジック ビジネスルールの 実 装 方 法 は? どのクラスに 何 と 言 うメソッドで コントローラから 呼 び 出 す 方 法 は? トランザクション 制 御 は? 15