Skip to content

Commit 14a131c

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

File tree

1 file changed

+341
-0
lines changed

1 file changed

+341
-0
lines changed

best_practices/business-logic.rst

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
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+
The name of your application's services should be as short as possible,
92+
ideally just one simple word.
93+
94+
Now you can use the custom slugger in any controller class, such as the
95+
``AdminController``:
96+
97+
.. code-block:: php
98+
99+
public function createAction(Request $request)
100+
{
101+
// ...
102+
103+
if ($form->isSubmitted() && $form->isValid()) {
104+
$slug = $this->get('slugger')->slugify($post->getTitle()));
105+
$post->setSlug($slug);
106+
107+
// ...
108+
}
109+
}
110+
111+
Service Format: YAML
112+
--------------------
113+
114+
In the previous section, YAML was used to define the service.
115+
116+
.. best-practice::
117+
118+
Use the YAML format to define your own services.
119+
120+
This is controversial, and in our experience, YAML and XML usage is evenly
121+
distributed among developers, with a slight preference towards YAML.
122+
Both formats have the same performance, so this is ultimately a matter of
123+
personal taste.
124+
125+
We recommend YAML because it's friendly to newcomers and concise. You can
126+
of course use whatever format you like.
127+
128+
Service: No Class Parameter
129+
---------------------------
130+
131+
You may have noticed that the previous service definition doesn't configure
132+
the class namespace as a parameter:
133+
134+
.. code-block:: yaml
135+
136+
# app/config/services.yml
137+
138+
# service definition with class namespace as parameter
139+
parameters:
140+
slugger.class: AppBundle\Utils\Slugger
141+
142+
services:
143+
slugger:
144+
class: "%slugger.class%"
145+
146+
This practice is cumbersome and completely unnecessary for your own services:
147+
148+
.. best-practice::
149+
150+
Don't define parameters for the classes of your services.
151+
152+
This practice was wrongly adopted from third-party bundles. When Symfony
153+
introduced its service container, some developers used this technique to easily
154+
allow overriding services. However, overriding a service by just changing its
155+
class name is a very rare use case because, frequently, the new service has
156+
different constructor arguments.
157+
158+
Using a Persistence Layer
159+
-------------------------
160+
161+
Symfony is an HTTP framework that only cares about generating an HTTP response
162+
for each HTTP request. That's why Symfony doesn't provide a way to talk to
163+
a persistence layer (e.g. database, external API). You can choose whatever
164+
library of strategy you want for this.
165+
166+
In practice, many Symfony applications rely on the independent
167+
`Doctrine project`_ to define their model using entities and repositories.
168+
Just like with business logic, we recommend storing Doctrine entities in
169+
the ``AppBundle``
170+
171+
The three entities defined by our sample blog application are a good example:
172+
173+
.. code-block:: text
174+
175+
symfony2-project/
176+
├─ ...
177+
└─ src/
178+
└─ AppBundle/
179+
└─ Entity/
180+
├─ Comment.php
181+
├─ Post.php
182+
└─ User.php
183+
184+
.. tip::
185+
186+
If you're more advanced, you can of course store them under your own
187+
namespace in ``src/``.
188+
189+
Doctrine Mapping Information
190+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191+
192+
Doctrine Entities are plain PHP objects that you store in some "database".
193+
Doctrine only knows about your entities through the mapping metadata configured
194+
for your model classes. Doctrine supports four metadata formats: YAML, XML,
195+
PHP and annotations.
196+
197+
.. best-practice::
198+
199+
Use annotations to define the mapping information of the Doctrine entities.
200+
201+
Annotations are by far the most convenient and agile way of setting up and
202+
looking for mapping information:
203+
204+
.. code-block:: php
205+
206+
namespace AppBundle\Entity;
207+
208+
use Doctrine\ORM\Mapping as ORM;
209+
use Doctrine\Common\Collections\ArrayCollection;
210+
211+
/**
212+
* @ORM\Entity
213+
*/
214+
class Post
215+
{
216+
const NUM_ITEMS = 10;
217+
218+
/**
219+
* @ORM\Id
220+
* @ORM\GeneratedValue
221+
* @ORM\Column(type="integer")
222+
*/
223+
private $id;
224+
225+
/**
226+
* @ORM\Column(type="string")
227+
*/
228+
private $title;
229+
230+
/**
231+
* @ORM\Column(type="string")
232+
*/
233+
private $slug;
234+
235+
/**
236+
* @ORM\Column(type="text")
237+
*/
238+
private $content;
239+
240+
/**
241+
* @ORM\Column(type="string")
242+
*/
243+
private $authorEmail;
244+
245+
/**
246+
* @ORM\Column(type="datetime")
247+
*/
248+
private $publishedAt;
249+
250+
/**
251+
* @ORM\OneToMany(
252+
* targetEntity="Comment",
253+
* mappedBy="post",
254+
* orphanRemoval=true
255+
* )
256+
* @ORM\OrderBy({"publishedAt" = "ASC"})
257+
*/
258+
private $comments;
259+
260+
public function __construct()
261+
{
262+
$this->publishedAt = new \DateTime();
263+
$this->comments = new ArrayCollection();
264+
}
265+
266+
// getters and setters ...
267+
}
268+
269+
All formats have the same performance, so this is once again ultimately a
270+
matter of taste.
271+
272+
Data Fixtures
273+
~~~~~~~~~~~~~
274+
275+
As fixtures support is not enabled by default in Symfony, you should execute
276+
the following command to install the Doctrine fixtures bundle:
277+
278+
.. code-block:: bash
279+
280+
$ composer require "doctrine/doctrine-fixtures-bundle"
281+
282+
Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and
283+
``test`` environments:
284+
285+
.. code-block:: php
286+
287+
use Symfony\Component\HttpKernel\Kernel;
288+
289+
class AppKernel extends Kernel
290+
{
291+
public function registerBundles()
292+
{
293+
$bundles = array(
294+
// ...
295+
);
296+
297+
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
298+
// ...
299+
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
300+
}
301+
302+
return $bundles;
303+
}
304+
305+
// ...
306+
}
307+
308+
We recommend creating just *one* `fixture class`_ for simplicity, though
309+
you're welcome to have more if that class gets quite large.
310+
311+
Assuming you have at least one fixtures class and that the database access
312+
is configured properly, you can load your fixtures by executing the following
313+
command:
314+
315+
.. code-block:: bash
316+
317+
$ php app/console doctrine:fixtures:load
318+
319+
Careful, database will be purged. Do you want to continue Y/N ? Y
320+
> purging database
321+
> loading AppBundle\DataFixtures\ORM\LoadFixtures
322+
323+
Coding Standards
324+
----------------
325+
326+
The Symfony source code follows the `PSR-1`_ and `PSR-2`_ coding standards that
327+
were defined by the PHP community. You can learn more about
328+
`the Symfony Code Standards`_ and even use the `PHP-CS-Fixer`_, which is
329+
a command-line utility that can fix the coding standards of an entire codebase
330+
in a matter of seconds.
331+
332+
.. _`full definition`: http://en.wikipedia.org/wiki/Business_logic
333+
.. _`Toran Proxy`: https://toranproxy.com/
334+
.. _`Composer`: https://getcomposer.org/
335+
.. _`MVC architecture`: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
336+
.. _`Doctrine project`: http://www.doctrine-project.org/
337+
.. _`fixture class`: http://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
338+
.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/
339+
.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/
340+
.. _`the Symfony Code Standards`: http://symfony.com/doc/current/contributing/code/standards.html
341+
.. _`PHP-CS-Fixer`: https://github.com/fabpot/PHP-CS-Fixer

0 commit comments

Comments
 (0)