SlideShare a Scribd company logo
 @shin1x1
2015/09/14 DevLove関西
レイヤードアーキテクチャを意識した
PHPアプリケーションの構築 ver2
http://www.slideshare.net/shin1x1/lt-up-49929619
レイヤードアーキテクチャ
レイヤードアーキテクチャ
(c) 2015 Masashi Shinbara @shin1x1
• アプリケーションをレイヤ(層)に分割
• レイヤは自身の役割を担う
• レイヤ間で協調して、処理を行う
OSI参照モデル
(c) 2015 Masashi Shinbara @shin1x1
7.Application
6. Presentation
5. Session
4.Transport
3. Network
2. Data link
1. Physical
MVC
(c) 2015 Masashi Shinbara @shin1x1
View
Controller
Model
MVCの悩み
CakePHP
(c) 2015 Masashi Shinbara @shin1x1
FatController -> FatModel
Fat Model
(c) 2015 Masashi Shinbara @shin1x1
• 1,000行を超える Model
• Model の役割が多すぎる
• DAO / バリデーション / ビジネスロジックなど
MVC
(c) 2015 Masashi Shinbara @shin1x1
View
Controller
Model
MVC + Service
(c) 2015 Masashi Shinbara @shin1x1
View
Controller
Model
Service
サービスレイヤを追加
(c) 2015 Masashi Shinbara @shin1x1
• Controller と Model の間のレイヤ
• 主にビジネスロジックとバリデーションを担う
• 1アクションメソッドに、1サービスクラス
結果
(c) 2015 Masashi Shinbara @shin1x1
• Fat(Controller¦Model) を、ある程度解消
• レイヤの責務があいまい

=> サービスが、セッションを操作等
• レイヤ間の処理の流れが統一できていない

=> サービスが、コントローラを操作等
レイヤを意識
Laravel
(c) 2015 Masashi Shinbara @shin1x1
• Laraval + AngularJS
• Laravel は、REST API の提供のみ
• UI 関連の処理は、AngularJS
• レイヤの責務と流れを意識
レイヤ構造
(c) 2015 Masashi Shinbara @shin1x1
Routing
Controller
Eloquent(ORM)
Service
レイヤの役割
(c) 2015 Masashi Shinbara @shin1x1
Routing
Controller
ORM
Service
ルーティング、認証、フィルタ
HTTPリクエスト、レスポンス
バリデーション、サービス実行
事前条件検証、ビジネスロジック
データベースアクセス、
エンティティ固有の処理
例: 書籍の予約
(c) 2015 Masashi Shinbara @shin1x1
•会員制書籍予約Webアプリケーション
•利用者は認証トークンが必要
•書籍の予約を行う
•予約する書籍と予約数を指定
例: 書籍の予約
(c) 2015 Masashi Shinbara @shin1x1
•POST /reservation
•X-Api-Token: ユーザ認証トークン
•asin=書籍コード
•quantity=予約数
Routingレイヤ
(c) 2015 Masashi Shinbara @shin1x1
•認証はミドルウェア(フィルタ)で実行

=> 未ログインなら、401を返す
•URIからコントローラを実行
Routing
Route::group(['before' => 'api_auth'],
function () {

Route::post('/reservation',
ReservationController::class.'@create');

}
); 認証フィルタ
Routing
Route::group(['before' => 'api_auth'],
function () {

Route::post('/reservation',
ReservationController::class.'@create');

}
);
URIとコントローラのマッピング
Controllerレイヤ
(c) 2015 Masashi Shinbara @shin1x1
•POSTパラメータ、セッションユーザ情報を取得
•バリデーション実行

(asinとquantityパラメータの形式チェック)
•サービスに必要なパラメータを渡して実行

(HTTPの関心事はサービスに持ち込まない)
•HTTPレスポンスを返す
Controller
public function create()

{

$validator = (new ReservationValidatorBuilder())->create(Input::all());

if ($validator->fails()) {

return $this->responseValidationError($validator->messages());

}



$reservation = $this->service->book($this->getUser(), Input::all());



return $this->responseCreated($reservation);

}
バリデーション
Controller
public function create()

{

$validator = (new ReservationValidatorBuilder())->create(Input::all());

if ($validator->fails()) {

return $this->responseValidationError($validator->messages());

}



$reservation = $this->service->book($this->getUser(), Input::all());



return $this->responseCreated($reservation);

}
サービスの実行
Serviceレイヤ
(c) 2015 Masashi Shinbara @shin1x1
•事前条件の検証

(書籍の在庫が足りているか?等)
•予約処理を実行
•DB操作をEloquent(ORM)で行う
Service
public function book(User $user, array $inputs)

{

$book = Book::where('asin', $inputs['asin'])->first();

if (empty($book)) {

throw new PreconditionException('book_not_found');

}

if ($book->inventory < $inputs['quantity']) {

throw new PreconditionException('not_enough_book_inventory');

}



$reservation = new Reservation();

DB::transaction(function () use ($user, $book, &$reservation, $inputs) {

$affectedRows = $book->decrementInventory($inputs['quantity']);

if ($affectedRows !== 1) {

throw new PreconditionException('not_enough_book_inventory');

}



$reservation->user_id = $user->id;

$reservation->book_id = $book->id;

$reservation->quantity = $inputs['quantity'];

$reservation->reservation_code = $reservation->generateReservationCode();

$reservation->save();

});



return $reservation;

}
Service
public function book(User $user, array $inputs)

{

$book = Book::where('asin', $inputs['asin'])->first();

if (empty($book)) {

throw new PreconditionException('book_not_found');

}

if ($book->inventory < $inputs['quantity']) {

throw new PreconditionException('not_enough_book_inventory');

}



$reservation = new Reservation();

DB::transaction(function () use ($user, $book, &$reservation, $inputs) {

$affectedRows = $book->decrementInventory($inputs['quantity']);

if ($affectedRows !== 1) {

throw new PreconditionException('not_enough_book_inventory');

}



$reservation->user_id = $user->id;

$reservation->book_id = $book->id;

$reservation->quantity = $inputs['quantity'];

$reservation->reservation_code = $reservation->generateReservationCode();

$reservation->save();

});



return $reservation;

}
必要なパラメータ
Service
public function book(User $user, array $inputs)

{

$book = Book::where('asin', $inputs['asin'])->first();

if (empty($book)) {

throw new PreconditionException('book_not_found');

}

if ($book->inventory < $inputs['quantity']) {

throw new PreconditionException('not_enough_book_inventory');

}



$reservation = new Reservation();

DB::transaction(function () use ($user, $book, &$reservation, $inputs) {

$affectedRows = $book->decrementInventory($inputs['quantity']);

if ($affectedRows !== 1) {

throw new PreconditionException('not_enough_book_inventory');

}



$reservation->user_id = $user->id;

$reservation->book_id = $book->id;

$reservation->quantity = $inputs['quantity'];

$reservation->reservation_code = $reservation->generateReservationCode();

$reservation->save();

});



return $reservation;

}
事前条件の検証
Service
public function book(User $user, array $inputs)

{

$book = Book::where('asin', $inputs['asin'])->first();

if (empty($book)) {

throw new PreconditionException('book_not_found');

}

if ($book->inventory < $inputs['quantity']) {

throw new PreconditionException('not_enough_book_inventory');

}



$reservation = new Reservation();

DB::transaction(function () use ($user, $book, &$reservation, $inputs) {

$affectedRows = $book->decrementInventory($inputs['quantity']);

if ($affectedRows !== 1) {

throw new PreconditionException('not_enough_book_inventory');

}



$reservation->user_id = $user->id;

$reservation->book_id = $book->id;

$reservation->quantity = $inputs['quantity'];

$reservation->reservation_code = $reservation->generateReservationCode();

$reservation->save();

});



return $reservation;

}
ビジネスロジックの実行
レイヤの依存、処理の流れ
(c) 2015 Masashi Shinbara @shin1x1
Routing
Controller
Eloquent(ORM)
Service
レイヤの依存、処理の流れ
(c) 2015 Masashi Shinbara @shin1x1
Routing
Controller
Eloquent(ORM)
Service
流れを一方向に固定
レイヤをまたぐのはアリ
(c) 2015 Masashi Shinbara @shin1x1
Routing
Controller
Eloquent(ORM)
Service
レイヤをまたぐのはアリ
(c) 2015 Masashi Shinbara @shin1x1
Routing
Controller
Eloquent(ORM)
Service
方向は変えない
サービスを中心に考える
(c) 2015 Masashi Shinbara @shin1x1
•サービス(ビジネスドメイン)が中心
•事前条件検証とビジネスロジック
•HTTPの関心事は持ち込まない

必要なもの引数で渡す(scalar, array, object)
ドメインをサービスで表現
(c) 2015 Masashi Shinbara @shin1x1
•クラス名、メソッド名はドメインの用語で

(ユビキタス言語)
•ユースケースをメソッドに実装
•ex) 書籍の予約

=> ReservationService#book()
サービスから作る
(c) 2015 Masashi Shinbara @shin1x1
• サービスとテストを先に実装
• サービスの最初の利用者は、テスト
• サービスを Web に結ぶのが、コントローラ
• サービスは、バッチ処理等からも使える
ドメインごとに名前空間を分ける
[package]
+ [AcmeReservation]
+ [Controller]
+ [Service]
+ [Model]
+ [Validation]
[AcmeUser]
+ [Controller]
+ [Service]
+ [Model]
+ [Validation]
PSR-4
結果
(c) 2015 Masashi Shinbara @shin1x1
• レイヤの役割に専念できる
• 流れが一方向なので、依存の方向が明確に
• サービス(ドメイン)に集中
• サービスをどう分割していくかが課題
まとめ
(c) 2015 Masashi Shinbara @shin1x1
• MVCだけじゃない
• 自分でレイヤ構造を考える
• レイヤの責務と処理の流れ
@shin1x1
(c) 2015 Masashi Shinbara @shin1x1

More Related Content

What's hot (20)

Vagrant で作る PHP 開発環境 [実践編]
Vagrant で作る PHP 開発環境 [実践編]Vagrant で作る PHP 開発環境 [実践編]
Vagrant で作る PHP 開発環境 [実践編]
Masashi Shinbara
 
Vagrant体験入門
Vagrant体験入門Vagrant体験入門
Vagrant体験入門
Masashi Shinbara
 
Vagrant で PHP 開発環境を作る ハンズオン
Vagrant で PHP 開発環境を作る ハンズオンVagrant で PHP 開発環境を作る ハンズオン
Vagrant で PHP 開発環境を作る ハンズオン
Masashi Shinbara
 
SQLによるDynamoDBの操作
SQLによるDynamoDBの操作SQLによるDynamoDBの操作
SQLによるDynamoDBの操作
Sugawara Genki
 
JavaScriptCore.framework の普通な使い方 #cocoa_kansai
JavaScriptCore.framework の普通な使い方 #cocoa_kansaiJavaScriptCore.framework の普通な使い方 #cocoa_kansai
JavaScriptCore.framework の普通な使い方 #cocoa_kansai
Tomohiro Kumagai
 
Twilio API を PHP で触ってみよう
Twilio API を PHP で触ってみようTwilio API を PHP で触ってみよう
Twilio API を PHP で触ってみよう
Masashi Shinbara
 
javascript を Xcode でテスト
javascript を Xcode でテストjavascript を Xcode でテスト
javascript を Xcode でテスト
Yoichiro Sakurai
 
Isomorphic web development with scala and scala.js
Isomorphic web development  with scala and scala.jsIsomorphic web development  with scala and scala.js
Isomorphic web development with scala and scala.js
TanUkkii
 
ネタじゃないScala.js
ネタじゃないScala.jsネタじゃないScala.js
ネタじゃないScala.js
takezoe
 
Sbtのマルチプロジェクトはいいぞ
SbtのマルチプロジェクトはいいぞSbtのマルチプロジェクトはいいぞ
Sbtのマルチプロジェクトはいいぞ
Yoshitaka Fujii
 
ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala
ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscalaビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala
ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala
takezoe
 
Scala が支える医療系ウェブサービス #jissenscala
Scala が支える医療系ウェブサービス #jissenscalaScala が支える医療系ウェブサービス #jissenscala
Scala が支える医療系ウェブサービス #jissenscala
Kazuhiro Sera
 
Scala + Finagleの魅力
Scala + Finagleの魅力Scala + Finagleの魅力
Scala + Finagleの魅力
Kota Mizushima
 
Yesod勉強会
Yesod勉強会Yesod勉強会
Yesod勉強会
Hideyuki Tanaka
 
CSS Preprocessor Hands-on
CSS Preprocessor Hands-onCSS Preprocessor Hands-on
CSS Preprocessor Hands-on
littlebustersreply
 
Serverless frameworkでお手軽lambda運用 at #nseg #93
Serverless frameworkでお手軽lambda運用 at #nseg #93Serverless frameworkでお手軽lambda運用 at #nseg #93
Serverless frameworkでお手軽lambda運用 at #nseg #93
ko ty
 
Serverless frameworkを使ってみた話 at #nseg #90
Serverless frameworkを使ってみた話 at #nseg #90Serverless frameworkを使ってみた話 at #nseg #90
Serverless frameworkを使ってみた話 at #nseg #90
ko ty
 
Lambda in java_20160121
Lambda in java_20160121Lambda in java_20160121
Lambda in java_20160121
Teruo Kawasaki
 
RxSwiftのデータバインディングだけ
RxSwiftのデータバインディングだけRxSwiftのデータバインディングだけ
RxSwiftのデータバインディングだけ
Hironytic
 
RxSwift
RxSwiftRxSwift
RxSwift
Kosuke Usami
 
Vagrant で作る PHP 開発環境 [実践編]
Vagrant で作る PHP 開発環境 [実践編]Vagrant で作る PHP 開発環境 [実践編]
Vagrant で作る PHP 開発環境 [実践編]
Masashi Shinbara
 
Vagrant で PHP 開発環境を作る ハンズオン
Vagrant で PHP 開発環境を作る ハンズオンVagrant で PHP 開発環境を作る ハンズオン
Vagrant で PHP 開発環境を作る ハンズオン
Masashi Shinbara
 
SQLによるDynamoDBの操作
SQLによるDynamoDBの操作SQLによるDynamoDBの操作
SQLによるDynamoDBの操作
Sugawara Genki
 
JavaScriptCore.framework の普通な使い方 #cocoa_kansai
JavaScriptCore.framework の普通な使い方 #cocoa_kansaiJavaScriptCore.framework の普通な使い方 #cocoa_kansai
JavaScriptCore.framework の普通な使い方 #cocoa_kansai
Tomohiro Kumagai
 
Twilio API を PHP で触ってみよう
Twilio API を PHP で触ってみようTwilio API を PHP で触ってみよう
Twilio API を PHP で触ってみよう
Masashi Shinbara
 
javascript を Xcode でテスト
javascript を Xcode でテストjavascript を Xcode でテスト
javascript を Xcode でテスト
Yoichiro Sakurai
 
Isomorphic web development with scala and scala.js
Isomorphic web development  with scala and scala.jsIsomorphic web development  with scala and scala.js
Isomorphic web development with scala and scala.js
TanUkkii
 
ネタじゃないScala.js
ネタじゃないScala.jsネタじゃないScala.js
ネタじゃないScala.js
takezoe
 
Sbtのマルチプロジェクトはいいぞ
SbtのマルチプロジェクトはいいぞSbtのマルチプロジェクトはいいぞ
Sbtのマルチプロジェクトはいいぞ
Yoshitaka Fujii
 
ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala
ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscalaビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala
ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala
takezoe
 
Scala が支える医療系ウェブサービス #jissenscala
Scala が支える医療系ウェブサービス #jissenscalaScala が支える医療系ウェブサービス #jissenscala
Scala が支える医療系ウェブサービス #jissenscala
Kazuhiro Sera
 
Scala + Finagleの魅力
Scala + Finagleの魅力Scala + Finagleの魅力
Scala + Finagleの魅力
Kota Mizushima
 
Serverless frameworkでお手軽lambda運用 at #nseg #93
Serverless frameworkでお手軽lambda運用 at #nseg #93Serverless frameworkでお手軽lambda運用 at #nseg #93
Serverless frameworkでお手軽lambda運用 at #nseg #93
ko ty
 
Serverless frameworkを使ってみた話 at #nseg #90
Serverless frameworkを使ってみた話 at #nseg #90Serverless frameworkを使ってみた話 at #nseg #90
Serverless frameworkを使ってみた話 at #nseg #90
ko ty
 
Lambda in java_20160121
Lambda in java_20160121Lambda in java_20160121
Lambda in java_20160121
Teruo Kawasaki
 
RxSwiftのデータバインディングだけ
RxSwiftのデータバインディングだけRxSwiftのデータバインディングだけ
RxSwiftのデータバインディングだけ
Hironytic
 

Viewers also liked (20)

traitを使って楽したい話
traitを使って楽したい話traitを使って楽したい話
traitを使って楽したい話
infinite_loop
 
東京から沖縄に移住したからこそわかるクラウドとコミュニティの有り難み 〜人はもっと自由になれる〜
東京から沖縄に移住したからこそわかるクラウドとコミュニティの有り難み 〜人はもっと自由になれる〜東京から沖縄に移住したからこそわかるクラウドとコミュニティの有り難み 〜人はもっと自由になれる〜
東京から沖縄に移住したからこそわかるクラウドとコミュニティの有り難み 〜人はもっと自由になれる〜
龍治 常盤木
 
Play jjug2012spring
Play jjug2012springPlay jjug2012spring
Play jjug2012spring
Takafumi Ikeda
 
【ハンズオン】初めてのUnityで作る「3D野球盤」_"8a1"20150204発表資料
【ハンズオン】初めてのUnityで作る「3D野球盤」_"8a1"20150204発表資料【ハンズオン】初めてのUnityで作る「3D野球盤」_"8a1"20150204発表資料
【ハンズオン】初めてのUnityで作る「3D野球盤」_"8a1"20150204発表資料
8a1
 
NetBeans、FuelPHP と過ごしたこの 2 ヶ月
NetBeans、FuelPHP と過ごしたこの 2 ヶ月NetBeans、FuelPHP と過ごしたこの 2 ヶ月
NetBeans、FuelPHP と過ごしたこの 2 ヶ月
suno88
 
Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開
Shogo Kawahara
 
Twilio を使えば簡単にできる アプリケーションと電話/SMS連携
Twilio を使えば簡単にできる アプリケーションと電話/SMS連携Twilio を使えば簡単にできる アプリケーションと電話/SMS連携
Twilio を使えば簡単にできる アプリケーションと電話/SMS連携
Masashi Shinbara
 
PSR-1 と PSR-2 を 5分でざっくり理解する
PSR-1 と PSR-2 を5分でざっくり理解するPSR-1 と PSR-2 を5分でざっくり理解する
PSR-1 と PSR-2 を 5分でざっくり理解する
Wataru Terada
 
Laravelのeloquent だけ入れた話
Laravelのeloquent だけ入れた話Laravelのeloquent だけ入れた話
Laravelのeloquent だけ入れた話
Masataka Kono
 
新標準PSRに学ぶきれいなPHP
新標準PSRに学ぶきれいなPHP新標準PSRに学ぶきれいなPHP
新標準PSRに学ぶきれいなPHP
Yusuke Ando
 
第7回こども病院移転計画調査委員会資料
第7回こども病院移転計画調査委員会資料第7回こども病院移転計画調査委員会資料
第7回こども病院移転計画調査委員会資料
f_kodomo
 
先取り!PHP 7 と WordPress
先取り!PHP 7 と WordPress先取り!PHP 7 と WordPress
先取り!PHP 7 と WordPress
Masashi Shinbara
 
SwiftとReactNativeで似たようなUIを作った際の記録
SwiftとReactNativeで似たようなUIを作った際の記録SwiftとReactNativeで似たようなUIを作った際の記録
SwiftとReactNativeで似たようなUIを作った際の記録
Fumiya Sakai
 
はじめて作ったアプリが10,000ダウンロード達成したから自慢する
はじめて作ったアプリが10,000ダウンロード達成したから自慢するはじめて作ったアプリが10,000ダウンロード達成したから自慢する
はじめて作ったアプリが10,000ダウンロード達成したから自慢する
Natsumi Oki
 
DMMの闇に触れた話
DMMの闇に触れた話DMMの闇に触れた話
DMMの闇に触れた話
Katsunori Tanaka
 
Trait とは? その使い道を考えてみる
Trait とは? その使い道を考えてみるTrait とは? その使い道を考えてみる
Trait とは? その使い道を考えてみる
Takuya Fujimura
 
20151205フルスクラッチcms作成のノウハウ With Laravel
20151205フルスクラッチcms作成のノウハウ With Laravel20151205フルスクラッチcms作成のノウハウ With Laravel
20151205フルスクラッチcms作成のノウハウ With Laravel
Takumi Yoshida
 
ビルドサーバで使うDocker
ビルドサーバで使うDockerビルドサーバで使うDocker
ビルドサーバで使うDocker
Masashi Shinbara
 
Laravelチュートリアルを作ってみた。
Laravelチュートリアルを作ってみた。Laravelチュートリアルを作ってみた。
Laravelチュートリアルを作ってみた。
Futoshi Endo
 
MySQL・PostgreSQLだけで作る高速でリッチな全文検索システム
MySQL・PostgreSQLだけで作る高速でリッチな全文検索システムMySQL・PostgreSQLだけで作る高速でリッチな全文検索システム
MySQL・PostgreSQLだけで作る高速でリッチな全文検索システム
Kouhei Sutou
 
traitを使って楽したい話
traitを使って楽したい話traitを使って楽したい話
traitを使って楽したい話
infinite_loop
 
東京から沖縄に移住したからこそわかるクラウドとコミュニティの有り難み 〜人はもっと自由になれる〜
東京から沖縄に移住したからこそわかるクラウドとコミュニティの有り難み 〜人はもっと自由になれる〜東京から沖縄に移住したからこそわかるクラウドとコミュニティの有り難み 〜人はもっと自由になれる〜
東京から沖縄に移住したからこそわかるクラウドとコミュニティの有り難み 〜人はもっと自由になれる〜
龍治 常盤木
 
【ハンズオン】初めてのUnityで作る「3D野球盤」_"8a1"20150204発表資料
【ハンズオン】初めてのUnityで作る「3D野球盤」_"8a1"20150204発表資料【ハンズオン】初めてのUnityで作る「3D野球盤」_"8a1"20150204発表資料
【ハンズオン】初めてのUnityで作る「3D野球盤」_"8a1"20150204発表資料
8a1
 
NetBeans、FuelPHP と過ごしたこの 2 ヶ月
NetBeans、FuelPHP と過ごしたこの 2 ヶ月NetBeans、FuelPHP と過ごしたこの 2 ヶ月
NetBeans、FuelPHP と過ごしたこの 2 ヶ月
suno88
 
Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開
Shogo Kawahara
 
Twilio を使えば簡単にできる アプリケーションと電話/SMS連携
Twilio を使えば簡単にできる アプリケーションと電話/SMS連携Twilio を使えば簡単にできる アプリケーションと電話/SMS連携
Twilio を使えば簡単にできる アプリケーションと電話/SMS連携
Masashi Shinbara
 
PSR-1 と PSR-2 を 5分でざっくり理解する
PSR-1 と PSR-2 を5分でざっくり理解するPSR-1 と PSR-2 を5分でざっくり理解する
PSR-1 と PSR-2 を 5分でざっくり理解する
Wataru Terada
 
Laravelのeloquent だけ入れた話
Laravelのeloquent だけ入れた話Laravelのeloquent だけ入れた話
Laravelのeloquent だけ入れた話
Masataka Kono
 
新標準PSRに学ぶきれいなPHP
新標準PSRに学ぶきれいなPHP新標準PSRに学ぶきれいなPHP
新標準PSRに学ぶきれいなPHP
Yusuke Ando
 
第7回こども病院移転計画調査委員会資料
第7回こども病院移転計画調査委員会資料第7回こども病院移転計画調査委員会資料
第7回こども病院移転計画調査委員会資料
f_kodomo
 
先取り!PHP 7 と WordPress
先取り!PHP 7 と WordPress先取り!PHP 7 と WordPress
先取り!PHP 7 と WordPress
Masashi Shinbara
 
SwiftとReactNativeで似たようなUIを作った際の記録
SwiftとReactNativeで似たようなUIを作った際の記録SwiftとReactNativeで似たようなUIを作った際の記録
SwiftとReactNativeで似たようなUIを作った際の記録
Fumiya Sakai
 
はじめて作ったアプリが10,000ダウンロード達成したから自慢する
はじめて作ったアプリが10,000ダウンロード達成したから自慢するはじめて作ったアプリが10,000ダウンロード達成したから自慢する
はじめて作ったアプリが10,000ダウンロード達成したから自慢する
Natsumi Oki
 
Trait とは? その使い道を考えてみる
Trait とは? その使い道を考えてみるTrait とは? その使い道を考えてみる
Trait とは? その使い道を考えてみる
Takuya Fujimura
 
20151205フルスクラッチcms作成のノウハウ With Laravel
20151205フルスクラッチcms作成のノウハウ With Laravel20151205フルスクラッチcms作成のノウハウ With Laravel
20151205フルスクラッチcms作成のノウハウ With Laravel
Takumi Yoshida
 
ビルドサーバで使うDocker
ビルドサーバで使うDockerビルドサーバで使うDocker
ビルドサーバで使うDocker
Masashi Shinbara
 
Laravelチュートリアルを作ってみた。
Laravelチュートリアルを作ってみた。Laravelチュートリアルを作ってみた。
Laravelチュートリアルを作ってみた。
Futoshi Endo
 
MySQL・PostgreSQLだけで作る高速でリッチな全文検索システム
MySQL・PostgreSQLだけで作る高速でリッチな全文検索システムMySQL・PostgreSQLだけで作る高速でリッチな全文検索システム
MySQL・PostgreSQLだけで作る高速でリッチな全文検索システム
Kouhei Sutou
 

Similar to レイヤードアーキテクチャを意識した PHPアプリケーションの構築 ver2 (15)

PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!
Shohei Okada
 
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterWTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniter
Masanori Oobayashi
 
Best practice laravel
Best practice laravelBest practice laravel
Best practice laravel
Risa Ohnishi
 
PHPフレームワーク入門
PHPフレームワーク入門PHPフレームワーク入門
PHPフレームワーク入門
Sho A
 
CodeIgniter入門
CodeIgniter入門CodeIgniter入門
CodeIgniter入門
Sho A
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装
infinite_loop
 
Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)
VOYAGE GROUP
 
10分でわかるFuelPHP @ 2011/12
10分でわかるFuelPHP @ 2011/1210分でわかるFuelPHP @ 2011/12
10分でわかるFuelPHP @ 2011/12
kenjis
 
第21回Creators MeetUp
第21回Creators MeetUp第21回Creators MeetUp
第21回Creators MeetUp
Kenichi Mukai
 
スマートフォン向けサービスにおけるサーバサイド設計入門
スマートフォン向けサービスにおけるサーバサイド設計入門スマートフォン向けサービスにおけるサーバサイド設計入門
スマートフォン向けサービスにおけるサーバサイド設計入門
Hisashi HATAKEYAMA
 
10分で分かる最近のCakePHP
10分で分かる最近のCakePHP10分で分かる最近のCakePHP
10分で分かる最近のCakePHP
Masashi Shinbara
 
10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya
 10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya 10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya
10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya
kenjis
 
CakePHP でお小遣い帳をつくってみよう
CakePHP でお小遣い帳をつくってみようCakePHP でお小遣い帳をつくってみよう
CakePHP でお小遣い帳をつくってみよう
Tomo Mizoe
 
PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!
Shohei Okada
 
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterWTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniter
Masanori Oobayashi
 
Best practice laravel
Best practice laravelBest practice laravel
Best practice laravel
Risa Ohnishi
 
PHPフレームワーク入門
PHPフレームワーク入門PHPフレームワーク入門
PHPフレームワーク入門
Sho A
 
CodeIgniter入門
CodeIgniter入門CodeIgniter入門
CodeIgniter入門
Sho A
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装
infinite_loop
 
Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)
VOYAGE GROUP
 
10分でわかるFuelPHP @ 2011/12
10分でわかるFuelPHP @ 2011/1210分でわかるFuelPHP @ 2011/12
10分でわかるFuelPHP @ 2011/12
kenjis
 
第21回Creators MeetUp
第21回Creators MeetUp第21回Creators MeetUp
第21回Creators MeetUp
Kenichi Mukai
 
スマートフォン向けサービスにおけるサーバサイド設計入門
スマートフォン向けサービスにおけるサーバサイド設計入門スマートフォン向けサービスにおけるサーバサイド設計入門
スマートフォン向けサービスにおけるサーバサイド設計入門
Hisashi HATAKEYAMA
 
10分で分かる最近のCakePHP
10分で分かる最近のCakePHP10分で分かる最近のCakePHP
10分で分かる最近のCakePHP
Masashi Shinbara
 
10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya
 10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya 10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya
10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya
kenjis
 
CakePHP でお小遣い帳をつくってみよう
CakePHP でお小遣い帳をつくってみようCakePHP でお小遣い帳をつくってみよう
CakePHP でお小遣い帳をつくってみよう
Tomo Mizoe
 

More from Masashi Shinbara (14)

日本語消えたスライド
日本語消えたスライド日本語消えたスライド
日本語消えたスライド
Masashi Shinbara
 
Twilio入門 -Web アプリ編-
Twilio入門 -Web アプリ編-Twilio入門 -Web アプリ編-
Twilio入門 -Web アプリ編-
Masashi Shinbara
 
いまどきのPHP開発現場 -2015年秋-
いまどきのPHP開発現場 -2015年秋-いまどきのPHP開発現場 -2015年秋-
いまどきのPHP開発現場 -2015年秋-
Masashi Shinbara
 
How to learn Laravel5 application from Authentication
How to learn Laravel5 application from AuthenticationHow to learn Laravel5 application from Authentication
How to learn Laravel5 application from Authentication
Masashi Shinbara
 
Ansible ではじめるサーバ作業の自動化
Ansible ではじめるサーバ作業の自動化Ansible ではじめるサーバ作業の自動化
Ansible ではじめるサーバ作業の自動化
Masashi Shinbara
 
VagrantユーザのためのDocker入門
VagrantユーザのためのDocker入門VagrantユーザのためのDocker入門
VagrantユーザのためのDocker入門
Masashi Shinbara
 
キャラ立ちしたエンジニアになる!
キャラ立ちしたエンジニアになる!キャラ立ちしたエンジニアになる!
キャラ立ちしたエンジニアになる!
Masashi Shinbara
 
Composer 再入門
Composer 再入門Composer 再入門
Composer 再入門
Masashi Shinbara
 
いまどきのPHP
いまどきのPHPいまどきのPHP
いまどきのPHP
Masashi Shinbara
 
もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境
もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境
もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境
Masashi Shinbara
 
Vagrant を Web開発環境に使う
Vagrant を Web開発環境に使うVagrant を Web開発環境に使う
Vagrant を Web開発環境に使う
Masashi Shinbara
 
PHP 5.5 Zend OPcache
PHP 5.5 Zend OPcachePHP 5.5 Zend OPcache
PHP 5.5 Zend OPcache
Masashi Shinbara
 
サーバの防災訓練
サーバの防災訓練サーバの防災訓練
サーバの防災訓練
Masashi Shinbara
 
Kansai PHP Users Group 2012年活動報告
Kansai PHP Users Group 2012年活動報告 Kansai PHP Users Group 2012年活動報告
Kansai PHP Users Group 2012年活動報告
Masashi Shinbara
 
日本語消えたスライド
日本語消えたスライド日本語消えたスライド
日本語消えたスライド
Masashi Shinbara
 
Twilio入門 -Web アプリ編-
Twilio入門 -Web アプリ編-Twilio入門 -Web アプリ編-
Twilio入門 -Web アプリ編-
Masashi Shinbara
 
いまどきのPHP開発現場 -2015年秋-
いまどきのPHP開発現場 -2015年秋-いまどきのPHP開発現場 -2015年秋-
いまどきのPHP開発現場 -2015年秋-
Masashi Shinbara
 
How to learn Laravel5 application from Authentication
How to learn Laravel5 application from AuthenticationHow to learn Laravel5 application from Authentication
How to learn Laravel5 application from Authentication
Masashi Shinbara
 
Ansible ではじめるサーバ作業の自動化
Ansible ではじめるサーバ作業の自動化Ansible ではじめるサーバ作業の自動化
Ansible ではじめるサーバ作業の自動化
Masashi Shinbara
 
VagrantユーザのためのDocker入門
VagrantユーザのためのDocker入門VagrantユーザのためのDocker入門
VagrantユーザのためのDocker入門
Masashi Shinbara
 
キャラ立ちしたエンジニアになる!
キャラ立ちしたエンジニアになる!キャラ立ちしたエンジニアになる!
キャラ立ちしたエンジニアになる!
Masashi Shinbara
 
もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境
もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境
もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境
Masashi Shinbara
 
Vagrant を Web開発環境に使う
Vagrant を Web開発環境に使うVagrant を Web開発環境に使う
Vagrant を Web開発環境に使う
Masashi Shinbara
 
Kansai PHP Users Group 2012年活動報告
Kansai PHP Users Group 2012年活動報告 Kansai PHP Users Group 2012年活動報告
Kansai PHP Users Group 2012年活動報告
Masashi Shinbara
 

レイヤードアーキテクチャを意識した PHPアプリケーションの構築 ver2

  • 4. レイヤードアーキテクチャ (c) 2015 Masashi Shinbara @shin1x1 • アプリケーションをレイヤ(層)に分割 • レイヤは自身の役割を担う • レイヤ間で協調して、処理を行う
  • 5. OSI参照モデル (c) 2015 Masashi Shinbara @shin1x1 7.Application 6. Presentation 5. Session 4.Transport 3. Network 2. Data link 1. Physical
  • 6. MVC (c) 2015 Masashi Shinbara @shin1x1 View Controller Model
  • 8. CakePHP (c) 2015 Masashi Shinbara @shin1x1 FatController -> FatModel
  • 9. Fat Model (c) 2015 Masashi Shinbara @shin1x1 • 1,000行を超える Model • Model の役割が多すぎる • DAO / バリデーション / ビジネスロジックなど
  • 10. MVC (c) 2015 Masashi Shinbara @shin1x1 View Controller Model
  • 11. MVC + Service (c) 2015 Masashi Shinbara @shin1x1 View Controller Model Service
  • 12. サービスレイヤを追加 (c) 2015 Masashi Shinbara @shin1x1 • Controller と Model の間のレイヤ • 主にビジネスロジックとバリデーションを担う • 1アクションメソッドに、1サービスクラス
  • 13. 結果 (c) 2015 Masashi Shinbara @shin1x1 • Fat(Controller¦Model) を、ある程度解消 • レイヤの責務があいまい
 => サービスが、セッションを操作等 • レイヤ間の処理の流れが統一できていない
 => サービスが、コントローラを操作等
  • 15. Laravel (c) 2015 Masashi Shinbara @shin1x1 • Laraval + AngularJS • Laravel は、REST API の提供のみ • UI 関連の処理は、AngularJS • レイヤの責務と流れを意識
  • 16. レイヤ構造 (c) 2015 Masashi Shinbara @shin1x1 Routing Controller Eloquent(ORM) Service
  • 17. レイヤの役割 (c) 2015 Masashi Shinbara @shin1x1 Routing Controller ORM Service ルーティング、認証、フィルタ HTTPリクエスト、レスポンス バリデーション、サービス実行 事前条件検証、ビジネスロジック データベースアクセス、 エンティティ固有の処理
  • 18. 例: 書籍の予約 (c) 2015 Masashi Shinbara @shin1x1 •会員制書籍予約Webアプリケーション •利用者は認証トークンが必要 •書籍の予約を行う •予約する書籍と予約数を指定
  • 19. 例: 書籍の予約 (c) 2015 Masashi Shinbara @shin1x1 •POST /reservation •X-Api-Token: ユーザ認証トークン •asin=書籍コード •quantity=予約数
  • 20. Routingレイヤ (c) 2015 Masashi Shinbara @shin1x1 •認証はミドルウェア(フィルタ)で実行
 => 未ログインなら、401を返す •URIからコントローラを実行
  • 21. Routing Route::group(['before' => 'api_auth'], function () {
 Route::post('/reservation', ReservationController::class.'@create');
 } ); 認証フィルタ
  • 22. Routing Route::group(['before' => 'api_auth'], function () {
 Route::post('/reservation', ReservationController::class.'@create');
 } ); URIとコントローラのマッピング
  • 23. Controllerレイヤ (c) 2015 Masashi Shinbara @shin1x1 •POSTパラメータ、セッションユーザ情報を取得 •バリデーション実行
 (asinとquantityパラメータの形式チェック) •サービスに必要なパラメータを渡して実行
 (HTTPの関心事はサービスに持ち込まない) •HTTPレスポンスを返す
  • 24. Controller public function create()
 {
 $validator = (new ReservationValidatorBuilder())->create(Input::all());
 if ($validator->fails()) {
 return $this->responseValidationError($validator->messages());
 }
 
 $reservation = $this->service->book($this->getUser(), Input::all());
 
 return $this->responseCreated($reservation);
 } バリデーション
  • 25. Controller public function create()
 {
 $validator = (new ReservationValidatorBuilder())->create(Input::all());
 if ($validator->fails()) {
 return $this->responseValidationError($validator->messages());
 }
 
 $reservation = $this->service->book($this->getUser(), Input::all());
 
 return $this->responseCreated($reservation);
 } サービスの実行
  • 26. Serviceレイヤ (c) 2015 Masashi Shinbara @shin1x1 •事前条件の検証
 (書籍の在庫が足りているか?等) •予約処理を実行 •DB操作をEloquent(ORM)で行う
  • 27. Service public function book(User $user, array $inputs)
 {
 $book = Book::where('asin', $inputs['asin'])->first();
 if (empty($book)) {
 throw new PreconditionException('book_not_found');
 }
 if ($book->inventory < $inputs['quantity']) {
 throw new PreconditionException('not_enough_book_inventory');
 }
 
 $reservation = new Reservation();
 DB::transaction(function () use ($user, $book, &$reservation, $inputs) {
 $affectedRows = $book->decrementInventory($inputs['quantity']);
 if ($affectedRows !== 1) {
 throw new PreconditionException('not_enough_book_inventory');
 }
 
 $reservation->user_id = $user->id;
 $reservation->book_id = $book->id;
 $reservation->quantity = $inputs['quantity'];
 $reservation->reservation_code = $reservation->generateReservationCode();
 $reservation->save();
 });
 
 return $reservation;
 }
  • 28. Service public function book(User $user, array $inputs)
 {
 $book = Book::where('asin', $inputs['asin'])->first();
 if (empty($book)) {
 throw new PreconditionException('book_not_found');
 }
 if ($book->inventory < $inputs['quantity']) {
 throw new PreconditionException('not_enough_book_inventory');
 }
 
 $reservation = new Reservation();
 DB::transaction(function () use ($user, $book, &$reservation, $inputs) {
 $affectedRows = $book->decrementInventory($inputs['quantity']);
 if ($affectedRows !== 1) {
 throw new PreconditionException('not_enough_book_inventory');
 }
 
 $reservation->user_id = $user->id;
 $reservation->book_id = $book->id;
 $reservation->quantity = $inputs['quantity'];
 $reservation->reservation_code = $reservation->generateReservationCode();
 $reservation->save();
 });
 
 return $reservation;
 } 必要なパラメータ
  • 29. Service public function book(User $user, array $inputs)
 {
 $book = Book::where('asin', $inputs['asin'])->first();
 if (empty($book)) {
 throw new PreconditionException('book_not_found');
 }
 if ($book->inventory < $inputs['quantity']) {
 throw new PreconditionException('not_enough_book_inventory');
 }
 
 $reservation = new Reservation();
 DB::transaction(function () use ($user, $book, &$reservation, $inputs) {
 $affectedRows = $book->decrementInventory($inputs['quantity']);
 if ($affectedRows !== 1) {
 throw new PreconditionException('not_enough_book_inventory');
 }
 
 $reservation->user_id = $user->id;
 $reservation->book_id = $book->id;
 $reservation->quantity = $inputs['quantity'];
 $reservation->reservation_code = $reservation->generateReservationCode();
 $reservation->save();
 });
 
 return $reservation;
 } 事前条件の検証
  • 30. Service public function book(User $user, array $inputs)
 {
 $book = Book::where('asin', $inputs['asin'])->first();
 if (empty($book)) {
 throw new PreconditionException('book_not_found');
 }
 if ($book->inventory < $inputs['quantity']) {
 throw new PreconditionException('not_enough_book_inventory');
 }
 
 $reservation = new Reservation();
 DB::transaction(function () use ($user, $book, &$reservation, $inputs) {
 $affectedRows = $book->decrementInventory($inputs['quantity']);
 if ($affectedRows !== 1) {
 throw new PreconditionException('not_enough_book_inventory');
 }
 
 $reservation->user_id = $user->id;
 $reservation->book_id = $book->id;
 $reservation->quantity = $inputs['quantity'];
 $reservation->reservation_code = $reservation->generateReservationCode();
 $reservation->save();
 });
 
 return $reservation;
 } ビジネスロジックの実行
  • 31. レイヤの依存、処理の流れ (c) 2015 Masashi Shinbara @shin1x1 Routing Controller Eloquent(ORM) Service
  • 32. レイヤの依存、処理の流れ (c) 2015 Masashi Shinbara @shin1x1 Routing Controller Eloquent(ORM) Service 流れを一方向に固定
  • 33. レイヤをまたぐのはアリ (c) 2015 Masashi Shinbara @shin1x1 Routing Controller Eloquent(ORM) Service
  • 34. レイヤをまたぐのはアリ (c) 2015 Masashi Shinbara @shin1x1 Routing Controller Eloquent(ORM) Service 方向は変えない
  • 35. サービスを中心に考える (c) 2015 Masashi Shinbara @shin1x1 •サービス(ビジネスドメイン)が中心 •事前条件検証とビジネスロジック •HTTPの関心事は持ち込まない
 必要なもの引数で渡す(scalar, array, object)
  • 36. ドメインをサービスで表現 (c) 2015 Masashi Shinbara @shin1x1 •クラス名、メソッド名はドメインの用語で
 (ユビキタス言語) •ユースケースをメソッドに実装 •ex) 書籍の予約
 => ReservationService#book()
  • 37. サービスから作る (c) 2015 Masashi Shinbara @shin1x1 • サービスとテストを先に実装 • サービスの最初の利用者は、テスト • サービスを Web に結ぶのが、コントローラ • サービスは、バッチ処理等からも使える
  • 38. ドメインごとに名前空間を分ける [package] + [AcmeReservation] + [Controller] + [Service] + [Model] + [Validation] [AcmeUser] + [Controller] + [Service] + [Model] + [Validation] PSR-4
  • 39. 結果 (c) 2015 Masashi Shinbara @shin1x1 • レイヤの役割に専念できる • 流れが一方向なので、依存の方向が明確に • サービス(ドメイン)に集中 • サービスをどう分割していくかが課題
  • 40. まとめ (c) 2015 Masashi Shinbara @shin1x1 • MVCだけじゃない • 自分でレイヤ構造を考える • レイヤの責務と処理の流れ
  • 41. @shin1x1 (c) 2015 Masashi Shinbara @shin1x1