The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xml:lang="ja-JP">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <link rel="stylesheet" type="text/css" href="style.css" />
  <title>policy 設計方針</title>
  <link rel="index"     href="./" />
  <link rel="up"        href="../" />
</head>
<body>

<div class="pod_title_block">
policy 設計方針


</div>

<!-- Begin TABLE_OF_CONTENTS -->
<div class="pod_toc">
<p>
<strong><span class="pod_lang">TABLE OF CONTENTS</span></strong>
</p>
<ul>
<li><a href="#NAME">
<span class="pod_lang">NAME

</span></a>
</li>
<li><a href="#DESCRIPTION">
<span class="pod_lang">DESCRIPTION

</span></a>
<ul>
<li><a href="#Xe9X96Xa2Xe6X95Xb0Xe3X81XaeXe5X88X86Xe3X81X91Xe6X96Xb9">
<span class="pod_lang">関数の分け方

</span></a>
</li>
<li><a href="#SQLXe3X82X92Xe8Xa8X98Xe8XbfXb0Xe3X81X99Xe3X82X8bXe5Xa0Xb4Xe6X89X80">
<span class="pod_lang">SQLを記述する場所

</span></a>
</li>
<li><a href="#DBXe3X81XaeXe3X83X88Xe3X83Xa9Xe3X83Xb3Xe3X82Xb6Xe3X82XafXe3X82Xb7Xe3X83Xa7Xe3X83Xb3">
<span class="pod_lang">DBのトランザクション

</span></a>
</li>
<li><a href="#Xe3X82XbbXe3X83X83Xe3X82Xb7Xe3X83Xa7Xe3X83Xb3Xe7XaeXa1Xe7X90X86">
<span class="pod_lang">セッション管理

</span></a>
</li>
<li><a href="#CGIXe5Xa4X89Xe6X95Xb0">
<span class="pod_lang">CGI変数

</span></a>
</li>
</ul>
</li>
<li><a href="#AUTHOR_INFORMATION">
<span class="pod_lang">AUTHOR INFORMATION

</span></a>
</li>
</ul>
</div>
<!-- End TABLE_OF_CONTENTS -->

<!-- Begin CONTENT -->
<!-- =encoding utf-8 -->

<hr />
<h1><a id="NAME">
<span class="pod_lang">NAME

</span></a></h1>

<p>
<span class="pod_lang">policy 設計方針

</span>
</p>


<hr />
<h1><a id="DESCRIPTION">
<span class="pod_lang">DESCRIPTION

</span></a></h1>

<p>
<span class="pod_lang">このドキュメントは、TripletaiL を利用する際の設計の推奨方針について記述します。

</span>
</p>

<p>
<span class="pod_lang">TripletaiL を利用する際に、必ずしもこの方針を採用する必要はありませんが、
採用が可能であれば検討して頂ければと思います。

</span>
</p>

<h2><a id="Xe9X96Xa2Xe6X95Xb0Xe3X81XaeXe5X88X86Xe3X81X91Xe6X96Xb9">
<span class="pod_lang">関数の分け方

</span></a></h2>

<p>
<span class="pod_lang">以下の考え方に沿って大まかに3種類に関数を分けます。

</span>
</p>

<ol>
<li>
<p>
<span class="pod_lang">DoXXX リクエストを受け付けて処理する部分

</span>
</p>

</li>
<li>
<p>
<span class="pod_lang">DispXXX 利用者に(次の)画面を返す部分

</span>
</p>

</li>
<li>
<p>
<span class="pod_lang">1や2から呼び出される処理部分

</span>
</p>

</li>
</ol>

<p>
<span class="pod_lang">例えば、アンケートに記入してフォームをsubmitした場合、submitしたデータの内容をチェックし、DBに保存する部分が1に相当します。
その後、アンケート回答ありがとうございました、などのHTMLを返す部分が2に相当します。

</span>
</p>

<p>
<span class="pod_lang">TripletaiL では、1を DoXXX という名前、2 を DispXXX という名前で書くことを推奨しています。
DoXXX という名前にしておくと、$TL-&gt;dispatch でそれぞれの関数に分岐させることが出来ます。

</span>
</p>

<p>
<span class="pod_lang">1及び2の一部処理を別関数に切り出した部分は、3になります。
3に関しては Do/Disp 以外で始まる名前を付けます。

</span>
</p>

<p>
<span class="pod_lang">例えば、フォーム内容をチェックするメソッドであれば CheckQuestionForm、
DBに保存する部分なら SaveQuestionForm 等です。

</span>
</p>

<pre class="pod_verbatim"><code> #Submitを押されたときの処理を実行する関数
 sub DoSubmit {
   #フォームをチェックする関数
   if(&amp;CheckQuestionForm) {
     #フォームのデータを保存する関数
     &amp;SaveQuestionForm;
     #ありがとうページを表示する関数
     &amp;DispThanksPage;
   } else {
     #エラーページを表示する関数
     &amp;DispErrorPage;
   }
 }</code></pre>

<p>
<span class="pod_lang">表示のみをする場合もDoを利用し、表示の関数は別に記述する事を推奨しています。

</span>
</p>

<pre class="pod_verbatim"><code> sub DoTop {
   &amp;DispTopPage;
 }</code></pre>

<p>
<span class="pod_lang">こうすることにより、コメントをDo関数内に記述することにより流れが分かりやすくなります。

</span>
</p>

<p>
<span class="pod_lang">フォームを表示し、submitすると確認画面を表示し、更にsubmitするとDBに保存し完了画面を表示するようなプログラムの場合は、
以下のようなコードになります。

</span>
</p>

<p>
<span class="pod_lang">dispatch でフォームの特定の値を使って、処理を分岐させます。
テンプレートは DispXxx関数の中で表示し、次の分岐先を addHiddenForm で指定します。

</span>
</p>

<pre class="pod_verbatim"><code> sub main {
   $TL-&gt;dispatch( $CGI-&gt;get('Command'),
     default =&gt; 'Form',
     onerror =&gt; \&amp;DoError,
   );
 }
 
 sub DoForm {
   &amp;DispForm();
 }
 
 sub DispForm { # フォームを表示
   my $error = shift; # エラーの場合はエラー内容
   
   my $t = $TL-&gt;newTemplate('form.html');
   if($error) {
     # $errorがあればエラー内容を画面に追加表示し
     # 入力されたフォームデータを戻す
     $t-&gt;node('error')-&gt;add({ ERROR =&gt; $error });
     $t-&gt;setForm($CGI);
   }
   $t-&gt;addHiddenForm({ Command =&gt; 'Confirm' });
   $t-&gt;flush;
 }
 
 sub DoConfirm {
   my $error = &amp;CheckQuestionForm; # 入力内容をチェック
   if($error) {
     &amp;DispForm($error); # エラーメッセージと共にフォームを再表示
   } else {
     &amp;DispConfirm;
   }
 }
 
 sub DispConfirm {
   my $t = $TL-&gt;newTemplate('confirm.html');
   # 入力内容を展開して再表示
   $t-&gt;expand({
     name =&gt; $CGI-&gt;get('name'),
     answer =&gt; $CGI-&gt;get('answer'),
   });
   $t-&gt;addHiddenForm({ Command =&gt; 'Commit' });
   $t-&gt;flush;
 }
 
 sub DoCommit {
   my $error = &amp;CheckQuestionForm; # 入力内容をチェック
   if($error) {
     &amp;DispForm($error); # エラーメッセージと共にフォームを再表示
                        # 通常は確認済みなのでここには来ない
   } else {
     &amp;SaveQuestionForm; # DBに保存
     &amp;DispEnd;
   }
 }
 
 sub DispEnd {
   my $t = $TL-&gt;newTemplate('end.html');
   $t-&gt;flush;
 }</code></pre>

<h2><a id="SQLXe3X82X92Xe8Xa8X98Xe8XbfXb0Xe3X81X99Xe3X82X8bXe5Xa0Xb4Xe6X89X80">
<span class="pod_lang">SQLを記述する場所

</span></a></h2>

<p>
<span class="pod_lang">基本的に、SQLは都度コードに記述することを推奨します。

</span>
</p>

<p>
<span class="pod_lang">SQLの発行をモジュール化するのは、複雑なSQLを発行する必要がある場合や、特定の手順で複数のSQLを発行しなければならない場合など、限定した利用にして下さい。

</span>
</p>

<p>
<span class="pod_lang">TripletaiL の DBクラスは、SQLの中に、SQLが記述されたファイル名と行番号をコメントで埋め込む機能があります。
この機能を利用すると、DBの負荷が高い場合に mysqladmin processlist の一覧を見て、負荷が高い場所をすぐに絞り込むことが可能です。

</span>
</p>

<p>
<span class="pod_lang">なるべくモジュール化しない方針としているのは、次のような理由からです。

</span>
</p>

<ul>
<li>
<p>
<span class="pod_lang">DBのデバッグ機能や、SQL の中にコメントでコードのファイル名・行番号を埋め込む機能などが有効に活用できなくなります。

</span>
</p>

</li>
<li>
<p>
<span class="pod_lang">モジュールに切り出すと、処理を追いかける場合に複数ファイルを閲覧する必要があります。TripletaiL ではエラー発生時にメール通知する機能がありますが、その場合に問題が起きたファイルから、素早く問題箇所を特定しにくくなります。

</span>
</p>

</li>
<li>
<p>
<span class="pod_lang">障害が発生した際などに、SQLを修正するときの影響範囲が広くなります。複数箇所から呼び出されている場合、共通の処理を修正するのがよいのか、問題が起きているプログラムからの呼び出しだけ別の処理に分けるのが良いのか、判断するのに時間がかかります。それぞれ直接SQLを記述している場合は、まず問題が起きている場所を修正することができ、他の同様箇所を直すかは後で検討することができます。

</span>
</p>

</li>
</ul>

<h2><a id="DBXe3X81XaeXe3X83X88Xe3X83Xa9Xe3X83Xb3Xe3X82Xb6Xe3X82XafXe3X82Xb7Xe3X83Xa7Xe3X83Xb3">
<span class="pod_lang">DBのトランザクション

</span></a></h2>

<p>
<span class="pod_lang">DBのトランザクションは、CGIの中で開始・終了することを推奨します。

</span>
</p>

<p>
<span class="pod_lang">CGIの中で、$DB-&gt;tx メソッドを使って下さい。

</span>
</p>

<p>
<span class="pod_lang">モジュールの中からSQLを発行する場合も、モジュール内では tx を使用しないようにします。

</span>
</p>

<p>
<span class="pod_lang">また、モジュールの中の SQL が他の SQL と組み合わせて使われていることが分かっている場合、以下のように記述し、tx 内での利用を強制します。

</span>
</p>

<pre class="pod_verbatim"><code> sub insert {
   my $DB = $TL-&gt;getDB('DB');
   $DB-&gt;inTx() or die &quot;transaction required&quot;;
   $DB-&gt;execute($sql, $param...);
 }</code></pre>

<p>
<span class="pod_lang">例えば、ポイントを管理するモジュールと、商品を管理するモジュールがあり、以下のように100ポイントで商品を購入する処理があったとします。

</span>
</p>

<pre class="pod_verbatim"><code> Point::spend($userid, 100);
 Goods::get($userid, '商品名');</code></pre>

<p>
<span class="pod_lang">このような利用をする場合、上記2つの呼び出しが同一トランザクションに入る必要がありますので、
Point::spend や Goods::get メソッドの実行に、トランザクションを要求するような記述をしておきます。

</span>
</p>

<p>
<span class="pod_lang">こうすることで、トランザクション制御を忘れて、DBの不整合を発生させてしまう可能性を低くすることが出来ます。

</span>
</p>

<p>
<span class="pod_lang">コードでは以下のようになります。

</span>
</p>

<pre class="pod_verbatim"><code> package Point;
 sub spend {
   my $DB = $TL-&gt;getDB('DB');
   $DB-&gt;inTx() or die &quot;transaction required&quot;;
   $DB-&gt;execute($sql, $param...);
 }
  
 package Goods;
 sub get {
   my $DB = $TL-&gt;getDB('DB');
   $DB-&gt;inTx() or die &quot;transaction required&quot;;
   $DB-&gt;selectAllHash($sql, $param...);
 }
  
 package Cgi;
 sub buyGoods {
   $DB-&gt;tx(sub {
     Point::spend($userid, 100);
     Goods::get($userid, '商品名');
   });
 }</code></pre>

<p>
<span class="pod_lang">このように書いておくと、トランザクションを使用し忘れ、以下のように書くとエラーになります。

</span>
</p>

<pre class="pod_verbatim"><code> package Cgi;
 sub buyGoods {
   Point::spend($userid, 100);
   Goods::get($userid, '商品名');
 }</code></pre>

<h2><a id="Xe3X82XbbXe3X83X83Xe3X82Xb7Xe3X83Xa7Xe3X83Xb3Xe7XaeXa1Xe7X90X86">
<span class="pod_lang">セッション管理

</span></a></h2>

<p>
<span class="pod_lang">TripletaiL では、セッションの機能を大幅に制限しています。
以下のような会員テーブルを用意し、会員のログイン認証に使うことを主目的に設計しています。

</span>
</p>

<pre class="pod_verbatim"><code> CREATE TABLE user_info (
   userid     INT      NOT NULL AUTO_INCREMENT,
   email      TINYBLOB NOT NULL,
   password   TINYBLOB NOT NULL,
   username   TINYBLOB NOT NULL,
   deletedate DATETIME NOT NULL DEFAULT 0,
   PRIMARY KEY (userid),
   UNIQUE KEY (email(255), deletedate)
 ) TYPE = InnoDB;</code></pre>

<p>
<span class="pod_lang">※deletedateは会員削除時にnow()にUPDATE。
有効な会員なら0とする。

</span>
</p>

<p>
<span class="pod_lang">会員IDはINTもしくはBIGINT型とすることを推奨しています。

</span>
</p>

<p>
<span class="pod_lang">会員IDに関連した情報を他にも多数保存することになるかと思いますが、
その際に主キーが文字列型などであると、JOINの速度が低下したり、
インデックスのサイズが大きくなり、パフォーマンスが落ちるためです。

</span>
</p>

<p>
<span class="pod_lang">また、セッションの機能では userid に相当する内容のみしか
格納することが出来ないようになっています。

</span>
</p>

<p>
<span class="pod_lang">これは、セッションの中にデータを安易に保存すると、以下のような
問題が発生する可能性があるためです。

</span>
</p>

<ul>
<li>
<p>
<span class="pod_lang">セッション情報はアクセスのたびに参照されるため、
セッションのサイズが大きくなると、パフォーマンスが
大きく低下します。

</span>
</p>

</li>
<li>
<p>
<span class="pod_lang">入力画面→確認画面→完了画面、のような画面遷移をする際に、
入力内容をセッションに保存するような利用をされてしまいます。

</span>
</p>

<p>
<span class="pod_lang">このような使い方をすると、複数のブラウザウィンドウ・タブで
利用したときに、正しく遷移できないことがあります。

</span>
</p>

</li>
<li>
<p>
<span class="pod_lang">セッション情報を制限することで、セッションテーブルが
固定長になります。

</span>
</p>

<p>
<span class="pod_lang">これにより、MySQL の MyISAM エンジンを利用する場合、
フォーマットが Fixed になり、高速なアクセスが可能になります。

</span>
</p>

</li>
</ul>

<h2><a id="CGIXe5Xa4X89Xe6X95Xb0">
<span class="pod_lang">CGI変数

</span></a></h2>

<p>
<span class="pod_lang">TripletaiL では、CGIが受け取ったフォームの内容が $CGI で常に参照できます。

</span>
</p>

<p>
<span class="pod_lang">必要のない限り、関数の引数に渡して別の名前で受け取るなどせず、
常にこの変数 $CGI を参照することを推奨します。

</span>
</p>


<hr />
<h1><a id="AUTHOR_INFORMATION">
<span class="pod_lang">AUTHOR INFORMATION

</span></a></h1>

<ul>
<p>
<span class="pod_lang">Copyright 2007 YMIRLINK Inc.

</span>
</p>

<p>
<span class="pod_lang">This framework is free software; you can redistribute it and/or modify it under the same terms as Perl itself

</span>
</p>

<p>
<span class="pod_lang">このフレームワークはフリーソフトウェアです。あなたは Perl と同じライセンスの 元で再配布及び変更を行うことが出来ます。

</span>
</p>

<p>
<span class="pod_lang">Address bug reports and comments to: tl@tripletail.jp

</span>
</p>

<p>
<span class="pod_lang">HP : http://tripletail.jp/

</span>
</p>

</ul>

<!-- End CONTENT -->

<div class="pod_title_block">
policy 設計方針


</div>

<!-- Begin INDEX -->
<hr />
<h1><a id="INDEX"><span class="pod_lang">INDEX</span></a></h1>
<div class="pod_idx_outer">
<ul class="pod_idx">
<li><a href="#AUTHOR_INFORMATION">AUTHOR INFORMATION</a></li>
<li><a href="#CGIXe5Xa4X89Xe6X95Xb0">CGI変数</a></li>
<li><a href="#DBXe3X81XaeXe3X83X88Xe3X83Xa9Xe3X83Xb3Xe3X82Xb6Xe3X82XafXe3X82Xb7Xe3X83Xa7Xe3X83Xb3">DBのトランザクション</a></li>
<li><a href="#DESCRIPTION">DESCRIPTION</a></li>
<li><a href="#NAME">NAME</a></li>
<li><a href="#SQLXe3X82X92Xe8Xa8X98Xe8XbfXb0Xe3X81X99Xe3X82X8bXe5Xa0Xb4Xe6X89X80">SQLを記述する場所</a></li>
<li><a href="#Xe3X82XbbXe3X83X83Xe3X82Xb7Xe3X83Xa7Xe3X83Xb3Xe7XaeXa1Xe7X90X86">セッション管理</a></li>
<li><a href="#Xe9X96Xa2Xe6X95Xb0Xe3X81XaeXe5X88X86Xe3X81X91Xe6X96Xb9">関数の分け方</a></li>
</ul>
</div>
<!-- End INDEX -->

<div class="pod_title_block">
policy 設計方針


</div>

</body>
</html>