Skip to content

Commit cca44be

Browse files
committed
[best practice] add business-logic.rst
1 parent 4ca4292 commit cca44be

File tree

1 file changed

+328
-0
lines changed

1 file changed

+328
-0
lines changed

best_practices/business-logic.rst

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
アプリケーションのビジネスロジックを整理する
2+
==============================================
3+
4+
コンピュータのソフトウェアの世界では、データがどのように作られ、表示され、保存され、変更されるかを決める
5+
現実世界のビジネスルールを実装したプログラムを **ビジネスロジック** あるいはドメインロジックと呼びます。
6+
( `完全な定義`_ を読む)
7+
8+
Symfony のアプリケーションでは、ビジネスロジックは、フレームワーク(例えばルーティングやコントローラの
9+
ような)に依存せず、自由に書くことができます。
10+
サービスとして使われる、ドメインのクラス・ Doctrine のエンティティ・通常の PHP のクラスは、ビジネスロジック
11+
の一例です。
12+
13+
ほとんどのプロジェクトにおいて、開発者は全てを ``AppBundle`` に置くべきです。AppBundleの中では、ビジネス
14+
ロジックを整理するための、どんなディレクトリでも作ることができます。
15+
16+
.. code-block:: text
17+
18+
symfoy2-project/
19+
├─ app/
20+
├─ src/
21+
│ └─ AppBundle/
22+
│ └─ Utils/
23+
│ └─ MyClass.php
24+
├─ vendor/
25+
└─ web/
26+
27+
クラスをバンドルの外に置く
28+
--------------------------------------
29+
30+
しかし、ビジネスロジックをバンドルの中に入れておく技術的な理由は何もありません。 ``src`` ディレクトリの
31+
下に好きな名前空間を定義して、クラスをそこに置くこともできます。
32+
33+
.. code-block:: text
34+
35+
symfoy2-project/
36+
├─ app/
37+
├─ src/
38+
│ ├─ Acme/
39+
│ │ └─ Utils/
40+
│ │ └─ MyClass.php
41+
│ └─ AppBundle/
42+
├─ vendor/
43+
└─ web/
44+
45+
.. tip::
46+
47+
``AppBundle`` を使うことをお勧めするのは簡単さのためです。バンドルの中に必要なものと
48+
バンドルの外でも問題ないものがよく理解できている場合は、クラスを ``AppBundle`` の外に
49+
置いても構いません。
50+
51+
サービス:名付けと形式
52+
---------------------------
53+
54+
ブログアプリケーションに、 "Hello World" のような投稿タイトルを "hello-world" のようなスラグに変換する
55+
ユーティリティが必要だとします。スラグは、投稿のURLの一部として使われます。
56+
57+
新しい ``Slugger`` クラスを ``src/AppBundle/Utils/`` ディレクトリの配下に作り、下記のような ``slugify()``
58+
メソッドを実装してみましょう。
59+
60+
.. code-block:: php
61+
62+
// src/AppBundle/Utils/Slugger.php
63+
namespace AppBundle\Utils;
64+
65+
class Slugger
66+
{
67+
public function slugify($string)
68+
{
69+
return preg_replace(
70+
'/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string)))
71+
);
72+
}
73+
}
74+
75+
次に、このクラスのための新しいサービスを定義します。
76+
77+
.. code-block:: yaml
78+
79+
# app/config/services.yml
80+
services:
81+
# サービス名は短くしましょう
82+
slugger:
83+
class: AppBundle\Utils\Slugger
84+
85+
伝統的に、サービスの名前は、衝突を避けるためにクラス名とクラスの場所を組み合わせたものでした。
86+
そうすると、このサービスは ``app.utils.slugger`` と呼ばれる *はず* です。しかし、短い名前を使うことで、
87+
コードの読みやすさと使いやすさは向上するでしょう。
88+
89+
.. best-practice::
90+
91+
アプリケーションのサービス名は可能な限り短くしましょう。一単語になるのが理想的です。
92+
93+
これで ``AdminController`` のようなどんなコントローラーからでも slugger を利用できるようになりました。
94+
95+
.. code-block:: php
96+
97+
public function createAction(Request $request)
98+
{
99+
// ...
100+
101+
if ($form->isSubmitted() && $form->isValid()) {
102+
$slug = $this->get('slugger')->slugify($post->getTitle()));
103+
$post->setSlug($slug);
104+
105+
// ...
106+
}
107+
}
108+
109+
サービス定義:YAML形式
110+
--------------------------
111+
112+
前のセクションでは、 YAML がサービスを定義するのに使われていました。
113+
114+
.. best-practice::
115+
116+
サービスを定義するときは YAML 形式を使いましょう。
117+
118+
これには異論があるでしょうが、経験上、開発者の間では YAML と XML が半々で使われており、ほんの少し YAML
119+
のほうが好まれています。
120+
どちらの形式も機能は同じなので、どこまでも個人の好みの問題です。
121+
122+
新人にもわかりやすく、シンプルな YAML をお勧めしますが、好きな形式を使って構いません。
123+
124+
サービス定義:クラス名をパラメータにしない
125+
-------------------------------------------
126+
127+
前の例で、サービスを定義するとき、クラス名をパラメータとして定義していないことにお気付きかもしれません。
128+
129+
.. code-block:: yaml
130+
131+
# app/config/services.yml
132+
133+
# クラス名をパラメータにしてサービスを定義
134+
parameters:
135+
slugger.class: AppBundle\Utils\Slugger
136+
137+
services:
138+
slugger:
139+
class: "%slugger.class%"
140+
141+
この使い方は煩雑で、アプリケーションのサービスには全く必要ありません。
142+
143+
.. best-practice::
144+
145+
アプリケーションのサービスクラス名をパラメータとして定義するのは止めましょう。
146+
147+
この使い方はサードパーティのバンドルから誤って取り入れられたものです。 Symfony がサービスコンテナ機能を
148+
実装したとき、開発者の中にはこのテクニックによってサービスを上書きできるようにした人もいました。
149+
しかし、クラス名を変更しただけでサービスを上書きするのは非常に稀なユースケースです。というのも、大抵の場合、
150+
新しいサービスには、上書きされるサービスとは違うコンストラクタ引数があるからです。
151+
152+
永続化レイヤーを利用する
153+
-------------------------
154+
155+
Symfony は、 各 HTTP リクエストに対する HTTP レスポンスを作ることだけを担当する HTTP のフレームワークです。
156+
そのため、 Symfony は永続化レイヤー(データベースや外部API)にアクセスする方法を提供していません。開発者は
157+
好きなライブラリやデータ保存方法を選ぶことができます。
158+
159+
実際には、 Symfony アプリケーションの多くは `Doctrine`_ に依存しており、エンティティやレポジトリを
160+
使ってモデルを定義しています。
161+
ビジネスロジックと同じく、 Doctrine のエンティティも ``AppBundle`` に配置すると良いでしょう。
162+
163+
一例として、サンプルのブログアプリケーションで定義した3つのエンティティがあります。
164+
165+
.. code-block:: text
166+
167+
symfony2-project/
168+
├─ ...
169+
└─ src/
170+
└─ AppBundle/
171+
└─ Entity/
172+
├─ Comment.php
173+
├─ Post.php
174+
└─ User.php
175+
176+
.. tip::
177+
178+
もちろん、独自の名前空間で ``src/`` の直下にエンティティを置くこともできます。
179+
180+
Doctrine のマッピング
181+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
182+
183+
Doctrine のエンティティは、データベースに保存することができるプレーンな PHP オブジェクトです。
184+
Doctrine は、クラスに対して定義されたマッピングメタデータによってエンティティを扱います。マッピングメタデータ
185+
を定義するには YAML, XML, PHP, アノテーション形式が利用できます。
186+
187+
.. best-practice::
188+
189+
Doctrine エンティティのマッピングにはアノテーションを使いましょう。
190+
191+
アノテーションは、マッピングを定義したり探したりするのに、現在のところ最も便利で素早く使える形式です。
192+
193+
.. code-block:: php
194+
195+
namespace AppBundle\Entity;
196+
197+
use Doctrine\ORM\Mapping as ORM;
198+
use Doctrine\Common\Collections\ArrayCollection;
199+
200+
/**
201+
* @ORM\Entity
202+
*/
203+
class Post
204+
{
205+
const NUM_ITEMS = 10;
206+
207+
/**
208+
* @ORM\Id
209+
* @ORM\GeneratedValue
210+
* @ORM\Column(type="integer")
211+
*/
212+
private $id;
213+
214+
/**
215+
* @ORM\Column(type="string")
216+
*/
217+
private $title;
218+
219+
/**
220+
* @ORM\Column(type="string")
221+
*/
222+
private $slug;
223+
224+
/**
225+
* @ORM\Column(type="text")
226+
*/
227+
private $content;
228+
229+
/**
230+
* @ORM\Column(type="string")
231+
*/
232+
private $authorEmail;
233+
234+
/**
235+
* @ORM\Column(type="datetime")
236+
*/
237+
private $publishedAt;
238+
239+
/**
240+
* @ORM\OneToMany(
241+
* targetEntity="Comment",
242+
* mappedBy="post",
243+
* orphanRemoval=true
244+
* )
245+
* @ORM\OrderBy({"publishedAt" = "ASC"})
246+
*/
247+
private $comments;
248+
249+
public function __construct()
250+
{
251+
$this->publishedAt = new \DateTime();
252+
$this->comments = new ArrayCollection();
253+
}
254+
255+
// getters and setters ...
256+
}
257+
258+
全てのメタデータ定義形式に同じ機能があり、何度も書いたようにどの形式を使うかは開発者の自由です。
259+
260+
データフィクスチャー
261+
~~~~~~~~~~~~~~~~~~~~~~~
262+
263+
Symfony にはデフォルトではデータフィクスチャー機能は存在しないため、フィクスチャーを扱うためには下記のコマンドを
264+
実行して DoctrineFixturesBundle をインストールする必要があります。
265+
266+
.. code-block:: bash
267+
268+
$ composer require "doctrine/doctrine-fixtures-bundle"
269+
270+
バンドルを ``AppKernel.php`` で有効化します。その際、 ``dev`` 環境と ``test`` 環境だけにしてください。
271+
272+
.. code-block:: php
273+
274+
use Symfony\Component\HttpKernel\Kernel;
275+
276+
class AppKernel extends Kernel
277+
{
278+
public function registerBundles()
279+
{
280+
$bundles = array(
281+
// ...
282+
);
283+
284+
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
285+
// ...
286+
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
287+
}
288+
289+
return $bundles;
290+
}
291+
292+
// ...
293+
}
294+
295+
296+
単純さを保つために、 `フィクスチャークラス`_ は *1つ* だけ作ることをおすすめします。クラスが大きくなりすぎる
297+
場合はもっとたくさんのフィクスチャークラスを作っても構いません。
298+
299+
少なくとも1つのフィクスチャークラスがあり、データベースへのログイン情報が正しく設定されているのを確認したら、
300+
下記のコマンドを実行するとフィクスチャーを読み込ませることができます。
301+
302+
.. code-block:: bash
303+
304+
$ php app/console doctrine:fixtures:load
305+
306+
Careful, database will be purged. Do you want to continue Y/N ? Y
307+
> purging database
308+
> loading AppBundle\DataFixtures\ORM\LoadFixtures
309+
310+
311+
コーディング規約
312+
------------------
313+
314+
Symfonh のソースコードは、 PHP コミュニティで定められた `PSR-1`_ と `PRS-2`_ のコーディング規約に
315+
従っています。詳しくは `Symfonyのコーディング規約`_ を読んでください。
316+
または、`PHP-CS-Fixer`_ コマンドを使うこともできます。PHP-CS-Fixerはコードベース全体のコーディング規約を
317+
ほんのの数秒で修正することができるコマンドラインツールです。
318+
319+
.. _`full definition`: http://en.wikipedia.org/wiki/Business_logic
320+
.. _`Toran Proxy`: https://toranproxy.com/
321+
.. _`Composer`: https://getcomposer.org/
322+
.. _`MVCアーキテクチャ`: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
323+
.. _`Doctrine`: http://www.doctrine-project.org/
324+
.. _`フィクスチャークラス`: http://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
325+
.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/
326+
.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/
327+
.. _`Symfonyのコーディング規約`: http://symfony.com/doc/current/contributing/code/standards.html
328+
.. _`PHP-CS-Fixer`: https://github.com/fabpot/PHP-CS-Fixer

0 commit comments

Comments
 (0)