Skip to content

Commit 4706d41

Browse files
committed
wip: [best practice] add business-logic.rst
1 parent a0b46f6 commit 4706d41

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

best_practices/business-logic.rst

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
アプリケーションのビジネスロジックを整理する
2+
==============================================
3+
4+
コンピュータのソフトウェアの世界では、データがどのように作られ、表示され、保存され、変更されるかを決める
5+
現実世界のビジネスルールを実装したプログラムを **ビジネスロジック** あるいはドメインロジックと呼びます。
6+
( `full definition`_ を読む)
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 is an HTTP framework that only cares about generating an HTTP response
156+
for each HTTP request. That's why Symfony doesn't provide a way to talk to
157+
a persistence layer (e.g. database, external API). You can choose whatever
158+
library of strategy you want for this.
159+
160+
In practice, many Symfony applications rely on the independent
161+
`Doctrine project`_ to define their model using entities and repositories.
162+
Just like with business logic, we recommend storing Doctrine entities in
163+
the ``AppBundle``
164+
165+
The three entities defined by our sample blog application are a good example:
166+
167+
.. code-block:: text
168+
169+
symfony2-project/
170+
├─ ...
171+
└─ src/
172+
└─ AppBundle/
173+
└─ Entity/
174+
├─ Comment.php
175+
├─ Post.php
176+
└─ User.php
177+
178+
.. tip::
179+
180+
If you're more advanced, you can of course store them under your own
181+
namespace in ``src/``.
182+
183+
Doctrine Mapping Information
184+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
185+
186+
Doctrine Entities are plain PHP objects that you store in some "database".
187+
Doctrine only knows about your entities through the mapping metadata configured
188+
for your model classes. Doctrine supports four metadata formats: YAML, XML,
189+
PHP and annotations.
190+
191+
.. best-practice::
192+
193+
Use annotations to define the mapping information of the Doctrine entities.
194+
195+
Annotations are by far the most convenient and agile way of setting up and
196+
looking for mapping information:
197+
198+
.. code-block:: php
199+
200+
namespace AppBundle\Entity;
201+
202+
use Doctrine\ORM\Mapping as ORM;
203+
use Doctrine\Common\Collections\ArrayCollection;
204+
205+
/**
206+
* @ORM\Entity
207+
*/
208+
class Post
209+
{
210+
const NUM_ITEMS = 10;
211+
212+
/**
213+
* @ORM\Id
214+
* @ORM\GeneratedValue
215+
* @ORM\Column(type="integer")
216+
*/
217+
private $id;
218+
219+
/**
220+
* @ORM\Column(type="string")
221+
*/
222+
private $title;
223+
224+
/**
225+
* @ORM\Column(type="string")
226+
*/
227+
private $slug;
228+
229+
/**
230+
* @ORM\Column(type="text")
231+
*/
232+
private $content;
233+
234+
/**
235+
* @ORM\Column(type="string")
236+
*/
237+
private $authorEmail;
238+
239+
/**
240+
* @ORM\Column(type="datetime")
241+
*/
242+
private $publishedAt;
243+
244+
/**
245+
* @ORM\OneToMany(
246+
* targetEntity="Comment",
247+
* mappedBy="post",
248+
* orphanRemoval=true
249+
* )
250+
* @ORM\OrderBy({"publishedAt" = "ASC"})
251+
*/
252+
private $comments;
253+
254+
public function __construct()
255+
{
256+
$this->publishedAt = new \DateTime();
257+
$this->comments = new ArrayCollection();
258+
}
259+
260+
// getters and setters ...
261+
}
262+
263+
All formats have the same performance, so this is once again ultimately a
264+
matter of taste.
265+
266+
Data Fixtures
267+
~~~~~~~~~~~~~
268+
269+
As fixtures support is not enabled by default in Symfony, you should execute
270+
the following command to install the Doctrine fixtures bundle:
271+
272+
.. code-block:: bash
273+
274+
$ composer require "doctrine/doctrine-fixtures-bundle"
275+
276+
Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and
277+
``test`` environments:
278+
279+
.. code-block:: php
280+
281+
use Symfony\Component\HttpKernel\Kernel;
282+
283+
class AppKernel extends Kernel
284+
{
285+
public function registerBundles()
286+
{
287+
$bundles = array(
288+
// ...
289+
);
290+
291+
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
292+
// ...
293+
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
294+
}
295+
296+
return $bundles;
297+
}
298+
299+
// ...
300+
}
301+
302+
We recommend creating just *one* `fixture class`_ for simplicity, though
303+
you're welcome to have more if that class gets quite large.
304+
305+
Assuming you have at least one fixtures class and that the database access
306+
is configured properly, you can load your fixtures by executing the following
307+
command:
308+
309+
.. code-block:: bash
310+
311+
$ php app/console doctrine:fixtures:load
312+
313+
Careful, database will be purged. Do you want to continue Y/N ? Y
314+
> purging database
315+
> loading AppBundle\DataFixtures\ORM\LoadFixtures
316+
317+
Coding Standards
318+
----------------
319+
320+
The Symfony source code follows the `PSR-1`_ and `PSR-2`_ coding standards that
321+
were defined by the PHP community. You can learn more about
322+
`the Symfony Code Standards`_ and even use the `PHP-CS-Fixer`_, which is
323+
a command-line utility that can fix the coding standards of an entire codebase
324+
in a matter of seconds.
325+
326+
.. _`full definition`: http://en.wikipedia.org/wiki/Business_logic
327+
.. _`Toran Proxy`: https://toranproxy.com/
328+
.. _`Composer`: https://getcomposer.org/
329+
.. _`MVC architecture`: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
330+
.. _`Doctrine project`: http://www.doctrine-project.org/
331+
.. _`fixture class`: http://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
332+
.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/
333+
.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/
334+
.. _`the Symfony Code Standards`: http://symfony.com/doc/current/contributing/code/standards.html
335+
.. _`PHP-CS-Fixer`: https://github.com/fabpot/PHP-CS-Fixer

0 commit comments

Comments
 (0)