3

ビジネス ロジック全体を改善し、コードの重複を避けるために、クライアントの Web サイトの 1 つをゆっくりと、しかし確実にリファクタリングしてきました。私が取り組んできた主な分野の 1 つは、基本的なショッピング カート アプリケーションの製品です。

  1. データベースに対する SQL クエリを使用したアイテム検索
  2. 商品詳細テンプレート
  3. かごの中身を見る
  4. チェックアウト (住所を入力、注文を確認、送信)

製品に関する多くのビジネス ルールがあります。たとえば、次のとおりです。

  • 閲覧可能な商品
  • 特定のユーザーが表示できる製品
  • 注文可能な製品
  • 各製品はどの形式で注文できますか

何年もの間、これらのルールはすべて Web サイト全体で複製されていました。一部は SQL クエリで、一部はアプリケーション ロジックで、一部は両方で、一部のページでは実装方法が異なっていました。実際、一部のルールは特定のページに実装されていません (設計によるものではありません)。

異なるルールを持つ新しいサブアプリケーションも作成されました。

そのため、製品が表示される場合は常に、アプリケーション ロジック (つまり、Product.isViewable()、Product.isOrderable() など) のすべてのビジネス ルールを含む "Product" クラスの使用を実装しました。

これにより、Product クラスを拡張することにより、そのアプリケーション固有のルールを使用して、各サブアプリケーションで Product データを使用できるようになります。

私が現在抱えている唯一の問題は、ColdFusion アプリケーション ロジック (if/else など) と SQL 条件の組み合わせを使用して、データベースにクエリを実行し、多くの「古い」ビジネス ルールを含むアイテム検索ページです。SQL クエリのスニペットを次に示します。

            WHERE   (
                    LTRIM(RTRIM(UCASE(STATUS))) = 'ACTIVE'
                    OR UCASE(VIEWABLE_IF_RETIRED_FLAG) = 'Y'
                    )
            <cfif SESSION.User.getSecurityLevel() LT 5>
                AND (
                    UCASE(ORDERABLE_FLAG) = 'Y'
                    OR UCASE(ELECTRONIC_ORDERABLE_FLAG) = 'O'
                    OR UCASE(ELECTRONIC_ORDERABLE_FLAG) = 'V'
                    )
            </cfif>

これらと同じルールが Product クラスに実装されています。

    function isVisibleIfRetired() {
        return getVisibleIfRetiredFlag() == "Y";
    }

    function isActive() {
        return getStatus() == "ACTIVE";
    }

    function isDigitalViewable() {
        return  UCase(getIsProductOrderableFlag()) == "Y"
            &&  (
                    UCase(getElectronicOrderableStatus()) == "V"
                ||  UCase(getElectronicOrderableStatus()) == "O"
                );
    }

これは私を夢中にさせます。なぜなら、それは依然として本質的に重複したコードであり、ビジネス ルールが変更された場合に両方の部分を管理する必要があるからです。

一部のアイテムのみを表示する場所 (「類似アイテム」ウィジェットや「カートの表示」など) があり、単純にデータベースからプロダクト キーを選択し、それらの値をループしてProduct インスタンスを作成し、クラスのビジネス ルールを使用して、製品を表示するかどうか、または表示する方法を決定します。

ただし、アイテム検索クエリは最大 2000 件のレコードを返すことができ、2000 件のレコードをループして 2000 件のインスタンスを作成し、ビジネス ルールに基づいて表示を決定することは [現実的に] できません。

何か案は?

4

2 に答える 2

2

考えてみてください: ルール インターフェイスを作成するとしたらどうなるでしょうか。IE は、ルールを記述した一連の CFC です。それらがすべて同じインターフェイスを (明示的またはダック タイピングによって) 実装している場合、データ アクセス オブジェクトを、Product クラスなどの他のクラスでデコレートできます。

これらのルールの API は (疑似コードで) 次のようになります。

component name="product is visible"{

    function ruleAsSqlFragment(){
        // return a chunk of SQL here that you can drop right into your query and would cause the query to be filtered as you wish.
        return "LTRIM(RTRIM(UCASE(STATUS))) = 'ACTIVE'
                OR UCASE(VIEWABLE_IF_RETIRED_FLAG) = 'Y'";
    }

    function validateRule(object){
        // this function might accept an object, validate it according to its rules and return true or false
        return object.isActive() or object.getViewableIfRetired()
    }

}

次に、オブジェクトを装飾するこれらのルールの配列を作成できます。

rules = [isVisibleRule,isSomethingElseRule,etc];

オブジェクト内では、単純に配列をループし、適切な関数を呼び出してルールを評価するか、正しい SQL フラグメントを生成することができます。関数または SQL 構文が true と評価された場合にのみ、ルールが満たされます。

全体として、SQL に対する従来のアプローチを使用して、ビジネス ロジックの重複を完全に回避できるかどうかはわかりません。結局、両者 (SQL と CF) は異なる言語です。

ドメイン固有言語 (DSL) を定義し、それを使用してルールを定義するという別のオプションがあると思います。次に、それらのルールを SQL に変換したり、オブジェクトに対して評価したりできるコンポーネントを作成します。次に、DSL で定義された特定のルールを正しい方法でコンポーネントに関連付け、必要に応じて評価する方法を見つけます。

もう 1 つの課題は、データベースがオブジェクト モデルを完全に反映していないように見えることです。これは非常に一般的です。これが事実である場合、このロジックを可能な限り分離する必要があると確信しています。

于 2013-01-14T17:46:06.727 に答える
1

これらのビジネス ルールの自動テスト (単体テストなど) から始めます。テストを聞くと、テストはオブジェクト モデルを提案します (たとえば、テストを作成するときに現在の実装を無視するなど)。

これらのビジネス ルールを実装するために必要な CFC がいくつか (場合によっては複数) 見つかる可能性があります。テストが存在するかのように記述します。

各ルールには正規の実装が必要です。つまり、ルールで表示可能な製品を決定する場合、1 つの CFC メソッドでそれを決定する必要があります。そのメソッドは、CFML、SQL、他の CFC メソッドの呼び出しなどを内部的に使用する場合があります。ポイントは、そのルールを使用する必要があるすべてのものは、そのメソッドを呼び出してそれを行うということです。テストでは、そのメソッドをテストして、ルールが正しく守られているかどうかを確認します。

自動テストの作成を開始していない場合は、MXUnit をチェックして、TDD または BDD について少し読んでください。 http://en.wikipedia.org/wiki/Behavior-driven_development

于 2013-01-15T14:59:46.213 に答える