4

アプリケーションのモンスターを継承しました。多くのお客様にご利用いただいている注文処理サイトです。古くて時代遅れで、深刻な更新が必要です。最も難しいのは、最初は 1 種類のクライアントを念頭に置いて作成されたものであり、その後、新しいクライアントが追加されると、基本的にクライアントごとに機能をオンまたはオフにする何百もの IF ステートメントでコードが荒廃したことです。次のようなものを想像してください (ColdFusion で記述):

<cfif clientId EQ "MIKE">
    <a href="cart1.cfm">Shopping Cart</a>
<cfelseif clientId EQ "JOE">
    <a href="cart2.cfm">Shopping Cart</a>
<cfelseif clientId EQ "BILL"
    OR clientId EQ "JILL"
    OR clientId EQ "RAY">
    <a href="cart3.cfm">Shopping Cart</a>
<cfelse>
    <a href="cart.cfm">Shopping Cart</a>
</cfif>

上記は、私がウェブサイト全体で扱っているもののきれいなバージョンです。

そのため、クライアント固有の構成を簡単にできるように Web サイトをリファクタリングしようとしています。基本的には、クライアントごとに機能を提供または非表示にします。

私はシンプルでクリーンな解決策だと思っていたものから始めましたが、維持するのが面倒になるのではないかと心配しています。

基本的に、私は各クライアントに、ファイルが保存されている Web ルートから離れたディレクトリを提供します。おそらく、Web サイトで使用されるドキュメントなどです。このディレクトリは、C:\clients\MIKE\. このディレクトリに、xml ファイルを保存します。これを config.xml と呼びましょう。最初に作成した config.xml ファイルの内容は次のとおりです。

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <cart>
        <url>cart1.cfm</url>
    </cart>
</root>

そのため、すべてのページ リクエストに対して、xml を探します。存在する場合は、各値をClientクラス インスタンスのプロパティにコピーします。

<cfcomponent
    hint="Represents configurable per-Client settings stored in a local Xml file">
    <cfscript>
        VARIABLES.CartLink = "";
    </cfscript>

    <cffunction name="init" return="Client" output="false">
        <cfargument name="clientId" type="String" required="true" hint="i.e. 'MIKE'"/>

        <cfscript>
            var _clientXml = XmlNew();

            THIS = setClientId(ARGUMENTS.clientId);

            _clientXml = read();

            if  (   StructKeyExists(_clientXml.XmlRoot, "cart")
                &&  StructKeyExists(_clientXml.XmlRoot["cart"], "url")
                )
                setCartLink(_clientXml.XmlRoot["cart"]["url"].XmlText);

            return THIS;
        </cfscript>
    </cffunction>

    <cffunction name="getCartLink" returntype="String" output="false">
        <cfreturn VARIABLES.CartLink />
    </cffunction>

    <cffunction name="setCartLink" returntype="Void" output="false">
        <cfargument name="cartLink" type="String" required="true" />

        <cfset VARIABLES.CartLink = Trim(ARGUMENTS.cartLink) />
    </cffunction>

    <cfscript>
        function getXmlFilePath() {
            return APPLICATION.ClientFilePath
                & "\" & getClientId() & "\config.xml";
        }
    </cfscript>

    <cffunction name="read" access="public" output="false" returntype="xml">
        <cfscript>
            var _clientXml = XmlNew();
            var _fileContents = "";

            _clientXml.XmlRoot = XmlElemNew(_clientXml, "root");

            if  (FileExists(getXmlFilePath()))
                _fileContents = FileRead(getXmlFilePath());

            if  (IsXml(_fileContents))
                _clientXml = XmlParse(_fileContents);

            return _clientXml;
        </cfscript>
    </cffunction>
</cfcomponent>

上記のサンプル xml を使用すると、ユーザーが「MIKE」クライアントでログインしている場合、REQUEST スコープの Client インスタンスのプロパティの値は「cart1.cfm」になりますcartLink

これで、単純にその値を探して、それを使用してアンカー タグを設定できます。

<a href="#REQUEST.Client.getCartLink()#">Shopping Cart</a>

私の目標は、コードをきれいに保ち、編集が必要な数百の IF ステートメントを回避することです。

しかし、今このデザインを見ると、これがさらにメンテナンスの悪夢になる可能性があることに気付きました。Web サイトには現在、約 30 ~ 40 のクライアントがあります。したがって、現時点では、30 ~ 40 個の xml ファイルを維持する必要があり、構成プロパティごとに任意の数のノードが必要です。さらに、新しい機能が追加されるたびに、新しいプロパティの新しいゲッター/セッター メソッドでクライアント クラスを更新する必要があります。

これを悪化させたくありません。どんな考えでも大歓迎です。

4

5 に答える 5

4

まず、ColdFusionを知らないので、コードを提供できません。

ただし、説明している問題は、一般に「マルチテナント」アーキテクチャとして知られています。アプリケーションが説明したプロセスを経て、これらの機能を遡及的にハッキングすることは珍しいことではありません。うまく終了することはめったにありません!ただし、用語をグーグルで検索すると、多少の喜びが得られる場合があります。

私の経験では、複雑さをどこに置くかを選択する必要があります。現在、それはコード内にあり、それが住むのに最悪の場所です。複雑なコードは、保守が難しく、バグが多く、変更/拡張が難しく、開発者を怒らせます。

データ/構成として複雑さを管理することは、はるかに優れた方法です。コードベースの複雑さを劇的に軽減し、一般的に管理が容易になります。あなたはこの道の最初の一歩を踏み出しましたが、私はそれを少し洗練させたいと思います。

まず、「設定より規約」の概念を紹介します。したがって、使用するカートの例では、ソリューション内のすべての可変要素にクライアントIDで名前を付けることを検討します。したがって、「cart3.cfm」の代わりに「cartMike.cfm」になります。これにより、XMLの変更の量が大幅に削減されます。

次に、デフォルトの概念を導入したいと思うかもしれません。これらのほとんどの場合、設定の80%はクライアント間で共通であり、クライアント固有の設定が必要なのは20%のみです。これらすべての設定を管理するのではなく、デフォルトを導入します。クライアント固有のデフォルトがない場合は、デフォルトファイルにあるものを使用します。

第三に、クライアントの種類(ゴールド/シルバー/ブロンズ、フリー/ SME /企業など)を紹介することをお勧めします。これには、これらのクライアントタイプごとにデフォルト構成を作成する必要がありますが、デフォルト構成ファイルのヒット率が高くなる可能性があります。

最後に、アプリケーションのライフサイクルを検討する必要があります。新しいクライアントのプロビジョニングプロセスはどうあるべきですか?それは主に技術的なタスクですか?その場合、バージョン管理で簡単に管理できるXMLファイルを引き続き使用します。それがより「ビジネス」タスクである場合は、よりユーザーフレンドリーなプロセスにラップする必要がありますが、構成を検証できる必要があります。たとえば、「cart」の構成設定が「/Mike/cart.cfm」の場合、ファイルが実際に存在することを確認する必要があります。おそらく、バージョン管理と、開発者からテスト、実稼働環境に移行するためのメカニズムも必要です。

于 2012-10-19T13:56:20.267 に答える
1

XMLファイルよりもDBにデータを保存したいです。ファイルを大量に更新するよりも、更新する方が簡単です (CMS を構築して自分で行うことができます)。

次に、次のように、すべてを大きな構造体として保存したいと思います。

stuClient = {
  cart_url = "cart1.cfm",
  other_url = "other2.cfm",
  ...
}

また、全体をセッション スコープに格納したいので、ページ リクエストごとに DB / XML ファイルを再クエリする必要はありません。クライアントIDを保存するためにすでにセッションを使用していると思います。

したがって、最終的にできることは、onApplicationStart で、すべてのクライアントのすべてのデータを 1 回取得し、それをアプリケーション スコープに格納することです。この部分は実際にはオプションです。なぜなら、onSessionStart で、このクライアントに関連するデータのほんの一部を取得し (アプリケーションに格納して開始した場合はおそらくクエリのクエリ)、それを構造体に変換するためです。

したがって、コードでは、必要に応じて stuClient.cart_url を参照するだけです。リクエストごとに CFC を初期化するのではなく、セッションごとに初期化します。データ構造で初期化されます。

次に、各ページで、次のようなものを使用できます<a href="#Client.getLink('cart_url')#">

 <cffunction name="getLink" returntype="String" output="false">
        <cfargument name="linktype" type="String" required="true" hint="e.g. 'cart_url'"/>

        <cfreturn VARIABLES.stuClient.CartLink>
    </cffunction>
于 2012-10-19T13:45:24.073 に答える
0

他の人が説明したことの例を示します。ここでの最も簡単な解決策は、これらすべての条件を、「設定」ごとの単純なメソッド呼び出しに置き換えることです。これは、あなたの状況に対処するための、実証済みの実証済みの手法です。セッションを使用していない場合でも、より基本的な方法でデータベースにアクセスするたびに、データベースにアクセスせずにデータをキャッシュまたは使用可能にする方法がいくつかあります。以下に、application.cfc、settings.cfc、およびテンプレートがあります。

//In your application.cfc onSessionStart (hopefully you are using sessions)
session.settings = createObject("component", "settings").init(clientID);

//In your settings.cfc 
//saving keystrokes with cfscript style functions
<cfscript>
function init(clientID){
   variables.clientID = arguments.clientID;
   setAllUserSettings();
   return this;
} 

function setAllUserSettings(){
   //query the database for a particular user 
   //you can keep the query in memory or set each row value to something
   //let's say you wanted to use the QoQ method
   variables.settings = qryAllUserSettings();       
}
</cfscript>

<cffunction name="getSetting">
    <cfarguments name="type" />
    //start query of query to get a particular setting
    //for our user
    <cfquery name="qry" dbtype="query">
         select value
         from variables.settings
         where setting = #arguments.type#
    </cfquery>

    <cfreturn qry.value />
</cffunction>

<cffunction name="qryAllUserSettings">
   <cfset var qry = "" />
   //we'll get every possible setting for this user
   <cfquery name="qry" datasource="dsn">
        select setting,value
        from tblSettings
        where clientID = variables.clientID
   </cfquery>
   <cfreturn qry />
</cffunction>

//In your ColdFusion Template
<a href="#session.settings.getSetting(type="cart")">Shopping Cart</a>
于 2012-10-19T15:43:51.593 に答える
0

前に述べたことを繰り返しますが、データベースに情報を保持することについて説明します。ColdFusion を使用すると、複数の Application.cfc ファイルまたは .cfm ファイルで同じ基本コードを使用できる独立したアプリケーションを作成できます。必要なのは、各ファイルに一意の this.name 変数があり、それが別の場所にあることを確認することだけです。クライアント フォルダ。次に、アプリケーションの起動時にクライアント固有の設定を読み取り、それらをアプリケーション スコープにキャッシュします。

ColdFusion (特にエンタープライズ バージョン) はまさにこの目的のために設計されており、オンラインで役立つ情報がたくさんあります。複数のアプリケーションを管理する方法と、application.cfc がこれを行うのにどのように役立つかについて調べてみてください。

于 2012-10-21T10:41:21.893 に答える